What an installation writes under its prefix, who may put and get what, and what breaks verification. To make a bucket, see prepare the bucket. The key grammar is the bucket contract.
extension schemas and the record schema the records were written under
the longest profile
dlq/year=/month=/day=/…
writer
records the writer could not take
the longest profile
holds/<id>/…
audit hold
legal holds placed and released
the longest profile
identity/tenant=<t>/purpose=<p>/<pseudonym>
writer
the sealed identity behind a pseudonym, for resolve
the longest profile
Rule
Detail
Placement
A reader finds a record by its ingest hour, not its own date
Names
A profile name must not contain /. A record whose tenant id contains / is dead-lettered
Catalogue
Written once. The same bytes again succeed; other bytes under the version stop the writer at start-up, or at the first record of a catalogue it did not start with
identity/
Exists only when a key provider is configured. keys.provider: none (no keys block) is the default (0055)
v0 archive
profile=<p>/tenant=<t>/year=..., digest/ and verified/ are read by nothing in v1. The previous release's CLI (v0.6.x) reads them. A bucket holding both needs both lifecycle rule sets
Exports
A separate bucket without Object Lock, with a lifecycle rule expiring export/. exports.bucket of the query configuration takes its own endpoint, pathStyle and credentialsSecret, inheriting none from the archive
Lifecycle
The chart creates no buckets (0065). The environment's bucket carries one rule per profile on <prefix>/records/<profile>/: Glacier Instant Retrieval at 30 days, Deep Archive at 1 year, expiration only after lock expiry. The profile leads records/ because a lifecycle filter matches a literal prefix
Per-tenant roles
Scope a role to arn:aws:s3:::<bucket>/<prefix>/records/<profile>/<tenant>/*
Four roles per installation, each bound to a service account (Pod Identity or IRSA); the chart has a serviceAccount per component. The receiver, the clock-sync job and the purge job get a service account and no S3 role.
Scope every role under the installation's prefix: the resource is arn:aws:s3:::<bucket>/<prefix>/*, and s3:ListBucket is conditioned on s3:prefix being <prefix>/*.
role
on the archive, under its prefix
elsewhere
writer
s3:PutObject, s3:PutObjectRetention, s3:GetObjectRetention, s3:PutObjectLegalHold under records/, catalogue/, schema/, dlq/, holds/ and identity/ (where there are keys); s3:GetObject and s3:ListBucket on records/, catalogue/, holds/, schema/, and identity/ where there are keys
kms:GenerateDataKey, kms:Encrypt on the bucket's key
verify job
s3:GetObject, s3:ListBucket; nothing is put
kms:Decrypt on the bucket's key
indexer (audit-observe)
s3:GetObject, s3:ListBucket on records/, catalogue/ and schema/; nothing is put
kms:Decrypt on the bucket's key
query service
s3:GetObject, s3:ListBucket
s3:PutObject, s3:GetObject on the exports bucket; kms:Decrypt
Role
On the archive, under the installation's prefix
notary
Get and list; put under seals/ and the object keys/roots.jwks; decrypt; use the seal key. No put under records/
purge job
None; it works on the index database
operator running audit hold
s3:PutObjectLegalHold (placing), s3:GetObjectLegalHold, s3:ListBucket, s3:PutObject on holds/
break-glass
s3:PutObjectLegalHold with s3:object-lock-legal-hold = OFF (releasing)
auditor
Read-only on records/ and catalogue/; reads show in the bucket access log, and through the query service as audit.get and audit.search
Rule
Detail
Separation
The writer may not put under seals/ or keys/. Verify job, indexer and query service write nothing into the archive
Deletes
No role gets s3:DeleteObject, s3:DeleteObjectVersion or s3:BypassGovernanceRetention
Legal hold
Only the writer holds s3:PutObjectLegalHold, and a put sends the header only when placing a hold. A refusal on a plain put means a version that sent it as OFF on every put: upgrade, do not grant
schema/
The writer reads the last composition back on every start. A policy that allows put but not get makes it write one object, take a 403 and die in a loop