Skip to content

feat(oracle): independent TS2021 handshake oracle + receipt tooling - #145

Merged
forkwright merged 4 commits into
mainfrom
feat/122-oracle-harness
Sep 9, 2026
Merged

feat(oracle): independent TS2021 handshake oracle + receipt tooling#145
forkwright merged 4 commits into
mainfrom
feat/122-oracle-harness

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Implements the unlock for hamma#122's completion authority: an independent oracle for the TS2021 capability-version handshake, with hash-bound receipts.

What's here

  • tools/oracle/run.sh boots headscale (0.23, containerized) with a harness-generated CA, drives the witness, writes + validates the receipt. write_receipt.py binds producer commit + oracle image digest + artifact SHA-256s; validate_receipt.py re-checks schema, hashes, and outcome invariants in CI.
  • crates/dictyon/examples/oracle_witness.rs — the witness. Uses dictyon's real key fetch + NoiseHandshake state machine (the units under test) but hand-drives the TLS+HTTP upgrade, so request construction is also under test rather than self-paired. Captures raw post-upgrade bytes verbatim (interpretation after capture, never before).
  • evidence/phase-a/ — receipt schema doc + the committed proof-of-record from this branch's tree (regenerated fresh every harness run; CI re-validates invariants rather than byte-matching, since Noise ephemerals make transcripts unique per run).
  • .github/workflows/oracle.yml — CI leg (docker on the runner; the script auto-detects podman/docker and only adds :Z under enforcing SELinux).

Oracle-caught defects (the point of the exercise)

  1. /key field casing: dictyon expected PublicKey; the live wire (headscale, matching tailscale.com) sends publicKey. The handshake could never have completed against a real server. Parser accepts both spellings now, with a regression test pinning the wire spelling.
  2. Response framing confirmed real: [0x02][BE16 len][payload] IS what headscale sends — that part of the mock was accidentally right; only the oracle could say so.
  3. Version floor finding: headscale 0.26 rejects capability v71 outright (minimum_version=v1.62.0). The 0.23 oracle accepts it. The CAPABILITY_VERSION=71 pin now has a documented external consequence — the bump decision is a protocol claim that needs its own evidenced lane.

Success case: v71 handshake completes against the oracle (101 + valid Noise response + process_response accepts). Mismatch case: a wrong-prologue initiation is refused. Both transcripts hash-bound in the receipt.

Verification

  • tools/oracle/run.sh end-to-end on menos: success + mismatch + receipt write + validation, all green (final run after all refactors).
  • cargo nextest run --workspace: 187/187.
  • cargo clippy -p dictyon --all-targets -- -D warnings: clean; cargo fmt --all: clean.
  • python3 tools/render_phase_a.py --check: contract green (no data-plane surfaces touched).

Does not close #122's Done when alone — the regression-test-on-drift item wants a CI-side assertion wired to the receipt (follow-up), and the tailscale.com canary (operator-run, credentials stay operator-owned) is the remaining byte-truth anchor.

…hamma#122)

The capability-version handshake now has an independent witness: a
containerized headscale harness (tools/oracle/) boots the reference-free
control plane, drives dictyon's real key fetch + Noise initiation +
HTTP upgrade through a hand-rolled witness (crates/dictyon/examples/
oracle_witness.rs — deliberately NOT wire::connect, so request
construction is also under test), and writes hash-bound transcripts +
a typed receipt under evidence/phase-a/oracle/, validated in CI by
tools/oracle/validate_receipt.py.

First-run findings, included in this PR:

- dictyon's /key parser expected "PublicKey" but the live wire
  (headscale, matching current tailscale.com) sends "publicKey" — the
  handshake could never have started against a real server. Parser now
  accepts both spellings with a wire-shape regression test.
- The [0x02][BE16 len] response-frame expectation IS the real protocol
  (headscale confirms it byte-for-byte); the earlier self-paired mock
  proved nothing either way, which is the point of the oracle.
- headscale 0.26 rejects dictyon's pinned capability v71 outright
  (minimum client v1.62) — the harness pins 0.23 for the success case
  and the version-floor question is now an evidenced finding, not a
  guess.

Mismatch case: a wrong-prologue initiation gets 101-then-nothing and is
correctly refused. The committed receipt binds this branch's tree; CI
regenerates fresh evidence every run and validates schema + hashes.

Refs #122
cargo deny/audit/osv flag rustls-pemfile as unmaintained — parse the
harness CA PEM with base64 (already a dep) instead. The oracle workflow's
action pins are the repo's real ones (checkout v7.0.1, rust-toolchain
stable), not the placeholders I wrote from memory. Receipt artifacts
regenerated from the final tree.
@forkwright
forkwright merged commit aa212b9 into main Sep 9, 2026
14 checks passed
@forkwright
forkwright deleted the feat/122-oracle-harness branch September 9, 2026 19:48
@github-actions github-actions Bot mentioned this pull request Sep 9, 2026
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.

Prove the capability-version handshake against an independent TS2021 oracle

1 participant