Rising Tide Developers
UI PackagePrivy

Privy Setup

Configuring Privy for a Rising Tide Protocol app - dashboard settings, how the Stacks wallet is provisioned, cross-subdomain sessions, and how BYOW sits alongside.

Dashboard configuration

  1. Enable Google and X as login methods. They are the only two the shared sign-in offers, and the client can only ask for less than the dashboard allows
  2. Leave embedded-wallet auto-creation off. The Stacks wallet is not a Privy login-time wallet - see below
  3. Copy the App ID into NEXT_PUBLIC_PRIVY_APP_ID

The cookie domain is not a dashboard setting here: RisingTideProviders passes cookieDomain: ".risingtide.app" to the provider itself, and only when the page is served from a risingtide.app host.

How the wallet is provisioned

A user who signs in with Google or X gets a Stacks-capable wallet without handling a key, but not through Privy's createOnLogin. On first sign-in the auth layer calls rtClient.privy.createOrGetWallet(token), and rt-api creates a Privy bitcoin-segwit server wallet for that user (secp256k1, which Stacks shares) and derives the Stacks address from it. Every Stacks transaction is then signed server-side through Privy's wallet API with no wallet prompt. That is why the shipped provider config turns Privy's own embedded wallet features off:

import { PrivyProvider } from '@privy-io/react-auth';

<PrivyProvider
  appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}
  config={{
    appearance: { theme: 'dark' },
    loginMethods: ['google', 'twitter'],
    embeddedWallets: {
      ethereum: { createOnLogin: 'off' },
      showWalletUIs: false,
    },
  }}
>
  {children}
</PrivyProvider>

This is what RisingTideProviders mounts from its privy config block, plus the cookie domain and the Ethereum chain entry the storage wallet needs. PrivyProvider comes from @privy-io/react-auth - there is no Rising Tide wrapper around it, and most apps never mount it directly: RisingTideProviders takes privy.appId, privy.appearance and an optional privy.loginMethods and mounts it in the correct order.

showWalletUIs: false is global. The one in-browser embedded-wallet action in the estate today is the owner's storage key signing a Save As, and it signs without a prompt for the same reason Stacks writes do; any embedded-wallet action added later inherits that.

PrivyTokenProvider

PrivyTokenProvider (from @rising-tide/ui/auth) reads the Privy access token and makes it available to the protocol auth context. You do not add it by hand - ConditionalProviders renders it, and RisingTideProviders renders that.

Provider ordering

PrivyProvider must be the outermost auth wrapper. Inverting the order does not fail at mount; it fails as a 401 on the first POST /api/users during signup, because PrivyTokenProvider calls usePrivy() outside Privy's context.

// Correct
<PrivyProvider appId={...}>
  <ConditionalProviders>{children}</ConditionalProviders>
</PrivyProvider>

// Wrong - 401 on first signup
<ConditionalProviders>
  <PrivyProvider appId={...}>{children}</PrivyProvider>
</ConditionalProviders>

BYOW (Bring Your Own Wallet)

BYOW users connect an existing Stacks wallet through Stacks Connect and sign in the browser. No Privy configuration is needed for it - BYOW runs independently of Privy. The option is hidden by default: AuthenticationChoice offers it only when the build does not set NEXT_PUBLIC_ENABLE_BYOW="false" AND either the server flag App.byowEnabled is on for the app (it defaults off; an operator flips it in rt-admin) or the page was opened with ?byow=1.

Cross-subdomain sessions

On any *.risingtide.app host the provider sets the Privy cookie domain to .risingtide.app, so a user signed in on one subdomain is authenticated on all of them. The session token each app holds in local storage is per origin, so signing out on one subdomain does not sign the others out until their own tokens expire.