System Requirements
| Software | Version | Required | Purpose |
|---|---|---|---|
| Bun | 1.1.0+ | Yes | JavaScript runtime and package manager |
| Redis | 7.0+ | Yes | Background job queues (BullMQ) and caching |
| Turso CLI | Latest | No | Cloud SQLite database management |
| Stripe CLI | Latest | No | Local webhook forwarding during development |
Install Dependencies
Clone and install
| Package | Path | Description |
|---|---|---|
@bunship/api | apps/api | Main Elysia API application |
@bunship/database | packages/database | Drizzle ORM schema and migrations |
@bunship/config | packages/config | Shared configuration |
@bunship/emails | packages/emails | React Email templates |
@bunship/eden | packages/eden | Type-safe API client |
@bunship/utils | packages/utils | Shared utilities |
Environment Configuration
Application Settings
These control the runtime mode and URL references used in CORS headers, email links, and OAuth redirects.| Variable | Description | Default |
|---|---|---|
NODE_ENV | development, production, or test | development |
API_URL | Public URL of the API (used in email links and OAuth callbacks) | http://localhost:3000 |
FRONTEND_URL | Public URL of your frontend (used for CORS and redirect URLs) | http://localhost:5173 |
JWT Authentication
BunShip uses a dual-token JWT strategy: short-lived access tokens and long-lived refresh tokens. Each needs its own signing secret.Database Setup
BunShip uses Drizzle ORM with Turso (libSQL/SQLite). For local development you can use a file-based SQLite database with zero external dependencies.Local Development (File-Based)
The default.env.example is already configured for local file storage:
Turso Cloud
For staging or production, connect to a Turso cloud database:Run Migrations and Seed
- Demo user —
[email protected]/demo123456 - Demo organization with a Pro plan on trial
- Sample projects attached to the organization
Other database commands
Other database commands
| Command | Description | |---------|-------------| |
bun run db:generate | Generate a new
migration after editing the Drizzle schema | | bun run db:push | Push schema directly to the
database (development only) | | bun run db:studio | Open Drizzle Studio, a visual database
browser | | bun run db:reset | Drop all tables and re-run migrations (destroys all data) |Stripe Setup
BunShip includes subscription management with free, Pro, and Enterprise tiers. All Stripe integration works in test mode without processing real payments.Create a Stripe account
Sign up at stripe.com. The dashboard starts in test mode by default.
Get your API keys
Navigate to Developers > API keys in the Stripe Dashboard and copy your Secret key
(starts with
sk_test_).Create products and prices
Go to Products in the Stripe Dashboard and create your pricing tiers. You need price
IDs for each plan and billing interval:
Set up webhooks
Stripe sends events (subscription created, payment failed, etc.) to your API via webhooks.For local development, use the Stripe CLI:The CLI prints a webhook signing secret (For production, add a webhook endpoint in the Stripe Dashboard
(Developers > Webhooks) pointing to
whsec_...). Add it to your .env:https://your-domain.com/webhooks/stripe and
subscribe to these events:checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.paidinvoice.payment_failed
Stripe billing is optional for initial development. If
STRIPE_SECRET_KEY is not set, billing
endpoints return a configuration error but the rest of the API works normally.S3 / R2 Storage Setup
File uploads use any S3-compatible object storage. Choose the tab matching your provider.- AWS S3
- Cloudflare R2
- MinIO (local)
s3:PutObject,
s3:GetObject, and s3:DeleteObject permissions scoped to that bucket.File storage is optional. If the S3 variables are not set, file upload endpoints return a
configuration error but the rest of the API works normally.
Redis Setup
Redis powers two subsystems: BullMQ background job queues and a general-purpose caching layer.Email Setup (Optional)
BunShip sends transactional emails (verification, password reset, team invitations) through Resend.Create a Resend account
Sign up at resend.com and copy your API key.
Verify your sending domain
In the Resend dashboard, go to Domains, add your domain, and configure the DNS records Resend
provides (SPF, DKIM, DMARC).
During development, Resend’s test mode sends to any email address without a verified domain. If
RESEND_API_KEY is not set, emails are logged to the console instead of being sent.OAuth Providers (Optional)
Google OAuth
- Go to the Google Cloud Console.
- Create a project (or select an existing one).
- Enable the Google+ API.
- Under Credentials, create an OAuth 2.0 Client ID.
- Add the authorized redirect URI:
http://localhost:3000/api/v1/auth/google/callback
GitHub OAuth
- Go to GitHub Developer Settings.
- Create a new OAuth App.
- Set the callback URL to:
http://localhost:3000/api/v1/auth/github/callback
Verifying the Installation
After completing the setup, start the development server and run through these checks.API documentation
Open http://localhost:3000/docs in your browser. You should see the
Scalar-powered OpenAPI documentation listing all available endpoints.
Troubleshooting
"Cannot find module" errors
"Cannot find module" errors
Clear the workspace and reinstall:
Database connection failed
Database connection failed
- File-based database: Ensure
TURSO_DATABASE_URL=file:../../local.dbis set in yourapps/api/.envfile. - Turso cloud: Verify the auth token is still valid:
Redis connection refused
Redis connection refused
Check that Redis is running and reachable:If you are using Docker, confirm the container is up:
Port 3000 already in use
Port 3000 already in use
Find and stop the process occupying the port:Or start BunShip on a different port:
JWT errors (token invalid / signature mismatch)
JWT errors (token invalid / signature mismatch)
Ensure both secrets are set and at least 32 characters long:
Stripe webhook signature invalid
Stripe webhook signature invalid
Make sure The CLI prints a new signing secret each time it starts. Copy it into your
STRIPE_WEBHOOK_SECRET matches the secret shown by the Stripe CLI or the
Stripe Dashboard. For local development, always use the Stripe CLI:.env.Complete .env Reference
Below is the full list of environment variables from.env.example:
| Variable | Required | Default | Description |
|---|---|---|---|
NODE_ENV | Yes | development | Runtime environment |
API_URL | Yes | http://localhost:3000 | Public API URL |
FRONTEND_URL | Yes | http://localhost:5173 | Public frontend URL |
DATABASE_URL | Yes | file:./local.db | Turso/SQLite connection string |
DATABASE_AUTH_TOKEN | No | — | Turso cloud auth token |
REDIS_HOST | Yes | localhost | Redis hostname |
REDIS_PORT | Yes | 6379 | Redis port |
REDIS_URL | Yes | redis://localhost:6379 | Redis connection URL |
JWT_SECRET | Yes | — | Access token signing secret (32+ chars) |
JWT_REFRESH_SECRET | Yes | — | Refresh token signing secret (32+ chars) |
STRIPE_SECRET_KEY | No | — | Stripe API secret key |
STRIPE_WEBHOOK_SECRET | No | — | Stripe webhook signing secret |
STRIPE_PRO_MONTHLY_PRICE_ID | No | — | Stripe price ID for Pro monthly |
STRIPE_PRO_YEARLY_PRICE_ID | No | — | Stripe price ID for Pro yearly |
STRIPE_ENTERPRISE_MONTHLY_PRICE_ID | No | — | Stripe price ID for Enterprise monthly |
STRIPE_ENTERPRISE_YEARLY_PRICE_ID | No | — | Stripe price ID for Enterprise yearly |
RESEND_API_KEY | No | — | Resend email API key |
EMAIL_FROM | No | — | Sender address for transactional emails |
S3_ENDPOINT | No | — | S3-compatible storage endpoint URL |
S3_BUCKET | No | — | Storage bucket name |
S3_ACCESS_KEY_ID | No | — | S3 access key |
S3_SECRET_ACCESS_KEY | No | — | S3 secret key |
S3_REGION | No | us-east-1 | S3 region |
GOOGLE_CLIENT_ID | No | — | Google OAuth client ID |
GOOGLE_CLIENT_SECRET | No | — | Google OAuth client secret |
GITHUB_CLIENT_ID | No | — | GitHub OAuth client ID |
GITHUB_CLIENT_SECRET | No | — | GitHub OAuth client secret |
TOTP_ISSUER | No | BunShip | Name shown in authenticator apps |
DEMO_MODE | No | false | Restricts destructive actions for demo environments |
CRON_SECRET | No | — | Shared secret for cron job endpoints |

