Skip to content

Add publisher-side provenance: map a code_id back to a commit#9

Merged
miharp merged 1 commit into
mainfrom
feat/publisher-provenance
Jul 24, 2026
Merged

Add publisher-side provenance: map a code_id back to a commit#9
miharp merged 1 commit into
mainfrom
feat/publisher-provenance

Conversation

@miharp

@miharp miharp commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Closes the one troubleshooting question the content-addressed design can't answer on its own: which control-repo commit produced the version this compiler is serving?

Why

A code_id is a content hash — great for "are these two compilers serving the same code?", useless for "what commit is this?". Sealing deliberately strips .git and .r10k-deploy.json, so a compiler carries no path back to a commit. Today you can detect that a compiler diverged but not trace what it came from.

How

The publisher already has the answer sitting in the staging tree: .r10k-deploy.json is excluded from sealing but not deleted. At seal time the publisher reads r10k's resolved commit (the signature field) and appends a record to <state>/provenance.jsonl, keyed by code_id.

codavox provenance <env> <code_id> reads that log locally on the publisher and prints the commit. No network I/O — per the design decision, the CLI is local-read only, so there's no second TLS client path to maintain.

$ codavox code-id production          # on a puzzling compiler
3224ddbe…
$ codavox provenance production 3224ddbe…   # on the publisher
a3f1c9e4b2d8    deployed 2026-07-24 12:00:00 -0400    sealed 2026-07-24T16:00:00Z

Three deliberate properties

  • Publisher-only. The log never enters an artifact or reaches a compiler, so it cannot influence a code_id or the bytes served. Reading .r10k-deploy.json here does not change sealing, which still excludes it — verified by a test that the code_id is identical with and without capture.
  • One code_id, many commits. A commit that leaves resolved content unchanged seals to the same id, so an id can list several commits — recorded history, not a conflict.
  • Best-effort, never load-bearing. A missing or malformed .r10k-deploy.json records nothing and never fails a deploy; a query with no record prints no provenance recorded and exits 0. This is not a no-fallbacks violation: that rule forbids serving wrong content while reporting success. Provenance is diagnostic — its honest absence is the correct answer, never a stand-in from another version.

Scope notes

  • First persistent state codavox keeps, and it stays on the publisher (0700 dir, 0600 file). Compilers remain stateless except for their version directories.
  • No HTTP endpoint yet — deferred until a remote consumer actually exists, rather than shipping unused surface.
  • code-id/code-content are untouched: still a single symlink read and a single file read.

Tests

  • internal/publish — capture, missing/malformed deploy file degrade cleanly, dedup, persistence across reopen, one-id-many-commits, env/id isolation, and code_id-unchanged-by-capture.
  • cmd/codavox — the subcommand reads the local log, --json, unrecorded-id is empty+exit 0, and bad-arg rejection.
  • Docs: publishing.md and commands.md.

Verified locally: go test -race ./..., golangci-lint 0 issues, gofmt/vet clean, markdownlint clean.

🤖 Generated with Claude Code

A code_id is a content hash, which answers 'are these compilers serving the
same code?' but not 'which control-repo commit produced it?'. The artifact
excludes .git and .r10k-deploy.json, so a compiler has no path back to a commit.
Troubleshooting a divergent compiler could tell you *that* it differed but not
*what* it came from.

The publisher already has the answer on disk: .r10k-deploy.json is excluded from
sealing but not deleted, so at seal time the publisher reads r10k's resolved
commit (the signature field) and appends a record to <state>/provenance.jsonl,
keyed by code_id. codavox provenance <env> <code_id> reads that log locally on
the publisher and prints the commit — no network I/O, so it needs no second
client path or TLS.

Three properties are deliberate and load-bearing:

- Publisher-only. The log never enters an artifact or reaches a compiler, so it
  cannot influence a code_id or the bytes served. Reading .r10k-deploy.json here
  does not change sealing, which still excludes it.
- One code_id, many commits. A commit that leaves resolved content unchanged
  seals to the same id, so an id can list several commits — recorded history,
  not a conflict.
- Best-effort, never load-bearing. A missing or malformed .r10k-deploy.json
  records nothing and never fails a deploy, and a query with no record prints
  'no provenance recorded' and exits 0. This is not a no-fallbacks violation:
  that rule forbids serving wrong content while reporting success. Provenance is
  diagnostic; its honest absence is the correct answer, never a stand-in from a
  different version.

The log is the first persistent state codavox keeps, and it stays on the
publisher; compilers remain stateless except for their version directories. No
HTTP endpoint yet — the query is local-read only until a remote consumer exists.

Signed-off-by: Michael Harp <mike@mikeharp.com>
@miharp
miharp merged commit 5507082 into main Jul 24, 2026
7 checks passed
@miharp
miharp deleted the feat/publisher-provenance branch July 24, 2026 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant