Design spec for qbit-worm-audit (docs/DESIGN.md), answering the owner's request for WORM audits, with HIPAA §164.312(b)/(c)(1) and six-year retention in mind. Owner decisions (2026-10-12): all recommendations accepted.
Append-only in the database: Postgres triggers reject UPDATE, DELETE and TRUNCATE on audit, audit_detail and every worm_audit_* table, and protect the lookup tables' ids and names. The runtime role gets only INSERT and SELECT. The only removal is worm_audit_purge(), a SECURITY DEFINER function run by a separate worm_audit_purger role, after retention, on a chain prefix only, and recorded (refusals too). Changesets ship as a rendered template.
Tamper evidence: a single asynchronous chainer (advisory lock, settle horizon, contiguous link ids, late-arrival and late-detail links) builds link_hash = SHA-256(prev || 0x00 || JCS(audit + details)) in the sidecar table worm_audit_chain. Hourly signed Ed25519 checkpoints carry Merkle roots. "Verify audit chain" is a standard streamed-ETL table action that names the exact break. A daily job alerts through the core QAlerts SPI.
Sealed copies: daily contiguous JSONL exports with a manifest and hashes go through qbit-object-storage with Object Lock COMPLIANCE (six years); the exports form their own manifest chain. "Verify sealed export" is the restore and verify tool.
PHI: the canonical format is agreed with the qbit-secured-data agent (qqq#1161, §14.2). Only the stored tokens (hmac:v1:, shred:v1:, ...) and value-free messages are hashed and sealed. secured_data_audit_key is never read or exported. A canary test checks there is no plaintext.
Assistant wall: worm_audit_* is denied to the assistant channel.
App home, tables, permissions, tests (Testcontainers Postgres and MinIO, concurrency), phases (a)/(b)/(c), and the kof22 infra proposal (an Object Lock bucket, a signing key, off-site replication).
## Summary
Design spec for qbit-worm-audit (`docs/DESIGN.md`), answering the owner's request for WORM audits, with HIPAA §164.312(b)/(c)(1) and six-year retention in mind. **Owner decisions (2026-10-12): all recommendations accepted.**
- **Append-only in the database:** Postgres triggers reject UPDATE, DELETE and TRUNCATE on `audit`, `audit_detail` and every `worm_audit_*` table, and protect the lookup tables' ids and names. The runtime role gets only INSERT and SELECT. The only removal is `worm_audit_purge()`, a SECURITY DEFINER function run by a separate `worm_audit_purger` role, after retention, on a chain prefix only, and recorded (refusals too). Changesets ship as a rendered template.
- **Tamper evidence:** a single asynchronous chainer (advisory lock, settle horizon, contiguous link ids, late-arrival and late-detail links) builds `link_hash = SHA-256(prev || 0x00 || JCS(audit + details))` in the sidecar table `worm_audit_chain`. Hourly signed Ed25519 checkpoints carry Merkle roots. "Verify audit chain" is a standard streamed-ETL table action that names the exact break. A daily job alerts through the core `QAlerts` SPI.
- **Sealed copies:** daily contiguous JSONL exports with a manifest and hashes go through qbit-object-storage with Object Lock COMPLIANCE (six years); the exports form their own manifest chain. "Verify sealed export" is the restore and verify tool.
- **PHI:** the canonical format is agreed with the qbit-secured-data agent (qqq#1161, §14.2). Only the stored tokens (`hmac:v1:`, `shred:v1:`, ...) and value-free messages are hashed and sealed. `secured_data_audit_key` is never read or exported. A canary test checks there is no plaintext.
- Assistant wall: `worm_audit_*` is denied to the assistant channel.
- App home, tables, permissions, tests (Testcontainers Postgres and MinIO, concurrency), phases (a)/(b)/(c), and the kof22 infra proposal (an Object Lock bucket, a signing key, off-site replication).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Design spec for qbit-worm-audit (
docs/DESIGN.md), answering the owner's request for WORM audits, with HIPAA §164.312(b)/(c)(1) and six-year retention in mind. Owner decisions (2026-10-12): all recommendations accepted.audit,audit_detailand everyworm_audit_*table, and protect the lookup tables' ids and names. The runtime role gets only INSERT and SELECT. The only removal isworm_audit_purge(), a SECURITY DEFINER function run by a separateworm_audit_purgerrole, after retention, on a chain prefix only, and recorded (refusals too). Changesets ship as a rendered template.link_hash = SHA-256(prev || 0x00 || JCS(audit + details))in the sidecar tableworm_audit_chain. Hourly signed Ed25519 checkpoints carry Merkle roots. "Verify audit chain" is a standard streamed-ETL table action that names the exact break. A daily job alerts through the coreQAlertsSPI.hmac:v1:,shred:v1:, ...) and value-free messages are hashed and sealed.secured_data_audit_keyis never read or exported. A canary test checks there is no plaintext.worm_audit_*is denied to the assistant channel.🤖 Generated with Claude Code