Skip to content
This repository was archived by the owner on Oct 31, 2026. It is now read-only.

Canonical records, DSSE through probityai, the k-session rule and a file store - #10

Merged
astrogilda merged 6 commits into
mainfrom
lane/l7-ratchet-hardening
Oct 17, 2026
Merged

astrogilda merged 6 commits into
mainfrom
lane/l7-ratchet-hardening

Conversation

@astrogilda

Copy link
Copy Markdown
Contributor

Hardening for receipts, the admission rule and the store, in six commits.

Every signed or digested record is now canonicalised with RFC 8785 through the probityai binding (the jcs-admit crate), and each decision and store record says which canonicalisation it used. Records written before this change carry no such field and are still read with the old sorted-keys form, so their digests keep verifying. Of the 105 recorded decisions and traces in the records test, 20 have different RFC 8785 bytes, all from number spelling. The cases package keeps zero dependencies.

DSSE signing and verification go through probityai.dsse. The old Python reader stays only in the tests, where a differential test checks the two agree on every envelope in the suite and on malformed ones.

ratchet.improve.sessions runs the k-session admission rule (k=8, n=5, the candidate against the parent and two identity copies) on one host or inside one Slurm allocation, and writes a bundle: manifest, every execution attempt, and the verdict. python -m ratchet.improve.sessions verify DIR recomputes the harm screens, the t statistics and the log checks from the bundle alone. The statistics match an independent implementation on 24 cohorts to within 1e-9. See docs/SESSIONS.md.

The Atlas store and Raindrop telemetry move to the atlas and telemetry extras. Without them Ratchet runs on a signed, append-only file store (RATCHET_STORE_DIR), hash-chained and checked on read.

probity-verify is pinned at 0.1.1 and agent-evidence-vectors at 0.19.0.

The probityai binding is not on PyPI yet, so it is pinned from git at jcs-admit 58a48b2. The demo image builds the native wheel in a manylinux stage at the same commit. Switching to probityai==0.1.0 from PyPI comes after that release.

Tests: on the gate box, each commit passed the cases suite (243), the main suite with a local Atlas (1443 to 1489 passed) and the records test.

ratchet.canonical.canonical_json admits the JSON text Python writes
and returns its RFC 8785 form from the probityai binding of the
jcs-admit crate: members sorted by UTF-16 code units, numbers in
ECMAScript form, NaN and infinities refused. Every decision, report,
trace, harness version and record digest in the harness now uses it.
Harness contents hold no floats, so every harness version is
unchanged. The case suite keeps no runtime dependency: it digests its
own definitions and world states as before, and those digests reach
the harness as values.

A digest can be recomputed only with the function that produced it,
so every record that carries one now names its canonicaliser:
decisions carry canonicalization rfc8785, and the Atlas store records
digest_canonicalization on every trace and record. A record that
names none was written before this change; the verifier, the evidence
collector and the store recompute its digests over sorted-keys JSON,
kept for that use alone. A decision naming any other canonicaliser is
not established.

tests/records re-derives stored records under both canonicalisers:
on the 105 public-benchmark decisions every payload is the sorted-keys
form it was signed as, and RFC 8785 differs from it only in number
spelling, in 20 of them.

probityai 0.1.0 is not on PyPI yet, so uv builds it from the merged
binding at a pinned commit, and the demo image builds the wheel in a
manylinux_2_28 stage so its build still installs no system package.
…dsse

The signer in ratchet.improve.receipt and the verifier in
ratchet.verify.dsse each carried their own PAE and Ed25519 code. Both
now go through the probityai binding of the dsse crate, from one
module: ratchet.verify.dsse keeps the structural read and the rule
that a key is tried only when a signature entry names it, so the
untrusted_key and signature_invalid reasons are unchanged.

verify() and verify_signature() take a threshold, so a countersigned
decision can be held to both of its keys. keyid_of and raw_public
move to the DSSE module, which the signer now imports.

The earlier pure-Python reader stays in the tests as an oracle: it
signs and verifies the same honest, malformed and forged envelopes as
the binding and must reach the same result, and it imports nothing
from the binding. The view's second signature pass, which ran the
same code twice, is removed.
Adds the replicated rule of ledger D37 as sessions.py: k sessions
(8 by default) of fresh parent, identity and candidate arms, n runs
per case per arm (5), randomly interleaved, each execution's endpoint
drawn and recorded, each arm on its own seed stream, a lost execution
rerun once in a random later slot. The candidate is admitted when its
smallest t against the three unchanged arms reaches t(0.95, k - 1)
and the harm screen passes; a zero SD, an invalid execution or a
firing log check (endpoint mix, completion time with trend, pairing)
makes the comparison inconclusive. There is no padding controller.

run_protocol runs every session on one host or in one Slurm
allocation and writes a bundle; sessions_live drives it against live
endpoints, re-reading the endpoint file and running a restart command
before each session. 'python -m ratchet.improve.sessions verify'
rebuilds each schedule from its seed, refuses a log that departs from
it, and recomputes the verdict from the bundle alone, in pure Python.

The statistics match the reference implementation that produced the
simulation tables on 24 fixed cohorts, to 1e-9 relative.
… file store

pymongo and raindrop-ai move to the atlas and telemetry extras. The
core install verifies receipts, runs the loop and the k-session
protocol, and keeps its records in a signed append-only file store:
every record is one line of records.jsonl, a DSSE envelope signed by
the store's key over the RFC 8785 record, linked to the line before it
by digest. Opening the store checks every line, so an edited, dropped,
reordered or foreign line refuses to open. Several processes can share
one store under an exclusive lock, and activation over it keeps the
Atlas rules: compare and swap on the active pointer, under the lock.

The store's plain record types move to ratchet.store.types, the Mongo
activator to ratchet.store.activation and the seeded transport to the
model client, so nothing in the core imports pymongo. The CLI uses the
file store when RATCHET_STORE_DIR is set and names the missing extra
when Atlas or Raindrop is asked for without it. A test imports and
opens the core with both packages refused.
The verifier and the vector corpus move from 0.1.0 and 0.17.9 to their
current releases; the pin test checks the new wheel hash and versions.
@astrogilda
astrogilda merged commit e0c31cd into main Oct 17, 2026
9 checks passed
@astrogilda
astrogilda deleted the lane/l7-ratchet-hardening branch October 17, 2026 12:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant