Security
:::info Source
Sourced from services/progress-service/SECURITY_MODEL.md in the documentation repo.
:::
1. Authentication
- JWT for internal callers.
X-API-Keyfor xAPI 3rd-party integrations (per-tenant keys; scopesxapi:read,xapi:write).- mTLS for inter-service NATS subscription (service identity
spiffe://ghasi/prod/progress-service).
2. Authorization
progress:statement:write— delivery, assessment, LRS clients.progress:statement:read— admins, compliance officers, self-reads (learner can read own).progress:transcript:read— learner (own), manager of learner's org unit, compliance officer.- ABAC:
actor.account.name == ctx.user.idfor self-read allowance.
3. Multi-Tenant Enforcement
| Layer | Rule |
|---|---|
| API | X-Tenant-Id matches JWT tid |
xAPI agent filter | Server narrows to caller's tenant |
| Postgres RLS | USING (tenant_id = current_setting('app.tenant_id')::uuid) |
| Partitioning | Tenant hash sub-partitions for hot months |
| Analytics firehose | Tenant-tagged Kafka messages |
4. Data Classification
- Restricted — statements containing PHI (healthcare tenants), quiz responses in
activity_state. - Confidential — attempts, completion records, transcripts.
- Internal — event metadata.
5. Encryption
- At rest: Postgres TDE + AES-256 on storage layer.
- In transit: TLS 1.3 + mTLS internal.
- HIPAA tenants: per-tenant DEK envelope encryption on
statements+activity_state.
6. Audit Logging
- Every read of another user's statements logged to audit table.
- Every xAPI query with
agentfilter logged. - Daily Merkle-anchored root.
7. xAPI Conformance Security
- Signed statements (xAPI extension
http://id.tincanapi.com/extension/jws): signature verified; actor binding checked. - Statements from 3rd-party LRS via xAPI push: source IP allowlisted + API key scoped.
- Reject statements with
authority.account.homePagenot in allowlist (tenant config).
8. Threat Model
| Threat | Mitigation |
|---|---|
| Forged statement (pretending to be user) | Actor verified against JWT sub; S2S signed with service identity |
| Cross-tenant statement injection | Tenant assertion via JWT tid + RLS |
| Statement tampering at rest | Merkle-anchored audit log detects changes |
| Completion record forgery | Derived server-side from statements with passed verb; evidence statement IDs listed |
| Analytics data exfiltration | Per-tenant Kafka topic ACLs; warehouse RLS |
| GDPR erasure bypass | Mandatory participation in erasure saga; replay test validates completeness |
9. GDPR Participation
- Receives
gdpr.subject_request.received.v1. - Deletes all statements, attempts, completion records for user.
- Retains anonymized aggregates (subject to tenant policy + legal requirement).
- Legal hold overrides erasure for tax-relevant data (rare for progress; mostly applies to billing).
- Emits
gdpr.subject_request.acknowledged.v1within 7 days. - Replay test in CI: after erasure, transcripts for user return empty; aggregates show anonymized record.
10. Compliance
- SOC 2: statements audit log, access logs, quarterly access reviews.
- HIPAA: progress data for healthcare tenants is PHI; per-tenant key + BAA with providers.
- FERPA: transcript access requires consent chain recorded in audit log.
- xAPI spec: conformance-tested via ADL test suite; certificate maintained.
11. Secrets Management
- DB credentials rotated quarterly via KMS.
- API keys hashed (argon2id).
- Raw keys shown once at creation; never logged in full.