feat(core): consolidate signing/verify primitives + scheme envelope (embedder P1a)#24
Merged
Conversation
…embedder P1a)
P1a moves the security contract into agentic-git-core so signer and verifier share
ONE source (no drift). Isolated to the agentic-git repo — the agend-terminal daemon
wiring + daemon signer swap is P1b.
- Move `ensure_key` (lock-free first-writer-wins `hard_link` provisioning) + its
`open_new_0600` helper verbatim from the run CLI into core as the single key
primitive; core gains a `getrandom` dep. The run CLI now calls
`core::ensure_key` / `core::sign_binding` (its local copies removed).
- Add `sign_binding` = ensure_key + sign — closes the reviewer4 hole where the
low-level `sign` panicked on a fresh home.
- `verify` becomes a typed `Result<(), VerifyError{MissingKey | MalformedTag |
MacMismatch | UnsupportedScheme{..}}>` (was `bool`). It parses a self-describing
tag ENVELOPE (`ag-hmac-sha256:v<algo>:<key-format>:<hex>`) BEFORE the MAC check,
so an HMAC scheme skew is diagnosable — the shim maps `UnsupportedScheme` to a
LOUD deny (`verify_sidecar`) instead of a silent "unbound".
- Add `BINDING_FORMAT_VERSION` / `HMAC_ALGO_VERSION` consts and `envelope_tag`
(the "can produce" capability).
ORDERING INVARIANT (fleet-safety — the emphasized landmine): the DEFAULT emit stays
the BARE hex tag (implicit scheme v1), byte-identical to the pre-P1a signer, so an
unswapped shim and every on-disk sidecar keep verifying. The envelope is a
capability core can parse + produce, NOT the default output (that flips in P2).
RED-first proven: making `sign_binding` emit the envelope fails the byte-identical
test against an independent python-HMAC oracle.
reviewer4 acceptance conditions (RED-first): #2 a malformed `ag-hmac-sha256:`
prefixed tag NEVER falls back to bare-hex legacy (extra colon / non-`v` algo /
non-hex / unknown key-format -> MalformedTag/UnsupportedScheme); #3 a newer scheme
or any tamper NEVER authenticates; #4 bare-hex legacy accepted (the migration
window). (#1 lockfile-single-source is P1b — agend-terminal.)
Regression: the run CLI's concurrent first-writer-wins is preserved — the existing
session_mode race tests (driving the real `run` binary) stay green through the core
reroute, plus a direct core-level N-thread race test.
cargo test (workspace) 0-failed; clippy --workspace --all-targets clean; cargo
package -p agentic-git-core green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>
… crates together (reviewer4 D1)
P1a added new public API (`VerifyError`, `ensure_key`, `sign_binding`) + a breaking
`verify` bool->Result change to agentic-git-core WITHOUT bumping its version, so the
version/package contract was incoherent: `cargo package -p agentic-git` resolves
`agentic-git-core = "0.1.0"` from crates.io (the OLD published API) and fails to
compile the new call sites — the PR's ubuntu CI failure. A released binary depending
on published core 0.1.0 could not build with the new code.
- Bump the workspace (core) version 0.1.0 -> 0.2.0 — a 0.x minor bump for the new +
breaking API, exactly what the workspace comment prescribes ("core bumps when its
API changes"). The shim keeps its own independent 0.2.2.
- Update the binary's dep requirement to `agentic-git-core { path, version = "0.2.0" }`.
- CI: package the two interdependent crates in ONE `cargo package -p
agentic-git-core -p agentic-git --allow-dirty` so the binary's verify build
resolves core from the just-packaged sibling (build-from-workspace), NOT the older
published registry version. No crates.io publish happens (the release-time publish
stays manual / operator-gated).
dev3 VERIFIED the crypto surface and reviewer4 confirmed no crypto fail-open — this
is purely the package/version contract fix; no core logic changed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Huang ChiaCheng (黃家政) <1557604+suzuke@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1a — consolidate signing/verify primitives into agentic-git-core (embedder migration)
Second phase of the embedder migration. agentic-git repo only (agend-terminal daemon wiring + the daemon signer swap are P1b) → independently mergeable / revertable. Base: P0 merge
1de4fc6.What
Moves the security contract into
agentic-git-coreso signer and verifier share ONE source:ensure_key— the lock-free first-writer-winshard_linkkey provisioning, moved verbatim from the run CLI into core (the single key primitive), with itsopen_new_0600helper. Core gains agetrandomdep. The run CLI now callscore::ensure_key/core::sign_binding(local copies removed).sign_binding= ensure_key + sign — closes the reviewer4 hole where the low-levelsignpanicked on a fresh home.verify→ typedResult<(), VerifyError>(MissingKey | MalformedTag | MacMismatch | UnsupportedScheme{tag_scheme, runtime_scheme}). Parses a self-describing tag envelope (ag-hmac-sha256:v<algo>:<key-format>:<hex>) before the MAC check, so an HMAC scheme skew is diagnosable — the shim mapsUnsupportedSchemeto a loud deny (verify_sidecar) instead of a silent "unbound".BINDING_FORMAT_VERSION/HMAC_ALGO_VERSIONconsts +envelope_tag(the "can produce" capability).⚠ Ordering invariant (fleet-safety)
The default emit stays bare hex (implicit scheme v1) — byte-identical to the pre-P1a signer — so an unswapped shim and every on-disk sidecar keep verifying. The envelope is a capability core can parse + produce, not the default output (that flips in a later phase). RED-first proven: making
sign_bindingemit the envelope fails the byte-identical test against an independent python-HMAC oracle (38a831c3…).reviewer4 acceptance conditions (RED-first, in
p1a_contract.rs)ag-hmac-sha256:-prefixed tag NEVER falls back to bare-hex legacy (extra colon / non-valgo / non-hex MAC / unknown key-format →MalformedTag/UnsupportedScheme).agentic-git run(#1) #3 a newer scheme (v2) or any tamper (bit-flipped MAC, wrong content, wrong MAC in a valid envelope) NEVER authenticates.agentic-git run(design for adversarial review) #1 lockfile-single-source is P1b (agend-terminal) — out of scope here.Regression
The run CLI's concurrent first-writer-wins is preserved: the existing
session_moderace tests (which drive the realrunbinary concurrently and assert one 32-byte key survives + both bindings verify) stay green through the core reroute, plus a new direct core-level N-thread race test.Verification
cargo test --workspace0-failed;cargo clippy --workspace --all-targets -D warningsclean;cargo package -p agentic-git-coregreen.For DUAL re-review
UnsupportedScheme/legacy split let a forged/downgraded tag authenticate?); the legacy window is not a downgrade vector.