Repository navigation
Conversation
…n holds Every value a prover must get exactly right -- the authority it names, the path it requests, the field names it reveals, whether the exchange commits a body credential -- was a caller argument with no home. `libid-transcript` had none of it: every occurrence of `login`, `username` or `api.x.com` in the crate was a test fixture. So each caller kept private copies, and a wrong one failed on chain, where the error names an offset rather than the constant behind it. `ceremony::profile` is that table. It mirrors `CeremonyProfile.sol` and the per-platform verifiers, and it is a SECOND COPY, which is the whole problem with writing one: a copy nothing checks looks authoritative and drifts in silence. So it is checked. `CeremonyProfile.t.sol` pins each platform id and authority id to a value computed with `cast keccak`, independently of that library; `profile_vectors` hashes the strings here and asserts the same hex. Two implementations now measure themselves against one set of numbers, so a string edited on either side turns one of them red instead of both staying green while a notary names a platform no verifier is registered for. Deriving those ids instead of pinning them would not do the same job, and it is worth saying why: a derivation follows whatever string is present, so `gitgub` would produce a fresh id and agree with itself perfectly. The literal is the pin. The contract makes the same choice -- its constants ARE `keccak256(bytes( "x"))`, and its test asserts them against a number computed elsewhere. What the table does NOT restate, it derives: `request_line` from method and path, so the two cannot disagree with it and the trailing space that stops `/user` matching `/users/me` cannot go missing; `attestation_count` from the sessions present, which is what REQ-COMMON-41 asks and what `CeremonyProfile.attestationCount` does. The end-to-end test now takes its layout arguments from the table rather than restating them, so the table has a caller and is not a third copy waiting for one. Still one-sided: the request lines and identity field names have no vector on the contract side, only the fixtures its verifier tests decode. Closing that needs a matching test there. Assisted-by: Claude Opus 5 Signed-off-by: xgreenx <xgreenx9999@gmail.com>
Contributor
Author
|
Closing while the generated version is built in The table here is hand-written, and the plan is to replace it with a generated, published crate driven by a profiles spec beside Nothing is lost: the values were each verified against the contract, and its keccak vectors pass against the numbers Reopens as a consumer of the generated crate once the contracts side lands. |
This was referenced Sep 8, 2026
Merged
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.
Stacked on #2 (
feat/ceremony-constructions). Independent of #9 — different files, no overlap.What was missing
Every value a prover must get exactly right was a caller argument with no home.
libid-transcriptheld none of it: every occurrence oflogin,username,api.x.comorapi.github.comin the crate was a test fixture. Each caller kept private copies, and a wrong one failed on chain, where the error names an offset rather than the constant behind it.api.x.comgithub.comCeremonyProfile.AUTHORITY_*api.x.comapi.github.comPOST /2/oauth2/token,GET /2/users/mePOST /login/oauth/access_token,GET /user_tokenRequestLine,_identityRequestLineid(string),usernameid(integer),login_identityFields()client_secret_tokenSentCommitments()0 vs 1Why a second copy is safe here
It is a second copy, and that is the whole problem with writing one — a copy nothing checks looks authoritative and drifts in silence. So it is checked.
CeremonyProfile.t.solpins each platform id and authority id to a value computed withcast keccak, independently of that library. The newprofile_vectorstest hashes the strings here and asserts the same hex. Two implementations now measure themselves against one set of numbers: a string edited on either side turns one of them red, instead of both staying green while a notary names a platform no verifier is registered for.Deriving the ids instead would not do the same job. A derivation follows whatever string is present — rename
githubtogitguband it produces a fresh id and agrees with itself perfectly. The literal is the pin. The contract makes the same choice: its constants arekeccak256(bytes("x")), and its test asserts them against a number computed elsewhere.What it derives rather than restates
request_line()from method and path, so the two cannot disagree with it and the trailing space — which is what stops/usermatching/users/me— cannot go missing.attestation_count()from the sessions present, which is what REQ-COMMON-41 asks and whatCeremonyProfile.attestationCountdoes. A count written beside the list can disagree with the list.uri()for the absolute formprover_generictakes before rewriting to origin-form.It has a caller
ceremony_end_to_endnow takes its layout arguments from the table instead of restating them, so this is not a third copy waiting for a consumer. The real consumer is the GitHub Token Service, which is the one session the deployment runs.Verification
cargo +nightly fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace— 107 passed (11 new: 7 table, 4 vectors)Still one-sided
The request lines and identity field names have no vector on the contract side — only the fixtures its verifier tests decode. Closing that needs a matching test in
libid-contractsassertingkeccak256of each verifier's literals against the same numbers. Worth doing; not in this PR.The stronger form, which this repo already has a precedent for in
handles.json+regen-identity-handles.py --check, is to generate both sides from one source with a CI drift check — removing the second copy instead of checking it.