orbseal/Docs

Guide: TaskFlow — Multi-Tenant SaaS Config

This guide walks through a real-world SaaS application (TaskFlow — a task management platform) that uses Orbseal to manage per-tenant configuration, per-environment infrastructure, and encrypted secrets.


The problem

TaskFlow is a multi-tenant SaaS. Each customer (workspace) has different limits and branding. Each deployment environment (production, staging) has different database URLs and secrets. Individual users can override their own preferences (theme, language).

Without Orbseal, this typically means:

  • A config table in the database with ad-hoc columns
  • Secrets hardcoded in environment variables per-deploy
  • No audit trail for who changed what
  • No type safety or schema validation for config keys

With Orbseal, all of this is structured, versioned, and encrypted.


Schema

# orb.yaml
plugin: taskflow

definitions:

  # ── User preferences (each user sets their own) ───────────────────────────
  theme:
    type: enum
    values: [light, dark, system]
    scope: user
    default: system
    overridable: true
    label: Theme
    component: select

  language:
    type: enum
    values: [en, tr, de, fr, es]
    scope: user
    default: en
    overridable: true
    label: Language
    component: select

  # ── Team / Workspace settings ─────────────────────────────────────────────
  max_seats:
    type: number
    scope: workspace
    default: 5
    label: Max Team Seats

  allowed_domains:
    type: json
    scope: workspace
    default: []
    label: Allowed Email Domains

  # ── App behavior (project scope) ─────────────────────────────────────────
  default_view:
    type: enum
    values: [list, board, calendar, timeline]
    scope: project
    default: list
    overridable: true
    label: Default View
    component: select

  features:
    type: json
    scope: project
    default: { "ai_assist": false, "recurring_tasks": true }
    label: Feature Flags

  # ── Infrastructure (environment scope) ───────────────────────────────────
  app_url:
    type: string
    scope: environment
    required: true
    label: App URL

  cdn_url:
    type: string
    scope: environment
    default: "https://cdn.taskflow.app"
    label: CDN URL

  # ── Secrets (E2E encrypted) ───────────────────────────────────────────────
  database_url:
    type: secret
    scope: environment
    required: true
    label: Database URL

  stripe_secret:
    type: secret
    scope: environment
    required: true
    label: Stripe Secret Key

Setup

# Create workspace and project
npx orbseal workspaces create "Platform"
npx orbseal use acme/platform
npx orbseal projects create "TaskFlow"
npx orbseal use acme/platform/taskflow

# Create environments
npx orbseal envs create "Production"
npx orbseal envs create "Staging"

# Sync schema
npx orbseal sync

Set values

# Workspace-level defaults (apply to all projects)
npx orbseal values set taskflow:max_seats 10 \
  --scope workspace --ref default

# Production environment
npx orbseal values set taskflow:app_url https://app.taskflow.io \
  --scope environment --ref production
npx orbseal values set taskflow:cdn_url https://cdn.taskflow.io \
  --scope environment --ref production

# Staging environment
npx orbseal values set taskflow:app_url https://staging.taskflow.io \
  --scope environment --ref staging

# Feature flags (project-wide)
npx orbseal values set taskflow:features \
  '{"ai_assist":true,"recurring_tasks":true}' \
  --scope project --ref taskflow

Seal secrets

# Generate keypair once per app
npx orbseal keygen
# Save the recovery phrase offline

# Register the public key
npx orbseal use --public-key "orbpk-..."
npx orbseal keys create "prod-worker" --env production

# Seal secrets (plaintext never leaves your terminal)
npx orbseal seal taskflow:database_url --scope environment --ref production
npx orbseal seal taskflow:stripe_secret --scope environment --ref production

Cut a release

npx orbseal use acme/platform/taskflow/production
npx orbseal release
✓ Release v1  etag: 9e691b38a0b25e68

  config keys  10
  secret keys  2

Multi-tenant: per-customer workspaces

When a new customer signs up, create a workspace for them:

npx orbseal workspaces create "Acme Corp"
# → workspace: acme-corp  (id: r7kvmx)

They get their own isolated config space. You can set different max_seats or allowed_domains per customer workspace without touching anyone else's config.

npx orbseal values set taskflow:max_seats 50 \
  --scope workspace --ref acme-corp

Resolve at runtime

// Your app server
const ORBSEAL_TOKEN  = process.env.ORBSEAL_TOKEN;   // orb_live_...
const ORBSEAL_PK     = process.env.ORBSEAL_PUBLIC_KEY;
const ORBSEAL_SK     = process.env.ORBSEAL_PRIVATE_KEY;

async function getConfig(userId) {
  const res = await fetch(
    `https://api.orbseal.com/v1/config/resolve?user=${userId}`,
    { headers: { Authorization: `Bearer ${ORBSEAL_TOKEN}` } }
  );
  const { config, secrets, etag } = await res.json();

  // Decrypt secrets
  const dbUrl = decrypt(secrets['taskflow:database_url'], ORBSEAL_PK, ORBSEAL_SK);

  return { config, dbUrl, etag };
}

The etag can be stored in a cache (Redis, memory) and compared on subsequent requests. If the etag hasn't changed, skip the resolve call and return the cached config.


Settings UI

Map each setting panel to an Orbseal scope:

Settings panel Scope Who controls it
Appearance (theme, language) user User themselves
Team (seats, domains) workspace Workspace admin
App behavior (views, features) project Developer / product
Infrastructure (URLs) environment DevOps
Secrets (DB, API keys) environment Developer (sealed)

Your settings UI can call /v1/config/user-values (via app key) to read and write user overrides live, without needing a release.


Related