Skip to content

Ceremony Cross-Document Protocol (CCDP)

This document defines the browser protocol across the Application and the documents it uses. It owns ceremony locations, navigations, messages, ordering, and compatibility. Authorization, platform-proof, and final-proof semantics are defined by the normative common ceremony and platform ceremony specifications.

CCDP uses the authenticated logical connection defined by Popup transport. That specification owns authentication, delivery, navigation, isolation fallback, and continuity; CCDP owns the protocol carried over it. Connection authentication exposes the authenticated peer origin to each participant, including after a fallback or replacement. Delivery remains best effort across replacement: CCDP does not turn carrier readiness into a delivery acknowledgement or replay messages lost during a transition. CCDP Distribution owns resource publication and response policies; the OAuth Bridge owns callback ingress and its API.

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

The actors, tables, and protocol prose below are the CCDP version-1 contract. Examples and the sequence diagram illustrate it. Package APIs, runtime type decoders, UI projections, tracing integrations, and proving algorithms are not part of this contract. The common specification’s Canonical Runtime spans the Application and its browser participants; Callback is its Redirect Runtime and Prover owns the platform-aware browser evidence checks. A Ledger Verifier means the downstream verification path defined in the common specification, not a browser component. CCDP ends at browser proof delivery; construction and submission of a ledger-specific Submission remain composition-owned.

  • ASM-CCDP-01: Browser origin enforcement, authenticated popup transport, and the configured Application, Bridge, and Distribution code execute correctly. Compromised code on any of those trusted origins is outside the browser credential-release guarantee; sharing an origin shares this failure domain.
  • ASM-CCDP-02: The OAuth Platform returns the selected profile’s response to its registered redirect URI. Headers or navigation may sever the opener. Authenticated carrier fallback is optional. Without one, a severed opener prevents Callback from reconnecting; CCDP does not promise completion when no connection can be made.
  • SP-CCDP-01 (depends on ASM-CCDP-01, ASM-CCDP-02): Against unrelated documents and origins, only the authenticated ceremony and frozen profile may release the captured return to Prover or initiate credential use. This does not defend against a compromised Application, Bridge, or Distribution, or certify user intent. Evidence: checked state/credential-flow invariants and supporting conformance tests, not proof of cryptographic soundness.

An actor is an operator or external system. An origin is the exact scheme/host/port authority used by browser security checks. A site is only the browser’s schemeful registrable-domain grouping: same-site actors may remain cross-origin and do not gain authority over each other. Application denotes both the actor and its top-level browser document when the distinction is immaterial. The CCDP participants are Application, Prefetch, Callback, and Prover. A recipient is the participant receiving a message; a composition is application logic which uses CCDP alongside other flows.

ActorBrowser authorityResponsibility
Applicationapplication originhosts the application document, owns the operation and ceremony state, and drives the protocol
OAuth BridgeOAuth bridge originpublishes ceremony configuration, serves the complete Callback document obtained from the Distribution with bridge-owned inputs, and owns OAuth registrations
CCDP DistributionCCDP origincontains the versioned resources and proving assets used by any number of OAuth Bridges; it may be the canonical libID distribution or an operator-selected replacement
OAuth PlatformOAuth-platform origin sethosts authorization/login documents and issues the OAuth return

The Application and OAuth Bridge may be operated together or independently; the selected CCDP Distribution may be published by either party or another one. Their origins may be same-origin, same-site, or cross-site. CCDP assumes none of those relationships. Browser authority is always established against an exact origin. A composition which continues the live popup connection after CCDP has the additional requirements below.

The OAuth redirect URI terminates on the bridge origin. The OAuth Bridge serves CCDP’s self-contained Callback artifact with its deployment inputs already inserted; artifact retrieval happens server-side, independently of OAuth requests. Callback captures and clears the OAuth return, then selects its bundled CCDP implementation without another browser request.

Multiple independently operated OAuth Bridges may select the same CCDP Distribution through its ccdpOrigin. The Distribution keeps no Bridge registry or reciprocal allowlist and exposes identical public CCDP resources across that relationship.

CCDP does not define documents, messages, or policy outside the ceremony. A composition may use the same popup and connection before or after CCDP, but those steps remain outside this protocol.

  • REQ-CCDP-01: The Composition MUST obey the popup transport’s continuity and origin rules when reusing the connection beyond CCDP. Necessity: completion does not transfer control to an unauthenticated document.

Carrying the live connection beyond Prover requires the next document to use the exact CCDP origin; same-site placement is insufficient. All code on that origin shares one browser authority and must therefore be mutually trusted.

Resources collectively means Prefetch, Callback, Prover, and Worker. Authorization is an external document, not a CCDP resource.

PropertyContract
Parameters
Name#ceremonyId#platformId#ceremonyVersion
Valueslowercase UUIDv4exact identifier from the selected platform profileunsigned 16-bit platform ceremony version
Location and contextCCDP origin; versioned, top-level, and non-isolated ceremony-popup document
RoleStarts the selected profile’s fetches before the Application continues through Prefetch to Authorization. It receives no authorization URL, OAuth return, or proof input.

Authorization GET platformAuthorizationUrl

Section titled “Authorization GET platformAuthorizationUrl”
PropertyContract
ParametersThe complete frozen URL is opaque to CCDP. The selected platform ceremony version owns its parameters.
Location and contextSelected OAuth Platform; top-level ceremony-popup document
RoleOwns login and consent during Authorization to Callback. No CCDP participant runs and no CCDP message or popup connection is exposed to this document.
External policyControlled 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.
PropertyContract
Location and contextOAuth Bridge origin at the fixed registered callback path /auth/callback; top-level, non-isolated document with complete bundled Callback code and bridge-owned deployment inputs
RoleAuthenticates the Application during Authorization to Callback, then privately carries the captured OAuth return in popup navigation to Prover during Callback to Prover. It installs no Service Worker, retains no state across navigation, and does not classify, prefetch, prove, verify, persist a checkpoint, or close the popup.
Failure and cleanupFailure before connection acceptance is displayed locally and cannot release the return; observable failure after acceptance uses CeremonyFailed. Terminal cleanup clears retained return bytes and releases listeners and references.
PropertyContract
Parameters
Name#ceremonyId#applicationOrigin#oauthQuery#oauthFragment
Valueslowercase UUIDv4exact Application origin authenticated by Callbackcaptured OAuth query, including leading ? when nonemptycaptured OAuth fragment, including leading # when nonempty
Location and contextCCDP origin; versioned, top-level ceremony-popup participant; cross-origin isolated before protocol readiness
Isolation replacement/prover/fallback under the same versioned base path; the same Prover participant, parameters, and protocol, using the popup transport’s isolation replacement when the primary response does not isolate
RoleAccepts the logical Application connection during Callback to Prover, then validates the retained OAuth return under the Application-selected profile and runs Prover execution. The selected platform ceremony owns its proof and evidence semantics.
Outcome and cleanupLocal proof delivery does not assert Application acceptance. Prover clears transient proving inputs and execution resources without closing or navigating the popup. UI is an implementation-owned projection of events.
PropertyContract
Location and contextCCDP origin; one content-addressed module Service Worker per release, shared by all included CCDP versions. Its response sets Service-Worker-Allowed: /; every Prefetch registers its build-pinned URL with scope: '/', following Worker selection.
RoleSupports selected-profile fetches and the popup transport’s same-origin continuity mechanism. It remains compatible with every included CCDP version, reconciles its asset cache under the Distribution contract, and does not intercept unrelated origin traffic.
PropertyContract
Location and contextCCDP origin; unversioned JSON that the Application fetches cross-origin before Prefetch
RoleNames the included CCDP versions and the platform ceremony versions available through each. The Distribution owns its grammar and response policy; the OAuth Bridge contract owns how the Application selects a version and client from it.
  • REQ-CCDP-02: Each Participant MUST use the frozen locations, state encoding, and compatible version selected below without silently selecting a newer version. Necessity: independently deployed documents must execute one protocol.

Prefetch and Prover routes are relative to {ccdpOrigin}/ccdp/v{CCDPVersion}. Worker uses the shared content-addressed path {ccdpOrigin}/ccdp/worker.{hash}.js, pinned by the Distribution rather than selected by the Application. Its hash is independent of CCDPVersion. Callback executes at the frozen redirectUri on the OAuth Bridge origin; the Distribution defines the public artifact the bridge retrieves to serve it. Authorization is the external frozen platformAuthorizationUrl, not a CCDP route.

Before launch, the Application freezes the CCDP origin, redirect URI, platform authorization URL, ceremony ID, platform ID, and platform ceremony version. This document defines CCDPVersion = 1. Before opening Prefetch, the Application checks that the Distribution’s version list includes that CCDP version; an absent version makes that protocol unavailable rather than launching into a missing route. The Application selects it in the Prefetch path, carries the same version through OAuth state, and uses the matching Prover path. Callback selects its bundled implementation from that state; fragments and messages do not repeat the version. Google returns state in the fragment, so the bridge cannot perform this selection at HTTP ingress.

Compatible implementation changes keep the version. A breaking fragment grammar, navigation order, message shape, direction, ordering, or validation rule increments it, publishes new document paths, updates the shared Worker, and adds that version’s implementation to the self-contained Callback artifact. The Worker supports all included versions, not just the most recently visited one. The previous version’s latest compatible release, including its bundled Callback implementation, remains available for a compatibility window, which ends when the Publisher stops including that version.

Once that window ends, a build may omit a retired Callback implementation. Its version then takes Callback’s local unsupported-version error path before connection setup, rather than requiring an older transport or error protocol. The popup owns that error display; it never falls forward to a different CCDP version or reports this failure as OAuth denial.

A later CCDP version substitutes its decimal version in the common path. The registered callback URL stays fixed: its document includes a closed set of supported implementations and enters the selected one directly. All browser documents execute embedded entry code. Internal bundle names are not protocol surface; obtaining Callback bytes from the Distribution does not change its OAuth Bridge execution origin.

The Prefetch and Prover paths select both CCDP version and document role.

Platform Ceremony Version is independent of CCDP version. The Prefetch fragment names it ceremonyVersion; the ProveIdentity field names the same value platformCeremonyVersion. Popup connection controls and the OAuth Bridge API are independently versioned as well.

  • REQ-CCDP-03 (upholds SP-CCDP-01): Each Participant MUST preserve, validate, clear, and restrict the URL inputs according to this section before using them.

The ceremony popup is a reusable browsing context, not an actor or document. It sequentially contains Prefetch → Authorization → Callback → Prover. Navigation creates a new JavaScript heap each time; no participant relies on document-local state surviving it. These origins may all be cross-site, and same-site placement grants no protocol authority.

Internal fragments use URL-search-parameter encoding after #. Producers emit each named field exactly once in the displayed order. Receivers require the exact field set, reject duplicates, and otherwise do not depend on parameter order.

The Prefetch and Prover routes have no query. Their fragments are never sent in HTTP requests and are copied and cleared before rendering, storage, or network use. Prover’s oauthQuery and oauthFragment are the sole internal credential-bearing navigation fields. They preserve the original two URL components separately, including empty values, with one outer URL-search-parameter encoding layer; decoding that layer reproduces the captured components without normalization or merging. The selected profile’s OAuth parser handles their contents later.

Callback constructs this fragment locally for the frozen CCDP-origin Prover; the Application receives neither the return nor the navigation target. The Prover captures and clears it before use. Any internal isolation replacement preserves the captured fragment and clears it again on arrival. No participant deliberately includes the captured return in a request query, connection notification, signaling record, Worker record, telemetry, or error. Opaque dependency error text follows the CeremonyFailed boundary, which does not promise automatic redaction. Proofs and other proving inputs never enter navigation fragments. The OAuth-platform-mandated query on redirectUri remains the sole credential-bearing HTTP-request URL.

CCDP is connection-neutral. It defines which document runs at each location, which participant initiates each navigation, what each message means, and their order. Each recipient validates its permitted inbound messages and enforces direction and state before acting.

  • REQ-CCDP-04 (upholds SP-CCDP-01): The CCDP participants MUST enforce the connection ID and origin restrictions below before accepting a protocol gate, releasing the OAuth return, or accepting proof work.

Origins use the canonical-origin and loopback rules owned by Popup transport, including its HTTP exception for exact localhost and 127.0.0.1 hosts at any valid port. Different ports, schemes, or those two hostnames remain different origins. URL-bearing fields retain their own path/query/fragment contract. The exception does not relax OAuth-platform TLS, external-resource policy, or browser secure context and isolation requirements.

The Application uses the ceremony ID as the popup connection’s Connection ID. Its popup-origin allowlist contains the exact OAuth Bridge origin of the frozen redirectUri and the exact configured CCDP origin, deduplicated when equal. It accepts prefetch-dispatch.finished, prover.started, IdentityProof, and UserDenied only from the authenticated CCDP origin; admission of the Bridge origin does not authorize those messages.

One CCDP Distribution serves Applications admitted by any number of independent OAuth Bridges without a Distribution-wide allowlist. Prefetch admits * for public asset fetching while still authenticating its exact Application peer.

Callback exact-authenticates the Application against its containing OAuth Bridge’s deployment allowlist under REQ-POPUP-ALLOW-01 and REQ-POPUP-ALLOW-02, before navigating with the captured return to the configured CCDP origin. The popup endpoint owns member validation and admission; Callback checks the input list’s structure and that the configured CCDP origin is a literal member. It sets applicationOrigin in the Prover fragment from that connection’s authenticated peer origin, never from OAuth parameters, request headers, a pattern member’s spelling, or an Application-supplied value. Prover requires that field to satisfy the canonical origin rule above and to contain no *, and admits only that exact applicationOrigin. Every document reading the field back applies the same two checks. Its connection authenticates the peer against that exact origin before readiness or proof requests, including after an isolation replacement or fallback-carrier selection. Missing or invalid origin input, an unavailable authenticated peer origin, or an origin mismatch fails locally before protocol readiness; it never falls back to open admission.

This is defense in depth against the opener navigating to another origin between Callback authentication and Prover’s fresh handshake: a retained window reference alone does not preserve its document’s origin. The fragment carries Callback’s restriction, not proof of the peer’s origin; connection authentication still establishes that. No additional handshake or configuration fetch is needed. This check does not protect against compromised code on an already trusted origin or change downstream proof verification.

Authenticated carrier fallback is optional and follows the popup transport’s authentication rules when configured at both endpoints. CCDP defines no signaling-service contract or mandatory fallback configuration. Without an available fallback, loss of the opener prevents Callback connection acceptance: it fails locally, releases no return, and sends neither CeremonyFailed nor UserDenied over a nonexistent connection. Prover’s same-origin isolation replacement does not repair this earlier failure.

The public Callback artifact contains no Bridge policy; the serving Bridge inserts its trusted configuration. Server-side artifact retrieval does not replace Callback’s credential-release check. Asset caching and popup-connection construction are outside CCDP.

  • REQ-CCDP-05 (upholds SP-CCDP-01): The Recipient MUST enforce the message shapes, permitted directions, state guards, and cardinalities in this section.

The following table is the complete CCDP version-1 message set.

MessageDirectionAccepted afterCardinality and effect
ProveIdentityApplication → ProverEvent(prover, started)exactly once; selects the profile for OAuth validation and proof execution
IdentityProofProver → ApplicationProveIdentity and valid OAuth acceptanceat most once; ends the Prover run
UserDeniedProver → ApplicationProveIdentity and valid OAuth denialat most once; reports platform denial without a technical error
CeremonyFailedPrefetch, Callback, or Prover → Applicationconnection acceptanceat most once; reports technical failure and ends the run
EventPrefetch, Callback, or Prover → Applicationconnection acceptance and the event’s documented emission pointcore occurrences follow the event catalog; additional observations do not advance the protocol

Every recipient requires a plain record with the exact fields, types, and bounds defined below. Unknown fields, coercion, normalization, defaults, and unrecognized discriminators are invalid. Messages outside the listed direction, predecessor, and cardinality are invalid. Unknown discriminators and decoder rejections fail the logical connection under REQ-POPUP-MSG-04; they are not silently ignored. A structurally valid message violating direction, predecessor, or cardinality fails an active ceremony without performing the invalid action. Denial, proof delivery, failure, and Application-local cancellation make later valid CCDP messages inert even when they race in transit; transport validation still applies.

These bounds are part of CCDP version 1, not deployment choices. Producers and recipients use the same bounds, including for otherwise unknown extension events. Text lengths count UTF-8 bytes; bounded text is nonempty and contains no Unicode Cc control characters. The selected platform may impose stricter semantic constraints on its own fields and proof.

FieldAccepted values
Platform IDs, event names, and attribute namesASCII matching ^[a-z][a-z0-9-]{0,63}$
ProveIdentity.platformCeremonyVersionInteger from 0 through 65535
ProveIdentity.clientId, IdentityProof.identity.oauthClientIdBounded text, at most 512 bytes
ProveIdentity.clientCredential, when present1–512 ASCII bytes in 0x21–0x7e
ProveIdentity.redirectUriBounded text, at most 2048 bytes; also satisfies the URI contract below
IdentityProof.identity.userId, IdentityProof.identity.userNameBounded text, at most 255 bytes; also satisfies the selected profile’s identity encodings
CeremonyFailed.messageBounded text, at most 2048 bytes
Event.instrumentation.operationId, when presentBounded text, at most 64 bytes; absent on core events
Event.instrumentation.attributes, when presentPlain record with at most 16 entries
String attribute valuesBounded text, at most 128 bytes
Numeric attribute valuesFinite numbers

Event.timestamp is finite and nonnegative. Boolean attribute values are also accepted. Empty instrumentation and attribute records are valid; empty text values are not. Other encodings, including nullable fields, follow their message and profile definitions. A malformed or over-bound extension is not an ignorable unknown event: it fails decoding under REQ-POPUP-MSG-04. Producers bound observations and failure text before sending them.

interface ProveIdentity {
type: 'prove-identity'
platformId: string
platformCeremonyVersion: number
clientId: string
redirectUri: string
codeVerifier: string | null
notaryAddress: string | null
clientCredential?: string
}

The Application sends the exact platformId and platformCeremonyVersion selected at launch. Prover requires that pair to be supported by its loaded implementation; this message selects its profile. The message is valid only after Event(prover, started). The remaining fields are the frozen client identifier and redirect, derived code verifier, resolved notary address, and optional public token-exchange credential. redirectUri is the canonical OAuth Bridge origin with the fixed /auth/callback path and no query or fragment. The Application derives it before OAuth; public bridge configuration carries no redirect field. The OAuth return is already retained by Prover and is not repeated in the message. Starting Prover initiates OAuth validation; it does not assert acceptance or mean that proof generation has already begun.

codeVerifier is null for a profile without PKCE; otherwise it is the canonical verifier derived under common §7.

notaryAddress may be supplied for any platform; a non-null value follows the origin policy. The Application can pass its resolved address uniformly without knowing which platforms use notarization. The selected platform ignores it when unused and requires a non-null address before starting work that needs notarization. The local HTTP exception needs no client option or environment override. A remote HTTP address is rejected, never upgraded or used as a downgrade fallback. The Application selects and freezes a supplied address before OAuth. Prover validates it before credential use and, when needed, derives the profile’s transport URLs from that same origin for all sessions, including the GitHub token request. Scheme conversion and endpoint paths do not select a different notary authority. It neither selects defaults nor accepts a separate profile, ledger identifier, hash, or testnet flag. The address changes network routing, not the proof statement or trusted signing keys.

When present, clientCredential is the nonempty printable ASCII value without whitespace frozen from the selected public platform configuration. Application forwards it unchanged; null, empty, wrongly typed, or whitespace/control-bearing values are invalid. The selected platform requires it before an exchange that needs it, and otherwise does not use it. GitHub uses it as client_secret. It is public application configuration, not a user access token, a signing key, or proof of the caller’s authority. Prover performs no configuration fetch.

The Application origin is trusted for this transient input because it already supplies the operation being authorized. It retains the authorization nonce; only the derived code verifier crosses this boundary. The message contains no authorization digest or operation data.

Prover exact-validates the CCDP record and selected platform/version before credential use. That profile parses the retained query/fragment pair, enforcing exact transport, fields, client/redirect checks applicable to the response, and success/denial grammar. It matches OAuth state to v<CCDPVersion>.<ceremonyId> using the versioned resource and the ID of the authenticated logical connection, not a second caller-selected expected state. The return is consumed once; no second request or replacement response can restart the run.

interface IdentityProof {
type: 'identity-proof'
identity: {
platformId: string
oauthClientId: string
userId: string
userName: string
}
proof: unknown
}

identity is a separate, exact-shaped record of prover-extracted strings: platform identifier, OAuth client identifier, user identifier, and userName. userName is the profile’s raw handle string (the signed email for Google), not a normalized handle or display label; normalization remains a separate consumption-time derivation under platform REQ-PLAT-08A through REQ-PLAT-08C. The selected platform validator checks their encodings and the platform/client binding to ProveIdentity. proof is the exact value defined by that platform ceremony version, without a nested identity copy. CCDP treats the proof as opaque; adding a platform does not change this message. Neither browser endpoint cryptographically verifies the delivered result; identity is non-authoritative until ledger verification. This browser delivery is not the common specification’s ledger-specific Submission. The composition adds its retained authorization and dispatch inputs without changing the delivered evidence.

interface UserDenied {
type: 'user-denied'
}

UserDenied reports only a valid, ceremony-bound OAuth-platform denial discovered by Prover while validating ProveIdentity. The Application resolves { status: 'denied' }. Prover sends it before token exchange, proof execution, or proving-operation events, never as a substitute for a failure.

Malformed, mismatched, or otherwise invalid OAuth returns use CeremonyFailed, not UserDenied. Denial has no acknowledgement and does not close or navigate the popup. Application cancellation is local, not a CCDP message; see Terminal outcomes.

interface CeremonyFailed {
type: 'ceremony-failed'
event: string
message: string
}

CeremonyFailed reports an observable technical failure after connection acceptance from whichever of Prefetch, Callback, or Prover is active. event is the nonempty, bounded, code-owned name of the failing core or implementation-defined operation; it is not a UI stage and need not have an earlier notification when failure preceded emission. message is bounded, opaque display text. Producers may preserve a caught error’s message or a thrown string, removing control characters and bounding its length. They do not serialize exception objects, stacks, nested causes, or arbitrary objects. There is no required code, reason enum, code-to-text mapping, or automatic credential-redaction guarantee: dependency error text may contain sensitive details. Recipients render it as text, never markup or control data, and exclude it from telemetry exports. The Application rejects the live ceremony.

Failure before connection acceptance has no CCDP path. Its display text may be rendered locally; an undeliverable report records a fixed local diagnostic, not the opaque error text. Reporting failure changes neither cleanup nor the ceremony outcome.

interface Event {
type: 'event'
event: string
phase?: 'started' | 'finished'
timestamp: number
instrumentation?: {
operationId?: string
attributes?: Record<string, string | number | boolean>
}
}
  • REQ-CCDP-06: Each Participant MUST emit and interpret the core events, extension boundary, and timestamps defined below. Necessity: readiness gates and observations must have the same meaning at independently deployed endpoints.

One event stream carries protocol readiness, operation timing, and additional platform observations. The same observations can drive UI or tracing; they do not require separate wire protocols. Event(name, phase) below abbreviates this record, not a distinct message type.

event is a nonempty core or implementation-defined operation name. phase marks an operation’s start or finish and is omitted for a single-shot observation. timestamp is a finite, nonnegative occurrence time in milliseconds on the browser’s epoch-relative performance timeline.

instrumentation is an optional plain record containing only the optional fields shown above. Its operationId is a nonempty identifier pairing repeated concurrent instances of the same operation. Its attributes is a bounded plain record of event-defined scalar measurements or facts; numeric values are finite.

Optional fields are absent when unused, not null. Event names, attribute names, string values, and record sizes follow the wire limits. Names and attribute meanings are code-owned, not supplied by OAuth returns or callers. No event contains a UI stage, display label, progress percentage, overall ceremony status, or error text. Technical failure uses CeremonyFailed.

The catalog includes Application-local observations so a complete timeline has one vocabulary. A local occurrence is not sent over CCDP. All other listed occurrences are required Event messages from the indicated document when their conditions are reached. Interrupted operations need not finish, and an inapplicable operation emits nothing.

EventStartFinish or observation
prefetch-dispatchApplication, locally before the first Prefetch navigationPrefetch, after authenticating the connection, registering the Worker, and dispatching selected-profile fetches. Permits Authorization navigation; downloads need not be complete.
authorizationApplication, locally when initiating Authorization navigationCallback, after capturing the OAuth return and authenticating the Application, before navigating to Prover. Includes the return and connection setup; asserts neither approval nor pure user-consent duration.
proverProver, after isolated connection readiness and installation of its CCDP handlers. Permits ProveIdentity; does not assert OAuth acceptance or ZK execution.Application, locally after accepting IdentityProof under the selected profile’s browser checks and assembling its result. Prover never sends this finish over CCDP.
prover-fallback—Prover, once after an isolation replacement, with the replacement navigation’s start timestamp and no phase. See Fallback timing.
token-fetchProver, when starting to obtain a usable access tokenProver, when that token is available, without waiting for its final attestation
token-attestationProver, when starting work to obtain the token attestationProver, when the complete attestation passes its required structural, request-binding, and commitment/opening checks
identity-fetchProver, when starting the platform identity requestProver, when its response has been received and parsed
identity-attestationProver, when starting work to obtain the identity attestationProver, when the complete attestation passes its required structural, request-binding, and commitment/opening checks
zk-proof-preparationProver, when starting input and proving-backend preparationProver, when both inputs and backend are ready
zk-proof-generationProver, when starting witness executionProver, when the ZK proof has been generated

prover-fallback is the only single-shot core event. Each core operation has one start and, on success, one finish per ceremony; these occurrences omit instrumentation.operationId. X and GitHub use all six proving operations; their token can become available before its attestation finishes. Google uses only the two ZK operations.

The six proving operations start only after ProveIdentity and valid OAuth acceptance. They may overlap according to the selected profile’s dependencies. Backend and input preparation need not wait for final attestations; proof delivery still waits for all required evidence. Events describe logical work, not a required scheduling algorithm or mutually exclusive execution intervals. No event adds browser cryptographic verification of proofs or attestations.

prefetch-dispatch.finished and prover.started are protocol gates, each accepted exactly once from its designated document at the matching phase. authorization.finished is emitted once before Callback departs, but does not require acknowledgement or introduce another gate before Prover readiness. Instrumentation or extension events cannot satisfy, duplicate, or bypass a gate.

Implementations and platform-version modules may add operation pairs or single-shot events without changing the message shape or core meanings. Extensions cannot reuse a core name for a different operation. Receivers validate the envelope and may ignore unknown extension names or attributes; they never treat those as readiness, cancellation, or success. Core names with wrong phase, sender, order, or cardinality are invalid, not extensions.

Repeated overlapping extension operations use instrumentation.operationId to pair starts and finishes; the identifier has no routing or authorization role and must not reuse ceremony IDs, OAuth state, credentials, or identity values. Observations and attributes contain no OAuth parameters, URLs or origins, identity data, proofs, witnesses, attestations, or raw exceptions.

The producing document may expose the same event locally before forwarding it to Application. Local observers require no Application roundtrip. Required events are emitted regardless of subscriptions; internal protocol handling precedes observer filtering, and observer/exporter failure cannot interrupt it. Application may merge local and remote events and derive UI stages, terminal status, or tracing spans. Such projections and telemetry export policy are outside CCDP, not additional messages or authorities.

Record the occurrence time as performance.timeOrigin + performance.now() when an operation starts or finishes. Preserve that timestamp across forwarding and delayed delivery; receipt time is not operation time. Independently running operations may overlap, and retrospective observations may arrive after later timestamps. Protocol ordering follows authenticated state and messages, never timestamp sorting. These browser observations are not the authenticated evidence timestamps of common §10 and never supply proof validity or metadata ordering.

Start/finish differences measure operation intervals. Do not sum overlapping intervals as total elapsed time, fabricate a finish after context loss, or turn missing observations into zero-duration work. Resource observations distinguish requests, shared-flight joiners, and actual network retrievals; a joiner or cache hit is not another download. The event model adds no separate collector, network export, or measurement acknowledgement.

The isolation replacement occurs during connection establishment, before ordinary CCDP delivery is available. The fallback document therefore records its navigation time origin as the occurrence timestamp of prover-fallback. Once its connection is ready, it sends that observation retrospectively, before Event(prover, started). The successful non-replacement path emits no prover-fallback.

prover.started.timestamp - prover-fallback.timestamp measures replacement navigation, document loading, and work up to Prover readiness. It excludes source-document work before navigation, such as preserving a port, and is not the exact additional cost against a hypothetical successful DIP path. This requires no stored timestamp, pre-authentication message, extra handshake, or new CCDP phase. If connection establishment fails, the observation stays local; it does not invent readiness or a completed interval.

CCDP upholds SP-CCDP-01 under ASM-CCDP-01 and ASM-CCDP-02. Authentication protects against other origins, not malicious scripts already served by a trusted origin. Callback’s carried applicationOrigin is a restriction that Prover authenticates against, not independently authenticated evidence.

A compromised Application can supply an operation the user did not intend. A compromised Bridge or Distribution can replace browser code and observe or withhold credentials. Browser isolation enables proving; it does not remove those code-supply-chain trusts. OAuth state binds a live ceremony, not human understanding of consent.

CCDP supplies no durable OAuth/proof recovery or guaranteed cancellation of already dispatched work. Background suspension, lost connections, and document replacement can prevent progress. A missing event or closed popup is never success or valid denial. Error text is deliberately useful for local debugging and is not guaranteed to be credential-free; the CeremonyFailed and Event contracts separate it from exported observations.

The selected platform and common ceremony rules own proof soundness, identity extraction, replay, freshness, and trust-root lifecycle. Browser structural checks do not authenticate notary signatures or verify generated proofs. Omitting those early checks delays some forgery/mismatch rejection to the Ledger Verifier; it changes neither its checks nor accepted proof statements.

  • REQ-CCDP-07 (upholds SP-CCDP-01): Each Participant MUST follow the phase guards, navigation ownership, and invariants in this section.

The protocol advances one named ceremony popup through Prefetch, Authorization, Callback, and Prover. Those route sections own each participant’s inputs, context, and role; Messages owns the records crossing the popup connection. The phases below own their sequencing, entry conditions, and exit conditions. Navigation retires the source document, and no later message can reactivate an earlier phase.

  • One live ceremony owns one authenticated popup connection. Connection ownership supplies message correlation and its private version; loaded resources supply the CCDP version. CCDP messages repeat neither.
  • Each participant accepts only exact records permitted by its direction, current state, and cardinality. Invalid sequencing fails an active ceremony; late valid traffic cannot change its terminal outcome. Malformed or unregistered records still fail transport under REQ-POPUP-MSG-04. Valid event extensions may be observed but never advance the protocol.
  • Browser-observed exact origins establish authority. Navigation history, request headers, and message fields do not substitute for connection authentication. Every accepted peer is bound to the one exact origin the browser reports, whether an exact allowlist member, an origin pattern, or * admitted it. A pattern member places every host under its suffix at any depth, and * places every origin, inside the trust boundary the transport assumes the operator controls.
  • Documents use only the frozen locations and fragments defined here. Messages select no document implementation or popup navigation destination. ProveIdentity carries the frozen platform selection and service-routing inputs used by that platform, not a navigation command.
  • Raw OAuth returns pass only from the cleared Callback capture to Prover’s private fragment, including any isolation replacement. Every arrival clears its URL before use; participants do not deliberately copy the return into an intermediate store, notification, or diagnostic. Opaque error text has the separate CeremonyFailed boundary. The platform-mandated callback query is the sole HTTP-request ingress exception.
  • Callback carries the return onward only after authenticating the Application. Prover validates it against the authenticated ceremony and selected profile before any credential-bearing request. Application receives only protocol outcomes and the final proof, whose evidence may contain profile-required disclosed fields. Authorization receives no CCDP message or connection.
  • Events report only their defined conditions. Required readiness events permit the next protocol action, but neither they nor other observations, carrier state, navigation, popup closure, or unvalidated proof delivery constitute ceremony success. Operation completion is not ceremony completion.
  • The Application owns terminal popup lifetime. CCDP outcomes do not initiate popup closure; documents still honor the popup transport’s authenticated closure control.
  • Cancellation and context-loss cleanup are best effort. CCDP has no durable checkpoint, ceremony recovery, or migration to another popup connection.

The protocol enters this phase on user activation. The Application records prefetch-dispatch.started locally, then initiates one named popup’s first navigation to Prefetch and establishes its connection there. A scripted opener may first reserve the popup at about:blank; if that fails, the same activation’s real anchor navigates it directly to Prefetch.

Prefetch clears and validates its fragment, accepts the connection, registers its build-pinned Worker, and awaits that Worker’s acknowledgement of selected-profile dispatch. It then sends Event(prefetch-dispatch, finished). Only after accepting that event from Prefetch, the Application records authorization.started locally and navigates the retained popup to Authorization at the frozen platformAuthorizationUrl. The Application owns this transition because it alone retains that URL; neither the URL nor a navigation command crosses the carrier. Authorization is not a participating document, so the navigation retires the Prefetch carrier while leaving the Application endpoint available for Callback.

Worker registration, activation, or selected-profile dispatch failure after connection acceptance sends CeremonyFailed for prefetch-dispatch instead of its finish event; Application rejects without navigating to Authorization. Download failure after successful dispatch remains an asset-cache concern and uses the normal cold-fetch path, not a late Prefetch failure. Failures before connection acceptance are reported locally and release no protocol message.

This phase begins when the Application initiates navigation to Authorization; CCDP cannot observe when the platform page loads. The OAuth Platform owns the popup and initiates browser navigation to the frozen redirectUri after approval or denial; neither CCDP endpoint initiates that transition. The Bridge serves the complete Callback, which captures and clears the return and enters its bundled CCDP implementation selected by state. The OAuth Bridge contract exclusively defines ingress.

Callback accepts the Application connection using the ceremony ID extracted from the captured state. This authenticates the Application against the Bridge’s deployment allowlist before the return can leave Callback. It sends Event(authorization, finished) before navigating onward. This reports return and connection readiness, not permission granted, and carries no OAuth-return data. Callback need not wait for an acknowledgement or Application scheduling before the Prover transition.

The popup-side Callback endpoint asks its connection to navigate to the frozen Prover location, supplying the ceremony ID, authenticated Application origin, and captured query/fragment as that route’s structured fragment. Callback owns this transition to keep the return private from Application and because the OAuth Platform may have severed Application’s direct popup handle.

Prover captures and clears the fragment, then accepts the same logical Application connection restricted to the carried origin under the origin policy. It sends Event(prover, started) only after cross-origin isolation is established and its CCDP handlers are installed. Connection establishment and any internal isolation transition are below CCDP: neither introduces another participant, message type, or phase. On the replacement path, the connected Prover first reports prover-fallback as specified in Fallback timing. The captured parameters survive that transition without passing through Application.

Application accepts one Event(prover, started) and sends one ProveIdentity using its frozen configuration and code verifier. It does not receive or parse the OAuth return. On receiving ProveIdentity, the selected platform/version validates the retained return before credential use. A valid denial sends UserDenied; malformed or mismatched input sends CeremonyFailed. Both end the run in this phase, as does context loss. Only valid OAuth acceptance enters Phase 4.

This phase begins only after Prover has validated and accepted the OAuth return in Phase 3. It runs the selected profile’s token exchange, notarization, and proof-generation steps as applicable. The profile determines which work runs in Prover and which, if any, uses a Bridge service. It sends the applicable core operation events and may add implementation or platform events. Each operation’s finished reports only that operation; events from overlapping operations are not forced into a global order. After all required proof and evidence work completes, Prover sends one IdentityProof, unless failure or context loss ends its work. Observable failure sends CeremonyFailed. Prover accepts no second proof request; Application-local cancellation makes any later delivery inert.

  • REQ-CCDP-08 (upholds SP-CCDP-01): Each Participant MUST treat the first valid terminal outcome as final and perform the cleanup described below without initiating popup closure or fabricating a successful operation finish.

Outcome cleanup releases ceremony resources, not the composition-owned popup connection. That connection remains available for navigation or closure under the popup transport contract, including after isolation.

Prover’s UserDenied reports valid OAuth denial; an active document’s CeremonyFailed reports failure; and IdentityProof delivers a proof. These outcomes are mutually terminal even when they race in transit. Denial resolves denied; an observable failure rejects the live ceremony. A failure before connection acceptance is reported locally. Application validates the delivered identity and selected platform/version proof under that profile’s browser checks and assembles its result before recording prover.finished locally. Neither endpoint adds local cryptographic proof or attestation verification; ledger verification remains authoritative.

The Application may cancel locally at any point, settling its run and ignoring late events or results before the composition navigates or closes the popup through popup transport. It sends no CCDP message and waits for no acknowledgement. Navigation or closure retires the current document; local cancellation alone does not stop remote proving. Neither path guarantees cancellation of already-dispatched server work.

Early failure, denial, and cancellation do not fabricate prover.finished; late traffic cannot reactivate the ceremony. Application-local event/status APIs are outside this protocol.

CCDP initiates no further navigation: the Application composition alone decides whether to retain, navigate, or close the popup because any subsequent flow is outside CCDP. Terminal cleanup follows the invariants.

The popup lifeline is one browsing context whose current document is replaced at every navigation; it does not imply shared document state.

sequenceDiagram
participant A as Application
participant P as Ceremony popup
Note over A,P: Phase 1 - Prefetch to Authorization
Note over A: Local prefetch-dispatch.started
A->>P: Navigate to Prefetch
P->>P: Prefetch accepts connection
P->>P: Prefetch registers Worker and dispatches selected-profile fetches
break Prefetch setup fails
P-->>A: CeremonyFailed
end
P-->>A: Event(prefetch-dispatch, finished)
Note over A: Local authorization.started
A->>P: Navigate away to Authorization
Note over A,P: Phase 2 - Authorization to Callback
Note over P: User completes login and consent in Authorization
P->>P: OAuth Platform redirects to redirectUri
P->>P: Callback starts and selects its bundled version
P->>P: Callback accepts authenticated connection
break Callback fails after connection acceptance
P-->>A: CeremonyFailed
end
P-->>A: Event(authorization, finished)
Note over A,P: Phase 3 - Callback to Prover
P->>P: Callback navigates to Prover with private return fragment
P->>P: Prover accepts connection with isolation established
opt Isolation replacement occurred
P-->>A: Event(prover-fallback), replacement navigation timestamp
end
P-->>A: Event(prover, started)
A-->>P: ProveIdentity
P->>P: Validate retained OAuth return
break Valid OAuth denial
P-->>A: UserDenied
end
break Invalid OAuth return
P-->>A: CeremonyFailed
end
Note over A,P: Phase 4 - Prover execution after OAuth acceptance
loop Applicable operations, possibly overlapping
P-->>A: Event(operation, started or finished)
end
break Prover fails
P-->>A: CeremonyFailed
end
P-->>A: IdentityProof
Note over A: Validate structure and assemble result
Note over A: Local prover.finished, completed

Terminal exits are shown without their cleanup details, which follow Terminal outcomes and the message contracts. Carrier mechanics and proof-generation internals are omitted.

Application, Prefetch, Callback, and Prover implementations conform to their roles together with the resource policies and popup transport they use. Required events remain required even when no UI or tracing subscriber is installed. Conformance tests support the browser guarantees; they do not prove the cryptographic properties delegated to the common and platform specifications.

  • TEST-CCDP-01 (exercises REQ-CCDP-01, REQ-CCDP-08): Proof delivery leaves the popup available for a same-origin composition; terminal traffic cannot restart CCDP.
  • TEST-CCDP-02 (exercises REQ-CCDP-02): Frozen version/path/state agree; unsupported or retired Callback versions fail locally rather than falling forward. An Application whose CCDP version is absent from the version list refuses before Prefetch navigation. Switching between included versions of one release uses the same Worker script URL and root registration; a changed Worker hash updates that registration rather than creating another scope.
  • TEST-CCDP-03 (exercises REQ-CCDP-03): Separate query/fragment bytes survive private navigation and isolation replacement, are cleared before use, and never appear in Application/control/signaling records. Duplicate or malformed fields fail.
  • TEST-CCDP-04 (exercises REQ-CCDP-04): Application admits the exact Bridge and CCDP origins and uses the ceremony ID as Connection ID. A different ID cannot bind that ceremony. When the Bridge and CCDP origins differ, Bridge-origin delivery cannot satisfy the Prefetch or Prover gate, deliver an identity proof, or report user denial, even though that origin is admitted. Public Prefetch authenticates its exact peer; Callback rejects an unadmitted Application; Prover rejects a different origin, including one occupying the same retained window after navigation. Callback rejects a peer claiming a pattern member’s own spelling, and Prover rejects a fragment carrying one. An exact member authenticates a peer observed at exactly that origin, and Prover receives that origin as applicationOrigin. Canonical HTTP loopback works at arbitrary ports.
  • TEST-CCDP-05 (exercises REQ-CCDP-05): Malformed, duplicated, wrong-direction, out-of-state, and post-terminal records cause no authorized action. Application-sent UserDenied is invalid. Proof payloads pass the selected platform version’s structural and result-consistency checks; userName preserves the profile’s raw handle string. An unknown discriminator or rejected decoder fails the logical connection; a structurally valid but out-of-sequence message fails an active ceremony without executing that transition. Late valid CCDP messages cannot change a settled ceremony outcome. A valid notary address is accepted for any platform, including one that does not notarize; null is accepted when unused but rejected before work requiring notarization. Malformed non-null addresses are rejected under the origin policy. A PKCE profile rejects a missing or malformed code verifier; a non-PKCE profile uses null. An optional public token-exchange credential is delivered unchanged; null, empty, wrong-type, or whitespace/control-bearing values reject. A platform requiring it rejects omission before exchange. No configuration fetch is made by Prover.
  • TEST-CCDP-06 (exercises REQ-CCDP-05, REQ-CCDP-06): X and GitHub each emit token-fetch and token-attestation independently; token availability does not imply attestation completion, and proof delivery waits for both attestations and the ZK proof. Only the designated core occurrences open gates; extensions cannot do so. Omitted instrumentation and either or both nested fields are accepted when valid; null, unknown instrumentation members, nonfinite attribute numbers, and top-level operationId/attributes are rejected. Overlap, occurrence timestamps, and retrospective fallback timing are preserved. Core and extension names and attribute keys accept a 64-character valid slug and reject 65 characters or invalid syntax. Failure text accepts 2048 UTF-8 bytes and rejects 2049; operation IDs and attribute text likewise test 64/65 and 128/129 bytes, including multibyte characters. Sixteen attributes pass; seventeen fail. Empty text and control characters fail. A valid unknown extension may be ignored; an over-bound one fails decoding regardless of subscriptions.
  • TEST-CCDP-07 (exercises REQ-CCDP-07): The four phases preserve navigation ownership and credential privacy; approval enters execution, bound denial exits before proving, and malformed returns abort. A stale active Worker cannot satisfy the prefetch-dispatch gate. Failure to establish or dispatch to the build-pinned Worker reports CeremonyFailed without Authorization navigation; successful dispatch does not wait for downloads. With no carrier fallback configured, an OAuth-severed opener leaves Callback in local failure without credential release or a fabricated denial. A configured fallback continues only after authenticating the same ceremony and admitted exact peer origins.
  • TEST-CCDP-08 (exercises REQ-CCDP-08): UserDenied/CeremonyFailed/IdentityProof and local-cancellation races settle once. Local cancellation sends no CCDP message; subsequent composition-owned navigation or closure cannot let late traffic revive the run. Errors are text-only, excluded from exported events, and undeliverable failures have a fixed local diagnostic. Outcome cleanup leaves the popup connection available, and its authenticated closure control still closes an isolated popup after the ceremony settles.