# Build Specification: Managed Automation Infrastructure Service

**Brand:** Own the Stack, at `ownthestack.io`.
**Business model:** Managed DevOps concierge. Each client leases a dedicated, isolated
DigitalOcean droplet running their own n8n instance (full n8n branding retained). The
monthly fee covers infrastructure leasing, server administration, security patches,
scheduled backups, monitoring, and priority support access. Workflow building,
troubleshooting, and integrations are sold separately through the Automation Library
or a direct project quote. Each library item shows an estimated implementation range.
The final project price is confirmed from the customer's software, setup, and scope,
then paid upfront. Anything outside that scope pauses for approval before cost is added.

**Pricing direction, corrected 09/27/2026:** No fixed starting implementation price. Quote work based on the customer's setup and agreed scope.

**Customer-facing copy:**

> Each Automation Library item will show an **estimated implementation price range**.
> The range is only a guide and may vary based on the customer’s software, setup, and scope.
>
> **Estimated implementation range. Final pricing may vary based on your specific setup.**
>
> If extra work comes up, we will check with the customer first before adding any additional cost.

Every implementation payment belongs to a named automation or accepted custom scope.
The service never holds customer credit or an unused hour balance.

This document is the implementation contract for the developer. It covers the full
system: infrastructure, per-client environments, automated provisioning, the client
portal, the admin portal, backups, monitoring, updates, and lifecycle handling.

**Progress note, 09/27/2026:** Jason's FINAL runbook records a completed manual baseline and a successful 36 minute 31 second restore on 09/22/2026. Automatic provisioning, GoHighLevel lifecycle handling, portal record integration, and fleet rollout remain separate deliverables. Client host alerting/security checks and notification delivery remain unverified in that runbook. Current sources: https://app.clickup.com/t/86ajxbucd .

---

## 0. What is already built. Read this first.

**Status as of 4 September 2026.**

**One server exists. Zero customer servers exist.** That is the whole picture.

The one that exists is the **control server**. It is the engine room: the machine that
builds, watches, and backs up every customer's server. No customer ever logs into it,
sees it, or runs anything on it. There will only ever be one of them no matter how
many customers you have.

Every customer gets their **own separate droplet**, created later by the pipeline in
section 4. None have been created yet. The first one will be built by hand, see
"Your first task" at the end of this section.

Everything listed below already exists and is working. Do not rebuild any of it.

### Domain
`ownthestack.io`. DNS is hosted at **Porkbun**, not DigitalOcean. Customer workspaces
will live at `{slug}.ownthestack.io`. Note that section 2.2 below assumes DigitalOcean
DNS; that is not what we are on, so the provisioning step that creates a customer's
DNS record must be written against the Porkbun API instead.

### The control server
This is the machine described above. It runs Coolify, the monitoring, the backups,
and eventually the provisioning service. Nothing customer-facing runs here, ever.

| | |
|---|---|
| Droplet name | **`control`** |
| Provider | DigitalOcean, New York |
| Size | 4 GB / 2 vCPU / 77 GB |
| IP | `206.189.230.74` |
| OS | Ubuntu 24.04.4 LTS |

Already hardened: ufw firewall allowing only 22, 80, 443, 8000 and 3001; password
authentication disabled, key-only SSH; fail2ban on sshd; unattended security upgrades
enabled.

### Installed and running on it
- **Coolify 4.3.15**, at `https://coolify.ownthestack.io` with a valid Let's Encrypt
  certificate. The localhost server is registered and the proxy is running. A project
  named `Automation Service` exists with a `production` environment.
- **Uptime Kuma**, container `uptime-kuma`, already installed and running, admin
  account created. **The login is in NordPass. Open it from there.** Email alerting is
  not configured yet; it needs a Resend API key.
- **restic 0.16.4**, backing up to Backblaze B2 bucket `kwagency-automation-backups`,
  repository path `control`. Credentials live in `/etc/backup/b2.env`, root only. A
  test backup has run and `restic check` passes. **The repository passphrase is held
  by the owner in NordPass and is not recoverable if lost.**

### Control plane database
Supabase project **`automation-service`**, region `us-east-1`, on the existing paid
organization. The full schema from section 5 is **already applied**: all twelve
tables, row level security enabled on every one, policies for both client and admin
roles, the `current_client_id()` and `is_admin()` helper functions, and the trigger
that creates a profile row on signup. Supabase's security advisor passes clean.
Write new migrations; do not recreate the schema.

### Not done yet
- The client environment template in section 3
- The provisioning pipeline in section 4
- Any customer droplet
- Wildcard and per-customer DNS records
- Email sending, from Uptime Kuma or the welcome email

### One change from what is written below
Section 4 assumes **Stripe** fires the webhook that starts a provisioning run. Signup
and payment are going through **GoHighLevel** instead. The ten steps are unchanged;
the trigger is a GoHighLevel webhook rather than a Stripe one. The same applies to the
lifecycle events in section 8: payment failed, payment recovered, and cancellation all
arrive from GoHighLevel.

### Tagging
No DigitalOcean project named **AI Biz** exists yet. Create one, move the `control`
droplet into it, and put every client droplet there as well.
Projects are cosmetic and nothing in the system reads them. What the admin portal and
cost reporting actually key off are the **tags** in section 2.1: `client:<slug>` and
`managed:true` on every client droplet. Tag every one.

### Your first task

Do not start with the automation. Build the first customer server **by hand**, start
to finish, writing down every step as you go.

1. Build the client environment template in section 3, the pinned n8n plus Postgres
   compose stack.
2. Deploy it manually to a new 2GB droplet through the existing Coolify at
   `https://coolify.ownthestack.io`, for the first real client.
3. Create their DNS record at Porkbun, confirm HTTPS issues, create their n8n owner
   account, add them to Uptime Kuma (login in NordPass), and add them to the nightly
   backup job.
4. Prove a backup of that instance restores onto a fresh droplet and the workflows
   still run.
5. Write up the runbook of what you just did.

That runbook is what the provisioning pipeline in section 4 gets built from. Phase 0
in section 12 exists for exactly this reason: you cannot reliably automate a process
nobody has run yet.

---

## 1. System overview

```
                             ┌────────────────────────────────────────────────┐
                             │                 DigitalOcean                   │
 Stripe ──payment webhook──┐ │                                                │
                           ▼ │  ┌──────────── CONTROL PLANE ───────────────┐  │
 Client ─▶ portal.<domain> ──┼─▶│ Client portal + Admin dashboard (this    │  │
 Admin  ─▶ admin.<domain>  ──┼─▶│ repo: React + Supabase)                  │  │
                             │  │ Provisioning service (Node + job queue)  │  │
                             │  │ Control-plane DB (Supabase Postgres)     │  │
                             │  └───────┬──────────────────┬───────────────┘  │
                             │          │ DO API           │ Coolify API      │
                             │          ▼                  ▼                  │
                             │  ┌───────────────┐  ┌──────────────────────┐   │
                             │  │ DO API:       │  │ Coolify (dedicated   │   │
                             │  │ droplets, DNS │  │ 4GB control droplet) │   │
                             │  └───────────────┘  └──────────┬───────────┘   │
                             │                     manages via SSH            │
 tracy.<domain>  ───HTTPS──┐ │  ┌────────────── CLIENT DROPLETS ───────────┐  │
 brandy.<domain> ───HTTPS──┼─┼─▶│ Droplet per client (2GB min):            │  │
 acme.<domain>   ───HTTPS──┘ │  │  ┌─────────┐ ┌────────────┐ ┌─────────┐  │  │
                             │  │  │ n8n     │ │ PostgreSQL │ │ proxy + │  │  │
                             │  │  │ (pinned)│ │ 16 (local  │ │ SSL     │  │  │
                             │  │  │         │ │ container) │ │(Coolify)│  │  │
                             │  │  └─────────┘ └────────────┘ └─────────┘  │  │
                             │  └──────────────────┬────────────────────────┘  │
                             │                     │ nightly                   │
                             │                     ▼                           │
                             │        Backblaze B2 / DO Spaces (encrypted      │
                             │        backups: pg_dump + n8n volume + keys)    │
                             │                                                │
                             │  Uptime Kuma (on control droplet): per-client  │
                             │  HTTPS health checks → Slack/email alerts      │
                             └────────────────────────────────────────────────┘
```

Key decisions (already made: do not relitigate):

- **One droplet per client.** Full isolation. No shared multi-tenant n8n, ever.
- **n8n keeps its own branding** inside the workspace. Own the Stack lives on the
  portal, the subdomain, and the service around it.
- **Coolify** is the deployment/orchestration layer (server management, app deploys,
  reverse proxy, SSL). We do not hand-roll nginx configs or certbot.
- **PostgreSQL container per client droplet**: never n8n's default SQLite.
- **The portal/admin app is built in this repository** (React + Supabase stack already
  present). The provisioning service is a small separate Node service (or Supabase
  edge functions if kept simple).

---

## 2. Infrastructure

### 2.1 Droplets

| Role | Size | Count | Notes |
|---|---|---|---|
| Coolify control | 4GB / 2vCPU | 1 | Coolify + Uptime Kuma. Nothing client-facing runs here. |
| Client droplet: standard | 2GB / 1vCPU ($12–18/mo) | 1 per client | n8n + Postgres. **2GB is the floor: 1GB will OOM.** Every client gets this by default. |
| Client droplet: upsized | 4GB / 2vCPU | exception only | Not a sellable tier. Used when a client's usage (browser automation, high volume) demands it; handled case-by-case per the fair-use clause in the client terms. |

**Pricing is a single flat tier ($50/mo).** There is no Standard/Pro product split: do
not build tier logic into billing or provisioning. Droplet size is an internal
operational lever, not a customer-facing plan.

- All droplets: Ubuntu LTS, created from a snapshot/cloud-init template (Docker
  preinstalled, SSH hardened, DO monitoring agent installed).
- **DO Cloud Firewall** on all client droplets: inbound 443/80 (proxy) and 22
  (restricted to control-plane IPs only). Nothing else.
- SSH: key-only, no root password, fail2ban. One provisioning keypair held by
  Coolify/control plane; no per-client SSH keys handed out by default.
- Tag every droplet `client:<slug>` and `managed:true`: the admin portal and cost
  reporting key off tags.

### 2.2 DNS and TLS

- DNS hosted on DigitalOcean DNS for the service domain.
- `*.{domain}` wildcard A record → a static "no such workspace" page on the control
  droplet (catch-all).
- Per-client A record `{slug}.{domain}` → that client's droplet IP, created via DO API
  during provisioning (one API call).
- Reserved subdomains (blocklist at slug generation): `www, api, app, portal, admin,
  mail, status, docs, billing, support`, plus profanity list.
- TLS: Coolify's built-in Let's Encrypt issuance per subdomain (HTTP-01). Certificates
  renew automatically. No wildcard cert needed since each droplet terminates its own TLS.

### 2.3 Coolify

- Single Coolify instance on the control droplet, API enabled, API token stored in
  control-plane secrets.
- Each client droplet is registered in Coolify as a **server** (via SSH) at provision
  time; the client stack is deployed to it as a **Docker Compose resource** from the
  template in §3.
- Coolify handles: deploys, restarts, proxy routing (`{slug}.{domain}` → n8n container),
  SSL, and its own scheduled DB backups feature (we still run our own backup job: §6).

---

## 3. Client environment template

One Docker Compose stack per client, templated by the provisioning service.
Variables: `SLUG`, `DOMAIN`, `N8N_VERSION`, `PG_PASSWORD`, `N8N_ENCRYPTION_KEY`,
`N8N_LICENSE_ACTIVATION_KEY`.

```yaml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}   # ALWAYS pinned. Never :latest.
    restart: unless-stopped
    environment:
      - N8N_HOST=${SLUG}.${DOMAIN}
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${SLUG}.${DOMAIN}/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}    # generated per client; see §6.3
      - N8N_LICENSE_ACTIVATION_KEY=${N8N_LICENSE_ACTIVATION_KEY}   # agency key; see below
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${PG_PASSWORD}
      - EXECUTIONS_DATA_PRUNE=true                  # or execution history eats the DB
      - EXECUTIONS_DATA_MAX_AGE=336                 # 14 days
      - N8N_DIAGNOSTICS_ENABLED=false
      - N8N_RUNNERS_ENABLED=true                    # sandboxed Code-node execution
      - GENERIC_TIMEZONE=America/New_York
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on: [postgres]
    deploy:
      resources:
        limits: { memory: 1200M }                   # 2500M on Pro tier
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${PG_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    deploy:
      resources:
        limits: { memory: 512M }
volumes:
  n8n_data:
  pg_data:
```

Coolify's proxy routes `https://{slug}.{domain}` → `n8n:5678`. Postgres is never
exposed publicly.

**License keys.** n8n mails a free Community Edition key to whatever address
requested it, so the request always goes out on the agency email address, never
the customer's. Nothing in signup may wait on a customer forwarding anything. One
key activates up to 5 instances, so the control plane holds a pool of keys with
their remaining activations and provisioning takes the next one with room left.
Setting `N8N_LICENSE_ACTIVATION_KEY` at first boot registers the workspace with no
visit to the settings screen; the variable has no effect once an instance is
already licensed. Registering only adds folders, debug in editor, and custom
execution data, so an unregistered workspace still ships.

---

## 4. Automated provisioning pipeline

**Trigger:** Stripe `checkout.session.completed` webhook (and `customer.subscription.*`
events for lifecycle: §8).

**Implementation requirements:**
- Webhook handler does nothing but validate the Stripe signature and enqueue a job.
  All work happens in a **durable job queue** (BullMQ + Redis on the control droplet,
  or Trigger.dev). Never provision inline in the webhook request.
- The pipeline is a **state machine**. Every step is idempotent (safe to re-run), and
  the current state is persisted to `provisioning_jobs` after each step so a failed
  run resumes rather than restarts. Failures surface in the admin portal.

**States/steps, in order:**

| # | State | Action |
|---|---|---|
| 1 | `slug_reserved` | Slugify company name from checkout metadata; check collisions + blocklist; write `clients` row. |
| 2 | `secrets_created` | Generate `PG_PASSWORD` + `N8N_ENCRYPTION_KEY`; store in secrets store; copy encryption key to key-escrow backup (§6.3). |
| 3 | `droplet_created` | DO API: create droplet from template snapshot, tag it, attach firewall. Poll until active + SSH-reachable. |
| 4 | `dns_created` | DO API: create `{slug}` A record → droplet IP. |
| 5 | `coolify_registered` | Coolify API: add droplet as server, validate connection. |
| 6 | `stack_deployed` | Coolify API: create Compose resource from template (§3), deploy, wait for healthy. Compose env carries `N8N_LICENSE_ACTIVATION_KEY` from the agency key pool, so the workspace boots registered. |
| 7 | `tls_ready` | Poll `https://{slug}.{domain}/healthz` until 200 with valid cert (LE issuance ~30–60s). |
| 8 | `owner_created` | Call n8n's owner-setup REST endpoint with the client's email + a generated strong password. |
| 9 | `enrolled` | Add monitor in Uptime Kuma (API); register backup job (§6); write `instances` row with all metadata. |
| 10 | `welcome_sent` | Transactional email (Postmark/Resend): workspace URL, login email, password-setup/reset link (prefer a reset link over emailing a raw password), portal URL, "what's included / how to file a ticket" one-pager. |
| ✓ | `live` | Client visible as Active in both portals. |

Target: payment → welcome email in **under 10 minutes**, zero manual steps.
Every step logs to `provisioning_events` for the admin timeline view.

---

## 5. Control-plane data model (Supabase Postgres)

Minimum tables (add columns as needed; RLS on everything client-facing):

- `clients`: id, company_name, slug, contact_email, stripe_customer_id, status
  (`active | suspended | cancelled`), created_at.
- `subscriptions`: client_id, stripe_subscription_id, status, current_period_end.
  (Single flat plan: no tier column. Keep it that way unless the product actually
  gains a second plan.)
- `instances`: client_id, droplet_id, droplet_ip, region, size, coolify_server_id,
  coolify_resource_id, n8n_version, url, health (`up | down | degraded`),
  last_health_check_at.
- `provisioning_jobs`: client_id, state (enum from §4), attempts, last_error,
  timestamps. `provisioning_events`: job_id, step, level, message, created_at.
- `backups`: instance_id, kind (`pg | volume`), location, size_bytes, status,
  started_at, finished_at.
- `tickets`: client_id, subject, body, status (`open | in_progress | done`),
  assigned_to, created_at.
- `hours_ledger`: legacy table from the original portal plan. Keep it protected, but
  do not build customer balances, hour purchases, or delivery deductions against it.
- `secrets`: never in this DB in plaintext. Use Supabase Vault or a SOPS/age file;
  DB stores references only.

---

## 6. Backups (part of the paid promise: treat as a feature, not a chore)

### 6.1 What gets backed up, per client, nightly
1. `pg_dump` of the client's Postgres (workflows, credentials, executions).
2. The `n8n_data` volume (small: config, binary data if any).
3. Both encrypted client-side (restic with a per-service repo key) and pushed to
   **Backblaze B2** (or DO Spaces) under `backups/{slug}/…`.

### 6.2 Retention & verification
- Nightly, keep 30 days; first-of-month, keep 12 months.
- Backup job reports success/failure to the control plane → `backups` table → admin
  portal shows last-backup age per client; alert if > 26h.
- **Quarterly restore drill:** restore a random client's backup to a scratch droplet,
  verify login + a workflow executes. Scripted; result logged.

### 6.3 Encryption-key escrow (critical)
`N8N_ENCRYPTION_KEY` encrypts every credential a client stores. **A database backup
without its key is garbage.** At provision time the key is written to (a) the runtime
secrets store and (b) an encrypted key-escrow file (SOPS/age) synced to B2 in a
separate bucket with separate access credentials. Restore runbook must document
key retrieval first.

### 6.4 Restore SLO
A single client fully restored onto a fresh droplet in **≤ 60 minutes** using only:
the backup bucket, the key escrow, and the provisioning pipeline (steps 3–9 reused
with `--restore-from` flag). This is also the disaster-recovery plan.

---

## 7. Monitoring & updates

**Monitoring**
- Uptime Kuma on the control droplet: one HTTPS monitor per client subdomain
  (60s interval) + cert-expiry check. Alerts → Slack + email.
- DO monitoring agent on all droplets: CPU/RAM/disk alerts at 80%.
- A control-plane cron syncs Kuma status → `instances.health` so both portals show
  live health without querying Kuma directly.

**n8n updates**
- Single pinned `N8N_VERSION` for the whole fleet, stored in control plane.
- Ring rollout: (1) internal staging instance + smoke tests (login, webhook fires,
  schedule fires, credential decrypts, execution completes) → (2) 2–3 canary clients,
  48h soak → (3) fleet in batches via Coolify API, health-checked between batches.
- Pre-update per-client `pg_dump` snapshot before each ring (n8n DB migrations are
  not reliably reversible; rollback = restore dump + previous image).
- Never enable auto-update/`latest`. OS patching: unattended-upgrades for security
  patches; monthly maintenance window for reboots, announced in the portal.

---

## 8. Lifecycle handling (Stripe-driven)

| Event | Action |
|---|---|
| `invoice.payment_failed` (final retry) | Auto-suspend: stop n8n container (data intact), portal shows "suspended: update payment", email client. |
| Payment recovered | Auto-resume: start container, verify health, email confirmation. |
| Subscription cancelled | 30-day grace: instance stopped, final backup taken. Offer paid **handoff**: droplet snapshot transferred to the client's own DO account, or full export (workflows JSON + credentials DB + key): this makes the "leased infrastructure" positioning real. After grace: destroy droplet, delete DNS record, retain final backup 90 days, then purge. |
| Client outgrows their droplet | Operational, not a plan change: resize the droplet (or migrate via the restore pipeline), update memory limits in Compose. Requires client consent per the fair-use clause; bill the difference separately. |

All transitions logged and visible in the admin portal.

---

## 9. Client portal (this repo)

Auth: Supabase Auth (email + magic link). Portal login is **separate** from n8n login.

Pages / features (MVP order):
1. **My workspace**: big "Open my workspace" button → `https://{slug}.{domain}`,
   status badge (Active/Suspended/Maintenance), plan tier, n8n version.
2. **Support / implementation requests**: ticket form (subject, description,
   attachments). Tickets are for communication and status. Automation Library work
   shows an estimated implementation range. Final and additional costs are discussed
   directly before work begins.
3. **Billing**: link to the GoHighLevel billing flow for subscription management.
4. **Docs**: getting-started guides, "what's included vs. separately priced" page, FAQ.
5. **Automation inventory** (phase 2): read-only list of the client's workflows
   (name, active, last run), pulled by a control-plane job via the client instance's
   n8n API key created at provisioning. Display-only; no editing from the portal.

## 10. Admin portal (this repo, role-gated)

1. **Fleet table**: every client: slug/URL, droplet (size, region, IP), health,
   last backup age, n8n version, subscription status, provisioning state. Filter/sort;
   red badges for anything unhealthy, backup-stale, or stuck.
2. **Client detail**: provisioning event timeline, instance actions (restart stack,
   re-run backup, resend welcome email, suspend/resume: all via control-plane API
   calls to Coolify/DO, never manual SSH), ticket history.
3. **Provisioning queue**: running/failed jobs, per-step errors, "retry from failed
   step" button.
4. **Fleet operations**: trigger update ring (§7), view fleet version drift,
   monthly infra cost per client (droplet size + storage vs. subscription price).

---

## 11. Security requirements

- No Docker socket ever exposed to client containers; client stacks run unprivileged.
- Secrets: DO API token, Coolify token, Stripe keys, restic keys in control-plane
  secret store only; per-client secrets per §5/§6.3. Nothing in git.
- Client droplets: firewall per §2.1, SSH restricted to control plane, fail2ban.
- Acceptable-use policy in the client agreement (no spam/scraping abuse: client
  workflows egress from our IPs; abuse gets one warning then suspension). Egress
  volume visible in DO monitoring per droplet.
- Admin portal behind Supabase Auth + role check; consider IP allowlist or MFA.
- Stripe webhook signature validation; Coolify + Kuma dashboards not exposed publicly
  (VPN/Tailscale or IP allowlist).

---

## 12. Build phases

**Phase 0: Manual concierge (week 1–2).** Stand up Coolify, template (§3), DNS/TLS,
backups (§6), Uptime Kuma. Onboard 1–3 pilot clients *by hand* using the runbook the
automation will later replace. Proves the environment before any glue code.

**Phase 1: Provisioning automation (week 3–5).** Stripe → queue → state machine (§4),
control-plane schema (§5), welcome email. Exit criteria: a test payment produces a
live workspace + email with no human touch, twice in a row, and a failed step resumes.

**Phase 2: Portals (week 5–8).** Client portal MVP (§9 items 1–5), admin fleet table
+ provisioning queue (§10), lifecycle handling (§8).

**Phase 3: Polish (ongoing).** Automation inventory, update-ring tooling, restore
drills, cost reporting.

**Cost/margin target.** At $50/mo flat, per-client cost runs ~$16/mo (droplet ~$12,
backup storage ~$1, amortized control droplet + email ~$1.50, Stripe fee ~$1.75),
leaving ~$34/mo margin. Every build decision should protect that: no per-client paid
tooling, no manual steps that consume staff time, no unbounded resource usage.

---

## 13. Tech stack summary

| Layer | Choice |
|---|---|
| Cloud | DigitalOcean (droplets, DNS, firewall, monitoring; Spaces or Backblaze B2 for backups) |
| Orchestration | Coolify (self-hosted, API-driven) |
| Client runtime | Docker Compose: pinned n8n + Postgres 16 per client droplet |
| Proxy / TLS | Coolify proxy + Let's Encrypt per subdomain |
| Provisioning | Node/TypeScript service, BullMQ + Redis (control droplet), DO API + Coolify API + Stripe SDK |
| Portals | This repo: React + Vite + shadcn/ui + Supabase (Auth, Postgres, RLS, edge functions) |
| Billing | GoHighLevel: one flat $50/mo subscription plus upfront Automation Library and custom-project payments |
| Backups | restic → Backblaze B2 (encrypted), SOPS/age key escrow |
| Monitoring | Uptime Kuma + DO monitoring, alerts to Slack/email |
| Email | Postmark or Resend (transactional) |

---

## 14. Explicit non-goals

- No shared/multi-tenant n8n instance, ever.
- No white-labeling or rebranding of the n8n UI.
- No workflow building, debugging, or credential management included in the base
  subscription. Automation Library work shows an estimated price range; final and
  additional costs are confirmed directly before work begins.
- No prepaid hour blocks, customer hour balances, or portal ledger workflow.
- Portal does not replace or proxy the n8n editor; it is a management layer only.
