Skip to content

feat!(jwks): the JWKS reading is an ordinary notarized session - #7

Closed
xgreenx wants to merge 22 commits into
feat/ws-attestationsfrom
feat/jwks-ceremony-attestation
Closed

xgreenx wants to merge 22 commits into
feat/ws-attestationsfrom
feat/jwks-ceremony-attestation

Conversation

@xgreenx

@xgreenx xgreenx commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #6 (feat/ws-attestations), which sits on #3.

The JWKS reading becomes an ordinary notarized session: the notary signs the section 9.1 record for it exactly as for a platform session and decides nothing about it. Deleted: the Merkle JwksRotationProof path (jwks/{notary,transcript,sol_types}.rs), the server-name dispatch, the jwks_enabled flag, and the deps only that path used.

What stays in jwks/: the constants, layout (reveal both directions whole, commit nothing), the prover (notarize_jwks returns the signed record), and a mock prover that synthesizes a byte-faithful session — the request as hyper writes it (pinned by a test that drives hyper's own encoder) and Google's real response framing (chunked by default) — for the keeper's end-to-end test. The wire struct is notary::NotarizedSession, an alias of libid-transcript's AttestationWire; serde field names unchanged.

On chain, GoogleJwtRoots (libid-org/libID-contracts#21) verifies the record through NotaryService and parses the JWKS itself.

examples/notarize_jwks.rs captures a real reading from a running notary as a JSON fixture (README: "Capturing a real reading"). The fixture committed to libid-contracts#21 came from it: a genuine MPC-TLS session with www.googleapis.com on 2026-09-03, origin-form request line on the wire, chunked body, four keys.

Pins: all libid-rs crates at the head of libid-org/libid-rs fix/origin-form-request-target (libid-rs#5) — the Rust prover was writing absolute-form request lines, which the verifiers refuse. Both pins return to a tag once that stack merges. Note for consumers: the [patch] of tlsnotary/tlsn in this manifest does not propagate; a crate depending on this library must repeat it (the keeper does).

Verification: cargo +nightly fmt --all -- --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all (16 passed, MPC and proxy smoke tests included).


Actualised against the moved base (2026-09-14)

The base gained libid-rs v0.3.0 (contains every rev this branch had pinned), the merged
notary#9 pins (libid-org/tlsn@550220e, libid-org/mpz@4db9454), and #6's rewrite of
server.rs and the browser bundle. This branch also carried five pre-force-push copies of that
work, which is why it showed as conflicting.

Resolved by a merge commit (no history rewrite): Hlib's files take the base wholesale; this
branch's intent — delete the Merkle path and the server-name dispatch, NotarizedSession, the JWKS
module, the example, the README paragraphs — is re-applied on top. One API move for v0.3.0:
attestation::tag(host) → AttestedData::authority_id_of(host).

So the pin paragraph above is now stale: all libid-rs crates are at tag v0.3.0, not a rev, and
notary#8 (which only moved those pins) is superseded.

Verified after the merge: cargo +nightly fmt --all -- --check,
cargo clippy --all-targets --all-features -- -D warnings, cargo test --all,
cargo build --example notarize_jwks.

Wondertan and others added 17 commits August 26, 2026 16:20
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Remove session creation and polling. Reclaim the ProxyMode channel, send one length-prefixed ceremony attestation, then close it. Pin the browser bundle to TLSNotary #1178 for prover-side finish().

Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Exercise a real local ProxyMode session through the WebSocket route, reclaim the prover channel, validate the signed ceremony attestation, and require EOF without any polling endpoint.

Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Pin the notary and release WASM build to the libid TLSN integration revision containing upstream PRs #1178 and #1179.

Assisted-by: GPT-5
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
The notary used to dispatch on the TLS-verified server name and answer
`www.googleapis.com` with a Merkle proof of its own shape, signed over a
digest of its own design, for the legacy `JwksOracle`. That was the
notary deciding what a session was for -- the profile-specific decision
REQ-COMMON-33 forbids it from making -- and a second wire format to keep
in step with a second contract. `IdentityJwksRoots` now verifies through
`NotaryService` like every Platform Verifier, so the second format has
no reader.

Delete the Merkle path: `jwks::notary`, `jwks::transcript`,
`jwks::sol_types`, `JwksRotationProof` and its serde helpers, the
`jwks_enabled` flag and the dispatch in `handle_verified_session`. The
notary signs the section 9.1 record for every session; which host it
observed is in the record, and the contract that reads it pins the host.

What makes the reading readable on chain is its layout: `jwks::layout`
reveals both directions whole and commits nothing, so the contract can
concatenate the revealed ranges and parse request line, `Host`, status
line, framing and JSON out of them with no cut able to hide a byte.
`run_jwks_prover` passes that layout; `notarize_jwks` reads the record
back. The mock prover synthesizes the same record from a fetched (or
fixture) body: the request bytes hyper writes -- derived from the very
request the real prover sends, and pinned against hyper's own encoder in
a test -- and a response under chunked framing by default (what Google
sends, so the on-chain de-chunker runs) or `content-length`.

The wire struct is now public as `notary::NotarizedSession`, an alias of
libid-transcript's `AttestationWire` rather than a second definition:
both parties speak it, so it is defined once where both can reach it.

libid-rs moves to the head of `fix/origin-form-request-target`. hyper's
`client::conn::http1` writes the request-target exactly as the `Uri`
displays, and `prover_generic` needs an absolute URI to know the host,
so at the previous pin every Rust-prover session put
`GET https://www.googleapis.com/oauth2/v3/certs HTTP/1.1` on the wire --
not the origin-form line the contracts pin. That rev rewrites the URI
before sending; libid-ceremony is added as a direct dependency for the
mock's record, and libid-attestations, alloy-*, serde_with and base64
go with the code that used them.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
libid-contracts renamed `IdentityJwksRoots` to `GoogleJwtRoots` and moved
it next to the ceremony contracts: it serves the `google/v1` Platform
Verifier and nothing else, and "identity" said nothing about that. The
notary never names the contract in code -- it signs the same record for
every session -- but its prose does, and prose that names a contract
that no longer exists sends a reader to grep for nothing.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
The mock prover synthesizes the JWKS session; nothing so far had put a
record Google actually served, signed by a real notary, in front of
`GoogleJwtRoots`. A reviewer of the contract has to trust that the mock's
idea of the wire -- origin-form request line, chunked framing, header
case -- is what the MPC session really produces, and the libid-rs
origin-form fix has only been proved by a hyper unit test, not end to
end.

`examples/notarize_jwks` connects to a running notary's TCP port, runs
the real prover against `www.googleapis.com`, and writes the record,
its signature, the address the signature recovers to and the notary's
`createdAt` (read from the header, bytes 32..40) to a JSON file that a
contract test can replay: trust the address, warp to `createdAt`, call
`rotate` with the real bytes. The README says how to run it with anvil
#0 as the notary key, so the address a test trusts is a known one.
`chrono` joins the dev-dependencies for the RFC 3339 `captured_at`
stamp; it is already in the graph through tlsn.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
… rewritten server

The base moved under this branch: libid-rs is released as v0.3.0 (which
contains every rev this branch had pinned), notary#9 landed the
libid-org/tlsn 550220e + libid-org/mpz 4db9454 pins, and #6 rewrote
server.rs (1224 lines) and the browser bundle around the reclaimed
WebSocket. This branch also carried five pre-force-push copies of that
work, so a plain merge collided with their rewritten versions.

Resolution, by ownership rather than by hunk:

  * Hlib's files take the base: server.rs, release.yml,
    build-tlsn-wasm.sh, the README's browser-bundle section, Cargo.lock,
    and every pin in Cargo.toml (all libid-rs crates at tag v0.3.0; tlsn
    and mpz at the merged notary#9 revisions).
  * This branch's intent is re-applied on top: the Merkle JWKS path is
    deleted (jwks/{notary,transcript,sol_types,crypto}.rs, JwksRotationProof,
    the jwks_enabled flag and the server-name dispatch in
    handle_verified_session, and the alloy-*/serde_with deps only it
    used); jwks/{mod,mock,prover}.rs, src/attestation.rs and
    examples/notarize_jwks.rs are this branch's; server.rs names the
    record `NotarizedSession`; the README's JWKS paragraphs and the
    "Capturing a real reading" section return.
  * One API move from 391078f to v0.3.0: `attestation::tag(host)` is now
    `AttestedData::authority_id_of(host)`. Nothing else in the JWKS code
    changed meaning.

hyper/hyper-util join the dev-dependencies: the mock's request-bytes
test drives hyper's own encoder and the base had dropped them. chrono
stays a dev-dependency for the example's RFC 3339 stamp.

Verified: `cargo +nightly fmt --all`, `cargo clippy --all-targets
--all-features -- -D warnings`, `cargo test --all`, and
`cargo build --example notarize_jwks`.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
… site

The merge resolution shortened two comments this branch had written: the
explanation of why the server-name dispatch is gone (the notary deciding
what a session is for is the profile-specific decision REQ-COMMON-33
forbids), and the doc line on the record builder. Restore both verbatim;
no code changes.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
863fdac left a doc comment separated from its item by a blank line, which
clippy rejects under -D warnings. Fold it into sign_ceremony_attestation's
existing doc block. No code changes.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
@xgreenx

xgreenx commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Replaced by #10, rebuilt as one clean commit on the current feat/ws-attestations: this branch had accumulated pre-force-push copies of #6 and needed a merge commit to become mergeable. #10 goes further and removes the JWKS prover helpers from the notary entirely; they move to the keeper in libid-org/keeper#7, which drops the keeper\x27s dependency on this crate. Branch left in place until keeper#7 merges, since keeper\x27s feat/notary-service-rotation still pins 67dd1fe from here.

@xgreenx xgreenx closed this Sep 14, 2026
xgreenx added a commit that referenced this pull request Sep 15, 2026
The notary used to dispatch on the TLS-verified server name and answer
`www.googleapis.com` with a Merkle proof of its own shape, signed over a
digest of its own design, for the legacy `JwksOracle`. That was the notary
deciding what a session was for -- the profile-specific decision
REQ-COMMON-33 forbids it from making -- and a second wire format kept in
step with a second contract. `GoogleJwtRoots` now verifies through
`NotaryService` like every Platform Verifier, so the second format has no
reader.

Delete the JWKS duty and everything only it used: `src/jwks/` (the Merkle
path, the prover helpers and the mock), the `jwks_enabled` flag, the
dispatch in `handle_verified_session`, the `Jwks`/`Http`/`Base64` error
variants, and the `alloy-*`, `serde_with`, `base64`, `k256` and non-test
`reqwest` dependencies. The notary signs the section 9.1 record for every
session; which host it observed is in the record, and the contract that
reads it pins the host.

The prover-side helpers were only ever keeper code hosted here for reuse,
and hosting them here made the keeper depend on this whole crate and match
its libid-rs revision exactly. They move to the keeper, on libid-rs's
primitives (`libid_tlsn::prover_generic`, `libid_transcript`). The notary
crate still builds as a library so the smoke tests can embed `run`.

Supersedes #7, whose branch carried pre-force-push copies of #6, and #8.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
xgreenx added a commit that referenced this pull request Sep 15, 2026
The notary used to dispatch on the TLS-verified server name and answer
`www.googleapis.com` with a Merkle proof of its own shape, signed over a
digest of its own design, for the legacy `JwksOracle`. That was the notary
deciding what a session was for -- the profile-specific decision
REQ-COMMON-33 forbids it from making -- and a second wire format kept in
step with a second contract. `GoogleJwtRoots` now verifies through
`NotaryService` like every Platform Verifier, so the second format has no
reader.

Delete the JWKS duty and everything only it used: `src/jwks/` (the Merkle
path, the prover helpers and the mock), the `jwks_enabled` flag, the
dispatch in `handle_verified_session`, the `Jwks`/`Http`/`Base64` error
variants, and the `alloy-*`, `serde_with`, `base64`, `k256` and non-test
`reqwest` dependencies. The notary signs the section 9.1 record for every
session; which host it observed is in the record, and the contract that
reads it pins the host.

The prover-side helpers were only ever keeper code hosted here for reuse,
and hosting them here made the keeper depend on this whole crate and match
its libid-rs revision exactly. They move to the keeper, on libid-rs's
primitives (`libid_tlsn::prover_generic`, `libid_transcript`). The notary
crate still builds as a library so the smoke tests can embed `run`.

Supersedes #7, whose branch carried pre-force-push copies of #6, and #8.

Assisted-by: Claude Fable 5.1
Signed-off-by: xgreenx <xgreenx9999@gmail.com>
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.

2 participants