Environment variables (ledger)
Worker APP_URL (added in R4)·
Public origin of your Vercel app. The worker embeds it in share links, vcard/QR URLs and "sign in at …" copy (src/site.ts). tinyai-id hardcoded https://tiny.technology here. Set it in wrangler.toml [vars]; defaults to http://localhost:3000.
Worker Web Push (added in R4)·
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY (base64url P-256 pair, npx web-push generate-vapid-keys) as wrangler secrets; optional VAPID_SUBJECT (defaults to mailto:admin@<APP_URL host>; tinyai-id defaulted to its own help address).
Model provider keys (R5, read by lib/chat/model.ts — set the ones you use)·
TINY_MODEL_PROVIDER (bedrock | openai | gemini | gateway), BEDROCK_MODEL_ID + AWS_REGION/BEDROCK_REGION + AWS_BEARER_TOKEN_BEDROCK, OPENAI_API_KEY + OPENAI_MODEL_ID, GEMINI_API_KEY/GOOGLE_API_KEY + GEMINI_MODEL_ID, AI_GATEWAY_API_KEY + AI_GATEWAY_MODEL_ID, optional STRANDS_ADDITIONAL_REQUEST_FIELDS (JSON), WEATHER_API_KEY (lib/utils getWeatherData).
Chat route (R5b)·
TOOL_REPO_ALLOWLIST — comma-separated GitHub owners whose raw tool files install_tool may fetch without a per-user trust grant (default strands-agents; tinyai-id hardcoded its maintainers). NEXT_PUBLIC_APP_URL is now required at runtime for the run-tool sandbox proxy and for links the agent hands out. If TINY_WORKER_URL is unset the app still builds; worker calls fail against tiny-worker-url-not-set.invalid and /api/health reports workerConfigured:false.
Worker MODEL_CONFIG_ENC_KEY (R7 settings)·
Wrangler secret used to AES-256-GCM-encrypt users' bring-your-own model API keys before they hit D1 (model_config, model_providers; see migrations 0016/0034). Falls back to INTERNAL_API_KEY when unset — set a dedicated one so rotating the internal key does not orphan every stored provider key. openssl rand -hex 32 is fine.
Worker WORKER_URL (added in R7, optional)·
Firmware channel pointers (/firmware/publish) name an https URL a device will download executable code from. tinyai-id pinned ["plugin.tiny.technology", "tiny.technology"]; the template accepts only the hosts of APP_URL and (if set) WORKER_URL. With neither set every publish is refused. Whatever you allow here must also be in the device-side MEDIA_HOSTS of your OTA client — that list is not in this repo.
Worker payments (R8) — off by default·
| Var | Kind | Meaning |
|---|---|---|
PAYMENTS_ENABLED |
var | Exactly "true" switches the wallet/x402 surface on. Unset/anything else: every /pay/* answers 404 {error:"payments disabled"} and the cron skips the reconcilers. Leave it off until the rest of this table is set. |
PAYMENTS_NETWORK |
var | base (real USDC on Base mainnet), base-sepolia (testnet trial credit) or tiny (your own self-hosted chain — R8 web/chain slice). Default in wrangler.toml: base-sepolia. |
DEPOSIT_ADDRESS |
var | Platform USDC deposit address on the selected network. Deposit claiming refuses when unset or not a checksummed 0x address. |
BASE_RPC_URL / BASE_SEPOLIA_RPC_URL |
var, optional | JSON-RPC used to verify deposit transfers. Defaults: the public mainnet.base.org / sepolia.base.org endpoints (rate-limited — set your own for anything beyond a demo). |
RECONCILE_ALARM_USER |
secret, optional | users.id paged (event ring + their Telegram bot) when a reconciliation row stays blocked across two ticks. Deliberately UNSET, never "": GET /pay/reconcile-status reports alarm.configured:false until you set it. |
The handlers are tinyai-id's payments.ts / deposits.ts / withdrawals.ts / reconcile-alarm.ts verbatim; only the gate and this documentation are template additions.
App side of payments (R8)·
PAYMENTS_ENABLED="true" must be set on the Vercel app too (same exact-match rule): until then /api/wallet*, /api/x402/*, /api/chain/* and /api/erc8004/* answer 404 before touching the worker or any chain. The app additionally reads PAYMENTS_NETWORK, X402_PAY_ALLOWLIST (extra payable hosts beyond your own app+worker), X402_FACILITATOR_URL, TINY_CHAIN_ID / TINY_CHAIN_RPC_URL / TINY_CHAIN_USDC_ADDRESS / TINY_CHAIN_EXPLORER_URL (only for PAYMENTS_NETWORK=tiny), BASE_RPC_URL / BASE_SEPOLIA_RPC_URL, and the payer/withdraw signer keys the upstream routes document inline (app/api/x402/pay/route.ts, app/api/wallet/withdraw/route.ts) — refused when they are Anvil's well-known dev keys outside a dev network (chain/dev-keys.mjs). Running your own tiny network (Besu QBFT, facilitator, rpc-proxy) is NOT in this template; chain/multinode/genesis-8470.json is tinyai-id's genesis kept as the sample to replace.
Worker email routing (R8, optional)·
Cloudflare Email Routing can hand mail for your domain to the worker (src/email.ts, the email export). Mail to <slug>@your-domain is forwarded to that tiny's customer.email; mail to tiny@/postmaster@/hello@ goes to you.
| Var | Kind | Meaning |
|---|---|---|
EMAIL_OWNER_FORWARD |
var | Your address for the operator local-parts. Unset → that mail is rejected (tinyai-id hardcoded the maintainer's personal address — DRIFT #30). /health reports emailForwardConfigured. |
CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN |
var + secret, optional | Email Routing only delivers to verified destinations. When a forward fails, the worker POSTs the owner's address to accounts/<id>/email/routing/addresses so Cloudflare starts verification (token needs Email Routing Addresses: Edit). Both unset → the step is skipped and the failure is only counted (stats KV tiny:fw:<slug>). |
Wire it in the Cloudflare dashboard: Email → Email Routing → Routing rules → catch-all → Send to a Worker → this worker. No [send_email] binding is needed; the handler only forwards.
Branding + UI (R9)·
Everything tinyai-id's root layout hardcoded (https://tiny.technology, "Tiny AI", @tinyaid, say.jpeg/tiny.mp4 OG media, the Vercel Analytics mount, a Google Maps browser key) is env here, read by apps/web/lib/site.ts and lib/config.ts.
| Var | Meaning |
|---|---|
NEXT_PUBLIC_SITE_NAME |
Brand shown in <title> template, header, manifest, OG siteName. Default tiny. |
NEXT_PUBLIC_SITE_TAGLINE |
description / OG / twitter text. Default "We're a software, together." |
NEXT_PUBLIC_SITE_TWITTER |
X handle (@ optional). Unset → no twitter block at all (tinyai-id used @tinyaid + a player card for tiny.mp4). |
NEXT_PUBLIC_SITE_OG_IMAGE (+ _WIDTH/_HEIGHT) |
OG image. Default: the bundled square /icon-512.png (512×512). Dimensions MUST match the real asset — crawlers reserve that box. |
NEXT_PUBLIC_VERCEL_ANALYTICS |
"1" mounts @vercel/analytics (components/Analytics.tsx). Unset: never imported at runtime. |
NEXT_PUBLIC_TINY_WORKER_URL |
Browser-visible copy of TINY_WORKER_URL (lib/public-config.ts). Chat, CommandPalette, UniverseDrawer and /tools slash-command fetch the worker's public /community, /get, /list, /tools/browse directly; tinyai-id hardcoded its production host there (DRIFT #33). Unset → .invalid host, fetches fail loudly. |
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY |
Browser key for the ambient map / /map (components/MapBackground). Unset: loadMapsApi() resolves false and nothing renders. tinyai-id shipped its own key as the code fallback — DRIFT #31. |
The mobile apps (apps/ios, apps/android)·
Build-time, not runtime — set before you build, in apps/ios/Secrets.xcconfig or
apps/android/local.properties (Android also reads the environment). Details in
guides/mobile-apps.md.
| Var | Meaning |
|---|---|
TINY_BASE_URL |
The web app's origin the binaries talk to — the apps' NEXT_PUBLIC_APP_URL. iOS: https:/$()/host (xcconfig comment escape) → Info.plist TinyBaseURL → Config.baseURL. Android: BuildConfig.TINY_BASE_URL → net/AppConfig.kt baseUrl (+ wear). The ONLY required app setting — worker + site name come from <TINY_BASE_URL>/api/health. Default https://tiny.technology. |
TINY_WORKER_URL |
(apps) Optional override of the worker origin, same name as the web var. Normally EMPTY: the apps discover the worker from GET <TINY_BASE_URL>/api/health → workerUrl at launch and cache it (iOS Config.workerURL / Deployment; Android AppConfig.workerUrl). Set it only for a deployment whose health route does not advertise a worker. |
robots.ts / sitemap.ts / manifest.ts derive their base URL from NEXT_PUBLIC_APP_URL (fallback http://localhost:3000) and the sitemap's tiny list from TINY_WORKER_URL's /community. Tailwind 3 + tailwindcss-animate, globals.css (the design tokens: --tiny-accent, --tiny-bg, hsl shadcn-style surfaces) and lib/theme.ts are ported verbatim; ESLint mirrors tinyai-id's config (hook-rule legacies are warnings, not errors).
Provisioning the worker's storage·
node scripts/bootstrap-cloudflare.mjs [--prefix <p>] [--app-url <origin>] [--migrate] [--dry-run] creates the 3 KV
namespaces, the D1 database, the 2 Vectorize indexes (1536d cosine — text-embedding-3-small — plus the name/userId
metadata indexes the memory queries filter on) and the R2 bucket under one prefix, then writes
apps/worker/wrangler.generated.toml (git-ignored) with the real ids and records everything in docs/PROVISIONED.md.
It is idempotent and only ever touches names carrying the prefix. node scripts/teardown-cloudflare.mjs --yes deletes
exactly what that ledger lists. Worker secrets (INTERNAL_API_KEY, OPENAI_API_KEY, optional VAPID/payments) are set with
wrangler secret put <NAME> --config wrangler.generated.toml.