Skip to content
Architecture

Architecture

One Dark Factory instance is one Helm release: a control plane, a gateway and one PostgreSQL database, serving one tenant. Agents run in sandbox pods, one pod per run, in a namespace of their own. The rest of this page lists the components; Agent runs follows one run through them.

    flowchart TB
  B["Browser"] -->|HTTPS| CP
  subgraph inst["Instance namespace"]
    direction LR
    CP["Control plane"] -->|PolicyWatch| GW["Gateway"]
  end
  subgraph sbx["Sandbox namespace"]
    SB["Sandbox pod<br/>runtime + harness"] --- PVC[("Task volume")]
  end
  SB -->|agent channel| CP
  SB -->|proxy, Git| GW
  CP --> DB[("PostgreSQL")]
  CP -->|API| FORGE["Forge"]
  GW -->|Git| FORGE
  GW --> EXT["Allowed hosts"]
  

Components

ComponentRuns asDoesHolds
Control planeDeployment dark-factory serveWeb UI, task and run scheduling, the agent channel for sandboxes, forge plugins, sandbox provisioning, policy distribution to the gatewayThe database connection; resolves credentials from Secret references when it uses them
GatewayDeployment dark-factory gateway (2 or more replicas)Egress proxy for sandbox HTTP(S), Git smart-HTTP proxy with push checks, live traffic eventsNo database access and no Kubernetes token; policies and forge credentials arrive from the control plane and stay in memory
migrate jobHelm pre-install and pre-upgrade hookSchema migrations, application role and grantsThe owner role’s password
Sandbox podPod per run in the sandbox namespace, Kata Containers where configuredThe project image with the runtime (factory-agent) and the harness (for example Claude Code over ACP) mounted as image volumesThe task volume at /data; the harness credential in memory
PostgreSQLExternalAll state: projects, personas, tasks, runs, transcripts and their binary content, requests, egress rules, audit log

The control plane and the gateway are the same binary in two modes. The control plane can run with several replicas: every replica serves the UI and agent connections and can start runs (the database prevents two runs of one task); jobs that must run once, such as the task volume cleanup, run on the replica holding the instance lease.

Connections

FromToProtocolAuthentication
BrowserControl plane :8080HTTPS through your ingress or Gateway API routeOIDC login, server-side session
SandboxControl plane :9443 (agent port)gRPC, bidirectional streammTLS: a per-pod certificate from cert-manager’s CSI driver, checked against the pod’s IP and UID
SandboxGateway :3128 (proxy)HTTP CONNECT and plain HTTP proxyingSource pod IP, looked up in the sandbox registry
SandboxGateway :8443 (Git)Git smart HTTP over HTTPS (instance CA)Source pod IP; the path names the task
GatewayControl plane :9444 (internal)gRPC (PolicyWatch, push records, events)mTLS
Control planeGateway :9444 (internal)gRPC Tail for the live traffic viewmTLS
Control planeForge APIHTTPS from a forge pluginThe project’s forge credential, added by the control plane
GatewayForge GitHTTPSThe project’s forge credential, injected by the Git proxy

Sandboxes resolve no DNS names: the proxy and control plane addresses are passed as IPs, and the generated NetworkPolicy of each run allows only the control plane, the gateway and explicitly allowed cluster services. Details: Security.

Kubernetes footprint

NamespaceContainsCreated by
Instance (for example dark-factory)Control plane, gateway, migrate job, Services, Secrets with credentials, Leasesthe Helm release
Sandbox (for example dark-factory-sandboxes)Sandbox pods, task volumes (PVCs), generated per-run NetworkPoliciesthe chart (sandboxNamespace.create) or you; never the control plane

The control plane gets the permissions it needs in these two namespaces only: pods, PVCs and NetworkPolicies in the sandbox namespace; leases, Secrets (read) and EndpointSlices in the instance namespace. The gateway and sandbox pods get no Kubernetes API token. At startup the control plane checks its permissions and refuses to start if one is missing.

Design records

The decisions behind this layout are recorded as ADRs in the repository, for example ADR-0029 (egress gateway), ADR-0030 (Kubernetes only) and ADR-0013 (single-tenant instances).