Repository navigation
docs(specs): define CCDP and ceremony service contracts - #13
Conversation
1795a93 to
42298b3
Compare
cebe2ed to
f055711
Compare
b98ba12 to
da4f501
Compare
52db663 to
4deb2a5
Compare
fe6cf6c to
1add82b
Compare
9074ebb to
04bb956
Compare
xgreenx
left a comment
There was a problem hiding this comment.
Reviewed at aaed5c1 against #28 (3804442) and libID-bridge-rs v0.5.0. Messages, fragments, routes, events, response headers and the Bridge config record match both implementations, every ID and cross-file anchor resolves, and lint adds 0 errors and 5 warnings. The gaps are in what the text leaves unsaid: the 7 comments marked Medium are where an independent implementer would build something incompatible, and the rest are wording and stale text.
Outside the diff:
| One CCDP Distribution serves Applications admitted by any number of independent | ||
| OAuth Bridges without a Distribution-wide allowlist. Prefetch uses | ||
| `allowedApplicationOrigins: '*'` for public asset fetching and authenticates | ||
| the exact Application peer. The Application exact-authenticates the configured |
There was a problem hiding this comment.
Medium: the Application side of connection setup is not specified. This says the Application authenticates only the CCDP origin, but Callback runs on the Bridge origin, so #28 admits the origin of redirectUri and ccdpOrigin (ccdp/client/client.ts:69). No REQ says the Connection ID equals ceremonyId (implied at :389 and :772, enforced at client.ts:94), and the rule that prover.started, IdentityProof and UserDenied count only from ccdpOrigin lives only in ccdp/client/ceremony.ts:257-261. An Application written from this text rejects Callback or never authenticates, so state all three rules here.
There was a problem hiding this comment.
Fixed in bbc336a: the Application uses the ceremony ID as the connection ID and admits the exact Bridge and CCDP origins (deduplicated if equal). Prefetch completion, Prover readiness, identity proof and user denial are accepted only from the CCDP origin.
| | [`CeremonyFailed`](#ceremonyfailed) | Prefetch, Callback, or Prover → Application | connection acceptance | at most once; reports technical failure and ends the run | | ||
| | [`Event`](#event) | Prefetch, Callback, or Prover → Application | connection acceptance and the event's documented emission point | core occurrences follow the [event catalog](#core-events); additional observations do not advance the protocol | | ||
|
|
||
| Every recipient requires a plain record with the exact fields, types, and bounds |
There was a problem hiding this comment.
Medium: the bounds "defined below" are never defined. :451 says "bounded" and :502 "bounded by the implementation", yet a decoder rejection fails the connection (REQ-POPUP-MSG-04). The implementation enforces an event-name slug ^[a-z][a-z0-9-]{0,63}$, 2048-byte failure text, 16 attributes, 128-byte attribute text and a 64-byte operationId (ccdp/limits.ts, primitives.ts:49-55), so an Application and a Distribution released apart can kill a ceremony over an extension event. Add a v1 limits table (identity strings can cite platform-ceremonies.md:109-116), or make over-bound extension events ignorable.
There was a problem hiding this comment.
Added the v1 wire-limits table and corresponding conformance coverage in bbc336a, including event/name grammar, failure text, attribute count/value bounds, operation IDs and finite numeric values. Over-bound extension records fail decoding; valid extensions remain observable without advancing the protocol.
| | Parameters | The complete frozen URL is opaque to CCDP. The selected platform ceremony version owns its parameters. | | ||
| | Location and context | Selected OAuth Platform; top-level ceremony-popup document | | ||
| | Role | Owns login and consent during [Authorization to Callback](#2-authorization-to-callback). No CCDP participant runs and no CCDP message or popup connection is exposed to this document. | | ||
| | External policy | Controlled entirely by the OAuth Platform. CCDP assumes nothing about its markup, scripts, headers, or origin transitions; it may sever the opener or browsing-context group. Callback reconnects without assuming direct window continuity. The selected platform ceremony version owns authorization request and return semantics. | |
There was a problem hiding this comment.
Medium: Callback's fallback is referenced but undefined. "Callback reconnects" here, "the configured popup fallback" at ccdp-distribution.md:312-313 and "deployment-policy sources" at oauth-bridge.md:61 have no input, carrier or CSP source behind them. Both implementations configure none, and libID-bridge-rs serves connect-src 'none' (src/artifact/mod.rs:108). State that v1 Callback has no fallback carrier and serves connect-src 'none', drop "deployment-policy sources", and say here that Callback fails locally when the platform severs the opener (ASM-CCDP-02).
There was a problem hiding this comment.
Clarified the optionality rather than prohibiting fallback in v1. Without a configured fallback, Callback uses connect-src 'none'; a severed opener fails locally without releasing the return or reporting denial. If a deployment supplies a fallback, it must preserve the same authentication and permit only its fixed required CSP sources. No fallback implementation is required by this PR.
| {"github":[1],"google":[1],"x":[1]} | ||
| ``` | ||
|
|
||
| The path is unversioned, beside `/ccdp/callback.html`: the set is a property of |
There was a problem hiding this comment.
Medium: versions.json cannot describe more than one CCDPVersion. REQ-DIST-05 now lets a release include several CCDPVersions, each with its own Prover, but this list is keyed by platform only, and nothing tells the Application which CCDPVersions are served. A dropped version leaves /ccdp/v{N}/prefetch as a 404 page that ends only when the user closes the popup, and each version's Prefetch registers its own worker.js at the root scope (:118). Require every included CCDPVersion's Prover to bundle exactly the listed pairs, list the included CCDPVersions so an Application can refuse before launch, and name one shared root Worker script.
There was a problem hiding this comment.
Implemented in bbc336a: /ccdp/versions.json now names ccdpVersions and the shared platforms map. Every included CCDP version supports exactly those pairs, and the Application checks protocol availability before Prefetch. All included versions register the shared /ccdp/worker.js at root scope. These are spec changes that need corresponding implementation follow-through.
| registered to that client, so a site borrowing another deployment's client | ||
| cannot receive its evidence. Depends on ASM-PROV-01, ASM-BROWSER-01. | ||
| The Identity Platform delivers an OAuth client's initial authorization | ||
| response only to that client's registered redirect origin. Subsequent browser |
There was a problem hiding this comment.
Medium: a borrowed client can receive the response under *. This still says borrowing another deployment's client "does not authorize receipt", and §Security (:1508-1510 and :1515-1518, outside the diff) says each Application deployment owns its own client. Bridges now admit patterns and * (oauth-bridge.md:58, ccdp.md:699-703), and a * Bridge releases IdentityProof to any origin. Qualify both by "Application origins the serving Bridge's allowlist admits", and replace the per-deployment-client sentence with Bridge-owned clients.
There was a problem hiding this comment.
Fixed both the property and security prose in bbc336a. Receipt depends on the serving Bridge's allowlist; * deliberately admits any Application origin. OAuth registrations belong to the Bridge, not necessarily to each Application deployment.
|
|
||
| For Callback, the bridge is a configuration-inserting, cached proxy to the | ||
| [CCDP Distribution](ccdp-distribution.md#callback-artifact). It neither | ||
| implements the document nor requires a TypeScript build. It serves no other |
There was a problem hiding this comment.
Low: implementation details in normative text. "Requires a TypeScript build" here, the new URL('/auth/callback', oauthBridge).href expression at :235, and build steps in ccdp-distribution.md (:210 "The build produces", :264 "built and tested", :278 "without dynamic import") tie conformance to one toolchain, which the PR description excludes. Describe the result instead, e.g. "redirectUri is the Bridge origin with path /auth/callback".
There was a problem hiding this comment.
Rephrased these as observable results: a fixed Bridge-origin /auth/callback URL and a self-contained Callback response whose selected implementation starts without another entry-script request. No TypeScript build or particular URL-construction/import mechanism is prescribed.
| - SP-CLIENT-01: | ||
| The Canonical Runtime rejects evidence issued to an OAuth client other than | ||
| the one fixed by its immutable ceremony profile. Depends on ASM-PROV-05, | ||
| The browser Prover rejects a parsed OAuth client identifier differing from |
There was a problem hiding this comment.
Low: the client source is stale. SP-CLIENT-01 says "fixed by its immutable ceremony profile", and so does REQ-COMMON-17 (:848-851, outside the diff), but a Platform Profile carries no client. REQ-PLAT-47 and REQ-PLAT-62 use the client frozen by the Application. Say "the client frozen for the ceremony" in both.
There was a problem hiding this comment.
Updated SP-CLIENT-01 and REQ-COMMON-17 to use the client frozen for the ceremony. Platform profiles continue to own protocol constants, not deployment-specific OAuth client IDs.
| only after authenticating the exact Application origin against the deployment | ||
| allowlist. The Canonical Runtime MUST carry the response only to the configured | ||
| Prover, preserving that authenticated origin restriction as defined by CCDP | ||
| (REQ-CCDP-03, REQ-CCDP-04). The set MAY contain more than one origin. |
There was a problem hiding this comment.
Low: "The set" has no antecedent after the rewrite, and the allowlist also admits patterns and *. Suggest: "The allowlist MAY hold several exact origins, origin patterns, or *."
There was a problem hiding this comment.
Removed the dangling 'The set' wording and referenced the canonical origin-allowlist rules, including exact origins, patterns and *.
| The Platform Verifier MUST compare the attested authority and the revealed | ||
| method and path with this profile under common REQ-COMMON-21A. | ||
|
|
||
| - REQ-PLAT-46 (upholds SP-EXCHANGE-01): |
There was a problem hiding this comment.
Low: X's twin checks kept the old wording. This PR moved GitHub's checks to the Prover against Application-frozen values (:1039-1055). The X equivalents REQ-PLAT-29 (:497-501), REQ-PLAT-29C (:582-588), REQ-COMMON-21B (ceremony-common.md:1166-1168) and TEST-PLAT-09C (:1268-1271) still say Canonical Runtime and "deployment profile", a term no spec defines. Apply the same wording.
There was a problem hiding this comment.
Aligned the X requirements, common request-binding rule and TEST-PLAT-09C with GitHub: the Prover checks against the Application-frozen client/redirect values, while the platform profile owns request constants. Removed the undefined deployment-profile wording.
| GitHub token exchange and identity notarization run in the browser; there is | ||
| no confidential exchange service. The identity platform | ||
| The Application, OAuth Bridge, and CCDP Distribution may have different operators. | ||
| They control their frontend, redirect deployment, public OAuth configuration, |
There was a problem hiding this comment.
Low: four items, three actors, "respectively". Suggest: "The Application controls its frontend, the OAuth Bridge its redirect deployment and public OAuth configuration, and the CCDP Distribution its browser code." The Application operator row (:94) still "configures clients", which now belong to the Bridge.
There was a problem hiding this comment.
Corrected both the control-boundary sentence and the Application-operator row: the Application owns its frontend, the Bridge owns its redirect deployment and public OAuth configuration, and the Distribution owns its browser code.
|
@xgreenx Posted individual replies in all 28 review threads for bbc336a and the signed preflight clarification c50d024. The threads distinguish applied fixes, intentional choices and out-of-scope details; leaving them open for your re-review. Cross-cutting notes from the review body: corrected the Redirect Runtime definition and refreshed the PR description's merged-dependency/implementation status. Validation: site tests and build pass. Full-suite spec lint remains at 80 errors / 97 warnings, versus 80 / 98 before the review fixes; no new diagnostics were introduced by those fixes. This remains a documentation-only change, with implementation follow-through called out in the relevant threads. |
|
versions.json is now
|
Preserve downstream verification while assigning local parsing and correlation to Prover, keeping original requirement IDs and the framing prerequisite. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Keep the readable routes, messages, events and phases. Delegate transport to the popup specification and move implementation APIs, build tooling and qualification to the implementation PR. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Report valid OAuth denial with one-way Denied. Application cancellation retires the local run; popup navigation and closure stay composition-owned. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Define Event as an interface like the other messages. Nest operationId and attributes under optional instrumentation and preserve their validation and protocol boundaries. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Rename Denied to UserDenied and Abort to CeremonyFailed, including wire discriminators, message links, conformance cases, and sequence labels. Preserve outcome semantics and local cancellation. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Accept a resolved notary address for any platform without requiring notarization. Keep nullable wire input and let the selected profile require an address only when needed. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Align browser identity ownership, GitHub routing and admission, and transport failure and closure semantics. Distinguish ledger-local verifier versions from ceremony versions without changing the browser flow or ledger verification. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Incorporate PR #31 commit 5afdf08: maximal JSON whitespace runs, single-range extraction with cross-range delimiter counting, header conformance vectors, and matching rationale. Preserve the reconciled browser validation, request-selected notary, and origin admission contracts. Correct the stale code_verifier circuit attribution and GitHub token-layout reference. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Allow same-origin trailing-slash redirects for directory paths that serve no resource, ending in an inert failure. Keep actual protocol and asset resources redirect-free and update the existing conformance case. Assisted-by: GPT-6 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Publish and forward the public token-exchange credential, reduce the Bridge to configuration and Callback hosting, and remove the GitHub event and network-policy exceptions. Transcript and proving semantics are reviewed in a separate profile PR. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Keep browser exchange as the current profile choice without prohibiting Bridge-assisted OAuth for another platform or later ceremony version. Scope route exclusions and tests accordingly, and remove stale confidential-endpoint prose from the Distribution boundary. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Rename the public PlatformConfig and ProveIdentity field and its prose references. Keep the OAuth client_secret parameter and all validation semantics unchanged. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Admit Origin-less configuration GET requests using browser same-origin Fetch Metadata without checking the Bridge origin against the allowlist. Keep explicit Origin admission and Callback authentication unchanged; leave redirect construction with the Application and registration with the operator. Update conformance cases. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
* docs(specs): admit origin patterns in application allowlists An operator serving one application per subdomain cannot enumerate them, so an allowlist member may now be a pattern; the popup transport owns its grammar and match, and no layer restates them. The union stays literal, so a pattern never absorbs the CCDP origin, and the peer is still bound to the exact observed origin. What widens is the type of an existing Callback input: an older Callback accepts a pattern and matches no peer, so a deployment waits for a Callback that understands one. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(specs): refuse star members and member spellings as origins A URL parser reports a suffix with a port, a trailing dot or an empty label canonical, so well-formedness needs byte checks the parser does not do, and the spec now names them rather than leaving each layer to infer them. A star that is not a well-formed pattern is a typo, not an exact origin, and admitting it only postpones the failure to a ceremony that never becomes ready. Canonicality is tested ahead of membership of either kind, because the literal branch otherwise lets a peer claim a member's own spelling and bind it. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(bridge): refuse IP-literal suffixes and a pattern CCDP origin A URL parser reports 127.0.0.1 canonical and every byte check passes it, so the grammar admitted https://*.127.0.0.1 although an address has no labels to delegate and the loopback exception covers exact hosts only. The CCDP origin reaches the Callback's frame-src, so a pattern in that input would reach a Content-Security-Policy, and the conformance list now says so. Rejecting an Origin that spells a pattern is the rule that a member spelling is never an observed origin, not an exception to literal membership. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(specs): match the merged client's origin pattern grammar The merged popup package spells a member `*.handles.link`, admits every depth below the suffix, and accepts a one-label suffix, so the grammar described here named a list the client rejects outright. The bridge refuses `*` and a one-label suffix at startup: refusal narrows admission, so the two sides cannot disagree, and an operator reads an error rather than a ceremony that never becomes ready. Member validation now happens in the popup endpoint, not the Callback. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(bridge): the bridge admits a subset, not exactly the same set A bridge refuses an origin carrying a byte its own policy composition cannot name, so it admits fewer origins than the browser side under the same member. Only the reverse would let the two disagree. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(specs): state the origin pattern rules, argue them less The pattern prose restated the transport's grammar and matching rule, then explained each rule it stated. A restated normative statement can drift from the one that owns it, and an explanation is not a requirement. The rules are unchanged: the member kinds, the two startup refusals, the subset relation and its direction, the literal union, the exact echoed origin, the deployment ordering, and where member validation happens. The cut examples and the rejected spellings live in the TEST entries. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(specs): the bridge narrows no allowlist the transport admits An allowlist as wide as `*` is the responsibility of whoever configures it, so the bridge-only startup refusals come out. The two versioning rules disagreed; the artifact contract now owns one coordinated-upgrade exception and the bridge cites it. TEST-CCDP-04 rejected any member's own spelling, which also rejects an exact member's ordinary case; it is a pattern member's spelling. Restored: the bound on the MAY, canonicality ahead of membership of any kind, and Callback's exact-authentication. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(bridge): the bridge narrows no member on width The byte filter it applies to every origin it reads is a member refusal, so a blanket claim that it adds none contradicts the paragraph below. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(bridge): the bridge and the browser admit the same origins The subset language covered a byte filter the bridge no longer applies to application origins. Only the CCDP origin becomes a policy source. Assisted-by: Claude Opus 5 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> --------- Signed-off-by: Green Baneling <XgreenX9999@gmail.com>
* docs(specs): platform ceremony versions come from the Distribution The Bridge advertised whatever versions its deployment listed, unconnected to what the Distribution serves. The Distribution now publishes the pairs it bundles at /ccdp/versions.json; the Bridge reads it, names one public client per version, and answers 503 on the configuration route until it has accepted one. Signed-off-by: Green Baneling <XgreenX9999@gmail.com> Assisted-by: Claude Fable 5.1 * docs(specs): the Application reads the version list, not the Bridge Green revised the design: the Bridge is static again and publishes only its OAuth clients, a default per platform with optional per-version overrides; the Application fetches the Distribution's versions.json cross-origin and resolves versions and clients itself. The Bridge-side retrieval, the 503 before the first accepted list, and the per-version record are withdrawn. Assisted-by: Claude Fable 5.1 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> * docs(specs): one OAuth registration per platform, no version overrides Per-version override clients are dropped at the reviewer's request (2026-09-29): one public OAuth registration per configured platform, independent of ceremony versions, and every enabled version runs it. The Application still resolves versions from the Distribution's list. Base sentences return where they fit, shrinking the diff against the base. Assisted-by: Claude Fable 5.1 Signed-off-by: Green Baneling <XgreenX9999@gmail.com> --------- Signed-off-by: Green Baneling <XgreenX9999@gmail.com>
Add the title metadata required by the documentation site now on main, including the existing popup transport specification. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Align Google delivery checks with ceremony implementation efac53c: preserve Prover nonce parsing without an expected-digest input, and require public-input consistency at generation and application acceptance. Update dependent tests and security claims without adding browser cryptographic verification or changing CCDP messages. Include GitHub token exchange in the existing Proxy transport summary. Assisted-by: GPT-5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
REQ-DIST-05 required old immutable URLs to stay available while any live ceremony might reference them, which forces every release to carry every earlier compatible release's assets. An origin now serves one release at a time: each CCDPVersion the Publisher includes serves its latest compatible release, and nothing from earlier ones. A ceremony running across the switch can fail and is started again. Older CCDPVersions stay available for as long as the Publisher includes them. Assisted-by: Claude Opus 5.5 Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Clarify connection ownership, client configuration, evidence fields, and security claims. Standardize wire bounds, optional fallback behavior, shared version discovery and Worker ownership, and browser cache reconciliation. Keep Bridge client-ID startup validation outside the specification. 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>
No released build registered a nested /ccdp/v1/ Service Worker, so the distribution needs no rule for one. Participants still resolve the canonical root registration. Signed-off-by: Wondertan <hlibwondertan@gmail.com> Assisted-by: Claude Opus 5.5
Normative browser and service contracts
CCDP relies on the popup transport specification merged in #22. The framing changes from #31 and GitHub browser-exchange profile from #35 are integrated; those are no longer pending companion PRs.
The readable protocol structure is retained, with stable requirement/test IDs and explicit security boundaries. Prover parses and correlates evidence; Application validates the delivered structure and frozen platform/client binding; ledger verification remains authoritative. Neither browser endpoint adds local notary-signature verification or a separate Google nonce-versus-expected-digest comparison.
Implementation APIs, module layout, proving/notary integration, build tooling, UI, metrics, and qualification live in the ceremony package on main, merged through #28 and subsequent changes. This PR changes specifications only. The previous architecture snapshot remains available.
The review pass clarifies connection/origin ownership, frozen client configuration, raw identity fields, and wildcard security claims. It also defines v1 wire limits, optional carrier-fallback behavior, multi-CCDP-version discovery through
versions.json, a shared/ccdp/worker.js, Worker-owned browser-cache reconciliation, and Callbackframe-src 'none', with conformance coverage.Implementation follow-up is needed for the updated version-list shape, shared Worker path and cache reconciliation, and Bridge CSP/origin handling. Bridge startup validation of client IDs is deliberately not standardized here. No mandatory Callback refresh interval or field renaming is introduced.