Skip to content

fix(examples): use Direct consent in the Next.js starter - #92

Merged
callumflack merged 2 commits into
mainfrom
callum/account-handoff
Oct 10, 2026
Merged

callumflack merged 2 commits into
mainfrom
callum/account-handoff

Conversation

@callumflack

Copy link
Copy Markdown
Member

The shipped Next.js starter sends users to Account with a legacy Session Relay URL. Current Account rejects that URL with client_id: Required, so the advertised browser integration cannot reach approval or data.

This migrates the starter to the existing SDK 4.3.1 Direct controller and React hook: create a signed request, open approval from the click, poll through the app backend, then read the configured ChatGPT scope automatically. App keys, scope selection and Personal Server resolution stay server-side. Failed reads can reuse the approved request; active flows cannot be reset into the SDK's stale-response race. Every HTTP 402 stops before payment signing, including explicit retries.

Standalone PR, base vana-com/vana-cli:main. No prerequisite PRs or stack. It does not depend on Unity Surfaces PRs or modify Account/Desktop. Legacy public exports, signed manifest and webhook stub remain compatible; the documentation identifies their limits and directs new browser integrations to Direct.

Review

  • Starter implementation: examples/nextjs-starter/src/config.ts, src/app/api/{connect,status,data}/route.ts, and src/components/ConnectFlow.tsx.
  • Root/starter documentation replaces the unsupported integration; legacy APIs receive precise deprecation guidance.
  • Actual-handler regression tests and a starter workflow cover route behavior and production build, including future example-only changes.

Validation

  • pnpm validate: 61 files, 816 tests pass; TypeScript, ESLint and formatting pass.
  • pnpm --filter nextjs-starter build: production build passes. No server app key appears in browser assets.
  • Actual Next.js starter at http://127.0.0.1:4317/, real SDK, fresh Chromium, isolated local transport fixtures: approval/data, denial/expiry with no reads, failed-read retry with one consent request, retry status failure without an unhandled rejection, blocked-popup recovery and mobile continuation pass. Signed app requests are independently verified by the fixture.
  • 402 challenges: one Personal Server attempt per read, no X-PAYMENT, no payment or acknowledgement effect. Explicit retry remains unpaid.
  • Independent scoped review findings resolved; repository autoreview reports no P0 findings.

The browser evidence uses local consent and Personal Server fixtures. It does not claim hosted owner approval, real provider delivery or a native mobile/Desktop launch. SDK acknowledgement is best effort; enclave-only delivery is not supported by this controller. Live use requires a registered app identity and an owner's available Personal Server data.

@callumflack

Copy link
Copy Markdown
Member Author

Independent verification: PASS+NOTES

Verifier: Codex shipping lane /root/merge_verify_92; I did not author this PR. Control UI + Poteto Shipping with the Codex adapter were used locally because Cursor cloud agents are unavailable. origin is unavailable, so the forge fallback is gh.

Verified patch:

  • Base: f99bec8b235c2c5fdb2bd11875051fb0632f7bfb
  • Head: 0bf22978f1b8cea3be1c27eef947f84e2055a5d3
  • Stable base-to-head patch ID: a3242bb7b801e9387da4fa75f5935bf876d51544

Both revisions produced successful production builds. Fresh Chromium profiles exercised the built parent at http://127.0.0.1:45193/ and built head at http://127.0.0.1:45192/, using isolated local consent/Personal Server fixtures and the real installed SDK.

Parent observation: the real starter generated /connect?sessionId=...&appUrl=...&secret=... without client_id. The current Account parseHandoffParams implementation, bundled directly into a local fixture, rejected that exact link with client_id: Required. This is an executable contract comparison; the fixture did not render live Account.

Head observations:

  • Mount created no request; clicking Connect created a signed Direct request, opened approval, and displayed configured ChatGPT data automatically in the original tab. The fixture independently verified app signatures; successful reads produced one read and one acknowledgement.
  • Denied/expired approval produced zero Personal Server reads.
  • A 402 produced one read attempt, zero X-PAYMENT headers and zero acknowledgement. Explicit retry reused the consent request, made one additional unpaid read and remained Payment required.
  • A failed read did not retry automatically. Try again reused the request and reached Data received; a retry status failure stayed in the UI without an unhandled rejection. Reset was absent during active flow/retry.
  • Blocked popup recovered through Open approval. A mobile browser context exposed the HTTPS Open Vana continuation without automatically opening a tab and subsequently displayed fixture data.
  • No browser page errors; the fixture private key was absent from head browser assets. The verification left tracked code untouched.

Notes / proof limits: these runs prove the built starter and SDK path against fake transport services. They do not prove hosted owner consent, real provider delivery, native mobile/Desktop launch, or production Account rendering. The mobile continuation destination was intercepted locally. SDK acknowledgement remains best effort; the starter's documented enclave-only limitation remains. No real account, provider or payment operation ran.

Local evidence: account-export-proof/verify92/{base-build.log,head-build.log,base-browser-proof.json,browser-proof.json} and scenario screenshots in the task artifact folder. No merge or push performed by this lane.

@callumflack
callumflack merged commit 1d95773 into main Oct 10, 2026
7 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 10, 2026
## [0.41.1](v0.41.0...v0.41.1) (2026-10-10)

### Bug Fixes

* **cli:** retain ownership checks after Account expiry ([#93](#93)) ([01986d6](01986d6))
* **examples:** use Direct consent in the Next.js starter ([#92](#92)) ([1d95773](1d95773))
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.41.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant