Skip to content

feat(core): consolidate signing/verify primitives + scheme envelope (embedder P1a)#24

Merged
suzuke merged 2 commits into
mainfrom
feat/p1a-core-primitives
Jul 8, 2026
Merged

feat(core): consolidate signing/verify primitives + scheme envelope (embedder P1a)#24
suzuke merged 2 commits into
mainfrom
feat/p1a-core-primitives

Conversation

@suzuke

@suzuke suzuke commented Jul 8, 2026

Copy link
Copy Markdown
Owner

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-core so signer and verifier share ONE source:

  • ensure_key — the lock-free first-writer-wins hard_link key provisioning, moved verbatim from the run CLI into core (the single key primitive), with its open_new_0600 helper. Core gains a getrandom dep. The run CLI now calls core::ensure_key / core::sign_binding (local copies removed).
  • sign_binding = ensure_key + sign — closes the reviewer4 hole where the low-level sign panicked on a fresh home.
  • verify → typed Result<(), 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 maps UnsupportedScheme to a loud deny (verify_sidecar) instead of a silent "unbound".
  • BINDING_FORMAT_VERSION / HMAC_ALGO_VERSION consts + 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_binding emit the envelope fails the byte-identical test against an independent python-HMAC oracle (38a831c3…).

reviewer4 acceptance conditions (RED-first, in p1a_contract.rs)

Regression

The run CLI's concurrent first-writer-wins is preserved: the existing session_mode race tests (which drive the real run binary 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 --workspace 0-failed; cargo clippy --workspace --all-targets -D warnings clean; cargo package -p agentic-git-core green.

For DUAL re-review

  • reviewer4: HMAC/envelope/typed-verify correctness + the byte-identical ordering invariant (a mistake here = fleet fail-closed).
  • dev3: no new fail-open (does the envelope parse or the UnsupportedScheme/legacy split let a forged/downgraded tag authenticate?); the legacy window is not a downgrade vector.

suzuke and others added 2 commits July 8, 2026 12:03
…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>
@suzuke suzuke merged commit 5306c85 into main Jul 8, 2026
4 checks passed
@suzuke suzuke deleted the feat/p1a-core-primitives branch July 8, 2026 04:22
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