Previously the runner unconditionally invoked the passthrough DEK resolver, which required a 32-byte key. Jobs configured with encryption_enabled=false arrive with EncryptedDek=nil and the resolver returned an 'expected 32-byte DEK, got 0' error, failing every run. When EncryptedDek is empty the runner now skips the encrypt stage and io.Copy()s the compressed stream straight into the upload pipe. The encrypted_dek on BackupCompleted stays empty as well, matching the server's expectation for an un-encrypted run. |
||
|---|---|---|
| .. | ||
| cmd/agent | ||
| internal | ||
| .gitignore | ||
| .gitkeep | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
backupy-agent
The Backupy agent — open-source (MIT) Docker service that runs alongside your application and ships encrypted database backups to S3.
Full spec:
docs/03-agent-spec.md. Wire protocol:docs/07-api-contract.md.
Current status
This directory is the D-01 + D-03 (partial) skeleton. What works today:
- Compiles to a tiny static binary (
make build). - Loads & validates bootstrap config from env.
- Opens an encrypted-at-rest BoltDB state file (AES-256-GCM, HKDF from key).
- Cobra-based CLI:
run,version,health-check,dump-state. - Distroless multi-arch Dockerfile (< 50 MB image, non-root).
What's stubbed:
- WSS protocol exchange — skeleton only (full impl: D-02).
- Register / Heartbeat — interfaces in place (D-04).
- Docker socket discovery — interface only (D-05).
- Backup drivers (pg_dump, mysqldump, …) — interfaces only (D-06+).
- Auto-update — not started (D-15).
Quickstart (Docker)
services:
backup-agent:
image: backupy/agent:1
environment:
BACKUPY_SERVER_URL: https://api.backupy.ru
BACKUPY_AGENT_KEY: ${BACKUP_KEY}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- backup-agent-state:/var/lib/backup-agent
restart: unless-stopped
volumes:
backup-agent-state:
Env vars
| Var | Required | Default | Notes |
|---|---|---|---|
BACKUPY_SERVER_URL |
yes | https://api.backupy.ru |
Must be https:// (override with BACKUPY_DEV_ALLOW_INSECURE=true for local dev). |
BACKUPY_AGENT_KEY |
yes | — | Format bkpy_(live|test)_<32 alnum>. Never logged. |
BACKUPY_STATE_DIR |
no | /var/lib/backup-agent |
Volume-mounted. Must be writable by uid 65532 (distroless nonroot). |
BACKUPY_LOG_LEVEL |
no | info |
trace/debug/info/warn/error. |
BACKUPY_DOCKER_SOCKET |
no | /var/run/docker.sock |
Mounted read-only. |
BACKUPY_DEV_ALLOW_INSECURE |
no | false |
Allows http:// server URL — dev only. |
Everything else (targets, schedules, retention, S3 creds, hooks) comes from
the server via ConfigUpdate after registration.
CLI
agent run # default; starts the service loop
agent version # print version / commit / build date (--json for machine output)
agent health-check # used by Docker HEALTHCHECK; exits 0 when healthy
agent dump-state # debug: pretty-print state.db as JSON
# add --allow-secrets to include decrypted payloads
Filesystem layout
/var/lib/backup-agent/
└── state.db # BoltDB, AES-256-GCM encrypted values (HKDF from agent key)
Buckets inside state.db:
| Bucket | Contents |
|---|---|
config |
last applied AgentConfig + version |
queue |
pending RunBackup envelopes keyed by run_id |
registry |
session_id, last server time, last heartbeat |
logs_buffer |
rate-limited LogEvent buffer when server unreachable |
Build
# Generate protobuf bindings first (from repo root).
make proto
# Local binary
cd apps/agent
make tidy
make build # → bin/agent
# Local docker image (uses repo root as build context)
make image
# Multi-arch image
make image-multiarch
Important:
make protofrom the repo root must run at least once beforego buildsucceeds. The agent'sgo.moduses areplacedirective pointing atpackages/proto/gen/go/v1, which is created bybuf generate— seepackages/proto/README.md.
Tests
make test # go test -race -cover ./...
Test packages that pass standalone (no proto codegen needed):
internal/config— env validation, agent-key regex, state-dir probe.internal/state— BoltDB roundtrip, AES-GCM encryption-at-rest check.internal/logging— level parsing, secret redaction.internal/wss— exponential backoff schedule + jitter band.
Packages that require generated proto code to compile:
internal/protointernal/wss/client.gocmd/agent/run.go
Security notes
- TLS 1.3 to all server endpoints (enforced by
coder/websocketdefaults). BACKUPY_AGENT_KEYis never logged (slogReplaceAttr redacts known keys defensively; the value is alsojson:"-"inConfig).- State at rest is AES-256-GCM keyed by HKDF-SHA256 of the agent key.
- Docker socket is mounted read-only.
- Container runs as uid 65532 (distroless
nonroot). - Image is distroless static — no shell, no package manager, no busybox.
License
MIT — see top-level LICENSE once added.