Overview
Framework-agnostic compliance policy used by the hosted AbsoluteJS.ai platform.
@absolutejs/compliancev0.7.0betaAuth & IdentityDurable messaging consent plus classification, residency, retention, SAR, erasure, and evidence primitives.
Framework-agnostic compliance substrate for AbsoluteJS: durable messaging-consent evidence and pre-send enforcement alongside declarative data classification, residency, retention, Subject Access Requests, erasure, and auditor evidence bundles. It composes with @absolutejs/dispatch and @absolutejs/audit without hard-coding a provider or framework.
bun add @absolutejs/complianceFramework-agnostic compliance policy used by the hosted AbsoluteJS.ai platform.
@absolutejs/compliance gives a control plane composable primitives. None of them know about a specific framework — SOC2, HIPAA, ISO 27001, and GDPR all map onto the same shape.
The messaging consent ledger records grant and revocation evidence at an exact tenant, recipient, program, purpose, and transport scope; its Dispatch policy denies unauthorized sends before the provider is called.
1. createCompliancePolicy({ classifications, tenantOverrides? })
Declarative shape. Each classification gets a stable id, retention window, optional residency region, optional erasureExempt flag, and an open flags bag.
2. createResidencyGuard(policy)
Pure check. The runtime, sync, queue, and blob layers call guard.check({ classification, region, tenant? }) before letting data move. Mismatches throw ResidencyViolation.
3. runRetention({ policy, scanners, deleters, audit?, ... })
Orchestrator. Each scanner streams expired records for a classification; each deleter removes them (batched). Per-scanner failures are isolated. Optional audit broker logs a 'compliance.retention.swept' event per class. dryRun: true counts without deleting.
4. runSubjectAccess({ subject, collectors }) + runErasure({ subject, erasers, ... })
Compose a "find / forget everything about user X" pipeline across packages. Each package provides a collector / eraser pair. The substrate runs them and returns a structured bundle.
runErasure automatically routes to eraser.anonymize for erasureExempt classifications (typical: anonymize audit-log subject references rather than delete the log itself). Records the erasure to audit if a broker is provided.
5. collectEvidence({ policy, period, sources })
Bundles per-source JSON evidence into a single structure an external auditor can read. Each source returns arbitrary JSON- serializable evidence for the period; the bundler doesn't interpret the shape. Ships with auditEvidenceSource(broker) for the typical "all audit events in the period" case.
A per-tenant override wins over the class default. GDPR-strict tenants riding a default-US-East platform get their own residency, retention, and erasure-exempt behavior without forking the policy.
createCompliancePolicy assigns each data classification a retention window, optional residency region, erasure exemption, and an open flags bag — with per-tenant overrides.
createResidencyGuard gives runtime, sync, queue, and blob layers a pure check before data moves; mismatches throw ResidencyViolation or return via a non-throwing inspect.
runRetention streams expired records through per-classification scanners and batched deleters, isolating per-scanner failures and supporting dryRun counts.
runSubjectAccess composes collectors across packages into one structured bundle; runErasure routes erasure-exempt classifications to anonymizers instead of deleters.
collectEvidence bundles per-source JSON for a period into a single structure an external SOC2, ISO, or HIPAA auditor can read, with auditEvidenceSource covering the audit-events case.
Retention sweeps and erasure runs log compliance events through an optional @absolutejs/audit broker, so the compliance actions are themselves evidenced.
Outcomes
Framework-agnostic compliance policy used by the hosted AbsoluteJS.ai platform.
The messaging consent ledger records immutable grant/revocation evidence at an exact tenant, program, purpose, transport, and recipient scope. Memory and Postgres stores are included. Its dispatch policy blocks messages before an adapter or provider call when evidence is missing or revoked.
1. createCompliancePolicy({ classifications, tenantOverrides? })
Hardening checklist
Follow in order
The messaging consent ledger records immutable grant/revocation evidence at an exact tenant, program, purpose, transport, and recipient scope. Memory and Postgres stores are included. Its dispatch policy blocks messages before an adapter or provider call when evidence is missing or revoked.
const store = createPostgresMessagingConsentStore(postgres);
const consent = createMessagingConsentLedger({ audit, store });
await consent.grant(
{
recipient: "+12025550100",
programId: "acme-incident-alerts",
purpose: "incident-alerts",
tenant: "tenant-a",
transport: "sms",
},
{ at: Date.now(), reference: "signup-42", source: "signup-form" },
);
const dispatcher = createDispatcher({
policies: [createMessagingConsentDispatchPolicy({ ledger: consent })],
sms,
});Declarative shape. Each classification gets a stable id, retention window, optional residency region, optional erasureExempt flag, and an open flags bag.
const policy = createCompliancePolicy({
classifications: {
pii: { id: "pii", retentionMs: 730 * DAY, residency: "eu" },
"audit-log": {
id: "audit-log",
retentionMs: Infinity,
erasureExempt: true, // SOX / many regulators require 7+ years
flags: { immutable: true },
},
operational: { id: "operational", retentionMs: 90 * DAY },
},
tenantOverrides: {
"gdpr-strict-tenant": { pii: { retentionMs: 90 * DAY } },
},
});Apply MESSAGING_CONSENT_POSTGRES_SCHEMA once. Signed provider STOP/START callbacks can update the same ledger, and every possible fallback transport must have evidence before Dispatch permits delivery.
import { createDispatcher } from '@absolutejs/dispatch';
import {
createMessagingConsentDispatchPolicy,
createMessagingConsentLedger,
createPostgresMessagingConsentStore
} from '@absolutejs/compliance';
const consent = createMessagingConsentLedger({
audit,
store: createPostgresMessagingConsentStore(postgres)
});
await consent.grant({
programId: 'acme-alerts',
purpose: 'incident-alerts',
recipient: '+12025550100',
tenant: 'tenant-a',
transport: 'sms'
}, {
at: Date.now(),
reference: 'signup-42',
source: 'signup-form'
});
const dispatch = createDispatcher({
messaging,
policies: [createMessagingConsentDispatchPolicy({ ledger: consent })]
});Declare classifications once, then let the residency guard gate data movement everywhere. Per-tenant overrides win over class defaults, so strict tenants ride the same policy object.
import {
createCompliancePolicy,
createResidencyGuard
} from '@absolutejs/compliance';
const DAY = 86_400_000;
const policy = createCompliancePolicy({
classifications: {
'audit-log': {
erasureExempt: true, // regulators often require 7+ years
flags: { immutable: true },
id: 'audit-log',
retentionMs: Infinity
},
operational: { id: 'operational', retentionMs: 90 * DAY },
pii: { id: 'pii', residency: 'eu', retentionMs: 730 * DAY }
},
tenantOverrides: {
'gdpr-strict-tenant': { pii: { retentionMs: 90 * DAY } }
}
});
const guard = createResidencyGuard(policy);
// throws ResidencyViolation if policy says 'eu'
guard.check({ classification: 'pii', region: 'us-east' });
// non-throwing variant
const violation = guard.inspect({
classification: 'pii',
region: 'eu',
tenant: 'acme'
});
if (violation !== null) {
return new Response(violation.message, { status: 451 });
}Each scanner streams expired records for its classification and each deleter removes them in batches. Failures are isolated per scanner, and dryRun: true counts without deleting.
import { runRetention } from '@absolutejs/compliance';
const report = await runRetention({
audit: broker,
deleters: {
'audit-log': (rows) => auditTable.delete(rows.map((r) => r.id)),
pii: (rows) => userTable.delete(rows.map((r) => r.id))
},
policy,
scanners: [
{ classification: 'audit-log', scan: auditTable.scan },
{ classification: 'pii', scan: userTable.scan }
]
});
// report.byClassification.pii = { scanned, deleted, durationMs }Each package contributes a collector and eraser pair. runErasure automatically routes erasure-exempt classifications to their anonymizer — audit-log subject references get anonymized rather than deleting the log itself.
import {
runErasure,
runSubjectAccess
} from '@absolutejs/compliance';
const bundle = await runSubjectAccess({
collectors: [
{
classification: 'pii',
collect: userTable.findBySubject,
name: 'profile'
},
{
classification: 'audit-log',
collect: auditTable.findBySubject,
name: 'audit'
}
],
subject: { subjectId: 'u-1', tenant: 'acme' }
});
await runErasure({
audit: broker,
erasers: [
{
classification: 'pii',
erase: userTable.deleteBySubject,
name: 'profile'
},
{
anonymize: auditTable.anonymizeSubject,
classification: 'audit-log',
name: 'audit'
}
],
policy,
subject: { subjectId: 'u-1', tenant: 'acme' }
});Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.
@absolutejs/compliance — framework-agnostic compliance substrate. The package gives a control plane five composable primitives, none of which know about a specific framework (SOC2 / HIPAA / ISO / GDPR are all expressible on top): 1. createCompliancePolicy(...) — declarative shape: classifications (tags + retention + residency + flags) and tenant overrides. 2. createResidencyGuard(policy) — pure check function. The runtime / sync / queue call guard.checkWrite({ classification, tenant, region }) b
type ComplianceAuditLike = {
append: (event: {
kind: string;
actor?: string;
target?: string;
metadata?: Record<string, unknown>;
}) => Promise<void> | void;
/**
* Optional reader — required only for `collectEvidence`. Returns
* audit events in the period bounded by `since` / `until` (inclusive
* / exclusive). Implementations stream large windows page-by-page.
*/
read?: (filter: {
since: number;
until: number;
kindPrefix?: string;
}) => AsyncIterable<{
kind: string;
at?: number;
actor?: string;
target?: string;
metadata?: Record<string, unknown>;
}>;
};@absolutejs/complianceThese playbooks show where this package fits, how to verify the combined system, and what changes before production.
Current package surface
Import surface · click to copy