Skip to content

Sign the runtime's own gate proposals with the operator identity (SPEC-006) - #388

Merged
IanFrelinger merged 1 commit into
masterfrom
claude/bridge-signing
Aug 24, 2026
Merged

Sign the runtime's own gate proposals with the operator identity (SPEC-006)#388
IanFrelinger merged 1 commit into
masterfrom
claude/bridge-signing

Conversation

@IanFrelinger

Copy link
Copy Markdown
Owner

Closes the last "gate record unsigned" gap in SPEC-006. The self-extend bridge recorded its automatic proposals through a signer-less GateStore, so a cycle's verdict carried no provenance while an operator's manual decision (via ashlar gates, #387) did. Now the bridge loads the operator identity and signs, so both halves of admission — the runtime's proposals and the human's decisions — carry the same signature when a key is present, and stay unsigned when none is (S-2).

What changed (src/ only — kernel-first, clean layer gate)

  • SelfExtendAdmissionBridge.TryRecordAsync opens the store with OperatorKey.TryLoad() and gains an optional signer seam: production loads the machine key, tests inject an identity so they never depend on ~/.ashlar/keys.
  • A corrupt operator key fails the record loud — the same GATE ERROR shape as an unreadable policy — never silently unsigned.
  • The production caller (SelfExtendRunnerAdapter) is unchanged: the new param is optional and trailing.

Verification

Ashlar.Tests.BackgroundAgents513/513 on net8.0 and net10.0, including a new test that a runtime proposal is signed and reads back (a keyless reader still verifies via the record's embedded key). No regressions in the existing bridge/M1 enforcement tests.

Next

With this, every gate record is signed when a key is present. The remaining SPEC-006 surface is user-visible: ashlar verify still prints unsigned — making it show a real signer fingerprint is the SPEC-003 ledger slice.

…C-006)
Closes the last "gate record unsigned" gap. The self-extend bridge recorded its automatic
proposals through a signer-less GateStore, so a cycle's verdict carried no provenance while an
operator's manual decision (via `ashlar gates`) did. Now the bridge loads the operator identity
and signs, so both halves of admission — the runtime's proposals and the human's decisions —
carry the same signature when a key is present, and stay unsigned when none is (S-2).
- TryRecordAsync gains an optional `signer` seam: production loads the machine key
(OperatorKey.TryLoad), tests inject an identity so they never depend on ~/.ashlar/keys.
- A corrupt operator key fails the record LOUD — the same "GATE ERROR" shape as an unreadable
policy — rather than silently recording unsigned, which would hide that signing was expected.
- The production caller (SelfExtendRunnerAdapter) is unchanged: the new param is optional and
trailing, so it defaults to the machine key.
Ashlar.Tests.BackgroundAgents 513/513 on net8.0 and net10.0, including a new test that a
runtime proposal is signed (read back by a keyless store, which verifies via the embedded key).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@IanFrelinger
IanFrelinger merged commit f7bb772 into masterAug 24, 2026
6 checks passed
@IanFrelinger
IanFrelinger deleted the claude/bridge-signing branch August 24, 2026 03:00
Sign up for freeto 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.

2 participants

@IanFrelinger@PlzTouchGrass