fix: align starter with the real Throttle platform contract - #1
Merged
Conversation
Four fixes from an end-to-end readiness evaluation against the live platform
(2026-07-20). Each one broke a real integration point when the starter was
used past the mocked quickstart:
1. Webhook envelope: real deliveries carry a required `version` field
("1") in the canonical flat envelope; the strict throttleEventSchema
rejected it, so EVERY real delivery would have failed validation. Added
`version` to the schema, the fixture, and every test event literal, plus
a regression test that the field is required.
2. Dashboard origin: `dashboard.usethrottle.dev` does not resolve — the
real dashboard is `app.usethrottle.dev`. Since the bridge and CORS/CSP
enforce an exact-origin match, the shipped default broke the real iframe
handshake. Fixed in wrangler.jsonc, README, local-development.md, and
the setup script's next-steps output. (Test fixtures that use the old
host as an arbitrary origin are self-consistent and unchanged.)
3. `pnpm setup` -> `pnpm run setup` in all docs: the bare form invokes
pnpm's own built-in `setup` command, which modifies the developer's
shell profile instead of running scripts/setup.mjs. The documentation
contract test now rejects the bare form.
4. Scope placeholders: `connector:read`/`connector:write` are not ids in
the Throttle scope catalog (and the JWT would never carry them), so the
Worker would 403 every request. Defaults are now `orders:read` +
`fulfillments:write` (real, extension-grantable ids that fit the demo
order-sync connector), with the mock bridge kept in sync and a README
note pointing at the scope catalog.
Verified: pnpm check passes end-to-end (format, lint, typecheck, all
package suites + root e2e/boundary/documentation tests, wrangler dry-run
build).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kalwiggins
added a commit
that referenced
this pull request
Jul 21, 2026
… behavior (#2) Found by a live end-to-end dry run against production (register → install → iframe launch → bootstrap → ping → real order.created). Three bugs meant the starter could not complete a real integration out of the box: 1. Identity verification rejected every real launch token: requiredClaims demanded `nbf`, but platform tokens carry iss/sub/aud/iat/exp only (and the docs never promise nbf). Dropped nbf from requiredClaims — jose still validates it when present. The "missing nbf" test now asserts acceptance. 2. Worker CORS only allowed the dashboard origin, but the iframe UI calls the Worker from the UI's OWN origin, so every UI request failed preflight in the documented split-origin deployment. Added an optional EXTENSION_UI_ORIGIN var (exact HTTPS origin, validated at boot) that is allowed alongside the dashboard origin; documented in wrangler.jsonc, README, cloudflare-deployment.md, and local-development.md. 3. The demo connector read `event.data.orderId`, but real order.created deliveries carry the full order under `data.order` — the demo terminal- failed on its first real event while its invented fixture kept tests green. Now reads data.order.id; the fixture and every event literal in tests model the real payload shape. Also repairs two breaks that slipped into main via #1 (its final check ran stale): an unused-var lint error in contracts.test.ts and three webhook verification tests whose signed rawBody fixture predated the required `version` field (digest recomputed). Verified: pnpm check (format, lint, typecheck, 137 root + all package tests, wrangler dry-run build) exit 0. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
What & why
An end-to-end readiness evaluation (2026-07-20) against the live platform found four defects that each break a real integration the moment a builder moves past the mocked quickstart. Companion platform PR: Epic-Design-Labs/app-throttle#765 (fixes the
extension.pingenvelope + publishes the starter docs page).The four fixes
Webhook envelope — every real delivery was rejected. Real deliveries send the canonical flat envelope with a required
version: "1"field (seebuildOutboundRequestin@platform/webhooks, and the envelope example on the events docs page). The strictthrottleEventSchemadidn't allowversion, so every real webhook failed validation → 4xx → platform retries → endpoint auto-disable. Addedversionto the schema, fixture, and all test event literals + a required-field regression test.Dashboard origin — real iframe handshake always failed.
dashboard.usethrottle.devdoes not resolve (the dashboard isapp.usethrottle.dev). Exact-origin matching meant the shipped default could never complete the bridge handshake in a real install. Fixed inwrangler.jsonc, README,local-development.md, and the setup script's next-steps output.pnpm setup→pnpm run setup. Barepnpm setupinvokes pnpm's built-in setup command — it appends PNPM_HOME config to the developer's shell profile and never runsscripts/setup.mjs(reproduced locally). All docs updated; the documentation contract test now rejects the bare form.Scope placeholders weren't real scope ids.
connector:read/connector:writearen't in the Throttle scope catalog (closest real ids are the pluralconnectors:*, which are payment-connector management scopes anyway) — an identity JWT can never carry them, so the Worker 403'd every authenticated request. Defaults are noworders:read+fulfillments:write(real, extension-grantable, fit the demo order-sync connector); the mock bridge stays in sync and the README config table links the scope catalog.Deliberately NOT changed
<this-repository-url>in the README quickstart stays a placeholder —verify-releaseintentionally warns downstream publishers to set their own repo URL, and that guardrail (plus its tests) depends on the placeholder. The published clone command lives on the starter-repository docs page.dashboard.usethrottle.devas an arbitrary origin value are self-consistent and untouched.Verification
pnpm checkpasses end-to-end: format, lint, typecheck, all 9 package suites + root e2e/workspace-boundary/documentation tests (137/137 root), and the wrangler dry-run build.🤖 Generated with Claude Code