Every setting you can change, where it lives, and what it affects.
Onekit starts with one Bun/TypeScript app targeting web, iOS, and Android. One provides routes and the web server; Tamagui provides shared UI; Better Auth and Drizzle use PostgreSQL; SST deploys the server and API to AWS. Native binaries have a separate Expo/EAS release process.
These are editable source and environment settings. An interactive setup wizard and interchangeable billing/storage modules are not implemented.
| Choice | Edit | Current behavior |
|---|---|---|
| Name, slug, description | src/config/app.ts | Public metadata and shared branding |
| Native URL scheme and identifiers | src/config/app.ts, app.config.ts | onekit scheme; sh.onekit.app identifiers (replace with your own) |
| Default appearance and accent | src/config/app.ts | System appearance, blue accent; screens use the configured accent where explicitly wrapped in a theme |
| Tokens, fonts, themes, responsive rules | src/tamagui/config.ts | Tamagui default configuration with long-form style props enabled |
| Appearance persistence | src/tamagui/provider.tsx, src/tamagui/theme-storage* | Browser local storage; native SecureStore; key is onekit-theme |
| Navigation and screens | app/, src/components/screen.tsx | File-based routes and shared shell |
| Web rendering and native bundler | vite.config.ts | SPA default, explicit SSG home route, Node deployment target, Metro native bundler |
| Auth policy and sessions | src/server/auth.ts | Email/password; optional Google/Apple; seven-day session, one-day refresh interval; reset, verification, and deletion hooks |
| Email delivery and templates | src/server/email.ts | Console logger locally, Amazon SES when deployed; three plain-text/HTML templates |
| Plans and billing | src/config/billing.ts, src/server/billing.ts | Stripe subscriptions by lookup key; local entitlement table |
| Database schema and migrations | src/server/db/schema.ts, drizzle/, drizzle.config.ts | PostgreSQL with Better Auth tables and reviewed SQL migrations |
| Local database | compose.yaml | PostgreSQL 17, loopback port 54329, persistent named volume |
| AWS infrastructure | infra/config.ts, sst.config.ts | Region, service sizing, scaling, architecture, OAuth secret toggles, deployment stages |
| Container runtime | Dockerfile, package.json | Node server, public origin build arguments, runtime secret injection |
| Native build variants | app.config.ts, eas.json | Development, preview, production |
| Dependency/tool versions and commands | package.json, lockfile | Pinned application dependencies and Bun version |
| Type checking and code style | tsconfig.json, biome.json | Strict TypeScript, Biome formatting/linting |
src/config/app.ts is bundled into clients. It must not contain credentials. Changing a name does not automatically rename the database, SST stack, package, theme storage key, or application-store listing; review those separately when adapting the starter.
Create an untracked .env from .env.example, generate a fresh auth secret with openssl rand -base64 32, and fill in the following values. Never reuse the example database credentials in a hosted environment.
| Variable | Required for | Value or rule |
|---|---|---|
DATABASE_URL | Database and auth requests; migration commands | PostgreSQL connection URL; local Compose uses postgresql://onekit:onekit@localhost:54329/onekit |
DATABASE_POOL_MAX | Optional connection tuning | Integer 1–100; default 10 per server process |
DATABASE_SSL_CA | Providers with a private CA | PEM certificate to trust, for example Supabase’s; verification stays enabled |
ZERO_UPSTREAM_DB | zero-cache, when DATABASE_URL is pooled | Direct connection URL as a role with REPLICATION; default DATABASE_URL. See Database |
ZERO_DB_PASSWORD | bun run db:migrate, optional | Also create zero-cache’s onekit_zero role with this password, plus its zero_cvr and zero_change databases. The deploy’s migrate task always sets it |
ONEKIT_DATABASE | SST deployment | rds (default) creates PostgreSQL in the stack; external uses the DatabaseUrl secret |
ONE_PUBLIC_ZERO_URL | Every signed-in client | zero-cache’s public origin, embedded in client builds; default http://localhost:4848. SST sets https://sync.<domain> |
ZERO_APP_URL | Local zero-cache (bun run zero:serve, zero:dev) | Where zero-cache reaches this app’s /api/zero/*; default the dev server, http://localhost:8081 |
ZERO_PORT | Local zero-cache | Port zero-cache listens on; default 4848 |
BETTER_AUTH_SECRET | Authentication | Random secret at least 32 characters long; keep stable between releases |
BETTER_AUTH_URL | Authentication | Canonical HTTP(S) origin without a path; local default http://localhost:8081; production requires HTTPS |
ONE_SERVER_URL | One build/server origin | Use the same canonical public server origin |
ONE_PUBLIC_SERVER_URL | Native auth/API client | Public origin embedded in client builds; must be reachable from the device; native release builds require HTTPS |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Optional Google login | Set both to enable; leave both empty to disable |
APPLE_CLIENT_ID, APPLE_CLIENT_SECRET | Optional Apple login | Set both to enable; the secret is a signed JWT requiring renewal |
MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET | Optional Microsoft login | Set both to enable |
MICROSOFT_TENANT_ID | Optional Microsoft tenant restriction | Entra tenant GUID; empty allows any Microsoft account (common) |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | Optional GitHub login | Set both to enable |
EMAIL_PROVIDER | Email delivery, and the default sign-in route | console (local only), file (local only, appends to EMAIL_OUTBOX), ses, or empty — empty also disables the emailed code, reset, and verification |
EMAIL_FROM | SES sender | Verified identity such as Onekit <no-reply@example.com>; optional for console and file |
EMAIL_OUTBOX | EMAIL_PROVIDER=file | Path the messages are appended to as JSON lines, for reading a code back from a script or test |
EMAIL_REGION | SES region | Falls back to AWS_REGION |
AUTH_EMAIL_SIGN_IN | Sign-in policy | off, code, or link; empty is code wherever EMAIL_PROVIDER is set and AUTH_PASSWORD is not true. link is not implemented yet and is refused |
AUTH_PASSWORD | Sign-in policy | true adds the password form and leaves AUTH_EMAIL_SIGN_IN off unless you name it; empty is off, except where it would be the only way in |
AUTH_PHONE_SIGN_IN | Sign-in policy | true accepts a phone number in the sign-in field and texts a code; needs SMS_PROVIDER. Never a way in on its own — a number is verified from the profile page |
SMS_PROVIDER | SMS delivery | console or file (local only), twilio, or empty to disable |
SMS_FROM | SMS sender | E.164 number such as +15550100000, or a Twilio Messaging Service SID; optional for console and file |
SMS_OUTBOX | SMS_PROVIDER=file | Path the texts are appended to as JSON lines |
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN | SMS_PROVIDER=twilio | Both required |
STORAGE_PROVIDER | Uploaded images | local (development only, refused in production), s3, or empty to hide the upload control |
STORAGE_ROOT | STORAGE_PROVIDER=local | Directory the files are written to; default .onekit/uploads |
STORAGE_BUCKET | STORAGE_PROVIDER=s3 | One private bucket; keys are prefixed per owner |
STORAGE_REGION | STORAGE_PROVIDER=s3 | Falls back to AWS_REGION |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Optional billing | Set both to enable checkout; see Billing |
AUTH_REQUIRE_EMAIL_VERIFICATION | Sign-in policy | true refuses sign-in until the address is confirmed; needs EMAIL_PROVIDER |
APP_VARIANT | Native app configuration | development (default), preview, or production |
ONEKIT_DOMAIN | SST deployment | Hostname in the selected AWS account’s Route 53 zone, such as app.example.com |
AWS_PROFILE | Optional local AWS credential selection | Existing profile for the intended account |
Server configuration is validated when used. Static page generation does not require database or auth secrets. In local development, native devices need a reachable server address; use one canonical origin consistently for auth and API configuration. Restart the server or rebuild clients after changing values that are read at startup or bundled into the app.
Uploaded images go to one private bucket with keys prefixed per owner, never a bucket per workspace: an AWS account gets 100 buckets by default, creating one is a slow control-plane call nobody wants inside a signup path, and isolation here is the application refusing to serve a key it does not own. Bytes go through the service rather than a presigned URL, which is what lets the server cap the size and establish what a file actually is — the stored content type is the one it sniffed, never the one the browser claimed, and SVG is refused outright because it is XML that can carry script. Set uploads: true in infra/config.ts to deploy the bucket; SST’s link gives the task role that bucket’s policy, so no keys are configured by hand.
Onekit ships passwordless: with EMAIL_PROVIDER set and nothing else named, the sign-in screen asks for an address and emails a six-digit code. See Authentication for the whole truth table, including what happens when neither switch nor transport nor OAuth pair is configured.
The browser auth client uses the current website origin. Native auth uses ONE_PUBLIC_SERVER_URL and SecureStore. OAuth callback URLs use ${BETTER_AUTH_URL}/api/auth/callback/google or /api/auth/callback/apple. See Authentication for provider setup and current limitations.
Set your final bundle/package identifiers before configuring provider applications or store listings. Development and preview append .development and .preview to the base iOS/Android identifiers; production uses the base identifiers. Names also include the variant outside production. The URL scheme is shared by the current variants, so validate deep links with the intended build installed.
Changing the URL scheme, package identifiers, plugins, or native configuration requires rebuilding the native app. EAS profiles do not provision project ownership, signing credentials, store records, or a production API origin; configure those for your own account. Align Apple provider configuration with the actual build identifier. Update public/favicon.svg and add native icons/splash assets before release.
The shared theme preference can be changed in Settings. To create a custom visual system, edit Tamagui tokens/themes and the consuming components together; the accent field is not an automatic recoloring mechanism for every element.
Email/password is enabled with a minimum password length of eight. Password reset, confirmation, password change, and deletion work when an email provider is configured; see Email. Organizations and role management require additional schema and application work.
Generate migrations after editing the schema, review the SQL, then apply them explicitly to the intended database. Deployment does not run migrations automatically. Use separate databases and secrets per stage, and size each process’s connection pool against the total service replica count. See Database.
Workspace tenancy is enforced in the application: every query over the eight CRM tables carries
organization_id, in one place, and tests/tenancy.test.ts holds that rule. The eight tables
also carry a PostgreSQL row-level policy, as a floor under a mistake rather than as the
mechanism — drizzle/0013_row_level_security.sql.
Each request runs in one transaction whose first statement is
select set_config('onekit.org_id', …, true), and the policies compare organization_id against
that setting. Two consequences worth planning for:
DATABASE_POOL_MAX now bounds
concurrent requests rather than concurrent queries. Size it against expected concurrency per
process, not against query volume.SET, which would
be silently wrong under transaction pooling — do not add one.The policies do nothing unless DATABASE_URL connects as an ordinary role. PostgreSQL bypasses
row-level security for a superuser or a role with BYPASSRLS no matter what the tables say, and the
kit’s Compose file ships a single superuser for convenience. The server logs one line at startup
when it detects this; tenancy still holds, but the backstop is inert. To get it, create a dedicated
non-owner role, run migrations as the owner, and point the app at the role:
create role onekit_app login password 'choose-a-strong-one';
grant usage on schema public to onekit_app;
grant select, insert, update, delete on all tables in schema public to onekit_app;
-- So tables created by later migrations are reachable too.
alter default privileges in schema public grant select, insert, update, delete on tables to onekit_app;
FORCE ROW LEVEL SECURITY is set on all eight tables, so a deployment that points DATABASE_URL
at the owning role is still subject to the policies — ownership is safe, only the two bypasses
are not.
infra/config.ts defaults to us-east-1, ARM64, port 3000, and local development port 8081. Production requests 0.5 vCPU/1 GB with one to two instances; other stages request 0.25 vCPU/0.5 GB with one instance. The deployed service and load balancer have ongoing infrastructure costs. PostgreSQL is Amazon RDS in the same stack by default (infraConfig.database); ONEKIT_DATABASE=external uses your own instead.
SST requires BetterAuthSecret, ZeroDbPassword and ZeroAdminPassword secrets for each stage, plus DatabaseUrl and ZeroUpstreamDb with an external database. Enable infraConfig.oauth.google or .apple and set the corresponding provider secret pair to deploy social login. Set infraConfig.email.from to a verified SES identity to deploy email delivery with the ses:SendEmail task permission. Setting only local .env values does not change the deployed service.
The domain determines the deployed public/auth origins. Production is protected and retains resources on removal; other stages use removal behavior. Review stack and stage identity before renaming either. Changing ports also requires keeping package scripts, Docker configuration, and health checks aligned. Follow Deployment for exact commands and prerequisites.
| Decision | Status |
|---|---|
| Core framework, package manager, database/ORM/auth | Accepted defaults implemented in source |
| Billing provider and plans for apps built with the kit | Stripe subscriptions implemented; plans in src/config/billing.ts |
| Transactional email provider and templates | Amazon SES implemented with console fallback; other providers can replace the transport in src/server/email.ts |
| Managed PostgreSQL host | Choose a compatible provider and supply its URL |
| Teams/organizations, storage, analytics, push | Optional future modules; absent |
| Onekit’s own pricing, purchase model, license, source delivery, support | Product decisions; see product brief |
Introduce additional options as complete, documented configurations. Test supported combinations before treating them as a buyer-facing feature.