tenwhy · stack

What we run, where

The system splits into always-on per customer (Maestro is awake, listening, ready to act) and on-demand (every specialist spins up a fresh Sapiom µVM when called, tears down after). Five external platforms carry the load. This page is what lives where, why, and how the pieces connect on signup and during the first build.

now  Built on Stripe Projects

The whole stack is being built using Stripe Projects — Implementation 2. Stripe Projects provisions the managed equivalents of the top five hand-wired services below; everything else is shared or unchanged across both implementations.

CapabilityHand-wired · Impl 1Stripe Projects · Impl 2
DatabaseRender PostgresNeon
µVM computeSapiom / BlaxelDaytona
AuthCustom magic link · Cloudflare EmailWorkOS
Secrets vaultInfisicalStripe Secret Store
Scheduler · jobsRender Cron JobsInngest
LLM gatewayOpenRouterOpenRouter shared
Source controlGitHubGitHub shared
Service hostingRenderRender unchanged
Edge · DNS · CDN · emailCloudflareCloudflare unchanged
Object storeCloudflare R2Cloudflare R2 unchanged
ObservabilitySentry · Better StackSentry · Better Stack unchanged

scope  The build target is Implementation 2 — Stripe Projects (summary above). The detailed walkthrough below still describes the Implementation 1 hand-wired topology; each Stripe Projects service maps onto the hand-wired one it replaces per the table above. The full side-by-side component map lives on the build page.

1 · At a glance

Five platforms carry the work. Each column shows what runs there.

RENDER

Long-running services

  • Maestro (multi-tenant) always
  • Dashboard (Next.js) always
  • Postgres always
  • Cron Jobs always
  • Built artifacts per app
CLOUDFLARE

Edge + storage + email

  • DNS · wildcard cert
  • CDN · DDoS
  • R2 (binary assets) always
  • Email Service (magic links)
SAPIOM / BLAXEL

µVM compute

  • Specialist µVMs on demand
  • Node-only base image
  • Async webhook back to Maestro
OPENROUTER

LLM gateway

  • Claude
  • OpenAI
  • Grok
  • Google
  • one key, one bill
GITHUB

Source of truth

  • tenwhy-brains/<cust> · private, per customer
  • tenwhy-tools/<name> · one per tool (CLI + Skill)
  • Platform monorepo
Third-party services · Infisical — secrets vault (µVMs fetch creds at runtime) · Sentry — exceptions · Better Stack — logs · uptime · on-call

2 · Always-on (per customer)

Four things run continuously for every signed-up customer. They are not "spun up on demand" — they're awake all the time, even when the customer is asleep.

ComponentWhat it doesWhere
Maestro Holds the customer's session. Interviews on first visit. Decides every action the workforce takes. Multi-tenant — one Maestro server hosts every customer's session; state lives in Postgres + brain repo so the server itself is stateless. always Render · web service
Dashboard Next.js. The customer's UI. Renders the chat with Maestro on the left, the generative widget canvas on the right (driven by Maestro's dashboard.json via json-render.dev). always Render · web service
Postgres Sessions, customer accounts, the tool catalog, agent task queues, audit logs. RLS on customer_id (decision #12). always Render · managed DB
Scheduler Cron jobs that fire shift events into Maestro (Lou daily, Ray weekly, Sal 24/7, etc.). The scheduler doesn't call specialists directly — it wakes Maestro, who decides. always Render · cron jobs

3 · On-demand (the workforce)

Every specialist (Max, Vic, Sac, Jon, Tom, Roy, Moz, Sid, Sal, Ray, Lou, Nic) is dormant by default. When Maestro decides to call one, a fresh Sapiom µVM boots, the tool runs, the µVM tears down. Nothing about the specialist persists between jobs except the artifacts it commits.

µVM life cycle

PhaseWhat happens
BootSapiom spawns a Node-only µVM. Base image has Node 20, git, curl, jq. ~1–2 s cold start.
IdentifyµVM is started with a short-lived bootstrap token, scoped to one job. Used once, to identify itself to Infisical.
Fetch secretsµVM hits Infisical with its bootstrap token, pulls exactly the secrets the specific tool needs (e.g. GitHub installation token for the brain repo).
Load toolThe tool comes from a prebaked image (or a shallow clone / R2 pull) — not a full git clone tenwhy-tools/<name> per boot, which would put GitHub availability and clone latency on the hot path. Loads the Skill into the agent's prompt, makes the CLI runnable.
RunSpecialist works: reads inputs, calls the LLM (via OpenRouter), invokes the CLI, writes outputs. Mutating/billable actions are only proposed (executed later by the platform broker).
DeliverReturns its envelope + artifacts + proposed brain updates; bulky output → R2 (scoped). The platform finalizes — validates, writes Postgres, renders + commits the canonical audit to the brain (decision #28).
WebhookPOSTs result + summary to Maestro's webhook endpoint. Includes a job ID + HMAC signature so Maestro can verify authenticity.
TeardownµVM exits. Billing stops.

Node-only ⇒ service  The base image is deliberately Node-only (decision S13). The rule that keeps it that way: anything non-Node is a service behind an HTTP boundary, never a package in the image — scraping (Scrapling), and likewise browser screenshots, headless PDF rendering, or any future binary dependency. The µVM stays small and fast; the heavy runtime lives in its own service the tool calls.

4 · Three data stores, one purpose each

Nothing lives in two stores. Each piece of data has exactly one home.

StoreContentsWhy here
Render Postgres Customer accounts · sessions · tool catalog · agent task queues · audit log Operational state. Frequent small writes. RLS scoping. Joinable.
GitHub · tenwhy-brains/<cust> The customer's "brain" — 8 HTML docs from Maestro + folders per specialist Portable, versioned for free, clean per-agent commit attribution.
Cloudflare R2 Binary assets — images, video, audio from Vic and other media-shaped tools Cheap storage, zero egress, CDN-backed URLs. Postgres pointers reference the keys.

5 · The signup sequence

What happens when a brand-new customer hits tenwhy.com and signs up.

  1. Visitor lands at tenwhy.com Marketing site (static-ish). Submits email to start. Render web service · cached at Cloudflare edge
  2. Magic-link email Custom auth: signed token → store in Postgres with 15-min TTL → send link via Cloudflare Email Service. Postgres (token) · Cloudflare Email (delivery)
  3. Customer clicks link Token verified, session cookie set (HTTP-only). Customer record + slug provisioned. Subdomain claimed: <slug>.tenwhy.com. Postgres (account) · Cloudflare DNS (wildcard cert)
  4. Brain repo created A private repo at tenwhy-brains/<slug> is created via the GitHub API. The customer's GitHub identity is not used; the platform PAT does it. GitHub API
  5. Maestro session opens Customer lands on their dashboard. Maestro greets them, starts the interview. Chat goes to the multi-tenant Maestro service; replies stream back via the OpenRouter gateway. Render (Maestro · dashboard) · OpenRouter (LLM)
  6. Brain begins to fill As the conversation produces facts, Maestro commits HTML documents to /maestro/ in the brain repo: business.html, customer.html, etc. The dashboard's right pane re-renders live. GitHub (commits) · Postgres (dashboard.json) · json-render.dev
  7. Interview ends · workforce is empty No specialists exist yet. Maestro proposes hires based on what the customer needs. Maestro (decision) · Postgres (proposed-hires table)

6 · The first build — todo app, end to end

Customer says "I want a todo app." What happens next, from Maestro's first move to a live URL.

  1. Maestro proposes hiring Nic Posts an Approve card to the dashboard. Customer clicks Approve. Dashboard · Postgres (hire approval)
  2. Maestro fires a µVM POST to Sapiom API: { tool: "tenwhy-tools/nic", spec: {…}, webhook: "https://maestro.tenwhy.com/v1/jobs/<id>" }. Sapiom returns a job ID immediately. Maestro is free to handle other customers. Maestro → Sapiom (async)
  3. µVM boots and prepares Node base image. Bootstrap token → Infisical → fetches GitHub installation token + Render API key (both scoped, short-lived). Clones tenwhy-tools/nic. Sapiom (µVM) · Infisical (secrets) · GitHub (clone)
  4. Nic builds the app Reads the brain (business.html, audience.html, etc.) for context. Generates a Next.js app via the LLM (OpenRouter). Writes the code to a fresh repo: tenwhy-builds/<cust>-todo-<short>. OpenRouter (LLM) · GitHub (new build repo)
  5. Deploy to Render Nic calls Render's API: create web service from the new repo, set env vars (data API key, slug, etc.), trigger deploy. Render builds + deploys. URL: <cust>-todo-<short>.onrender.com. Render API (create service) · Render (build + deploy)
  6. Nic commits to the brain Updates /nic/builds.html in the brain repo: lists this build, URL, repo, status, timestamp. Updates dashboard.json in Postgres so the dashboard shows a "Your todo app" widget. GitHub (brain) · Postgres (dashboard.json)
  7. Nic webhooks Maestro POSTs { status: "ok", artifact: { type: "render-service", url: "…" }, log: "…" } to Maestro's webhook. HMAC-signed for verification. µVM exits. Maestro webhook · µVM teardown
  8. Maestro tells the customer Next chat turn: "Your todo app is live at cust-todo-x12.onrender.com." Dashboard widget appears. Done. Maestro reply · dashboard re-render

7 · Auth and secrets — the security model

Two layers: customer-facing auth (humans into the dashboard) and service-to-service auth (Maestro → Sapiom, µVM → vault, µVM → GitHub/Render).

SurfaceMechanism
Customer loginMagic link · token in Postgres (15 min TTL) · email via Cloudflare Email Service · HTTP-only session cookie after click
Dashboard API callsSession cookie · CSRF token · per-customer rate limit
Maestro → SapiomLong-lived Sapiom API key (in Infisical, fetched once at Maestro boot)
µVM → InfisicalBootstrap token (one-time, per job, injected by Maestro at µVM-create call). µVM exchanges it for scoped secrets.
µVM → GitHubGitHub App installation token (1 hour TTL, scoped to one repo)
µVM → Render APIScoped Render API key (in Infisical, granted only when the tool is one that can deploy)
µVM → Maestro webhookHMAC-SHA256 signed with a per-job secret. Maestro verifies before accepting result.
µVM → OpenRouterScoped OpenRouter key with per-job spend limit

runtime ≠ CI  This is the runtime security boundary — and it, not agent-cli-kit verify, is what stops a misbehaving tool. verify checks a CLI's shape in CI; the boundary is enforced here: per-job scoped credentials with a read/write split (read for plan, write for apply), network-enforced egress allow-listing (open verification on the build page), a brain-path allowlist on commits, and signed tool releases. Conformance is quality; the sandbox is security (design #32).

8 · Stack decisions locked in this session

Ten decisions made today — each one a constraint on everything downstream.

#Decision
S1Always-on per customer = Maestro warm + scheduler + DB + dashboard. Specialists on-demand.
S2Maestro is the only orchestrator. Scheduler fires events into Maestro; Maestro decides whether to wake a specialist.
S3Three output channels: deployables → Render service per artifact · text/data → brain repo · binaries → Cloudflare R2.
S4Maestro host = multi-tenant Render service. Stateless; session state in Postgres + brain.
S5Built artifacts (todo apps, sites, etc.) host = one Render service per artifact. Default URL *.onrender.com; optional custom domain.
S6Database = Render Postgres (already in the PRD).
S7Object store = Cloudflare R2.
S8Scheduler = Render Cron Jobs. Each cron hits a Maestro webhook with a shift signal.
S9µVM communication = async + webhook. Maestro posts job, µVM hits Maestro webhook when done. HMAC-signed.
S10Customer auth = custom magic link + Cloudflare Email Service. No third-party auth vendor.
S11Frontend = Next.js on Render.
S12LLM gateway = OpenRouter. One key, one bill, four providers (Claude · OpenAI · Grok · Google).
S13µVM base image = Node-only. All tool CLIs must be Node-based.
S14Secrets management = Infisical (vault). µVM fetches scoped, short-lived creds at runtime via bootstrap token.
S15Vault product = Infisical (start managed, option to self-host later).
S16DNS + CDN + edge = Cloudflare in front of Render. Wildcard cert for *.tenwhy.com.
S17Observability = Sentry (errors) + Better Stack (logs · uptime · alerts).
S18URLs = subdomain per customer (<slug>.tenwhy.com) + *.onrender.com per artifact with optional custom-domain wiring.

9 · Still open — for a later session

Each now has a decided approach (below). None block starting v0.1.

AreaQuestion
CI/CDDecided. Maestro/dashboard deploy via Render's GitHub hook. Each tenwhy-tools/<name> repo runs a GitHub Action on push — agent-cli-kit verify (contract conformance) + Skill lint + CLI tests + the harness-seam check. Green is the gate to publish into the catalog: the automated half of the factory's Reviewer.
Backups / DRDecided. Postgres → Render automated snapshots + periodic logical dumps to R2. R2 → cross-region bucket replication. Brain repos → GitHub is canonical (already durable) + a periodic mirror. Secrets → the vault is the source of truth. Plus a quarterly restore drill so recovery is exercised, not assumed.
Rate limitingDecided. Cloudflare in front for DDoS; a per-customer token-bucket (Postgres, or Upstash if it gets hot) on Maestro's webhook + the dashboard API, keyed by the same customer_id as RLS.
Cost ceiling per customerResolved (design #31) — enforced ahead of the fact: a preflight monthly-cap check before dispatch + provider-side hard limits on both impls; self-reported cost_usd is reconciliation only. Better Stack alerting still wanted.