Files
agentic-dev/docs/decisions/adr-027-hosted-saas-and-runner-split.md
Danijel Martinek 442b70476c docs(work): add Veect ADRs 027-029, glossary terms, first two PRDs
Records the 2026-07-12 grill-session decisions repurposing this repo
for Veect v1 as a hosted SaaS: ADR-027 (hosted distribution +
control-plane/runner split), ADR-028 (iframe canvas), ADR-029
(DesignDoc v1 canonical + editor rebuild). Seeds the work pipeline
with the platform-retrofit and walking-skeleton PRDs (approved) and
adds the Veect product-domain section to the glossary. These
consciously supersede the .proto bundle's Electron/local-first
distribution decisions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 12:17:35 +02:00

62 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-027 — Hosted SaaS distribution and the control-plane/runner split
**Status:** Accepted
**Date:** 2026-07-12
## Context
This repo is being repurposed from the `template-vertical` starter into **Veect** — a design-system-native canvas that connects to a code repository, discovers its real components and tokens, lets a designer compose screens constrained to that system, and publishes real TSX as an ordinary pull request. The product specification bundle lives in `.proto/veect-product-docs/` (tech spec rev 1.2.2, design spec, UI gap spec, PRD, strategy docs).
That bundle **locked** a distribution decision on 2026-07-10: standalone desktop app (Electron, bundled Node runtime) as the default, plus a self-host Docker image, with "do not relitigate" markers. Large parts of the tech spec derive from it: the `node` PATH shim, Electron ABI policy, `safeStorage` credential storage, GitHub OAuth *device flow* (a desktop app cannot hold a client secret), per-workspace SQLite, and the "designer needs no dev environment" assumption (A5) satisfied by bundling the runtime.
On 2026-07-12 the founder superseded that decision during the architecture grill session: **Veect v1 is web-based — a hosted cloud SaaS — with a Tauri desktop shell later.** The spec itself declared the editor UI shell-agnostic and Tauri a documented alternative, so the editor carries over; but "who runs the server" changes everything downstream: auth posture, secret storage, isolation, persistence, and milestone acceptance criteria.
The central new problem: Veect executes **untrusted customer repo code by design**`pnpm install` lifecycle scripts, `next dev`/Vite servers, arbitrary config plugins. On a desktop that risk belonged to the user's own machine (mitigated by a workspace-trust prompt). Hosted, it lands on Veect's infrastructure and neighbors' tenants.
## Decision
**1. Veect v1 is a hosted cloud SaaS.** Users sign in from a browser; no local install is required for the primary path. Assumption A5 is fully restored — stronger than the Electron plan ever made it. A Tauri desktop shell is a later distribution, not v1.
**2. The architecture splits into a control plane and workspace runners, joined by a single runner protocol.**
| Part | What it is | Where it runs |
| --- | --- | --- |
| **Control plane** | Multi-tenant service: auth, tenancy, workspace/project metadata, design-doc persistence, AI proxying, runner orchestration. Template-shaped: Payload + Postgres, tRPC (ADR-013 stands), features in `packages/*`, UI in `apps/web-next` | Veect-operated infrastructure |
| **Cloud runner** | Executes a workspace's repo code: clone, worktrees, dependency installs, discovery scans, preview adapters, git checkpoint/publish. **One isolated container per workspace**: CPU/mem limits, egress allowlist (npm registry, git host), no cross-tenant network | Veect-operated infrastructure |
| **Local runner** (CLI agent) | The same runner contract implemented as a CLI the developer runs against a local checkout; the browser editor reaches it directly at `127.0.0.1` after pairing with the hosted session | User's machine |
The **runner protocol** (plain WS/JSON — deliberately *not* tRPC, because a slim CLI must implement it and the browser must speak it to a local runner) is the generalization of tech spec §9's "common runtime protocol". One protocol, N runner implementations — cloud, local, and later Tauri-embedded.
**3. v1 connection modes: remote repos (cloud runner) + local checkouts (local runner).** The self-host Docker image is **cut from v1** (fast-follow for enterprise); the core stays 12-factor/deployable-anywhere so it is not foreclosed.
**4. Identity is provider-agnostic.** Email/password accounts via the existing `auth` feature; repo access is a per-workspace credential (PAT or OAuth). The device-flow screens are retired. A GitHub App is a compatible later upgrade.
**5. Control-plane persistence is Payload + Postgres.** Users, workspaces, projects, designs, revisions, and AI runs are Payload collections behind the template's repository pattern. The spec's per-workspace SQLite (§11) is **deleted from v1**: runners hold only ephemeral working state and are rebuildable from the control plane + git. Canonical design state = Postgres (working) + `design.veect.json` on the branch (shared, reviewed) with file-wins reconciliation.
**6. The telemetry and compliance stances flip with the distribution.** "No telemetry v1" was a desktop-privacy posture; operating a multi-tenant service reverses it — the OTel/Sentry stack (ADR-014/017), `core-audit`, `core-consent`, `core-dsr`, and `core-analytics` are **kept and wired** (an EU-entity SaaS with user accounts has GDPR obligations on day one, and the activation/NSM metrics require product analytics). Demo content features (`blog`, `media`, `navigation`, `marketing-pages`) and `apps/web-tanstack` are deleted; `apps/cms` stays as the Payload admin; the landing page becomes a static route in `web-next`.
**7. AI compose is Veect-metered with a BYO escape hatch.** Server-side Anthropic key with per-tenant quotas (Haiku 4.5 default, Sonnet opt-up) *and* an optional per-workspace BYO key override, both from day one. The §14 "what was sent" disclosure is a hard requirement since prompts transit Veect infrastructure.
## Alternatives considered
- **CLI + Docker local-first (`npx veect` + self-host image).** The closest web-based reading of the original spec: repos, secrets, and execution stay on the user's machine. Rejected: reintroduces the install/dev-environment friction for designers that the founder explicitly wants gone; the developer-champion has to run/maintain a server for the team.
- **Docker-only self-host.** Simplest release engineering, strongest data-residency story. Rejected: heaviest first-run, clumsy local-folder support, no zero-install designer path.
- **Electron per spec.** Rejected by the founder's web-first direction; Tauri (not Electron) is the eventual desktop shell, and the runner protocol keeps that path open.
- **Shared-host execution with OS-level separation (cloud side).** Cheapest isolation. Rejected: indefensible to the engineering/security veto persona for untrusted-code execution; container-per-workspace is the industry floor (Codespaces/StackBlitz class), hardening to gVisor/microVMs later without protocol changes.
## Consequences
- **Positive:** zero-install activation for designers (the <20 min time-to-value target gets its biggest lever); the template's machinery survives largely intact (Payload repositories, tRPC, generators, conformance, dev-seed, work pipeline); `InMemoryEventBus`/`PayloadJobsEventBus` and `IJobQueue` are all genuinely useful again; one runner codebase serves cloud, CLI, and future Tauri.
- **Negative / accepted:** repo code and env values now transit and execute on Veect infrastructure the enterprise trust question sharpens ("your code leaves your machines") and needs a security FAQ before design-partner outreach; per-workspace compute (installs, dev servers) is a real COGS line requiring hibernation/pooling; the local runner is a second runner implementation to maintain from v1; publishes author as the workspace's stored credential (multi-seat honesty must be surfaced in the publish UI).
- **Retired spec surface (do not implement):** Electron shell, bundled-runtime PATH shim, ABI policy, `safeStorage`, auto-update/adapter-drain, deep links, GitHub device flow, per-workspace SQLite, self-host Docker (v1), SSH-via-system-agent for cloud runners (returns with the local runner, where an agent exists).
- **Milestones M0M7 must be re-derived**: the M0 acceptance ("boots with no system Node") is meaningless hosted; its replacement is the walking-skeleton PRD (sign-in connect fixture repo cloud runner one real component in the editor).
- The `.proto` bundle remains the product-behavior authority **except** where this ADR and the grill decisions supersede it; the authority table in `.proto/veect-product-docs/README.md` §2 is amended by this ADR.
## Related
- ADR-013 (tRPC conventions stands), ADR-014/017 (instrumentation exporter layer now stays), ADR-015 (events/jobs both impls in use), ADR-016 (realtime control-plane pushes), ADR-025 (EU compliance baseline applies for real now)
- ADR-028 (iframe canvas), ADR-029 (DesignDoc v1 + editor rebuild)
- `.proto/veect-product-docs/veect-technical-spec.md` rev 1.2.2 (superseded on distribution, §3611 partially retired)
- Glossary: "Veect product domain" section in `docs/glossary.md`