Monolith is experimental software (v0.x). It provides security and compliance primitives in the database; it is not a turnkey HIPAA, SOC 2, or PCI control set. This document states plainly what is shipped, what is not, and where the responsibility boundary sits, so no one mistakes a building block for a finished control.
Please report suspected vulnerabilities privately to security@standardapplied.com (or open a GitHub security advisory). Do not file public issues for security reports. We aim to acknowledge within a few business days. As an experimental project there is no formal SLA yet.
- Field encryption (
@Encrypted). Envelope encryption for annotated fields, with a pluggableKeyProvider. Each value carries a fresh data key under AES-256-GCM, and the ciphertext is bound to its field context (table.column) as associated data, so a blob copied to a different column or table fails to decrypt rather than silently substituting (wire version 3; version-2 data stays readable). The context istable.column, not row identity, so it does not detect a swap between two rows of the same column; bind a primary key into the context if you need per-row binding. Default key custody is in-process:PgCryptoinitializes aLocalKeyProviderthat reads the data-encryption key from theMONOLITH_FIELD_KEYenvironment variable. This is appropriate for development and self-managed deployments that control the process environment; it is not an HSM/KMS integration. - Forced row-level security (
@AccessControlled,@Tenant). Generates PostgresFORCE ROW LEVEL SECURITYpolicies over a unified grant model (allow/deny, roles, resource/*). Tenant isolation is emitted as aRESTRICTIVEpolicy so it ANDs with the grant check. Policies are keyed oncurrent_setting('app.actor')/app.tenant, which the application must set per request/transaction. - Write audit trails (
@Audited). Append-only audit table plus a write-capturing trigger and an immutability guard. This captures changes (insert/update/delete), not reads. - SQL-identifier safety for replication. Logical-replication slot and publication names are validated
against a strict identifier pattern before they reach privileged admin SQL (
Wal.validateSlot), so a slot name cannot inject into or break the replication/admin path. - Generated policy SQL escaping.
@AccessControlledliteral values (e.g.resource) are SQL-escaped during code generation so an annotation value cannot terminate a string literal or break out of a generated policy.
These are tracked in the roadmap under "close the compliance gate (HIPAA)". Until they land, do not represent a deployment as HIPAA-ready on the basis of this library alone:
- KMS/HSM key custody. No managed-KMS
KeyProvideradapter; the default key lives in the process environment. Key rotation is not operationalized. - Read-access audit.
@Auditedrecords writes, not who viewed data — which HIPAA requires for PHI. - PHI-safe observability. The telemetry seam exists, but there is no shipped redaction policy to keep sensitive values out of traces, metrics, and logs.
- Set actor/tenant context. RLS policies are only as strong as the
app.actor/app.tenantsession settings; a query that forgets to set them will be denied or under-scoped, not silently elevated, but the application owns establishing them correctly. - Authorize network-facing subscriptions. The optional Helidon
LiveQueryWsListeneraccepts a<QueryName>:<param>frame.build()is fail-closed: it throws unless you install anauthorize(session, query, param)hook, so production code must callauthorize(...). TheallowAllForDevelopment()escape hatch exists only for local development and must never reach a deployed system — forbid it in review or static checks. Keep the default param-length bound. Unknown-query and unauthorized rejections are deliberately indistinguishable so clients cannot probe which queries exist. - Protect
MONOLITH_FIELD_KEYand the database/replication credentials as you would any secret; the library does not manage their lifecycle.
Pre-1.0: only the latest published version receives fixes.