TILLARK · AGENT

A small binary that runs where your data lives.

PREVIEW

The agent is the only Go component in TillDev — a single static, stdlib-only binary that runs in your (or our) environment. It claims jobs, drives the engines locally, encrypts client-side, and reports signed manifests. It caches its policy, so it can execute a pre-authorized failover even if the control plane is unreachable.

01What it does

Claim, run, sign, report

The control plane never touches your backup bytes — the agent does. It registers with an Ed25519 public key, polls for pending jobs, claims one atomically, and runs the loop below. Backup data is encrypted inside the agent before it leaves your environment; the control plane only ever receives a hybrid-signed manifest and an integrity result.

text
┌─ heartbeat ────────────────────────────────────────────────┐
│  register (Ed25519 pubkey) → heartbeat → CLAIM a job         │
│                                    │                          │
│         ┌──────────────────────────┴───────────┐             │
│    backup                                   restore/verify    │
│      │                                          │             │
│  run engine (argv-only)              VERIFY manifest signature │
│  encrypt CLIENT-SIDE                     │  ✗ → refuse: the    │
│  stream ciphertext → target              │      engine never   │
│      │                                   │      runs (red)     │
│  sign manifest (Ed25519+ML-DSA-65)       │  ✓                  │
│      │                              run engine restore         │
│  report ─────────────────────────▶ report + integrity result  │
└──────────────────────────────────────────────────────────────┘
   no decrypted credential ever transits this channel (Phase 1)
Verify before restore
The restore path refuses to call the engine until the manifest signature verifies — a tampered manifest means the executor never runs and the outcome is red. The agent also reconciles the job’s snapshot reference and org against the signed manifest, so a swapped catalog column or a cross-org id is refused.
02Run

Build and run

Because the module has zero third-party dependencies (AES-256-GCM and Ed25519 come from Go’s standard library), it builds and runs with no network — the right shape for a self-hosted or air-gapped, always-on daemon.

bash
# Build a single static binary (stdlib-only → no network, no go.sum).
GOPROXY=off go build -o ark-agent ./cmd/ark-agent

# Run it against your control plane.
TILLARK_CP_URL=https://ark.example \
TILLARK_AGENT_TOKEN=tark_… \
TILLARK_ENGINES=restic \
  ./ark-agent
03Config

Environment variables

VariableMeaning
TILLARK_CP_URLControl-plane base URL. HTTPS only. Required.
TILLARK_AGENT_TOKENThe tark_… bearer token the agent authenticates with. Required.
TILLARK_ENGINESComma list of engines this agent may drive (default restic).
TILLARK_AGENT_DATA_DIRWhere the signing key and the cached policy live (path-traversal-safe).
TILLARK_POLL_INTERVAL_MSHow often to poll for pending jobs.
TILLARK_HEARTBEAT_INTERVAL_MSHow often to heartbeat + refresh cached policy.
TILLARK_ALLOW_INSECURE_LOCALSet 1 to permit a localhost http control plane — local dev only.

Mint the token with tilldev ark tokens create --scope operator (an agent needs operator to claim and report). The secret is shown once and stored sha256-hash-only.

04Security

Security properties

The agent connects to user-named engines and endpoints and shells out to binaries — exactly the surfaces an attacker probes. Each is closed by construction, and each property is unit-tested (including against a real subprocess for exec, and a live cross-language crypto vector):

PropertyHow
Argv-only exec (CWE-78)A real subprocess with an argv array — never a shell. NUL-byte rejection, output cap, timeout-kill, and a minimal env allowlist so the engine never inherits the agent’s secrets.
SSRF-safe (CWE-918)Target / replica URLs are validated and the resolved IP is pinned — a public name resolving to link-local / metadata is refused. DNS rebinding is closed.
Client-side AEADData is AES-256-GCM encrypted in the agent before upload; the storage vendor sees only ciphertext.
Signed manifestsEd25519 now (ML-DSA-65 registered), byte-compatible with the control plane’s envelope — either side reads the other’s ciphertext.
HTTPS-only channelThe control-plane client refuses an https→http downgrade or cross-host redirect, so the tark_ token can’t reach a cleartext or foreign endpoint. No decrypted credential transits this channel in Phase 1.
Autonomy, safelyThe offline policy cache drives a pure failover-guardrail decision (quorum, confirmation window, staleness bound, anti-flap) and defers entirely while the control plane is reachable.
05Honesty

The Phase-1 boundary

The command construction (argv), signing, verification, SSRF guarding, and autonomy logic are real and tested today. The actual engine subprocess run, object-storage streaming, --json parsing, and content hashing land at vendor wireup — the deliberate last step.

Never a fabricated success
Until an engine executor is provisioned, the agent returns a clear ErrExecutorNotProvisioned rather than fake a green backup. The one unforgivable bug in a backup product is a false success — so the agent refuses to claim one.

See Architecture for how the agent fits the control plane, Security for the crypto details, or the CLI reference. Back to the TillArk overview.