Skip to content

verified: t27c signs receipts and run-record judges their level -- R3-1 tool half (Closes #7332) - #7363

Merged
gHashTag merged 2 commits into
masterfrom
claude/signed-receipt-tool-7332
Oct 7, 2026
Merged

gHashTag merged 2 commits into
masterfrom
claude/signed-receipt-tool-7332

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Closes #7332 (tool half; the contract half is #7344, on master as e436d84). Epic #6655, Round 3, item R3-1.

What changes

  • t27c receipt-key init | show. init makes an Ed25519 key pair. The private key is a 32-byte seed with mode 600, written to $T27_RECEIPT_KEY or ~/.config/t27/receipt-ed25519.key. It refuses a path inside the repository: .. and symlinks are resolved first, and a subdirectory or a gitignored path still counts as inside. It never overwrites an existing key and never prints the key. The public half is committed as .trinity/keys/<key_id>.pub, where key_id is the first 16 hex characters of SHA-256 of the public key. show exits 0 if the key is registered, 1 if there is no key or it is not registered, and 2 if refused.
  • t27c silicon --nonce <hex> signs every receipt when a key is present. The signed message is the domain line t27-receipt-v1 followed by nine name=<json> lines in the spec's order, with the nonce inside the message. Without a key the receipt is written UNSIGNED and the tool says so: level NONE, no claim. A bad nonce (not hex, or under 16 bytes) or a refused key stops the run before any hardware work (exit 2).
  • t27c run-record --challenge <hex> --require-level none|author|fresh prints each receipt's authentication code and level, in the order of signed_receipt.t27: unsigned -> key not registered -> bad signature -> no nonce -> nonce is not the challenge. The run's level is the minimum over its receipts. Citable now means run-complete AND level >= required. A complete run below the required level prints NOT CITABLE at X -- the run reaches only Y and exits 1.
  • A disclaimer line in every report: 0 levels are device-rooted. A host signature proves authorship and integrity, never that a given die computed the result. That is R3-2.

Acceptance (from #7332)

  • Signed receipt verifies: FRESH for its own challenge (unit + integration).
  • A one-byte edit fails: verdict_word, seeds, utc_unix and the IDCODE each give BAD_SIGNATURE.
  • A replayed receipt against a new challenge is AUTHOR, not FRESH. Stapling a new nonce onto an old signature gives BAD_SIGNATURE.
  • A key path inside the repository is refused, including a subdirectory, missing/../repo/k and a symlink.
  • Receipts in the pre-R3 shape (R2-5 capstone: no nonce/key_id/signature) are UNSIGNED -> level NONE and stay citable at the default --require-level none, so the R2 exit semantics are unchanged and nothing is claimed retroactively. The capstone's three receipt files were bench-local and gitignored, so this is pinned by tests on the same shape (a_receipt_from_before_the_rule_is_unsigned, one_unsigned_or_edited_receipt_lowers_the_whole_run), not re-run on those three files.

Evidence (Railway lab t27c-lab, head 0742384)

  • cargo build --release -p t27c: clean. ed25519-dalek 2.2.0 was already in Cargo.lock through jsonwebtoken, so the lock gains one dependency line and no new crate.
  • cargo test --bin t27c -- r3_signed_receipt r2_silicon_receipt run_record: 18/18. the_constants_are_the_specs parses specs/verified/signed_receipt.t27, so the Rust constants cannot drift from the spec. the_mirror_answers_the_specs_vectors runs the spec's 10 vectors through the Rust mirror.
  • --test signed_receipt_reader: 5/5. The test signs with ed25519-dalek over a message it builds itself, so writer and reader are checked independently. --test run_record_reader: 13/13, so R2-4 is unchanged.
  • The lab caught one real bug, fixed in the second commit: resolved_path gave up on .. under a directory that does not exist, so base/missing/../repo/k escaped the inside-the-repository check.
  • gitleaks on both commits: no leaks. The test key is the deterministic seed [7u8; 32], built in code; no key file is in the tree.

Competitor comparison

System Authorship Freshness Device-rooted
Phala / NVIDIA CC (TEE) hardware-signed quote nonce per request yes (vendor root of trust)
EigenAI operator stake re-execution challenge no
Gonka chain tx from host key per-block no
zkML (EZKL, DeepProve) proof is unforgeable proof binds the input n/a; 1000-100000x overhead
t27 before none (unsigned JSON) none (replayable) no
t27 after this PR Ed25519 host key, registered in-repo verifier nonce signed in no -- R3-2 (DNA_PORT)

t27 now has the same envelope as Gonka/EigenAI-class systems: host-key authorship plus verifier-chosen freshness. It keeps its stronger content: a bit-exact integer verdict, the toolchain matched to the seal, and agreement across three placements. The remaining gap to a TEE is device rooting, which is R3-2. The gap to stake-based systems is operator independence, which is R3-3.

Foreign code

Rust in bootstrap/ under the owner's standing rule (2026-10-06, translated: add the label yourself and do the foreign part). The new entry in tools/policy/foreign-exceptions.txt (top block) covers bootstrap/Cargo.toml and the new integration test; service.rs and main.rs were already listed. Every decision lives in specs/verified/signed_receipt.t27. The Rust is I/O plumbing that mirrors it, and a test pins the constants to it.

🤖 Generated with Claude Code

gHashTag and others added 2 commits October 7, 2026 15:07
…-1 tool half (Refs #7332)

WIP: lab build pending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck; lock ed25519-dalek

resolved_path gave up on a path like base/missing/../repo/k (Path::file_name
is None for ..) and returned it unnormalized, so a key path spelled through a
missing directory escaped the inside-the-repository refusal. Walk the path
component by component instead: canonicalize every existing prefix, apply
. and .. lexically under a missing one. Caught by the r3_signed_receipt
test on the Railway lab.

Refs #7332

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag gHashTag added the owner-approved-foreign Owner-approved exception to the only-t27 rule: hand-written foreign code allowed in this PR label Oct 7, 2026
@gHashTag
gHashTag enabled auto-merge (squash) October 7, 2026 08:19
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-07 08:20:12 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 48
PRs with All Checks Green 2
READY 2
FAILING 48
PENDING 0
NO CHECKS YET 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=1aa228450491 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@gHashTag
gHashTag merged commit f8ad9e7 into master Oct 7, 2026
27 of 35 checks passed
gHashTag added a commit that referenced this pull request Oct 7, 2026
…rigin/master (Closes #7371) (#7399)

The Only-t27 gate had three holes, all used by #7363 (804 hand-written Rust
lines in bootstrap/src/service.rs):

- locally the exception list was read from the working tree, so a branch
  could list itself;
- in CI the label owner-approved-foreign waived the whole check, and the
  label is applied through the same account the agents use;
- an exception covered a whole file with no limit on how much it may grow.

The rule now has check_budget() in specs/policy/own_language.t27: at most
40 added hand-written foreign lines per file and 80 per diff, read from
`git diff --numstat --no-renames`. It reads no exception list and no label
lifts it. Deletions are free; specs, generated roots, prose and data do
not count; the gate's own foreign files (lefthook.yml, its workflow) do.
Unreadable numstat lines fail closed.

- gen/c/policy/own_language.c regenerated with t27c gen-c (Railway lab).
- lefthook.yml: own-language reads the list from origin/master; a new
  foreign-budget command on pre-commit and pre-push.
- own-language.yml: a budget step with no label condition, compiled from
  BASE (HEAD only while BASE predates check_budget).
- own-language is now a required check in the master ruleset.

Verified on the lab: 32/32 zig tests, the C test main exits 0, 19/19 hand
mutants killed (limits 40/80 moved both ways, > vs >=, every counted
kind dropped, protected/generated/own branches flipped, binary '-',
empty path, digit bounds, base 10, the total and unreadable failures).
Negative control: the #7363 merge diff gives 3 DENYs (service.rs,
signed_receipt_reader.rs, the 80-line total). This diff adds 34 foreign
lines. Committed by the owner with LEFTHOOK=0: the gate denies every
local change to its own files, by design.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

owner-approved-foreign Owner-approved exception to the only-t27 rule: hand-written foreign code allowed in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Verified Compute R3-1: signed receipts with a verifier nonce (Ed25519)

1 participant