Skip to content

Internalize provisioning + session.established event - #1171

Merged
jbojcic1 merged 1 commit into
sdk/accounts-slicefrom
sdk/provision-internalization
Jul 28, 2026
Merged

jbojcic1 merged 1 commit into
sdk/accounts-slicefrom
sdk/provision-internalization

Conversation

@orveth

@orveth orveth commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves provisioning into the SDK on the auth lifecycle and adds a session.established event so the host seeds its caches by plain reads. Removes the web-side provisioning orchestration.

SDK

  • Provision runs internally on every auth-established transition (signUp / signInGuest / signIn / restore / mid-session identity change), fired post-establish — as the settled current identity, never mid-switch — and guarded by an in-memory fingerprint (userId, email, emailVerified) so it re-runs only on identity change.
  • Master's failure behavior is preserved exactly: upsert with withRetry (2×), no retry on ZodError, terminal failure throws. On restore that surfaces the error boundary, as live does today.
  • New session.established event { user, accounts } with replay-latest (a late subscriber gets the current payload immediately); cleared on session end.
  • acceptTerms(timestamp) decoupled from provision — the host stamps the real click timestamp.
  • accounts.list() and session.established return bare Account[]; getExtendedAccounts stays exported as a caller-side selector for isDefault. See Accounts — make isDefault a first-class, SDK-owned field with change notification #1170 for the deferred design that would make isDefault a first-class SDK-owned field with change notification.

Web

  • Deleted the hasUserChanged gate and ensureUserData's provisioning call from _protected.tsx — provisioning is SDK-internal now.
  • New session.established consumer seeds UserCache + AccountsCache from the event at boot.
  • Pending pre-login terms replayed via acceptTerms with the real click timestamp.
  • Restore path re-throws a terminal provision failure to the error boundary (matches live); boots anonymous only on a true restore failure.

Verification

  • fix:all clean · wallet-sdk 132 pass / 0 fail + typecheck green · web 38 pass / 0 fail + typecheck green.
  • Regressions covered red-on-revert: restore re-provision after a prior failure; same-user in-tab re-login re-emits session.established (fingerprint + retained-event reset on session end).

Sequencing

Merges into sdk/accounts-slice; accounts-slice + #1167 then go to master as one whole.

@vercel

vercel Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Ready Ready Preview, Comment Jul 28, 2026 1:21pm

Request Review

@supabase

supabase Bot commented Jul 28, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

Comment thread packages/wallet-sdk/domain/sdk/session-established-provisioner.ts Outdated
Comment thread packages/wallet-sdk/domain/sdk/events.ts Outdated
Comment thread packages/wallet-sdk/domain/sdk/events.ts Outdated
Comment thread packages/wallet-sdk/domain/accounts/accounts-api.ts Outdated
Comment thread apps/web-wallet/app/entry.client.tsx Outdated
Comment thread packages/wallet-sdk/domain/sdk/sdk.ts Outdated
Comment thread packages/wallet-sdk/domain/user/auth-service.ts Outdated
Comment thread packages/wallet-sdk/domain/user/auth-service.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move this and pendingGiftCardMintTermsAcceptedAt related code after these cache prefetches since they are only needed down there were acceptTerms is called?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — pure locality move. The storage get/remove is independent of the prefetches, so reordering doesn't change behavior; it just puts the pending-terms reads next to the acceptTerms call that uses them. I'll fold it in.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2aff3f6 — pure move, the pending-terms reads now sit right above the acceptTerms block. Read/remove logic unchanged.

Comment thread apps/web-wallet/app/routes/_protected.tsx
Comment thread apps/web-wallet/app/routes/_protected.tsx
Provisioning moves out of the web host into the SDK, fired on auth
session-start and guarded by an in-memory identity fingerprint. The host
seeds its caches from a new replay-latest auth.session-started event
instead of orchestrating provisioning itself.

SDK:
- Provision the settled user post-establish on every auth transition
  (signUp / signInGuest / signIn / restore / mid-session change),
  fingerprint-guarded (userId, email, emailVerified) so it re-runs only on
  an identity change. Master's failure behavior is preserved: upsert via
  withRetry, no retry on ZodError, terminal failure throws.
- New auth.session-started event { user, accounts } with replay-latest;
  events.clear() drops all retained replay payloads on session end.
- acceptTerms(timestamp) decoupled from provision; the host stamps the
  real acceptance time.
- createUserProvisioner owns the fingerprint guard and its reset;
  auth-service only notifies that a session started and the SDK layer
  decides that means provisioning.
- accounts.list() and the event return bare Account[]; getExtendedAccounts
  stays exported as a caller-side selector for isDefault (see #1170).

Web:
- Delete the hasUserChanged gate and ensureUserData provisioning from
  _protected.tsx; a boot-registered session-started consumer seeds the
  user + accounts caches.
- Restore path re-throws a terminal provision failure to the error
  boundary (matches live), booting anonymous only on a genuine restore
  failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@orveth
orveth force-pushed the sdk/provision-internalization branch from 2aff3f6 to 973f625 Compare July 28, 2026 13:20
@jbojcic1
jbojcic1 merged commit 75a10a8 into sdk/accounts-slice Jul 28, 2026
6 checks passed
@jbojcic1
jbojcic1 deleted the sdk/provision-internalization branch July 28, 2026 13:24
orveth added a commit that referenced this pull request Jul 28, 2026
…and provisioning

Extracts the accounts slice of the wallet into @agicash/wallet-sdk: the web
app reads accounts, session keys, user, and provisioning through the SDK
contract instead of owning that logic.

- accounts namespace (sdk.accounts.*) backed by an in-SDK repository, with a
  /temporary bridge for repositories not yet migrated.
- session-scoped key plumbing: derivation memos fenced by an AbortController
  scope and revocable on session end; web key queries call the derivation
  leaves directly.
- user provisioning internalized on the auth lifecycle, fingerprint-guarded,
  fired on a replay-latest auth.session-started event the host seeds its
  caches from; acceptTerms decoupled from provision.
- session-lifecycle hardening: typed SessionEndedError / DisposedError,
  terminal dispose, explicit guarded Encryption facade.
- accounts.list() and the event return bare Account[]; getExtendedAccounts
  exported as a caller-side selector for isDefault (see #1170).

Squashes #1167 (includes #1168 and #1171).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
jbojcic1 pushed a commit that referenced this pull request Jul 28, 2026
…and provisioning

Extracts the accounts slice of the wallet into @agicash/wallet-sdk: the web
app reads accounts, session keys, user, and provisioning through the SDK
contract instead of owning that logic.

- accounts namespace (sdk.accounts.*) backed by an in-SDK repository, with a
  /temporary bridge for repositories not yet migrated.
- session-scoped key plumbing: derivation memos fenced by an AbortController
  scope and revocable on session end; web key queries call the derivation
  leaves directly.
- user provisioning internalized on the auth lifecycle, fingerprint-guarded,
  fired on a replay-latest auth.session-started event the host seeds its
  caches from; acceptTerms decoupled from provision.
- session-lifecycle hardening: typed SessionEndedError / DisposedError,
  terminal dispose, explicit guarded Encryption facade.
- accounts.list() and the event return bare Account[]; getExtendedAccounts
  exported as a caller-side selector for isDefault (see #1170).

Squashes #1167 (includes #1168 and #1171).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 973f625b Deployed Jul 28, 2026 by vercel[bot]
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