station-report.yaml you sent back, ingested: enough of your cluster’s shape to render the next release into it, plus your contract saying what that release must prove.
It is one file with named sections — profile, values, contract, receipts — each carrying the sha256 of its own text in a manifest at the foot. The checks below are the same checks they always were, expressed against sections instead of archive members.
Ingest, then gate — ten checks
- S1 report shape — exactly one YAML document, a mapping,
schema_version: 1, and under the size a person could have read before sending it. (There is no archive, so there is nothing to traverse out of: this is the same question in the new shape.) - S2 completeness — every section role this report kind requires is present and non-empty.
- S3 manifest identity — the report names this station, every declared section hashes to the text that is there to read, nothing undeclared rides along, and a top-level key that is neither a manifest field nor a declared section is a refusal.
- S4 no plaintext secrets — each section is parsed back into its own format (values as YAML, profile as an env file) and scanned for secret-shaped keys with real values, plus credential patterns anywhere in the bytes: PEM private keys,
sk-,ghp_,AKIA…,AIza…,xox…, JWTs,client-key-data. - S5 render —
helm templatesucceeds with your values over the chart defaults. - S6 resources — every container, init containers included, declares cpu and memory, requests and limits.
- S7 no hostPath — no workload mounts a hostPath volume, at all. There is no permitted path and no exception.
- S8 digest-pinned — every image reference ends in
@sha256:. - S9 contract — every
require:item is matched by evidence this release produced, or explicitly waived. - S10 report scope — the report does not exceed the telemetry rung your own contract declares. This is us enforcing your policy against ourselves: you can read the packager and see it cannot collect above its rung, but only this check proves we would not keep a report that did. A contract with no
report_scopeis refused here as well as locally. See the telemetry ladder.
0 on pass, 3 on refusal, dated report either way. A refusal prints file, line and rule — never the value — so it is safe to paste into a ticket. A second run the same day rotates the earlier report aside rather than overwriting it.
Your require: list
Sending it back
--submit-dry-run to send. --station must equal the path segment the edge allows you to write to, which is the same string as VEXA_CHANNEL_USER. The destination comes from your contract’s report_scope.destination — and that block is commented out in kit/verify/policy.example.yaml, so uncomment it before your first submit: a contract with no report_scope is refused before a byte moves, at both ends.
It validates the report against report.v1 and against your contract’s report_scope before anything leaves, prints the path so you can open it first, and pushes with your own credential — scoped to your station’s path and nothing else. The document validated is the document sent; there is no second payload assembled beside it. It returns submitted: channel.vexa.ai/vexa/stations/<you>/bundles:<date> and a digest:. The digest is your receipt — it names the exact bytes that left your perimeter, and we can be held to it.
Each gated publish then writes a report naming the station, the chart and its sha256, your values section’s sha256, your contract id and hash, the evidence file, the timestamp, and a verdict per check and per contract item. Generated, not written.
Rung. The ingest and gate lane is in the tree; the worked example committed here is synthetic — no real station report has been ingested yet. See what’s proven, and where.