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