Self-hosting
Environment variables.
Every server-side variable, what it unlocks, and the dev/prod credential convention.
Copy .env.example → .env.local for development; set the same variables in your host's dashboard for production. User secrets (model API keys, Notion tokens, wallet) are never env vars , users paste those in the app and they stay in their browsers.
Reference
| Variable | Unlocks |
|---|---|
GOOGLE_CLIENT_ID / _SECRET | Gmail + Calendar OAuth (one Web client, both redirect URIs) |
SLACK_CLIENT_ID / _SECRET | Slack OAuth (HTTPS redirect required, see OAuth setup) |
GITHUB_CLIENT_ID / _SECRET | GitHub OAuth for the dev origin |
GITHUB_CLIENT_ID_PROD / _SECRET_PROD | GitHub OAuth app used when serving the production host |
XOME_PROD_HOST | Which hostname counts as production (default app.xome.bot) |
NEXT_PUBLIC_PRIVY_APP_ID | Privy embedded Solana wallets (public id, client-side) |
SOLANA_RPC_URL | Server-side Solana RPC (e.g. Helius). Defaults to the public mainnet RPC |
NEXT_PUBLIC_JUPITER_BASE | Jupiter API base (defaults to the keyless lite tier) |
The *_PROD convention
OAuth credentials are picked by request origin at runtime. When a request arrives on the production host, *_CLIENT_ID_PROD / *_CLIENT_SECRET_PROD are used, falling back to the base variables when unset. This exists because GitHub OAuth apps allow only one callback URL each, so you use two GitHub apps (dev + prod), while Google and Slack support multiple redirect URIs on one app and need no _PROD pair.
.env.local (excerpt)
# one Google Web client serves both origins GOOGLE_CLIENT_ID=...apps.googleusercontent.com GOOGLE_CLIENT_SECRET=GOCSPX-... # two GitHub apps: base = localhost, _PROD = app.xome.bot GITHUB_CLIENT_ID=Ov23...dev GITHUB_CLIENT_SECRET=... GITHUB_CLIENT_ID_PROD=Ov23...prod GITHUB_CLIENT_SECRET_PROD=... NEXT_PUBLIC_PRIVY_APP_ID=cm... SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=...
.env.local is gitignored, keep it that way. SOLANA_RPC_URL stays server-side (the app proxies RPC through /api/solana/rpc, which rejects cross-site callers so third parties can't burn your quota).