Environment Variables
The four env scopes — frontend-public, Rust runtime, build secrets, and Edge secrets — and every key the template reads.
A desktop app has more than one environment boundary. This template has four, and knowing which is which is the difference between a value that is safe to ship in the app and one that leaks a secret.
The four scopes
| Scope | Prefix / where | Bundled into the app? |
|---|---|---|
| Frontend | VITE_* in .env | Yes — inlined into the webview bundle. Public by design. |
| Rust runtime | SENTRY_DSN, SENTRY_ENVIRONMENT, UPDATE_SERVER_URL | No — read by the Rust process at runtime (or baked at compile time). |
| Build / CI secrets | TAURI_SIGNING_*, APPLE_*, WINDOWS_CERTIFICATE* | No — supplied to release builds only; no-op when unset. |
| Edge secrets | CREEM_*, BILLING_SUCCESS_URL | No — set with supabase secrets set; never in the app .env. |
The Supabase URL and publishable key are safe to ship because Row Level
Security enforces authorization server-side. A Supabase service-role key is
not safe and must never appear in .env. The minisign private key never
enters the repo — only the public key lives in tauri.conf.json.
All frontend config is read once in shared/config.ts; see .env.example for
the annotated source of truth. Every VITE_* key below already ships in
.env.example.
Frontend reference (VITE_*)
Feature flags
Prop
Type
Supabase
Prop
Type
Creem product IDs
Prop
Type
Product IDs are not secrets. shared/price-config.ts maps them to plan tiers;
an unset or unknown ID falls back to free, so an unconfigured build never
wrongly unlocks paid features.
Observability, analytics, and support
Prop
Type
Rust runtime env
Read by the Rust process, not bundled into the webview. Each is read via
std::env::var at runtime, falling back to option_env! (baked at compile
time), so you can supply them either way.
Prop
Type
Update endpoint precedence (src-tauri/src/commands/update.rs): a non-empty
runtime UPDATE_SERVER_URL wins; otherwise the compile-time option_env!
value; otherwise the built-in default https://updates.soarstarter.com.
Separately, tauri.conf.json hardcodes an endpoints entry and the minisign
public key — rebrand both to point at your own feed. See
Auto-Update.
Build stamps
The About-page diagnostics show a build time and commit SHA read via
option_env!("VITE_BUILD_TIME") and option_env!("VITE_COMMIT_SHA") at Rust
compile time. They are not in .env.example and the shipped CI does not
inject them, so they default to "dev". Set them in your release build
environment if you want real stamps.
Build / CI secrets
Every signing input is env-driven and no-ops when unset, so forks and local builds keep working without secrets.
Prop
Type
Edge secrets
Set with supabase secrets set; read by functions under supabase/functions/.
They are reference-only in .env.example (commented out) — never put real
values in the app .env.
Prop
Type
Safe-to-ship checklist
| Value | Safe in app bundle? | Why |
|---|---|---|
| Supabase URL | Yes | Identifies the project endpoint. |
| Supabase publishable key | Yes | Intended for clients; RLS decides which rows the user can read. |
| Creem product IDs | Yes | Identifiers only; they do not create checkout sessions by themselves. |
| Sentry DSN | Yes, with opt-in | A DSN is an ingest address, not a signing secret; user opt-in still gates reporting. |
| Analytics write key | Treat as public | It ships to the webview if set. |
| Supabase service-role key | No | Bypasses RLS. Server-side only. |
| Creem API key / webhook secret | No | Server credentials; Edge secrets only. |
| minisign private key | No | Signs your updates; only the public key ships (in tauri.conf.json). |
| Code-signing certificates | No | Release secrets for CI or your signing machine. |
