What each piece is, plainly. Three grades:Solid — does what it says, tested, safe to depend on within the line
its page draws.
Scaffold — the shape is right and the API is stable, but a part you
would need in production is yours to supply: persistence, a scheduler, a
real hash.
Simulated — logs and returns success without doing the thing. A
register to write real results into, not a control.
The source file for each is in src/compliance/; the per-file notes in its
README
are the authority if this table and it ever disagree.Declaration#
| Piece | Grade | Notes |
|---|
JURISDICTIONS, resolve() | Solid | 71 regimes, consistency-tested; dated (AS_OF); a reference table, not legal advice |
@Jurisdiction, @Personal, @DataSubjectAge, @ParentalConsent, applyCompliance() | Solid | refusals, the age gate and retention registration are tested end to end on sqlite |
complianceSheet(), renderComplianceSheet() | Solid | |
@Security and the framework table | Scaffold | audit, classification, lineage, immutability, session isolation, WORM and rate-limit registration are applied; encryption, masking, retention and RLS need your field lists; consent, tls, pseudonymize, breachDetection, backupVerification, queryFirewall, crossBorderLog and the dsar/pia/consentVersioning/sensitiveDataDiscovery options wire nothing. setupSecurity() now says so — one warning per model naming each and what to call instead (onUnwired: 'warn' | 'throw' | 'ignore') — and securityWiring(Model) separates wired controls from flags, so a flag is never read as evidence. See Frameworks |
@TableSecurity, @DatabaseSecurity | Solid as metadata | nothing enforces them |
The Scaffold grade on @Security is checkable per model rather than taken
on trust. A boot-time assertion keeps a flag from standing in for a control:import { securityWiring } from 'ts-prorm-orm';
declare const models: Array<{ name: string }>;
for (const Model of models) {
const { flagOnly } = securityWiring(Model);
if (flagOnly.length) console.warn(`${Model.name}: recorded, not enforced: ${flagOnly.join(', ')}`);
}
Protection#
| Piece | Grade | Notes |
|---|
FieldEncryption | Solid, narrow | real AES-256-GCM; enc:v2:<keyId>: with a keyring for rotation (enc:v1: still read); scrypt for a passphrase with a stored salt — an unsalted passphrase still gets the old single SHA-256 and a warning; keys in process memory, no KMS; string fields only |
DataMasker | Solid | a display control; the value is still in the result before the hook |
PseudonymizationService | Solid, with a caveat | HMAC is real; reversibility is only as good as the mapping store you configure |
DataClassifier | Solid | metadata only |
SensitiveDataDiscovery | Scaffold | column names only; sample analysis is declared and not implemented |
RowLevelSecurity, SessionIsolation | Scaffold | the filter covers every model read, write and insert (not raw query()); register with { requireContext: true } to fail closed; setContext() does not propagate through AsyncLocalStorage as expected — use runInContext(); initRLS() / clearContext() are no-ops; per-tenant keys registered here are not used by encryption. Prefer the database's RLS |
TLSEnforcer | Solid, narrow | the config check flags TLS that does not verify the certificate (rejectUnauthorized: false, trustServerCertificate: true, weak sslmode) and an optional minVersion; verifyRuntime() asks the server whether the session is encrypted (PostgreSQL, MySQL/MariaDB, SQL Server; unknown elsewhere) |
QueryFirewall | Solid, narrow | regex over prorm.query only; defence in depth, never the injection defence |
RateLimiter | Scaffold | per-minute enforced; hourly/daily tracked and not checked; persistence scaffolded and unused |
Subject rights#
| Piece | Grade | Notes |
|---|
RightToErasure | Solid | knows nothing of backups |
DataPortabilityExporter | Solid | JSON / CSV / NDJSON; streams |
DSARWorkflow | Scaffold | the state machine is right; the request store is an in-memory Map |
ConsentRecord | Solid | append-only |
ConsentVersioning | Solid | a second, uncoordinated consent table — pick one |
PrivacyImpactAssessment | Solid as a record | severity is caller-assigned |
Retention and integrity#
| Piece | Grade | Notes |
|---|
DataRetentionPolicy | Solid | no scheduler; approximate month/year arithmetic |
ImmutableRecord | Solid | hooks — binds ORM traffic; REVOKE at the database too |
WORMStorage | Scaffold | the state machine and tamper log work; the chain hash is SHA-256 over a canonical (sorted-key) record, and verifyIntegrity() checks the chain linkage and re-hashes the current record against the last entry. Still application-level: a direct edit of both the record and its log rows is not caught — use object-lock or a ledger store for storage-level WORM |
ComplianceAuditTrail | Solid | a wrapper over AuditLogger |
DataLineage | Solid | |
BackupVerification | Scaffold | for filesystem backups the checksum is a real SHA-256 of the file and encryption is a plaintext heuristic; for cloud sources both are inconclusive unless you supply computeChecksum. testRestore() only confirms the staging database is reachable over TCP and reports the row-count snapshot — it does not restore anything |
BreachDetector, SecurityMonitor | Scaffold | heuristics work; in-memory alerts; sees one process |
CrossBorderLog | Solid, with a caveat | the check and the log work; autoLog does not log automatically — call logTransfer() |
Everything on the platform policies page is
running in production and holds itself to a test that every one of the 150
names an event that is emitted and an action that is implemented. The parts
that are scaffolds in the library — backups, restore testing, credential
rotation, failed-login response — are real operations there.Two things that are true of all of it#
[!IMPORTANT]
None of this is certified. No auditor has signed a report saying that
using this library makes a system SOC 2 or HIPAA compliant, and none will:
those reports are about a system and an organisation, not a dependency.
What the library gives you is controls that are declarative, checkable and
enforceable at the schema, and documentation that says exactly where each
one stops.
The database's own control is always better. RLS policies, GRANTs,
TLS at the connection, encryption at rest, the engine's audit log: each binds
every client that ever connects, where the library's binds only code that
goes through it. Reach for the engine's first and use these to carry what the
engine cannot see — the user id behind a connection, the lawful basis for a
column, the age of the person in the row. Modified at 2026-10-04 00:40:16