Code tour
A package-by-package map of the codebase for an engineer who has not read it
yet: what each component owns, how the two run modes differ, and the two request
paths that matter most — creating a branch, and connecting to one. Every claim
cites the file it comes from, so you can read along in the source. Module:
github.com/abd-ulbasit/pgoverlay, Go 1.26.
If you want the prose overview instead, read architecture first; for the reasoning behind the structure, see design decisions.
1. The one-paragraph mental model
pgoverlay gives every git branch / pull request its own instant, isolated, production-shaped Postgres database — Neon-style branching, but self-hosted on your own Docker host or Kubernetes cluster. The trick is copy-on-write: you seed a source database once into a read-only base layer, and each "branch" is a thin writable layer stacked on top of that shared base. Creating a branch copies no data — it mounts the shared base read-only and gives the branch an empty scratch layer for its writes — so branch creation is O(1) in database size instead of O(data). A branch is a real Postgres instance (a container/pod) that boots on the merged view; you connect to it like any Postgres. The whole system is a thin control plane in Go that never touches data files from the host — all data operations happen inside containers — plus a tiny SQLite metadata store and a pluggable copy-on-write backend (OverlayFS, ZFS, or Kubernetes CSI clones).
The "instant cheap branch" insight, made concrete: see
internal/engine/saga.go (the create saga) and internal/cow/plan.go (how the
overlay layers are named and stacked). The default overlay backend's
load-bearing details — recovery_init_sync_method=syncfs in the in-container
entrypoint, which stops Postgres from copying the whole DB up on first boot,
and the lazyrw shim (internal/cow/lazyrw), which stops it copying every
table it reads — are documented in docs/architecture.md.
2. Components & responsibilities
graph TD
subgraph clients["Clients"]
pgb["pgb (CLI, cobra)<br/>internal/cli<br/>local mode: embeds engine<br/>server mode: REST client"]
ghook["pgoverlay-github<br/>internal/ghook<br/>PR webhook → branch-per-PR"]
psql["psql / app<br/>Postgres wire client"]
end
subgraph branchd["branchd (control-plane daemon) — cmd/branchd"]
api["REST API :7070<br/>internal/api<br/>JSON over engine + RBAC"]
proxy["pgproxy :6432<br/>internal/pgproxy<br/>wire-protocol router"]
reconcile["reconcile loop<br/>internal/engine/reconcile.go<br/>TTL reap + GC + drift"]
ha["leader election<br/>internal/ha + api/leader.go"]
metrics["metrics<br/>internal/metrics"]
end
engine["engine — internal/engine<br/>sagas: create / from-branch / reset / destroy<br/>+ compensations, diff, masking, freeze DAG"]
planner["cow.Planner — internal/cow<br/>layer naming + overlay plan"]
registry["registry (SQLite)<br/>internal/registry<br/>state machine + journal + tokens"]
runtime["runtime.Driver — internal/runtime"]
docker["DockerDriver<br/>docker.go"]
kube["KubeDriver<br/>kube.go / kube_csi.go"]
pgb -->|server mode| api
ghook -->|REST| api
psql -->|"dbname@branch"| proxy
pgb -->|local mode| engine
api --> engine
proxy --> registry
reconcile --> engine
ha --> api
engine --> planner
engine --> registry
engine --> runtime
runtime --> docker
runtime --> kube
One sentence per box, then a short subsection each.
cmd/pgb → internal/cli — the CLI
cmd/pgb/main.go is a short shim that runs cli.NewRootCmd() with
ExecuteC and maps the error to an exit code (cli.ExitCode: pgb doctor
exits 2 when it cannot compute a plan). The real CLI lives in
internal/cli/root.go (cobra commands: source, branch — including
recover — connect, diff, history, doctor, gc, token, version).
It has two modes, chosen by the --server flag / PGOVERLAY_SERVER env:
- Local mode (
--serverunset):open()inroot.gobuilds anenginedirectly over a Docker driver and the local SQLite registry — the CLI is the engine. Construction is lazy (inside each command'sRunE) so--helpand tests never touch Docker; metadata-only commands (connect,history) useopenRegistry()and never touch Docker at all. The registry is opened with the same at-rest keys branchd uses (openRegistryAt), and the command context carries thelocal:<os user>audit actor. - Server mode (
--server http://branchd:7070):serverClient()returns anapiclient.Clientand the CLI becomes a thin REST caller; the token comes fromPGOVERLAY_TOKEN. The URL is validated up front (apiclient.ValidateBaseURL), and the client retries503s and, for idempotent requests,502/504and connection resets.
Depends on: internal/engine, internal/registry, internal/runtime
(local) or internal/apiclient (server).
cmd/branchd — the control-plane daemon
cmd/branchd/main.go is the long-running daemon. One run() builds a single
engine + registry and starts several goroutines under an errgroup
(golang.org/x/sync/errgroup):
- REST API on
--api-addr(default:7070) —internal/api. - Postgres wire-protocol router on
--pg-addr(default:6432) —internal/pgproxy. - Reconcile loop —
engine.RunReconcileon a ticker (--reconcile-interval, default 60s). - Optionally leader election (
--leader-elect, kube only) —internal/ha.
It wires internal/metrics into the engine and serves /metrics. At startup
configureSecrets sets up at-rest encryption of rotated branch passwords
under a dedicated key, independent of PGOVERLAY_TOKEN (which must be at
least 16 characters, config.ValidateAdminToken): $PGOVERLAY_SECRET_KEY,
else --secret-key-file, else <state dir>/secret.key, generated on first
start. Retired keys ($PGOVERLAY_SECRET_KEY_PREVIOUS) and the legacy
token-derived key stay decrypt-only, and ReencryptSecrets moves every row
under the primary key before serving. Storage backend selection
(--runtime docker|kube, --cow overlay|zfs|csi, --kube-storage
hostpath|csi) is validated in resolveStorage. Graceful shutdown
(drainAPI) stops admitting mutations, gives in-flight requests
--shutdown-timeout, cancels (and so rolls back) what is left, and only then
releases the HA Lease; it leaves branch containers running — they are
durable state.
cmd/pgoverlay-github → internal/ghook — the GitHub App webhook receiver
cmd/pgoverlay-github/main.go boots an HTTP server; the logic is in
internal/ghook/service.go. It receives GitHub pull_request webhooks
(POST /webhook), verifies the HMAC-SHA256 signature in constant time
(verifySignature), and maps PR lifecycle to branches via the REST API
(internal/apiclient):
opened/reopened/synchronize→ ensure a branch exists (handleEnsure→ensureBranch);synchronizeoptionally resets it (ResetOnPush).closed→ destroy the branch (handleClosed).
Branch names are gh-<repo-key>-pr-<n> or gh-<repo-key>-<sanitized-ref>,
where the repo key is six hex characters of the SHA-256 of the lowercased
owner/name. The rule lives in the public pgoverlayconnect/names.go
(RepoKey, PRBranchName, RefBranchName) so the service and the connect
helpers derive the same names, and a golden table
(pgoverlayconnect/testdata/branch_names.json) is shared by the Go, ghook and
JS tests. The prefix keeps webhook branches apart from hand-made ones by
convention (the engine does not reserve it), the repo key keeps repositories
apart, fork PRs are always named by number, and long refs are cut and
suffixed with a hash so they stay distinct. When GitHub App / PAT creds are
configured it posts a pgoverlay/branch commit status (success only once the
branch is ready) and keeps a live PR comment with the connect string (and,
with DiffOnPush, a schema/data diff). Webhook deliveries are acked
immediately and the branch op runs detached (a 5-minute background
context) because GitHub abandons deliveries after ~10s but provisioning at pod
speed can take longer (dispatch). Deliveries for one branch run one at a
time in arrival order (a per-branch queue), a repeated X-GitHub-Delivery
id is ignored, and with GitHub credentials a closed delivery for a PR that
GitHub reports open is ignored.
Depends on: internal/apiclient, internal/api (wire types),
pgoverlayconnect (naming), internal/ghook/githubapp.go (App auth:
per-installation tokens).
internal/engine — the brains
The orchestration layer. engine.go defines the Engine struct (it holds a
registry, a runtime driver, a cow.Planner, plus options for credential
rotation, max-branches quota, TTL policy, and metrics). Everything mutating a
branch is a saga with compensations:
saga.go—CreateBranch,ResetBranch,DestroyBranch, and the sharedprovisionstep that fans out to overlay /provisionZFS/provisionCSI.recover.go—RecoverBranch: restart a failed branch on its recorded volumes with no re-clone.freeze.go—CreateBranchFrom(branch-from-branch): the frozen-layer DAG.diff.go—DiffBranch(throwaway branch from the same recorded base, dump both, diff host-side).reconcile.go—PlanReconcile/ApplyReconcile/RunReconcile.csi.go,zfs.go— the non-overlay copy-on-write provisioning paths.
Depends on: internal/registry, internal/runtime, internal/cow,
internal/pgctl (seeding), internal/metrics.
internal/registry — SQLite metadata store
Pure-Go SQLite (modernc.org/sqlite, no cgo). It owns the state machine
(branches move creating → ready → resetting → ready, plus destroying →
destroyed, and failed, which reset or recover can leave; sources move
seeding → ready | failed), the transitions/audit journal (who did
what — actor column), sources and their generations, frozen layers,
mask scripts, hashed API tokens, and rotated branch passwords
encrypted at rest (crypto.go). Schema is versioned via PRAGMA
user_version; schema.go holds the migration list — currently v15
(see migrations in internal/registry/schema.go). Crucially SQLite is a
single writer: one branchd writes (with HA, the elected leader), and you
must not run local-mode pgb against a registry a branchd is using.
internal/runtime — the Driver interface
runtime.go defines Driver: EnsureImage, CreateVolume, RemoveVolume,
CloneVolume, RunHelper (one-shot data containers), StartBranch (the
long-lived branch Postgres), Exec/ExecOutput, Inspect, StopRemove,
ListManaged, ListHelpers, and ListManagedVolumes (with creation times,
so GC can spare young volumes). Two implementations:
docker.go—DockerDriver: named volumes + containers, branches published on a pinned127.0.0.1port with theunless-stoppedrestart policy; the Docker endpoint is resolved like thedockerCLI does (dockercontext.go;ssh://endpoints are refused).kube.go/kube_podspec.go/kube_csi.go—KubeDriver: branches as pods, two storage strategies (hostPath on one node, or CSI PVC clones).
Every managed resource is labelled pgoverlay.instance=<id> (see
LabelInstance) so reconcile only ever reclaims resources belonging to its
registry.
internal/cow — copy-on-write planning and the in-branch pieces
plan.go decides volume/layer names and the overlay stack order; the
overlay mount itself happens inside the branch container via an embedded
entrypoint script (//go:embed entrypoint.sh). PlanBranch lays out the
lowerdirs (frozen layers newest-first, source last) and renders
PGOVERLAY_LOWERS. The Planner also produces the exact zfs argv the engine
runs in privileged helpers, and the source/branch layer names for every backend.
Those files are pure, with no I/O. Around them:
lazyrw/lazyrw.cis the lazyrwLD_PRELOADshim (C, with its own tests underlazyrw/testand the postgres import audit underlazyrw/audit);lazyrw/distholds its four committed builds andSHA256SUMS, whichhack/build-lazyrw.shreproduces byte for byte.lazyrw.goembeds them (Variants,VerifyLazyRW), andinstall.gobuilds the install helper's command and environment (OverlayInstall) and names thecow-modevalues.usage/pgoverlay-du.cis the static FIEMAP tool that counts exclusive bytes;usage.goembeds its builds fromusage/distand renders the helper commands that run it (usage, and the XFS extent size hint).fsprobe.gois the copy-up probe (ProbeCopyUp): it runs through the driver and reportsclone,copyorunknown.
internal/pgctl — seeding helpers
seed.go (pg_basebackup) and seeddump.go (pg_dump) run the source copy
through the runtime driver as one-shot helper containers — pgoverlay never
touches data files from the host. pg_basebackup is physical/crash-consistent
(needs REPLICATION); pg_dump is logical (works against managed Postgres like
RDS/Neon/Supabase that forbid physical replication). settle.go (Settle)
then recovers, freezes and cleanly shuts down a basebackup seed in a helper
on the branch image; the dump script runs the same VACUUM itself.
internal/apiclient — the Go REST client
client.go — a thin typed client over branchd's /v1 API, used by the CLI in
server mode and by ghook. Speaks the wire types defined in internal/api.
SDKs and the GitHub Action
pgoverlaytest/(Go) — spin up an ephemeral branch in a test, get aProxyDSNthrough the router (WithProxyHost/PGOVERLAY_PROXY_HOSTwhen the router has its own address) and a directDSN, tear it down.pgoverlayconnect/(Go) — resolve a connect string at runtime, and the branch-naming rule shared with the GitHub App service.sdk/jsandsdk/js-connect— JS equivalents (index.mjs+.d.ts).action/— a composite GitHub Action (action.yml+entrypoint.sh, plus a separateaction/destroy) for CI pipelines.
3. The two ways it runs
pgoverlay is one engine with two frontends. The same engine code runs in
both modes; only what wraps it changes.
graph LR
subgraph local["Local mode (laptop / dev)"]
cli1["pgb"] --> eng1["engine (embedded)"]
eng1 --> reg1["SQLite ~/.pgoverlay"]
eng1 --> dkr["Docker daemon<br/>branch = container"]
end
subgraph deployed["Deployed mode (Kubernetes)"]
cli2["pgb --server"] --> bd["branchd<br/>(Deployment)"]
ghk["pgoverlay-github"] --> bd
app["app / psql"] --> bd
bd --> eng2["engine"]
eng2 --> reg2["SQLite on PVC/hostPath"]
eng2 --> k8s["Kubernetes API<br/>branch = pod"]
end
- Local:
pgbembeds the engine (cli/root.go: open()), uses the Docker driver, and writes the SQLite registry under~/.pgoverlay. Great for a single developer. Because SQLite is single-writer, you must not run this while abranchdshares the same registry. - Deployed:
branchdruns as a normal Kubernetes Deployment (no CRDs, no operator —docs/architecture.md), owns the registry, and exposes the REST API + the Postgres router.pgb(with--server) andpgoverlay-githubbecome REST clients; apps connect through the proxy. HA runs multiple replicas with leader election so only one accepts writes.
4. End-to-end: what happens when you create a branch
Two entry points converge on the same saga: pgb branch create (or a REST
POST /v1/branches) and a PR webhook. Below is the deployed path through
branchd; the steps in engine are identical in local mode.
sequenceDiagram
autonumber
participant U as pgb / ghook
participant API as api.Server<br/>(handlers.go)
participant E as engine.CreateBranch<br/>(saga.go)
participant R as registry (SQLite)
participant D as runtime.Driver
participant PG as branch Postgres
U->>API: POST /v1/branches {name, source}
API->>API: bearer auth + role≥operator + leader gate
API->>E: CreateBranch(ctx, name, source, ttl)
E->>E: validate name, checkQuota()
E->>R: GetSourceByName (must be ready)
E->>R: CreateBranchCtx → state=creating
Note over E,D: provision() — each step registers a compensation
E->>D: CreateVolume (rw layer)
E->>D: RunHelper: write entrypoint.sh + lazyrw builds, mkdir upper/work
E->>D: StartBranch (overlay mounted IN-container, shim preloaded)
E->>R: SetBranchContainer(cid)
E->>PG: waitReady (pg_isready, ≤90s — covers any WAL recovery)
E->>PG: observeCowMode (read /pgoverlay/rw/cow-mode)
E->>PG: applyMasking (psql over local socket)
E->>PG: rotateBranchCredentials (if enabled)
E->>D: Inspect → host:port
E->>R: MarkBranchReady → state=ready
E-->>API: Branch{host, port, proxy_database}
API-->>U: 201 Created
Walking each step with the file that does it:
- Auth & gate —
internal/api/middleware.gochecks the bearer token and minimum role;s.mutate(operator, …)inapi/server.goalso composes the HA leader gate (api/leader.go), so non-leaders return503 not leader. The handler iscreateBranchininternal/api/handlers.go. - Validate + quota —
CreateBranchininternal/engine/saga.gocallsvalidateBranchName(anchored regex^[a-z0-9][a-z0-9-]{0,40}$, which also blocks path-traversal because names flow into volume/dataset/container names) andcheckQuota(--max-branches, returnsErrQuotaExceeded→ 403). - Resolve source —
reg.GetSourceByName; the source must be in stateready(registry). - Insert the row —
reg.CreateBranchCtxwrites a branch row in statecreating(and its journal row, in one transaction), pinningSourceVolume(the source's current generation volume) and naming the rw volume viafreshBranchLayer: the lowest generation of the name that no registry row has ever used, so a reused branch name never lands on a volume another branch still depends on. - Provision (the saga body) —
provisioninsaga.go. For the overlay backend it builds acow.Planfrom the layer chain, then runs steps that each push a compensation onto anundostack: - rw volume —
drv.CreateVolume. Compensation:RemoveVolume. - entrypoint install —
installOverlayEntrypointruns an Alpine helper with the command and environment fromcow.OverlayInstall: it writescow.EntrypointScriptinto the rw volume, createsupper/andwork/, and installs the four lazyrw shim builds intolazyrw/(each passed base64-encoded in its own environment variable and checked against its SHA-256). - start branch —
startOverlayBranchmounts the sourceroat/pgoverlay/lower0, frozen layersroat/pgoverlay/lower1..N, the rw volume at/pgoverlay/rw, setsPGDATA=/pgoverlay/merged,PGOVERLAY_LOWERSandPGOVERLAY_LAZYRW, and runs the entrypoint that assembles the OverlayFS mount inside the container, self-tests and probes the shim, and execs Postgres with it preloaded (or without it, reportingeager). Compensation:StopRemove. - If any step fails,
fail()unwinds theundostack in reverse — no orphaned volumes or containers, ever. - Readiness + masking + credentials —
awaitAndMark(saga.go):SetBranchContainerfirst (so a concurrent reconcile treats the in-flight container as owned, not an orphan), thenwaitReady(pg_isreadyloop, 90s budget — this is where WAL crash recovery happens for basebackup seeds that were not settled; on Kubernetes a fatal pod state such asImagePullBackOfffails it early, with the reason), thenobserveCowMode(cowmode.go: reads the branch'scow-modefile forpgoverlay_branch_cow_mode; best-effort, never fails the saga), thenapplyMasking(per-source SQL via in-containerpsqlover the local socket, so the branch never serves unmasked data), then optionalrotateBranchCredentials. Throughout, the saga'skeepAliveheartbeat bumps the row'supdated_at, so reconcile's stuck detector never fails a slow but live create. - Address + mark ready —
inspectAddrpollsdrv.Inspectuntil a routable host:port appears (k8s pod IPs lag exec-readiness), thenreg.MarkBranchReadyCtxflips the row toreadyand records host/port. - Response — the handler returns a
Branchincludingproxy_database(dbname@branch), so the caller knows how to connect through the router.
The PR-webhook path reaches the same place: ghook/service.go:ensureBranch
calls apiclient.CreateBranch → POST /v1/branches → the saga above.
5. Connecting to a branch — the pgproxy flow
Per-branch host ports are annoying to track, so branchd bundles a
Postgres wire-protocol router (internal/pgproxy/proxy.go). You connect to
:6432 and put the branch name in the database parameter as
dbname@branchname. The proxy resolves the branch, rewrites the database back
to its real name, and then splices raw bytes — SCRAM authentication flows
through untouched because the proxy never participates in auth.
sequenceDiagram
autonumber
participant C as psql / app
participant P as pgproxy :6432<br/>(proxy.go)
participant R as registry
participant B as branch backend
C->>P: TCP connect
C->>P: SSLRequest
alt TLS configured (--pg-tls-*)
P-->>C: 'S' + TLS handshake
else no cert
P-->>C: 'N' (plaintext)
end
C->>P: StartupMessage {database: "app@pr-42", user, …}
P->>P: splitDatabase → dbname="app", branch="pr-42"
P->>R: ResolveBranch("pr-42") → host:port (ready only)
alt unknown / not-ready / unreachable
P-->>C: FATAL "pgoverlay: database not available" (uniform refusal)
else resolved
P->>P: rewrite database back to "app", re-encode startup
P->>B: dial host:port, replay StartupMessage
Note over C,B: raw byte relay both ways<br/>(SCRAM auth passes through)
C->>B: auth + queries (relayed)
B-->>C: responses (relayed)
end
Step by step (all in internal/pgproxy):
- Accept & startup phase —
handleConnanswersSSLRequestwith'S'and a TLS upgrade whenTLSConfigis set (--pg-tls-cert/--pg-tls-key), else'N'.GSSEncRequestis answered'N'. ACancelRequest(plaintext, or inside TLS) is forwarded byte for byte to the backend of the one live session holding its key (cancel.go); unknown or ambiguous keys are dropped. The client must send its first byte withinFirstByteTimeout(2s) and finish the startup packets withinStartupTimeout(10s from accept), so a client that connects and dribbles can't pin a goroutine;MaxStartupsPerIP(64) caps one address's connections still in startup, andMaxConns(256) caps them all. - Parse the StartupMessage —
routereadsstartup.Parameters["database"]andsplitDatabase(startup.go) splits on the last@intodbnameandbranch. No@→3D000 invalid_catalog_namerefusal. - Resolve —
RegistryResolver.ResolveBranchlooks the branch up in the registry and returnshost:portonly if the branch isready. Unknown name, not-ready, dial failure — all collapse to the same generic refusal (genericRouteRefusal), so an unauthenticated client cannot tell them apart. It can still confirm that a ready branch exists, because the branch's auth challenge is relayed before any credentials; a full fix needs the proxy to take part in auth. The real reason is logged server-side. After a failed dial the resolver'sRefreshhook re-reads the branch's address from the runtime once (rate-limited per branch), in case it moved. - Rewrite & relay — the proxy sets
databaseback to the realdbname, re-encodes the startup message, dials the backend, writes the rewritten startup, and relays the backend's startup response frame by frame until the firstReadyForQuery(recordingBackendKeyDatafor cancels), which must arrive withinAuthTimeout(30s). Thenrelay()copies bytes in both directions with anIdleTimeout(15m, on reads and writes); once one direction ends, the other gets a 5s grace. Everything after the startup message — including the SCRAM challenge/response — is opaque to the proxy.
Backend dials are always plaintext (branches are local/cluster-internal); client-facing TLS is the security boundary.
6. Deployment topology
In Kubernetes, branchd is a plain Deployment with a namespace-scoped Role — no
CRDs, no operator. Branch pods are created directly via the Kubernetes API by
the KubeDriver. There are two storage models.
graph TB
subgraph cluster["Kubernetes namespace: pgoverlay"]
direction TB
ghk["pgoverlay-github<br/>(Deployment)"]
bd["branchd<br/>(Deployment; HA = N replicas<br/>+ Lease leader election)"]
lease["coordination.k8s.io/Lease<br/>pgoverlay-branchd"]
svcapi["Service :7070 (REST)"]
svcpx["Service :6432 (pg router)"]
subgraph hostpath["hostPath model (default)"]
node["one storage node (--kube-node)<br/>data root /var/lib/pgoverlay"]
bp1["branch pod pr-1<br/>SYS_ADMIN, nodeName-pinned<br/>overlay mounted in-container"]
bp2["branch pod pr-2"]
node --- bp1
node --- bp2
end
subgraph csi["CSI model (--kube-storage csi)"]
pvc1["PVC clone → branch pod<br/>no SYS_ADMIN, schedules anywhere"]
pvc2["PVC clone → branch pod"]
end
ghk -->|REST| svcapi --> bd
bd --- lease
bd --> bp1
bd --> bp2
bd --> pvc1
bd --> pvc2
svcpx --> bd
end
dev["pgb --server / apps"] --> svcapi
dev2["apps / psql"] --> svcpx
- hostPath (default) — "volumes" are subdirectories of
--kube-data-root(default/var/lib/pgoverlay) on one storage node named by--kube-node; helper pods are one-shot, branch pods are plain pods, all pinned withnodeName, and branch pods carrySYS_ADMINfor the in-container overlay mount (internal/runtime/kube.go). Single-node scope, but works on any cluster. - CSI (
--kube-storage csi) — every volume is a PVC and every branch is a PVC clone (dataSource, or VolumeSnapshot+restore with a snapshot class). Branch pods need noSYS_ADMINand no node pin, so they schedule anywhere — the multi-node payoff (internal/runtime/kube_csi.go). The CoW economics then belong to the CSI driver (instant on EBS/Ceph/zfs-localpv). - HA —
--leader-electmakes replicas contend for a Lease (internal/ha/leader.go); only the leader runs reconcile and accepts mutating/v1writes (theLeaderGateininternal/api/leader.goreturns 503 on non-leaders), which keeps the single-writer SQLite registry safe. The leader labels its podpgoverlay.leader=trueand the chart's API Service selects that label, so API traffic reaches the leader. - Helm chart and manifests live under
deploy/helm/pgoverlay/(deployment, services for API and proxy, RBAC, PVC, NetworkPolicy, the ghook Deployment).
Where to read next
- The create/reset/destroy sagas and compensations:
internal/engine/saga.go. - Branch-from-branch and the frozen-layer DAG:
internal/engine/freeze.go. - Overlay layer naming and the in-container mount plan:
internal/cow/plan.go(+ the embeddedentrypoint.sh). - The state machine + migrations:
internal/registry/schema.go. - Reconcile/GC convergence:
internal/engine/reconcile.go. - The prose design notes that complement this tour: architecture.
- The places where the obvious implementation was wrong: deep dives.