Skip to content

Architecture

This page describes Forginate's architecture as it exists today: the services that make up the platform, the data stores behind them, the tenant model, how factory runs are scheduled, and the security/isolation model.

Forginate is a multi-tenant "AI software factory": each tenant runs a project through an LLM-driven pipeline — intake → discovery → planning (TOC + iterative section docs) → architect review → build (per-task agent runs) → review → QA → deploy — on Kubernetes, with model inference delegated to whichever provider is configured per tier.

The public product is served at dev.forginate.com. The SPA talks to the REST API under /api/* on the same host.

Service topology

The platform is a TypeScript monorepo (saas/) deployed as a set of Kubernetes services from a single Helm chart:

ServiceRole
api-gatewayPublic HTTP + WebSocket termination. Authentication (session cookies, personal access tokens), all /api/orgs/:orgSlug/... REST routes, SSE event streams, the deploy-agent WebSocket (/agent/ws), webhooks (GitHub, Stripe), and serving of the built SPA bundle.
orchestratorBackground control loops. Schedules factory and chat-editor runs as Kubernetes Jobs, watches them, and runs the deployment sweeps (planner, dispatcher, monitor, AWS account vendor, cost pull).
factory-runnerThe containerised factory pipeline. One ephemeral pod per run; executes the actual LLM work (planning docs, build agents, audits) and reports usage and events back to the gateway's internal API.
chat-editorThe inline document-editor agent service ("vibe editor"). Streams LLM turns over SSE and proposes document patches via a propose_edit tool loop.
qa-runnerSandboxed QA harness for exercising built applications.
shared/dbNot a service — the shared Drizzle schema, migrations, and DB client library used by every service.
webThe React SPA (Vite, TanStack Query, Tailwind, Radix). Bundled into the api-gateway's static assets and served by the gateway; there is no separate web server in the deployed shape.

There is also a separate public, unauthenticated share portal surface (token-scoped document review for outside customers), served by the same gateway under /api/share/* and gated so the public share host can reach only those routes.

                        ┌────────────────────────────┐
   Browser (SPA)  ────▶ │        api-gateway         │ ◀──── deploy agent
   /api/* + SSE/WS      │  auth · REST · SSE · /agent│  (outbound WebSocket
                        │  /ws · webhooks · SPA      │   from customer cluster)
                        └──────┬──────────┬──────────┘
                               │          │ internal API (cluster-only)
              Redis pub/sub +  │          │
              job queues       │          │
                        ┌──────▼───┐  ┌───▼──────────────┐
                        │ orchestr.│  │  factory-runner  │  one pod per run
                        │  sweeps  │──│  chat-editor     │  (k8s Jobs)
                        │  + k8s   │  │  qa-runner       │
                        │  Jobs    │  └──────────────────┘
                        └──────┬───┘

            ┌──────────────────┼───────────────────┐
            ▼                  ▼                   ▼
     CNPG Postgres          Redis               MinIO
     (all tenant state)  (queues, pub/sub,   (doc bodies, artifacts,
                          sessions, rate      attachments, logs)
                          limits)

Data stores

  • Postgres (CloudNativePG). The single source of truth for all tenant state: organizations, users, memberships, projects, products, requests, documents and versions, runs, usage events, deployments, deploy targets, vended AWS accounts, tokens, audit events, billing state. Every tenant-owned row carries an org_id.
  • Redis. Job/dispatch queues, pub/sub channels (e.g. the deploy:cmd channel the orchestrator uses to hand deployment commands to whichever gateway instance holds the agent's socket), rate-limit counters, and live UI fan-out.
  • MinIO (S3-compatible object storage). Document bodies, uploaded attachments, run artifacts and transcripts, and post-mortem logs. Postgres rows hold pointers into MinIO, not the payloads themselves.

Tenant model

Organization  (the billable entity; slug appears in every API path)
 ├── Memberships   (user ↔ org, role: owner | admin | developer | viewer)
 ├── Projects      (a software product being built; may be linked to a
 │   │              GitHub repo via the Forginate GitHub App)
 │   ├── Products / Requests   (improvement & build requests moving
 │   │                          through the pipeline)
 │   ├── Documents             (planning docs: TOC + section docs, with
 │   │                          versions, approvals, feedback, shares)
 │   ├── Runs                  (each factory pipeline execution — one
 │   │                          k8s Job / pod, with live cost + events)
 │   └── Deployments           (running instances of the built app on a
 │                              deploy target)
 ├── Deploy targets  (customer k8s cluster via deploy agent, or a
 │                    Forginate-vended AWS account)
 ├── Providers       (BYO LLM provider configs)
 └── Billing         (Stripe customer, plan, usage, invoices)

Isolation is enforced logically at the database layer — every query in the gateway is scoped by the authenticated org — and physically at the run layer (see below).

How factory runs are scheduled

  1. A user action (advancing a request, running build tasks, requesting a doc regeneration, etc.) creates a run row in Postgres.
  2. The orchestrator claims pending runs and creates one Kubernetes Job per run with a deliberately locked-down pod spec:
    • restartPolicy: Never, backoffLimit: 0 — a failed run fails; nothing silently retries and re-spends money.
    • activeDeadlineSeconds wall-time cap (raised high enough that a run patiently waiting out a provider outage isn't SIGKILLed).
    • automountServiceAccountToken: false — the runner has zero Kubernetes API access.
    • ttlSecondsAfterFinished so logs remain fetchable post-mortem before the cluster reaps the Job.
    • A configurable runtimeClassName (NetworkPolicy is the primary sandbox boundary today).
  3. The factory-runner pod boots, reads a short-lived run-scoped agent token from its environment, and fetches its manifest (what to do, which model tiers, cost limits) from the gateway's cluster-internal API.
  4. During execution the runner emits durable events (progress, per-call usage, questions for the user) to the gateway; the SPA follows along over SSE.
  5. On completion the runner posts exact token usage per model; the gateway reprices server-side from the admin-managed tier price table (the runner carries no price table — its own cost figure is advisory and used only for the in-pod spend cap).
  6. If an LLM provider hits a hard wall (credit exhaustion, overload, rate limit), the runner does not die: it keeps its full in-memory conversation and retries the same call every 15 minutes for up to 24 hours, in place, so nothing completed is ever re-spent. The UI shows a "delayed but still running" badge.

Security / isolation model

  • Authentication. Web login ("Sign in with GitHub") produces a JWT session cookie (forginate_session) signed by the api-gateway. Programmatic access uses personal access tokens (fgn_pat_..., stored hashed, scoped read/write/admin). See API.
  • Per-org authorization. Every tenant route is nested under /api/orgs/:orgSlug/ and re-checks the caller's membership and role on that org. Admin-only routes require elevated roles.
  • Run isolation. Each run is its own pod with an ephemeral filesystem, no service-account token, a wall-time deadline, and a per-run dollar cost cap.
  • Deploy-agent trust model. Customer-cluster deploy agents dial out to the gateway (GET /agent/ws with an enrollment key); Forginate never needs inbound access to a customer cluster, and every frame in both directions is schema-validated before it is acted on. A malformed frame is answered with an error and dropped, never crashed on.
  • Vended AWS accounts. Each aws_account deploy target is a dedicated AWS account created under Forginate's AWS Organization for that customer, so cloud-hosted workloads are account-isolated from each other.
  • Share portal. Public document shares are capability-token URLs scoped to a single product; revoked/expired tokens are rejected, and reads are limited to approved document versions.
  • Audit log. Security- and billing-relevant actions (token creation, member changes, approvals, plan changes) are written to an append-only audit table, queryable by org admins.

What this page does not cover

Internal deployment details (Helm values, cluster operations, backup runbooks) live in the repository's docs/ and deploy/ directories and are aimed at operators, not end users.

Forginate — build software with an AI factory.