Phase (b) of docs/DESIGN.md §6: sealed exports. Stacked on #6. This PR contains #6's commits until #6 merges; review only the last commit.
Exporter: daily (exportCron, 02:00 UTC) and "Export now" (a streamed-ETL table action on worm_audit_export, permission worm_audit.export).
Exports contiguous ranges up to the latest signed checkpoint, streamed in 1,000-link pages to temp files: links.jsonl (link plus the exact canonical content), checkpoints.jsonl and manifest.json (file hashes, head hash, prevManifestSha256).
Each object goes through qbit-object-storage with LockMode.COMPLIANCE and retain-until = now + retentionYears, and is checked by head. Version ids are recorded in worm_audit_export.object_versions.
It refuses and alerts when the bucket lacks Object Lock or versioning. It takes the chainer's writer lock.
Verify sealed export: a record action that is also the restore tool.
Checks the manifest hashes, COMPLIANCE and retention, and the manifest chain; detects an overwrite against the pinned version and still verifies the sealed version; recomputes the links and checkpoint signatures; compares with the live chain and live canonical content, naming the exact link.
Purged links are verified from the sealed copy only. Writes a SEALED_EXPORT verification row.
Stand-in: qbit-object-storage RC.2 has no versioned GET, so ObjectStorageSealedStore.getVersion uses object storage's configured S3 connection (rate limiter, key normalisation). Replace it when object storage adds a version-aware get.
README: "Sealed exports" (bucket requirements and IAM).
Tests (CI=true ./mvnw verify with Docker): 67 pass, including a MinIO Testcontainers run with an Object Lock BACKUP bucket. It covers the round trip; COMPLIANCE on every object with retain-until about 6 years out; a version delete refused; overwrites detected while the sealed version verifies; a live tamper reported as "link 1 (audit 1): live content differs from the sealed copy"; and a non-lock bucket refused with an alert.
## Summary
Phase (b) of `docs/DESIGN.md` §6: sealed exports. **Stacked on #6.** This PR contains #6's commits until #6 merges; review only the last commit.
- **Exporter:** daily (`exportCron`, 02:00 UTC) and "Export now" (a streamed-ETL table action on `worm_audit_export`, permission `worm_audit.export`).
- Exports contiguous ranges up to the latest signed checkpoint, streamed in 1,000-link pages to temp files: `links.jsonl` (link plus the exact canonical content), `checkpoints.jsonl` and `manifest.json` (file hashes, head hash, `prevManifestSha256`).
- Each object goes through qbit-object-storage with `LockMode.COMPLIANCE` and retain-until = now + `retentionYears`, and is checked by `head`. Version ids are recorded in `worm_audit_export.object_versions`.
- It refuses and alerts when the bucket lacks Object Lock or versioning. It takes the chainer's writer lock.
- **Verify sealed export:** a record action that is also the restore tool.
- Checks the manifest hashes, COMPLIANCE and retention, and the manifest chain; detects an overwrite against the pinned version and still verifies the sealed version; recomputes the links and checkpoint signatures; compares with the live chain and live canonical content, naming the exact link.
- Purged links are verified from the sealed copy only. Writes a `SEALED_EXPORT` verification row.
- **Stand-in:** qbit-object-storage RC.2 has no versioned GET, so `ObjectStorageSealedStore.getVersion` uses object storage's configured S3 connection (rate limiter, key normalisation). Replace it when object storage adds a version-aware `get`.
- README: "Sealed exports" (bucket requirements and IAM).
**Tests** (`CI=true ./mvnw verify` with Docker): 67 pass, including a MinIO Testcontainers run with an Object Lock BACKUP bucket. It covers the round trip; COMPLIANCE on every object with retain-until about 6 years out; a version delete refused; overwrites detected while the sealed version verifies; a live tamper reported as "link 1 (audit 1): live content differs from the sealed copy"; and a non-lock bucket refused with an alert.
🤖 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
Phase (b) of
docs/DESIGN.md§6: sealed exports. Stacked on #6. This PR contains #6's commits until #6 merges; review only the last commit.exportCron, 02:00 UTC) and "Export now" (a streamed-ETL table action onworm_audit_export, permissionworm_audit.export).links.jsonl(link plus the exact canonical content),checkpoints.jsonlandmanifest.json(file hashes, head hash,prevManifestSha256).LockMode.COMPLIANCEand retain-until = now +retentionYears, and is checked byhead. Version ids are recorded inworm_audit_export.object_versions.SEALED_EXPORTverification row.ObjectStorageSealedStore.getVersionuses object storage's configured S3 connection (rate limiter, key normalisation). Replace it when object storage adds a version-awareget.Tests (
CI=true ./mvnw verifywith Docker): 67 pass, including a MinIO Testcontainers run with an Object Lock BACKUP bucket. It covers the round trip; COMPLIANCE on every object with retain-until about 6 years out; a version delete refused; overwrites detected while the sealed version verifies; a live tamper reported as "link 1 (audit 1): live content differs from the sealed copy"; and a non-lock bucket refused with an alert.🤖 Generated with Claude Code