← Docs

Self-Hosting Guide

Deploy your own Daslab instance with a custom domain, OAuth integrations, and full control over your data.

Architecture Overview

Daslab has a simple architecture:

ComponentTechnologyPurpose
ServerBun + HonoAPI, website, OAuth, WebSockets
DatabasePostgreSQLUsers, orgs, jobs, credentials
RedisUpstash RedisJob queues, caching, rate limiting
File StorageCloudflare R2 (S3-compatible)User uploads, CLI releases
Push NotificationsAPNsiOS push notifications
EmailResendTransactional email (magic links, invites)

The server is a single Bun process that handles everything — API requests, website pages, OAuth flows, WebSocket connections, and background job processing.

Prerequisites

  • A domain name (e.g. daslab.run)
  • A server or PaaS that can run Bun (Render, Railway, Fly.io, VPS, etc.)
  • PostgreSQL database
  • Redis instance (Upstash recommended)
  • Cloudflare account (for R2 file storage)

1. Domain & DNS Setup

You'll need these DNS records pointing to your infrastructure:

RecordTypeTargetPurpose
yourdomain.comCNAMEYour server hostAPI + website
tmp.yourdomain.comCNAMER2 custom domainUser file uploads
releases.yourdomain.comCNAMER2 custom domainCLI binary releases

Cloudflare R2 Custom Domains

For file storage, set up two R2 buckets with custom domains:

  1. Go to Cloudflare Dashboard > R2 > Create Bucket
  2. Create a bucket for user uploads (e.g. daslab-user)
  3. Under Settings > Custom Domains, add tmp.yourdomain.com
  4. Repeat for CLI releases bucket with releases.yourdomain.com

Cloudflare automatically manages the DNS and SSL for R2 custom domains.

2. Environment Variables

Set these on your server. The only required variable for domain configuration is PUBLIC_BASE_URL — all OAuth callback URLs, canonical URLs, sitemaps, and SEO metadata derive from it automatically.

Core Configuration

# Your domain — this is the single source of truth for all server URLs
PUBLIC_BASE_URL=https://yourdomain.com

# Database
DATABASE_URL=postgresql://user:pass@host:5432/daslab

# Redis
UPSTASH_REDIS_URL=rediss://default:xxx@xxx.upstash.io:6379

# File storage (Cloudflare R2)
R2_ACCESS_KEY_ID=your_r2_access_key
R2_SECRET_ACCESS_KEY=your_r2_secret_key
R2_ACCOUNT_ID=your_cloudflare_account_id
R2_BUCKET_NAME=daslab-releases
R2_USER_BUCKET_NAME=daslab-user
R2_PUBLIC_URL=https://tmp.yourdomain.com       # Public URL for user file uploads

# Email
FROM_EMAIL=Daslab <noreply@yourdomain.com>     # Sender address for transactional email
CONTACT_EMAIL=hello@yourdomain.com             # Contact email shown in legal pages

AI Providers

At least one LLM provider key is required. Add as many as you need:

ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-...
GEMINI_API_KEY=...

Email (Resend)

Required for magic link authentication and email invites:

RESEND_API_KEY=re_...

Emails are sent from the address configured in FROM_EMAIL. Configure your domain in the Resend dashboard to verify DNS records (SPF, DKIM, DMARC).

Apple Push Notifications

Required for iOS push notifications:

APNS_TEAM_ID=your_apple_team_id
APNS_KEY_ID=your_apns_key_id
APNS_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
APNS_BUNDLE_ID=com.yourcompany.Daslab

Observability (Optional)

OTEL_EXPORTER_OTLP_ENDPOINT=https://your-otel-collector:4318   # OpenTelemetry traces
ADMIN_LOGS_KEY=your_admin_logs_key                              # Key for /admin/logs endpoint

3. OAuth Provider Setup

Daslab supports authentication and integration via several OAuth providers. Each requires registering an application on the provider's developer portal and configuring the callback URL.

All callback URLs follow the pattern: https://yourdomain.com/auth/{provider}/callback

GitHub

Used for: User login/signup, GitHub repository access
  1. Go to GitHub Developer Settings
  2. Click New OAuth App (or edit existing)
  3. Set the fields:
- Application name: Daslab

- Homepage URL: https://yourdomain.com

- Authorization callback URL: https://yourdomain.com/auth/github/callback

  1. Copy the Client ID and generate a Client Secret
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
Scopes requested at runtime: user:email, repo, read:org

Google

Used for: User login/signup (Google Sign-In), Gmail, Calendar, Drive, Sheets access
  1. Go to Google Cloud Console > APIs & Credentials
  2. Click Create Credentials > OAuth 2.0 Client ID
  3. Set application type to Web application
  4. Add these Authorized redirect URIs (both are required):
- https://yourdomain.com/auth/google/callback (for service integrations)

- https://yourdomain.com/auth/google/signin/callback (for user login)

  1. Copy the Client ID and Client Secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
Scopes requested at runtime:
  • Sign-in: openid, email, profile
  • Gmail: gmail.readonly, gmail.send
  • Calendar: calendar, calendar.events
  • Drive: drive
  • Sheets: spreadsheets, drive.readonly
Note: Google requires an OAuth consent screen to be configured. For internal/testing use, set the app to "Internal" (Google Workspace) or "Testing" (personal accounts, limited to 100 test users). For production, submit for verification.

Apple

Used for: User login/signup (Sign in with Apple)

Apple Sign-In uses native iOS authentication — there is no server-side OAuth redirect. The iOS app handles authentication directly via ASAuthorization and sends the identity token to the server for verification.

No callback URL configuration is needed. The server verifies tokens against Apple's JWKS endpoint (https://appleid.apple.com/auth/keys).

# Defaults to your app's bundle identifier
APPLE_CLIENT_ID=com.yourcompany.Daslab

Figma

Used for: Figma file access (organization-level integration)
  1. Go to the Figma Developer Portal
  2. Create a new app or edit an existing one
  3. Set the Callback URL: https://yourdomain.com/auth/figma/callback
  4. Copy the Client ID and Client Secret
FIGMA_CLIENT_ID=your_figma_client_id
FIGMA_CLIENT_SECRET=your_figma_client_secret
Scopes requested at runtime: files:read

Slack

Used for: Slack workspace access (organization-level integration)
  1. Go to Slack API > Your Apps
  2. Create a new app — you can use the manifest at server/slack-manifest.yml (update the URLs in the manifest to your domain first)
  3. Under OAuth & Permissions, add a Redirect URL:
https://yourdomain.com/auth/slack/callback
  1. Under Event Subscriptions, set the Request URL:
https://yourdomain.com/webhooks/slack/events
  1. Copy the Client ID and Client Secret from Basic Information
SLACK_CLIENT_ID=your_slack_client_id
SLACK_CLIENT_SECRET=your_slack_client_secret
SLACK_SIGNING_SECRET=your_slack_signing_secret  # For webhook signature verification
Bot scopes: app_mentions:read, channels:history, channels:read, chat:write, chat:write.customize, commands, groups:history, groups:read, im:history, im:read, reactions:read, reactions:write, users:read User scopes: channels:history, channels:read, groups:history, groups:read, im:history, im:read, search:read
Note: The server/slack-manifest.yml file contains hardcoded URLs that must be updated to your domain before creating the Slack app from the manifest.

Canva

Used for: Canva design access, assets, comments (organization-level integration)
  1. Go to the Canva Developer Portal
  2. Create a new integration
  3. Set the Redirect URL: https://yourdomain.com/auth/canva/callback
  4. Set the Webhook URL: https://yourdomain.com/webhooks/canva/default
  5. Copy the Client ID and Client Secret
CANVA_CLIENT_ID=your_canva_client_id
CANVA_CLIENT_SECRET=your_canva_client_secret
Scopes requested at runtime: design:meta:read, design:content:read, design:content:write, asset:read, asset:write, brandtemplate:meta:read, brandtemplate:content:read, folder:read, folder:write, comment:read, comment:write, profile:read, collaboration:event
Note: Canva uses PKCE (S256) for the OAuth flow. The server handles code verifier/challenge generation automatically.

Xero

Used for: Accounting access — invoices, payments, bank transactions, contacts (organization-level integration)

Xero is multi-tenant: one OAuth connection can access multiple organizations. Daslab creates one asset per tenant.

  1. Go to the Xero Developer Portal
  2. Create a new app (Web App type)
  3. Set the Redirect URI: https://yourdomain.com/auth/xero/callback
  4. Copy the Client ID and Client Secret
XERO_CLIENT_ID=your_xero_client_id
XERO_CLIENT_SECRET=your_xero_client_secret
Scopes requested at runtime: openid, profile, email, offline_access, accounting.invoices, accounting.payments.read, accounting.banktransactions.read, accounting.contacts.read, accounting.attachments.read, accounting.settings.read

Upwork

Used for: Upwork organization/freelancer access (organization-level integration)
  1. Go to the Upwork Developer Portal
  2. Create a new API application
  3. Set the Callback URL: https://yourdomain.com/auth/upwork/callback
  4. Copy the Client ID and Client Secret
UPWORK_CLIENT_ID=your_upwork_client_id
UPWORK_CLIENT_SECRET=your_upwork_client_secret

LINE

Used for: LINE messaging bot with account linking (webhook-based, not OAuth)
  1. Go to the LINE Developers Console
  2. Create a Messaging API channel
  3. Under Messaging API, set the Webhook URL: https://yourdomain.com/webhooks/line/bot
  4. Enable Use webhook
  5. Copy the Channel Access Token and Channel Secret
LINE_CHANNEL_ACCESS_TOKEN=your_line_channel_access_token
LINE_CHANNEL_SECRET=your_line_channel_secret

Account linking connects LINE users to Daslab accounts. When a user taps "Link Account" in the LINE chat, they're redirected to your server to authenticate.

Telegram Bot

Used for: Telegram chat integration (webhook-based, not OAuth)
  1. Create a bot via @BotFather on Telegram
  2. Copy the bot token
  3. Register the webhook by running:
bun run scripts/telegram-setup.ts

This reads PUBLIC_BASE_URL from your environment and registers the webhook URL at https://yourdomain.com/webhooks/telegram/bot.

TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_WEBHOOK_SECRET=your_webhook_secret  # optional, for verification

4. Webhook URLs

Several providers deliver real-time events via webhooks. These URLs must be registered in each provider's dashboard. All are served by the Daslab server automatically — you just need to tell each provider where to send events.

ProviderWebhook URLWhere to configure
Slackhttps://yourdomain.com/webhooks/slack/eventsSlack App → Event Subscriptions → Request URL
Canvahttps://yourdomain.com/webhooks/canva/defaultCanva Developer Portal → Integration → Webhook URL
Telegramhttps://yourdomain.com/webhooks/telegram/botAuto-registered via bun run scripts/telegram-setup.ts
LINEhttps://yourdomain.com/webhooks/line/botLINE Developers Console → Messaging API → Webhook URL
Vapihttps://yourdomain.com/webhooks/vapi/callsVapi Dashboard → Assistant → Server URL
17TRACKhttps://yourdomain.com/webhooks/17track/events?type=package17TRACK API Console → Settings → Webhook URL

5. Database Setup

Run the server once with your DATABASE_URL set — it automatically runs migrations on startup:

bun run src/index.ts

Migrations are defined in server/src/db.ts in the migrate() function and run sequentially. The server tracks which migrations have been applied and only runs new ones.

6. iOS Client Configuration

The iOS app connects to Daslab Cloud by default. To connect to your own instance:

  1. Open Profile in the app (avatar in top-right)
  2. Under Server Accounts, tap Add Server
  3. Enter your server URL (e.g. https://yourdomain.com)
  4. Sign in with your credentials on the self-hosted server

Multi-Server Support

The iOS app supports simultaneous connections to multiple Daslab servers. Organizations from all connected servers appear in a single merged list, grouped by server. When you navigate into an org, API calls automatically route to the correct server.

  • Each server has its own auth token stored in Keychain (isolated per server)
  • The share extension works with orgs from any connected server
  • Swipe to delete a server account from Profile to disconnect

Building from Source

If building the iOS app from source, update the default server URL in:

  • Daslab/Services/ServerConfig.swiftdefaultURL
  • DaslabShare/SharedDataService.swiftdefaultServerURL

7. CLI Configuration

The CLI connects to https://daslab.run by default. To use your own server:

export DASLAB_API_URL=https://yourdomain.com

Or pass it per-command:

DASLAB_API_URL=https://yourdomain.com daslab jobs list

If building the CLI from source, the default is set in cli-rust/src/api.rs.

8. Optional Services

These services enhance functionality but aren't required for core operation:

ServiceEnv VarPurpose
E2BE2B_API_KEYCode execution sandbox
TavilyTAVILY_API_KEYWeb search for AI
Brave SearchBRAVE_SEARCH_API_KEYWeb search (alternative)
DeepgramDEEPGRAM_API_KEYSpeech-to-text transcription
LiveKitLIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_WS_URLReal-time voice/video
AirtopAIRTOP_API_KEYBrowser automation
Google MapsGOOGLE_MAPS_API_KEYGeocoding and maps
BrandfetchBRANDFETCH_CLIENT_IDCompany logo/brand lookup

9. Deployment Checklist

Use this checklist when setting up a new instance or migrating to a new domain:

Infrastructure

  • [ ] Server running with Bun
  • [ ] PostgreSQL database provisioned
  • [ ] Redis instance provisioned
  • [ ] PUBLIC_BASE_URL set to your domain
  • [ ] DNS records pointing to your server
  • [ ] SSL/TLS configured (most PaaS handle this automatically)

File Storage

  • [ ] Cloudflare R2 buckets created (user uploads + releases)
  • [ ] R2 custom domains configured (tmp. and releases. subdomains)
  • [ ] R2 credentials set in environment variables

Authentication Providers

  • [ ] GitHub OAuth App — callback URL updated
  • [ ] Google OAuth — both redirect URIs added, consent screen configured
  • [ ] Apple Sign-In — bundle ID matches APPLE_CLIENT_ID
  • [ ] Figma — callback URL updated (if using Figma integration)
  • [ ] Slack — redirect URL updated, event subscription URL updated (if using Slack)
  • [ ] Canva — callback URL and webhook URL updated (if using Canva)
  • [ ] Xero — redirect URI updated (if using Xero integration)
  • [ ] Upwork — callback URL updated (if using Upwork integration)

Webhooks

  • [ ] Telegram — webhook registered via bun run scripts/telegram-setup.ts
  • [ ] LINE — webhook URL updated in LINE Developers Console
  • [ ] Slack — event subscription request URL updated
  • [ ] Vapi — server URL updated on each assistant
  • [ ] 17TRACK — webhook URL updated in API console
  • [ ] Canva — webhook URL updated in developer portal
  • [ ] server/slack-manifest.yml — hardcoded URLs updated to your domain (if using manifest)

Email

  • [ ] Resend API key set
  • [ ] Domain verified in Resend (SPF, DKIM, DMARC records)
  • [ ] FROM_EMAIL and CONTACT_EMAIL set to your domain

iOS App

  • [ ] Server URL configured (either in-app or compiled default)
  • [ ] Push notification credentials set (APNS_* variables)

CLI

  • [ ] DASLAB_API_URL set or compiled with correct default

10. Upgrading

The server is stateless — deploy new code and restart. Database migrations run automatically on startup.

For zero-downtime OAuth domain migration:

  1. Add the new callback URLs to each OAuth provider dashboard (keep the old ones)
  2. Deploy the server with the new PUBLIC_BASE_URL
  3. Verify all OAuth flows work with the new domain
  4. Remove the old callback URLs from each provider dashboard

This ensures no authentication flow breaks during the transition.

11. Troubleshooting

OAuth callback errors

If OAuth redirects fail after a domain change, the most common cause is a mismatch between PUBLIC_BASE_URL and the callback URLs registered with the provider. Check:

# What the server thinks its URL is
curl https://yourdomain.com/health | jq '.apiUrl'

Compare this with the redirect URIs configured in each provider's dashboard.

File uploads returning 404

Ensure the R2 custom domain is configured and propagated:

curl -I https://tmp.yourdomain.com
# Should return 200 or 403 (bucket exists), not DNS errors

Push notifications not delivered

Verify your APNs credentials:

  • APNS_BUNDLE_ID must match the iOS app's bundle identifier exactly
  • The APNs key must be associated with the correct Team ID
  • The key must have the Apple Push Notifications service (APNs) capability enabled

Emails not sending

Check that your domain is fully verified in Resend. All three DNS records (SPF, DKIM, DMARC) must be configured correctly. The sender address is configured via the FROM_EMAIL environment variable.