Skip to content

feat(callback): retrieve the artifact, and refuse to start without one - #9

Merged
SupremaLex merged 9 commits into
feat/ceremony-github-tokenfrom
feat/callback-artifact-retrieval
Sep 14, 2026
Merged

SupremaLex merged 9 commits into
feat/ceremony-github-tokenfrom
feat/callback-artifact-retrieval

Conversation

@SupremaLex

@SupremaLex SupremaLex commented Sep 9, 2026

Copy link
Copy Markdown
Member

Stacked on #8 (base feat/ceremony-github-token). Review #8 first.

#8 configures and serves the callback document but does not fetch it: a deployment named a file, or got a compiled-in floor that completes no ceremony. This adds the retrieval specs/oauth-bridge.md § Callback document asks for, and nothing else.

What it does

  • At startup the bridge retrieves {ccdp_origin}/ccdp/callback.html, validates and composes it, and publishes document and headers as one value. A deployment that cannot retrieve its artifact does not start: build_state returns Err, the process exits non-zero, the port is never bound. Taken from @Wondertan's review of feat!: the OAuth Bridge — configuration and the callback document #8.
  • Every five minutes it revalidates with If-None-Match. 304 keeps the document; a 200 that composes replaces it and advances the ETag; anything else keeps the last valid one. Connect 15 s, request 20 s, the body bounded while it is read. Any 3xx is refused, never followed. No cookie, credential, query or incoming header goes upstream.
  • CALLBACK_ARTIFACT_PATH and the compiled-in callback.html are gone; the fixture artifact lives under tests/fixtures/.
  • build_state is async.

Also here

  • src/fixtures.rs (feature fixtures, reached by the integration tests through a self dev-dependency): one set of fixtures for the crate's own tests and tests/: Distribution::shared(), Config::fixture, AppState::fixture, the request builders.
  • tests/startup.rs runs the real binary on a TOML file over TCP: every route, SIGINT, a configuration it refuses.
  • Three commits by @Wondertan from fix(bridge): classify GitHub OAuth errors with JSON whitespace #10: tlsn libid-org/tlsn@94aaaf33 and mpz 1dd2349 matching notary RC3, JSON whitespace accepted in token responses, libid-rs at tag v0.4.0.

For review

  • tokio-rustls is default-features = false on purpose: rustls resolves with ring alone, its defaults would add aws-lc-rs, and two providers panic at runtime. cargo tree -i aws-lc-rs stays empty.
  • hyper is driven directly, no client crate: one connection every five minutes, and refusing a 3xx is a match arm rather than a setting.

Verification

127 tests: 88 unit, 37 over the router, 2 over the socket. upstream.rs at 97% line coverage. The retrieval tests run against an axum fixture on loopback. Measured against a running Distribution: retrieve, 304, replace on a new validator, 304 against the new one, serving throughout; exit 1 naming the URL when it is unreachable.

SupremaLex and others added 9 commits September 14, 2026 14:50
The callback artifact is retrieved from `{ccdpOrigin}/ccdp/callback.html`
before the listener binds and revalidated every five minutes against the
`ETag` it came with; a failed refresh leaves the served document as it is,
and a valid replacement replaces the document and its policy together.
Redirects are refused; the request carries no cookie, credential or query.
A deployment that cannot retrieve its artifact exits non-zero.

There is no file override: the artifact is always the Distribution's, and a
development stack serves its own over HTTP on `localhost` or `127.0.0.1`.
`src/artifact/callback.html` is test data, now under `tests/fixtures/`, and
the tests retrieve it from one Distribution on loopback per test binary,
served on a runtime that outlives the tests.

Assisted-by: Claude Opus 5
Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
`callback_source` answers a `304` to its unconditional request as
`ArtifactUnavailable`, like every other answer that is not the artifact.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
`libid_server_rs::fixtures` holds the shared Distribution, the dead wire
port and the deployment builder, compiled for the crate's tests and, under
the `fixtures` feature, for the integration tests; `tests/http.rs` and the
artifact tests build from it. `tests/startup.rs` runs the binary on a
configuration file: it serves, stops on SIGINT, and refuses a configuration
naming no platform.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
| Before | After |
|---|---|
| `callback_source(origin, allowed)` | `Published::retrieved(&upstream, allowed)` |
| `artifact(response)` | `Fetched::of(response)` |
| `fixtures::config(args)` | `Config::fixture(args)` |
| `fixtures::deployment(args)` | `AppState::fixture(args)` |
| `fixtures::distribution()` | `Distribution::shared()` |

`assemble` stays a free function: it returns `TokenResponse`, which is
libid-ceremony's to construct. No behaviour changes.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Production:
- `ON_EVERY_RESPONSE`: the `no-store` and `nosniff` pair, declared once in
  `routes` and carried by the health, callback, configuration and token
  responses.
- `Published::log`: the one log line naming a document's URL, validator and
  policy, at retrieval and at replacement.

Tests:
- `tests/http.rs`: one request builder (`token_request`), one body builder
  (`token_body_with` / `token_body_without`), one permit-holding poster;
  `post_token` takes the origins as a list.
- `session.rs`: one assembly helper over a duplex socket, with the notary's
  behaviour as an enum; one bearer opening.
- `request.rs`: one layout helper. `upstream.rs`: one deployed-Distribution
  helper.

jscpd: 12 clones, 66 lines → 1 clone, 5 lines; the one left is
`redirect_uri` beside `notary_host` (#10's function, kept verbatim).

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
The duplex writer of that case is dropped before the assembly reads, so the
reader sees end of file; the two record failures are asserted by their own
words, end of file and the budget.

Assisted-by: Claude Fable 5.1
Signed-off-by: SupremaLex <georglutsenko@gmail.com>
Use released libid-rs v0.3.0 and the matching TLSN/MPZ pins, including the MPZ active-context lifetime fix.

Assisted-by: GPT-6
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Pin the shared Rust transcript reader and classify the actual error field using the same reader.

Assisted-by: GPT-6
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
Replace the closed whitespace PR dependency with its released successor while retaining the matched RC3 TLSN and MPZ runtime patches.

Assisted-by: GPT-6
Signed-off-by: Wondertan <hlibwondertan@gmail.com>
@SupremaLex
SupremaLex force-pushed the feat/callback-artifact-retrieval branch from daebcf3 to 14bbdb2 Compare September 14, 2026 12:04
@SupremaLex
SupremaLex merged commit e63ca70 into feat/ceremony-github-token Sep 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants