Skip to content

test: the live ceremony suite, with a notary of its own - #12

Draft
SupremaLex wants to merge 3 commits into
feat/ceremony-github-tokenfrom
feat/live-ceremony-tests
Draft

SupremaLex wants to merge 3 commits into
feat/ceremony-github-tokenfrom
feat/live-ceremony-tests

Conversation

@SupremaLex

@SupremaLex SupremaLex commented Sep 11, 2026

Copy link
Copy Markdown
Member

The suite behind --features live-ceremony: real MPC-TLS sessions against github.com and api.x.com through an in-process notary, with the bridge under test publishing the configuration a ceremony starts from. Based on #8.

What runs

tests/ceremony starts the real binary on a configuration file, with the loopback port to bind in its environment (tests/common/bridge.rs, shared with tests/startup.rs), reads GET /api/v1/ceremony/config from it as an application would, and runs the notarized sessions of a ceremony with a prover of its own (prover.rs: libid_tlsn::prover_generic over TCP) through a notary of its own (notary.rs: libid_tlsn::verifier, signing with a fresh key). Each record the notary signs is checked against the rules the Platform Verifier applies on chain (session.rs): exact coverage, the request revealed whole with nothing committed, the bearer's commitment framed by "access_token":" and readable nowhere, exactly one authorization: Bearer header with its value committed in the identity request, the two identity members revealed and nothing else.

GitHub (github.rs): the token request is the canonical five-field form of libid#35 — client_id, code, redirect_uri, code_verifier, client_secret — with the published clientCredential as client_secret, laid out whole; a record laid out with the credential committed, the shape the v0.4.0 profile table still declares, is refused. The identity read is GET /user with the bearer committed; the integer id is revealed with the byte that closes it, login as a string.

Rung Needs Proves
a refused code the App MPC-TLS to github.com; GitHub's bad_verification_code
a refused credential the App GitHub's incorrect_client_credentials
a refused bearer nothing MPC-TLS to api.github.com; a 4xx
a real authorization the App, the test account, Chrome both sessions attested, both signatures recovering to the suite's notary, the login the account's

X (x.rs): the same two sessions for the public PKCE client; a refused-bearer rung, and a real authorization from a signed-in Chrome (browser/x.rs), which restores a saved session and never opens X's sign-in pages unattended. X examines a fresh sign-in, and a run nobody is watching has no answer for what it asks: a session that no longer authenticates stops the rung and names the headed export that makes a new one, and the export refuses a headless run. The rung says which way X took it, the consent screen or a redirect straight back for an app this account has already authorized.

The browser driver (browser/) is one core — Chrome over CDP, a redirect watcher that reads the code off the request nothing listens for, screenshots and page dumps under BROWSER_TRACE — and one module per platform.

Variables

From the environment or a gitignored .env.test; a missing one fails the run. GH_OAUTH_CLIENT_ID, GH_OAUTH_CLIENT_SECRET (the bridge under test publishes it) and LIBID_TEST_PUBLIC_ORIGIN; GH_TEST_ALICE_USERNAME, GH_TEST_ALICE_PASSWORD, GH_TEST_ALICE_TOTP_SECRET and a Chrome for the GitHub authorization; X_OAUTH_CLIENT_ID, LIBID_TEST_X_REDIRECT_URI, X_TEST_ALICE_USERNAME, X_TEST_ALICE_PASSWORD, X_TEST_ALICE_EMAIL and the optional X_TEST_ALICE_COOKIES for X. The README's Testing section has the table.

CI

ceremony.yml: pull requests run the rungs that need no account; the nightly and dispatched runs add the GitHub authorization, one rung at a time; the X authorization runs weekly on a schedule of its own and on a dispatch with x, because a saved session and the account behind it degrade over months. The two schedules carry their own concurrency group. Each job is skipped where its secrets are absent. Not a required check.

Verification

Hermetic: 60 unit, 19 over the router, 4 over the binary, and 7 in the suite (the request layouts, the refused hidden-credential layout, the TOTP code). Live, locally: 12 passed — the three GitHub rungs that need no account, the X refused bearer, and the real GitHub authorization with both sessions attested. clippy -D warnings under --all-features, nightly rustfmt, --locked.

Known gaps

  • The X authorization rung has not passed end to end: X refuses the headless sign-in; the headed export is the way in.
  • libid-rs's profiles::GITHUB still declares secret_field: Some("client_secret"); the suite lays the request out with secret_field: None per libid#35 until the table follows.

@SupremaLex
SupremaLex force-pushed the feat/callback-artifact-retrieval branch from daebcf3 to 14bbdb2 Compare September 14, 2026 12:04
@SupremaLex
SupremaLex force-pushed the feat/live-ceremony-tests branch from de77edd to f4d0158 Compare September 14, 2026 12:04
@SupremaLex
SupremaLex changed the base branch from feat/callback-artifact-retrieval to feat/ceremony-github-token September 14, 2026 13:18
@SupremaLex
SupremaLex force-pushed the feat/live-ceremony-tests branch 7 times, most recently from 96f4001 to 5023921 Compare September 16, 2026 10:09
Behind `--features live-ceremony`, `tests/ceremony` runs the real binary
on a configuration file, reads `/config` from it as an application would,
and runs the notarized sessions of the GitHub and X ceremonies with a
prover of its own through an in-process notary built from `libid-tlsn`.
Each record is checked against the rules the Platform Verifier applies:
exact coverage, the request revealed whole, the bearer's framed
commitment, the one bearer header, the two identity members.

GitHub's token request is the canonical five-field form, the published
`tokenExchangeCredential` last as `client_secret`, laid out whole; a
record laid out with the credential committed is refused. Three GitHub
rungs need no account: a refused code and a refused credential fail the
token session with GitHub's own `error`, a bearer GitHub did not issue
fails the identity session. The fourth signs in as the test account in
Chrome, with the password and the TOTP code, authorizes the App and runs
both sessions. X's rungs run the same way: a refused bearer, and a real
authorization from a signed-in Chrome.

`tests/common/bridge.rs` starts the binary and speaks HTTP/1.1 to it for
both `tests/startup.rs` and the suite; `routes::CONFIG_PATH` is public.
The libid-rs crates, the TLSNotary fork and its mpz patches are optional
dependencies of the feature.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The README's Testing section names the rungs, their variables and the
export. `ceremony.yml` runs the rungs that need no account on pull
requests, adds the GitHub authorization on schedule and dispatch, and
runs the X authorization on a dispatch with the `x` input.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
@SupremaLex
SupremaLex force-pushed the feat/live-ceremony-tests branch from 5023921 to 9a6df80 Compare September 16, 2026 14:20
X examines a fresh sign-in, and a run nobody is watching has no answer for
what it asks, so an unattended run no longer opens X's sign-in pages: a
saved session that does not authenticate stops the rung and names the
headed export that makes a new one, and the export itself runs only in a
visible Chrome. The rung says which way X took it, the consent screen or
a redirect straight back for an app this account has already authorized.

It runs on a weekly schedule of its own beside the dispatch, because a
saved session and the account behind it degrade over months: the run that
finds them gone is better a scheduled one than the next demo. The two
schedules carry their own concurrency group, so neither cancels the
other, and the nightly rungs skip the weekly one.

Assisted-by: Claude Opus 5
Signed-off-by: SupremaLex <georglutsenko@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.

1 participant