feat(secrets): per-repo secrets — vault to labctl broker exec (tracer bullet) (#104) #112
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab!112
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/104"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Per-repo secrets, foundation slice: operator-managed named values an agent can use without ever holding them in ambient env.
Closes #104
What landed
0008_repo_secrets, both dialects):repo_secrets— name^[A-Z][A-Z0-9_]*$unique per repo, operator description, value sealed with the existing master key (AES-256-GCMnonce||ciphertext, ADR-0006 style). Metadata reads never select the blob.GET/POST /api/v1/repos/{id}/secrets,PATCH/DELETE .../secrets/{sid}(rotate = wholesale new value; delete immediate). Secrets section on the repo settings page; rotate reveals a blank field — the old value is never fetched. Tests assert no response body ever carries a value.GET /agent/v1/secrets(names + descriptions),POST /agent/v1/secrets/values(partial miss → 404 naming the missing, no values).labctl secret list;labctl secret exec <NAME...> -- <cmd>injects each value into the child process env only (never argv, never the session env, never disk), preserves the child's exit code (documented deviant exit contract, likepr checks), and fetches at exec time — rotation is live, no respawn.## Secretssection in the generated context file — the norm (never echo/persist; use only insidesecret exec), the single-quotesh -cpattern incl. the double-quote trap, the spawn-time inventory, and thelabctl secret listpointer. Repos with zero secrets render byte-identically (frozen goldens untouched; new golden pins the with-secrets render).internal/secretsmatcher (shared kernel for #105–#108, no consumer yet): streaming detection of a value in exact, base64 (all 4 alphabets), hex (both cases), and URL-encoded forms, exactly-once across arbitrary chunk boundaries (carry window + end-offset threshold; boundary-sweep tested at every split point down to 1-byte feeds).Verification
go test ./...green except pre-existing sandboxinternal/tmuxxfailures (tmux cannot spawn sessions here; verified pre-existing on base via stash).golangci-lint run ./...(pinned 2.12.2): 0 issues.gofmtclean.go build -tags ui ./cmd/...with embedded dist: OK.tsc && vite buildall green.$NAMEinjection, live rotation, exit-code passthrough, unknown-name failure, argv exactness, cross-repo token scoping.Out of scope (per issue): the four harness layers (#105 redaction, #106 pre-push guard, #107 tracker-write sanitization, #108 exposure detection), audit log, cross-repo sharing, any read-back path.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SvDTdGmhGMUr46tATHc4AY
End-to-end foundation slice: an operator creates a named secret on the repo's settings page; an instance uses it through the broker without the value ever entering ambient env, argv, or any readable API. - store: repo_secrets table (0008, both dialects) — name ^[A-Z][A-Z0-9_]*$ unique per repo, description, value sealed by the master-key vault (AES-256-GCM nonce||ciphertext); metadata reads never select the blob. - operator API + UI: write-only create/rotate/delete + metadata list under /api/v1/repos/{id}/secrets, Secrets section on repo settings; no endpoint or UI path ever returns a value (credentials-vault discipline, ADR-0006). - agent surface: GET /agent/v1/secrets (names+descriptions) and POST /agent/v1/secrets/values, both scoped by the run token's run row; labctl secret list / secret exec <NAME...> -- <cmd> injects values into the child process env only, preserves the child's exit code, and fetches at exec time — rotation is live, no respawn. - seeder: conditional Secrets section in the generated context file (ADR-0035): usage norm, single-quote sh -c pattern, spawn-time inventory, labctl secret list pointer; secret-less repos render byte-identically (goldens untouched). - internal/secrets: shared streaming matcher detecting a value in exact, base64 (4 alphabets), hex (2 cases), and URL-encoded forms, correct across arbitrary chunk boundaries — the kernel for #105–#108, no consumer yet. Closes #104 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SvDTdGmhGMUr46tATHc4AY