feat(oracle): independent TS2021 handshake oracle + receipt tooling - #145
Merged
Conversation
…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.
…vs docker .RepoDigests/.Id)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.shboots headscale (0.23, containerized) with a harness-generated CA, drives the witness, writes + validates the receipt.write_receipt.pybinds producer commit + oracle image digest + artifact SHA-256s;validate_receipt.pyre-checks schema, hashes, and outcome invariants in CI.crates/dictyon/examples/oracle_witness.rs— the witness. Uses dictyon's real key fetch +NoiseHandshakestate 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:Zunder enforcing SELinux).Oracle-caught defects (the point of the exercise)
/keyfield casing: dictyon expectedPublicKey; the live wire (headscale, matching tailscale.com) sendspublicKey. The handshake could never have completed against a real server. Parser accepts both spellings now, with a regression test pinning the wire spelling.[0x02][BE16 len][payload]IS what headscale sends — that part of the mock was accidentally right; only the oracle could say so.minimum_version=v1.62.0). The 0.23 oracle accepts it. TheCAPABILITY_VERSION=71pin 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_responseaccepts). Mismatch case: a wrong-prologue initiation is refused. Both transcripts hash-bound in the receipt.Verification
tools/oracle/run.shend-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 whenalone — 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.