The reports bucket
Optional: hand this page to your coding agentThe steps work by hand too.Show the whole prompt
Read https://intentius.io/terragucci/reference/reports-bucket/.
Write a script that reads estate.json and index.json at the top of my reports prefix with a read-only identity, validates each against the JSON Schema the page names, and prints every waiting wave with its project, age and report.
Read only: write nothing to the bucket.
Never apply, approve (a pull request review or `terragucci approve`), override a policy denial (`terragucci override`), use `--mode apply`, or merge; never touch `.chant/allowed_signers` or `chant/lifecycle`.reports.bucket names an S3 or GCS bucket or an Azure Blob container, and terragucci writes under one prefix in it (reports.prefix, empty by default). A release adds keys and never moves one, so a program can rely on the layout below.
| Key | Written by | What it is |
|---|---|---|
<prefix>/index.json, index.html |
every upload | every project’s runs, newest first |
<prefix>/<project>/index.json, index.html |
every upload | one project’s runs, newest first |
<prefix>/<project>/inventory.json |
a tf-apply wave’s upload |
the resources each root holds, from its newest applied wave |
<prefix>/<project>/changes.json |
a tf-apply wave’s upload |
what each applied wave did to each resource, newest first |
<prefix>/<project>/migrations/<name>.json |
wave 1 of tf-apply, when it runs a state migration |
the migration’s record: each root’s state version and digest before and after, never a state’s contents |
<prefix>/<project>/states.json |
a tf-apply wave’s upload |
the state version id each root’s applies left, newest first; never a state’s contents |
<prefix>/<project>/edges.json |
an upload whose roots read another root’s state, or a tf-apply wave that changed a root |
each root’s reads, its newest plan and its newest apply that changed it; never a state’s contents |
<prefix>/<project>/ephemeral.json |
the ephemeral job and the sweep, each time a pull request’s copy applies or is destroyed | the live copies: each pull request’s roots and state keys, its commit, when it applied and when it expires (terragucci.ephemeral/v1, the rows of the estate’s projects[].ephemeral) |
<prefix>/<project>/<yyyy>/<mm>/<commit>/<stage>[-wave-<n>]/ |
every upload | one run’s report directory, below |
<prefix>/<project>/runs/<commit>/run.json, run.html |
each tf-apply wave job |
the run view: every wave of the commit’s apply, its roots and what they read, where it stands, its blast radius and its timeline; terragucci estate reads it for the dependency graph |
<prefix>/traces/<trace id>.html |
an upload whose run sent a trace | a page that forwards to the run’s report.html |
<prefix>/estate.json, estate.html |
terragucci estate |
every project’s latest plan, drift check and apply waves |
<prefix>/history.json, history.html |
terragucci estate |
every resource address with each apply that changed it |
<prefix>/dora.json |
terragucci estate |
the four DORA metrics per project and for the estate, week by week |
<prefix>/audit.jsonl, audit.html, audit.json |
terragucci audit |
the audit trail, its page and its summary |
<prefix>/views/ |
nothing | kept for a viewer’s own files |
| Part | Value |
|---|---|
<project> |
the repo’s <host>/<path>, such as github.com/acme/infra, slashes included; a repo with no git remote is named after its directory |
<yyyy>/<mm> |
the month the run finished, in UTC |
<commit> |
the full SHA |
<stage> |
tf-plan, tf-drift or tf-apply; a tf-apply directory ends in -wave-<n> |
A run’s directory
Section titled “A run’s directory”| File | What it is |
|---|---|
report.json |
the report, terragucci.report/v1; see Report JSON schema |
report.html |
the report as a page, with report.json inline |
note.md |
the pull request note |
summary.txt |
the grouped plan as text |
gitlab-terraform.json |
GitLab’s reports:terraform counts |
roots/<root>/plan.txt, roots/<root>/plan.json |
each root’s full plan, and its JSON with sensitive values redacted |
roots/<root>/cost.json |
the estimator’s output, with cost set |
intent.json |
the description check’s decision, after respond description |
Links inside a run’s files are relative, so they resolve wherever the bucket is served. A row’s path in an index.json is relative to that index.
JSON Schemas
Section titled “JSON Schemas”Each object a program reads names its schema in its schema field. The package’s dist/ directory ships the matching JSON Schema:
| Object | schema |
JSON Schema |
|---|---|---|
report.json |
terragucci.report/v1 |
dist/report.schema.json, also exported as @intentius/terragucci/report.schema.json |
index.json |
terragucci.report-index/v1 |
dist/report-index.schema.json |
estate.json |
terragucci.estate/v1 |
dist/estate.schema.json |
each line of audit.jsonl |
terragucci.audit/v1 |
dist/audit.schema.json |
inventory.json |
terragucci.inventory/v1 |
dist/inventory.schema.json |
changes.json |
terragucci.changes/v1 |
dist/changes.schema.json |
history.json |
terragucci.history/v1 |
dist/history.schema.json |
states.json |
terragucci.state-versions/v1 |
dist/state-versions.schema.json |
edges.json |
terragucci.state-edges/v1 |
dist/state-edges.schema.json |
dora.json |
terragucci.dora/v1 |
dist/dora.schema.json |
run.json |
terragucci.run/v1 |
dist/run.schema.json |
Within v1 a release only adds fields, and an existing field keeps its meaning. A reader checks schema and ignores fields it does not know. The schemas leave objects open, so a validator accepts a field a later release added.
npm pack @intentius/terragucci
tar -xzf intentius-terragucci-*.tgz package/dist/estate.schema.jsonReport JSON schema lists the fields of index.json, estate.json, inventory.json, changes.json, history.json, states.json and edges.json, Delivery metrics the fields of dora.json, and The audit trail the fields of an audit entry.
The views prefix
Section titled “The views prefix”terragucci never writes or reads under <prefix>/views/. A tool that renders your reports, such as a static page built in a scheduled job, writes its files under <prefix>/views/<tool>/. An upload whose project name would start with views is refused before anything is written; give the repo a git remote.
Whatever serves the bucket serves views/ too. A presigned link opens one object, and the front door serves every key under the prefix to whoever your identity provider signs in.
Reading it
Section titled “Reading it”| To read | An identity needs |
|---|---|
| every project’s runs and the estate | GetObject on <prefix>/*index.json and <prefix>/estate.json |
| every project’s resources | GetObject on <prefix>/*inventory.json |
| every root’s state versions | GetObject on <prefix>/*states.json |
| every root’s cross-state edges | GetObject on <prefix>/*edges.json |
| every live ephemeral environment | GetObject on <prefix>/*ephemeral.json |
| every resource’s history | GetObject on <prefix>/history.json, or on <prefix>/*changes.json for the rows without approvers |
| a run’s report | GetObject on <prefix>/*/report.json |
| the audit trail | GetObject on <prefix>/audit.jsonl |
| the delivery metrics | GetObject on <prefix>/dora.json |
A reader needs no list permission: an index row names each run’s directory, and the report names each root’s plan files.
These docs count page views and clicks with PostHog. They set no cookies, store nothing in your browser, and send nothing when your browser asks not to be tracked.