Skip to main content

Overview

:::info Source Sourced from services/content-service/SERVICE_OVERVIEW.md in the documentation repo. :::

Companion: 03 content-service · 02 DDD · 13 Security · 04 Event-Driven

1. Purpose​

The content-service is the Content-Packaging authority within the Ghasi-edTech platform. It transforms authored course content into immutable, signed, runtime-ready PlayPackages, produces encrypted per-device Bundles for offline consumption, and drives SCORM/HTML5/xAPI export pipelines. The Player runtime treats PlayPackages as read-only artifacts; all build-time logic lives here.

2. Bounded Context​

Content-Packaging — classified as Supporting domain. Downstream of Authoring; upstream of Catalog, Delivery, Sync, and the Player runtime.

Why Supporting, Not Core​

Content-Packaging is essential but not differentiating. The platform's competitive advantage lies in authoring intelligence and adaptive learning paths (Core domains). Packaging is a well-understood transformation pipeline whose value comes from correctness and security, not novelty.

3. Responsibilities​

AreaWhat Content-Service Owns
PlayPackage buildTransforms a published course version into an immutable PlayPackage with manifest, pinned assets, navigation model, and AI config
Package signingJWS signature over the SHA-256 hash of all assets using tenant signing key
Offline bundlesAES-256-GCM encrypted per-device bundles with HKDF-derived keys
License envelopesSigned, time-bound, device-bound, feature-gated license tokens embedded in bundles
SCORM exportSCORM 1.2 and SCORM 2004 4th Edition zip generation with imsmanifest.xml
HTML5 exportStandalone HTML5 zip for LMS-less deployment
xAPI/cmi5 exportxAPI-compatible export with activity profiles
SCORM importSandboxed ingestion of third-party SCORM zips with manifest validation
Bundle revocationPermanent revocation of packages and bundles; propagated via events
Tamper detectionReceives client-reported hash mismatches; escalates via security events

4. Non-Responsibilities​

AreaOwnerWhy Not Content-Service
Content authoringauthoring-serviceAuthoring owns the draft lifecycle; content-service consumes the published snapshot
Media transcodingmedia-serviceContent-service references media assets by ID; it does not own transcoding
Course catalog / discoverycatalog-serviceCatalog projects PlayPackage metadata; content-service does not own listings
Enrollment decisionsenrollment-serviceContent-service reacts to enrollment events but does not decide who enrolls
Sync protocolsync-serviceSync-service owns delta pull/push; content-service registers bundles as sync entities
Runtime playbackdelivery-service / PlayerThe Player is a dumb, deterministic renderer of PlayPackage manifests
Key managementKMS (Vault/AWS KMS)Content-service uses KMS for signing and encryption but does not own key lifecycle

5. Dependencies​

5.1 Upstream Dependencies​

DependencyPatternPurpose
authoring-serviceEvent consumerConsumes authoring.course_draft.published.v1 to trigger PlayPackage builds
media-serviceACL (HTTP)Resolves asset references, fetches asset metadata and download URLs
identity-serviceEvent consumer + ACLConsumes identity.device.bound_for_offline.v1; fetches device public keys for bundle encryption
enrollment-serviceEvent consumerConsumes enrollment.created.v1 to trigger bundle creation
marketplace-serviceEvent consumerConsumes marketplace.license.revoked.v1 to trigger bundle revocation
KMS (Vault/AWS KMS)InfrastructureTenant signing keys, HKDF key derivation for bundle encryption
S3/R2InfrastructureArtifact storage for zips, bundles, and export outputs

5.2 Downstream Consumers​

ConsumerPatternWhat They Consume
catalog-serviceEvent consumerConsumes content.play_package.built.v1 to update catalog entries
delivery-serviceHTTP (ACL)Fetches PlayPackage manifests for runtime delivery
sync-serviceEvent consumerConsumes content.play_package.bundle.published.v1 to register bundles for device sync
notification-serviceEvent consumerConsumes export completion and tamper events for alerting
analytics-serviceEvent consumerConsumes all content events for build metrics and tamper reporting

5.3 Events Consumed​

EventProducerPurpose
authoring.course_draft.published.v1authoring-serviceTriggers PlayPackage build pipeline
enrollment.created.v1enrollment-serviceTriggers bundle creation for enrolled user's devices
identity.device.bound_for_offline.v1identity-serviceRegisters device for bundle encryption; triggers bundle creation if enrollment exists
marketplace.license.revoked.v1marketplace-serviceTriggers bundle revocation for affected enrollments
gdpr.subject_request.received.v1platform (cross-cutting)Participates in GDPR erasure saga for bundle and license data

6. Slice Involvement​

SliceScopeMilestone
S0 — Package Build PipelinePlayPackage build from published draft, manifest assembly, asset pinning, signing, basic GET APIsM1
S1 — Offline Bundles + License EnforcementPer-device encrypted bundles, license envelopes, revocation flow, tamper detectionM1
S2 — SCORM 1.2 ExportSCORM 1.2 imsmanifest.xml generation, zip packaging, SCORM Cloud validationM2
S4 — SCORM 2004 + xAPI ExportSCORM 2004 4th Ed, xAPI/cmi5 export, HTML5 standalone, SCORM import pipelineM3

7. Architectural Freeze Points​

FreezeWhat Is FrozenFrozen By
F05LicenseEnvelope schema — field set, signing algorithm, device-binding derivationStart of M1
F15PlayPackage.manifest schema v1.0 — module structure, navigation model, assistant config shapeStart of M1
F01EventEnvelope schema — all events conform to 04 Event-DrivenEnd of M0

After freeze, changes require architectural review and a new version with dual-publish window.

8. Service Readiness Levels​

LevelDescriptionTarget Milestone
L3Package build pipeline operational, bundle encryption working, basic monitoring, contract tests, automated failoverM1
L4Full SCORM/xAPI export, chaos-tested, production-grade observability, security pen-tested, SLO trackingM3

9. Architecture Diagram​

┌───────────────────────────────────────────────┐
│ API Gateway / Edge │
└──────────────────┬────────────────────────────┘
│
┌──────────────────▼────────────────────────────┐
│ content-service │
│ │
│ ┌─────────────┐ ┌──────────────────────┐ │
│ │ HTTP API │ │ NATS Subscribers │ │
│ │ (Inbound) │ │ (Event Consumers) │ │
│ └──────┬──────┘ └──────────┬───────────┘ │
│ │ │ │
│ ┌──────▼─────────────────────▼───────────┐ │
│ │ Application Layer │ │
│ │ │ │
│ │ BuildPlayPackage CreateBundle │ │
│ │ RevokePackage IssueLicense │ │
│ │ ExportSCORM ImportSCORM │ │
│ │ ExportHTML5 ExportXAPI │ │
│ │ DetectTamper HandleGDPR │ │
│ └──────────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────────▼────────────────────┐ │
│ │ Domain Layer │ │
│ │ │ │
│ │ PlayPackage (Aggregate) │ │
│ │ PlayPackageBundle (Aggregate) │ │
│ │ LicenseEnvelope (Value Object) │ │
│ │ PackageManifest (Value Object) │ │
│ └──────────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────────▼────────────────────┐ │
│ │ Infrastructure Layer │ │
│ │ │ │
│ │ PostgresRepo S3StorageClient │ │
│ │ NATSPublisher MediaClient (HTTP) │ │
│ │ KMSClient IdentityClient (HTTP) │ │
│ │ JWSSigner AESGCMEncryptor │ │
│ │ SCORMZipBuilder ManifestValidator │ │
│ └────────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
│ │ │
┌────────────┘ ┌──────┘ ┌──────┘
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Postgres │ │ S3 / R2 │ │ KMS │
│ (content) │ │ (artifacts│ │ (Vault) │
│ │ │ bundles) │ │ │
└───────────┘ └───────────┘ └───────────┘

10. Key Design Decisions​

10.1 PlayPackage Is Immutable After Build​

Once a PlayPackage reaches built status, it is never modified. A new course version produces a new PlayPackage. This ensures:

  • Deterministic Player behavior (same package = same experience)
  • Tamper detection via SHA-256 hash comparison
  • Safe caching at every layer

10.2 Bundle Per-(Enrollment, Device)​

Each offline bundle is encrypted for exactly one device. This enables:

  • Per-device key revocation without affecting other devices
  • License enforcement at the cryptographic layer, not just the application layer
  • Forensic tracing of any leaked content

10.3 Player Is Dumb, Service Is Smart​

All build-time logic (manifest assembly, asset resolution, navigation computation, AI config injection) lives in content-service. The Player runtime is a deterministic renderer that reads the manifest and plays assets. This makes the Player portable, testable, and lightweight.

10.4 Export Formats Are Separate Artifacts​

SCORM, HTML5, and xAPI exports are generated as separate FormatArtifacts on the PlayPackage, not transformations of the PlayPackage itself. This allows:

  • Independent generation schedules (S2 vs S4 slices)
  • Format-specific validation (SCORM Cloud, xAPI conformance)
  • Different retention and distribution policies per format