Skip to content

Latest commit

 

History

History
94 lines (73 loc) · 4.47 KB

File metadata and controls

94 lines (73 loc) · 4.47 KB

pgctl — Agent Guide

Postgres backup, restore and verification. A backup nobody has restored is a rumour; backup verify — the drill — is what turns it into a fact, on a schedule.

Layout

  • cmd/pgctl/main.go — the command surface (backup list|run|verify, restore, version), flag parsing, per-operation timeouts, signal handling.
  • internal/config/config.Load, the target file, and its validation.
  • internal/pg/ — the whole implementation:
    • runner.goRunner, the one interface: build an argv, run it on the far end, read stdout. SSHRunner (default) and LocalRunner (--local, the recovery image).
    • backup.go — list, take, prune, and the backup-selection guards.
    • verify.go — the drill.
    • restore.go — point-in-time restore.

Build: go build ./... · test: go test ./... · vet: go vet ./....

One dependency (goccy/go-yaml, for the config). Add another only for a real reason.

Two execution modes, and why it matters

  • Over ssh (default). Suits a database that only listens on its own host: pgctl ssh's in, then docker execs into the postgres container — which already holds the object-storage credentials barman needs. pgctl handles no secrets.
  • Locally (--local). Inside the recovery image, which carries barman and postgres alongside the binary. For the day the host you would have ssh'd into is the thing you are recovering from.

Runner.HasBarman() decides how a barman command is spelled. Getting it wrong is silent, and it fails at the moment someone is trying to recover.

Rules the code already learned the hard way

Read the comments before changing these — each one is a bug that shipped:

  • SQL goes through stdin, never argv. ssh flattens argv into one string for the remote shell, and quoting does not survive the hop.
  • An empty read is never a zero. Not knowing what a database holds must never be treated as knowing it is empty — that is what licenses a drop.
  • Guards fail closed. Anything unverifiable is a failure, never an assumption.
  • Prune only after the backup succeeds, or a run of failures eats the last good backup.

Spec-driven development (OpenSpec)

This repo uses OpenSpec. Baseline specs of current behavior live in openspec/specs/<capability>/spec.md; project context and spec rules are in openspec/config.yaml. Known ambiguities/possible bugs are tracked as GitHub issues.

New features, fixes, and any behavior changes MUST follow the OPSX workflow (https://github.com/Fission-AI/OpenSpec/blob/main/docs/opsx.md):

  1. /opsx:propose — create a change (proposal → spec deltas → design → tasks)
  2. /opsx:apply — implement the change from its artifacts
  3. /opsx:archive — merge the deltas into openspec/specs/ when shipped

Don't edit openspec/specs/ directly for new behavior — that directory reflects what is built and deployed; it changes via archived change deltas (or /opsx:sync). Keep specs observable-behavior-only, with GIVEN/WHEN/THEN scenarios per openspec/config.yaml rules.

Specs are strictly implementation-agnostic: they describe functional, externally observable behavior and MUST NOT mention programming languages, frameworks, libraries, file or directory names in the source tree, or class/function names. The litmus test: the implementation could be rewritten in a different language without editing any spec. Implementation notes belong in change design.md/tasks.md artifacts and this file, never in specs.

The deliberate exception is documented in openspec/config.yaml: the external programs pgctl drives and the on-disk artifacts it produces for them (barman's commands, PG_VERSION, recovery.signal, postgresql.auto.conf) are observable behavior — a rewrite must still produce exactly those, or the restored database does not work.

Specs use example fixtures, never real infrastructure. No real hostnames, containers, buckets, endpoints, roles, or schema/table names — example-production, s3://example-backups, SELECT count(*) FROM public.users. This tool is published; strangers read its specs.

Capabilities

  • cli-surface — commands, flags, target selection, execution mode, version, timeouts, signal handling.
  • backup-management — list, take, prune, and the selection/staleness guards.
  • restore-drill — the drill, end to end, and every guard in it.
  • point-in-time-restore — restore into a kept directory, optionally to a time.