Environment Variables
Who needs this page? Anyone who's setting up Stripe, Resend, Sentry, Google Maps, or any other third-party service. If you're not at that point yet, skip this for now and come back when a service needs its keys connected.
When your app talks to a service like Stripe or Supabase, it needs to prove "I'm the right app, I'm allowed to do this." It proves this with a key. A long string of letters and numbers, like a password.
Those keys can't live in your code. If they did, anyone who downloaded your app could read them. Instead, they live in environment variables. Special slots that get plugged into the app at build time.
You'll mostly hear them called env vars.
Where keys live in Appsanic
Keys live with the service they belong to: Dashboard → Connectors, then that service's page. That's where you paste keys, test the connection, and disconnect. Everything you save is encrypted (AES-256-GCM) before it's stored, and saved secrets render as dots - they never display again, not even to you.
From there it's automatic: when the agent builds, it turns each connected service's public keys into the right env vars in your app's code. You almost never wire an env var by hand.
One thing the agent will never do is ask for a key in chat. If you paste one there, it refuses and points you to the connector page - chat history is not a safe place for secrets.
The one distinction that actually matters
Two kinds of env vars:
"Public" keys. Safe to bundle with the app
These get included in the JavaScript that lives on each user's phone. Use them only for keys that are designed to be public:
- The Supabase anonymous key. Protected by the privacy rules on each table.
- Stripe's publishable key. Stripe's own design, safe to expose.
- The Google Maps key (restricted to your app's bundle ID).
- PostHog's project key.
Public keys have names that start with EXPO_PUBLIC_. That prefix is what makes them travel to the phone.
"Server-only" keys. Never bundle with the app
These stay on your server and never travel to phones. Use them for anything that, if leaked, would let someone misuse your account:
- Supabase service role key (full admin access).
- Stripe secret key (can charge real money).
- Stripe webhook secret.
- Resend API key (can send emails from your domain).
- Any OAuth client secret.
Server-only keys have names without the EXPO_PUBLIC_ prefix.
Where do server-only keys actually sit? Two places, depending on the job:
- Keys the AI uses on your behalf (Supabase admin, Stripe secret, Resend) live encrypted in the connector. The AI works through server-side tools and never sees the key itself.
- Keys your app needs at runtime (an OpenAI key, say) go into a small relay - a piece of server code that holds the key and talks to the service for your app. With Supabase connected, the agent deploys the relay as an Edge Function and tells you exactly where to paste the key as a Supabase secret.
If you're unsure: default to server-only. Making it public later is easy. Making something server-only after it's been released publicly means rotating the key and re-releasing.
A common mental check
"If someone screenshotted my app's code and pasted it on Twitter, would the world be in trouble?"
If yes. That key should be server-only.
How keys get into your app
When you build your app, the system:
- Reads the public keys from your connected services.
- Inlines the
EXPO_PUBLIC_*ones into the JavaScript that's sent to the phone. - Leaves the server-only ones where they are: encrypted connector storage and your relay's secrets.
- Never includes a server-only key in what's sent to the phone.
You don't have to do anything for this to work; it's automatic.
When you change a key
- Public keys are bundled into the app, so they require a new build to take effect. The agent will tell you when this is the case.
- Connector changes (new keys, the Account Access toggle) take effect on the next build.
- Relay secrets stored in Supabase take effect within about a minute of saving them in the Supabase dashboard.
If a key leaks
Bad day, fixable. Steps:
- Immediately rotate the key in the service's own dashboard (Stripe, Supabase, etc.). The leaked one is dead the moment you rotate.
- Update the value on that service's connector page (Dashboard → Connectors) - and in your Supabase secrets, if a relay uses it.
- Trigger a new build (for public keys).
- Check recent usage in the service's dashboard for anything suspicious.
More than one environment (dev / staging / production)
By default, your project has one environment. If you want separate keys for development, staging, and production:
"Give me three environments: development, staging, production. Each has its own Supabase project, Stripe keys, and domain."
This is mostly for enterprise teams; most apps don't need it.
What never goes in env vars
- Data that changes per user. Env vars are the same for every user. They're build-time constants. User-specific data goes in your database.
- Things that change often. Env vars require a build to update. If something changes daily, it belongs in your database.
- Raw passwords. Passwords are hashed in auth systems, not stored as env vars.
What you'll typically end up with
After connecting a few services, the public side of your app's env list might look like:
# Supabase (public. Safe to bundle)
EXPO_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
# Stripe (publishable key only)
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
# Maps
EXPO_PUBLIC_GOOGLE_MAPS_API_KEY=AIzaSy...
# Error monitoring (a DSN is public by design)
EXPO_PUBLIC_SENTRY_DSN=https://...
# Analytics
EXPO_PUBLIC_POSTHOG_KEY=phc_...
EXPO_PUBLIC_POSTHOG_HOST=https://us.i.posthog.comThe agent fills these in automatically from your connected services. The secret keys - Supabase service role, Stripe secret, Resend, an OpenAI key - never appear in this list. They stay encrypted in their connectors, or in your relay's Supabase secrets.
Under the hood (for engineers)
EXPO_PUBLIC_* and NEXT_PUBLIC_* are the conventional prefixes for build-time client inlining; unprefixed vars stay in Node.js process scope and are available only to API routes and server functions. Standard process.env.X access from any module.
The agent writes an up-to-date .env.example to your repo on each export so engineers cloning the project know exactly which keys they need.
When you reference an env var in server code (your relay, an Edge Function), the agent fills in the pin like:
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2024-04-10", // pinned to whatever SDK version was installed
});Avoid editing the apiVersion by hand without checking the Stripe changelog. Newer Stripe versions occasionally rename fields.
Next
Read Over-the-Air Updates. How to push fixes to live users in minutes, no App Store wait.
