Configuration
The API reads its settings from environment variables. Everything optional is off when unset.
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL. Without it, data lives in memory and is lost on restart. |
PORT | Listen port (default 3000). |
PUBLIC_URL | Public https address of the API (calendar links, admin). |
APP_URL | Address of the web app (invites, Apple sign-in redirect). |
CORS_ORIGIN | Allowed web origin for the app. |
TRUST_PROXY | 1 behind a reverse proxy. |
METRICS_TOKEN | Optional 64-character lowercase hex secret for private monitoring. |
RESEND_API_KEY, RESEND_FROM | Email (verification codes, invites). |
GOOGLE_CLIENT_IDS, APPLE_CLIENT_IDS | Accepted audiences for Google/Apple sign-in. Empty disables the provider. |
S3_ENDPOINT, S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | Private EU bucket for photos and files. |
ADMIN_SECRET_KEY | Enables the admin dashboard. |
ASSISTANT_PROVIDER, ASSISTANT_API_KEY, ASSISTANT_MODEL | Server-side assistant model (openai, anthropic or mistral). |
VOICE_API_KEY | Speech in and out for plans with voice. |
OPENAI_API_KEY, LIST_AI_*, ANTHROPIC_API_KEY | Optional model for shopping-list categories. |
The app reads EXPO_PUBLIC_API_URL and, for social sign-in, EXPO_PUBLIC_GOOGLE_{WEB,IOS,ANDROID}_CLIENT_ID and EXPO_PUBLIC_APPLE_SERVICE_ID.
Upload recovery
With PostgreSQL enabled, upload tickets survive API restarts. The signed PUT and initial completion window last 15 minutes. A successful completion can be retried for another 24 hours after that window and returns the same photo or file, provided it still exists and the caller retains access. Completion never recreates a deleted record.
The object sweeper removes expired unfinished uploads after a 24-hour grace period for in-flight PUT requests. Member erasure and family purging also invalidate their tickets and schedule unfinished objects for removal. Completed ticket receipts expire without deleting the saved media. Retried storage deletions use the existing orphan cleanup queue.
The app can capture photos, files and attachment messages into a separate local binary queue while offline, then resume when it has a connection. It stores at most 20 pending items and 100 MiB total. Web payloads use IndexedDB transactions; native payloads live in the app cache directory and are not backed up to iCloud or Google. Browser quota/eviction and operating-system cache purges can remove local bytes, and the OS does not guarantee background work. Queue data is cleared on sign-out or family/member revocation. If a saved completion receipt has expired, the user must discard the pending entry and select the file again; a completion that may have been sent is never restarted with a new ticket.