Every policy in one file you can review.
A policy file declares how your sources are protected: schedules, retention, where copies may land, recovery objectives and verification. Keep it next to your code, review a change like any other, and see exactly what it does before anything changes. A plan applies whole or not at all, and every change lands in the audit log.
Say only what you mean to set
A file has a version and a list of policies. Each entry names a registered source and, when a source has more than one policy, the policy’s name (it is default when left out).
# yaml-language-server: $schema=https://tilldev.dev/schemas/tillark-policies.v1.json
version: 1
policies:
- source: orders-db # a registered source, by name or id
schedule: "0 */6 * * *" # UTC
rpo: 7h30m
retention:
daily: 14
monthly: 12
placement:
residency: eu
copies: 2
- source: ledger
name: pitr # a second policy on the same source
rto: 15m
verify:
cadence: "0 4 * * *"- A setting the file leaves out stays as it is. An entry for a policy that exists changes only what it gives; an entry for a new one starts from the defaults in the settings table.
- Durations take seconds or units:
15m,1h30m,2d. Schedules are 5-field cron expressions in UTC. - YAML or JSON. The first line above points your editor at the file’s JSON Schema, so it completes keys, explains each one and marks mistakes as you type.
See the change before it happens
tilldev ark export -o policies.yaml # every policy as it stands
# edit policies.yaml, then:
tilldev ark plan policies.yaml # what it would change; changes nothing
tilldev ark apply policies.yaml # shows the plan and asks before applyingA plan lists what the file would create (+), change (~, each setting from its current value to the new one) and remove (-). Policies the file doesn’t mention are listed and kept.
$ tilldev ark plan policies.yaml
Plan: 1 to create, 1 to change.
~ orders-db / default
retention.daily 7 → 14
placement.copies 1 → 2
+ ledger / pitr new
rpo 5m
rto 15m
verify.cadence 0 4 * * *
Not in the file, so kept (--prune removes them):
orders-db / weeklyA change that weakens protection is flagged under its policy, so it can’t slip through review: one that keeps fewer backups, asks for fewer copies, lets a copy sit with the source’s provider or in its region, lets copies share credentials, changes where copies may be kept, loosens the recovery-point objective, or stops backups, scheduled verification, proving older backups, reading the repository’s data or pausing on a canary alarm.
On the dashboard, the Policy file panel on the Policies page does the same: load the current policies or open a file, plan it, and apply the plan.
Refused, with the line that has it
A file with a problem is never applied, in part or at all. Each problem points at its line and column, and a misspelt key comes with the one you probably meant.
$ tilldev ark plan policies.yaml
policies.yaml:8:7 policies[0].retention.dayly: unknown key; did you mean "daily"?
That problem stops the file from being applied; nothing was changed.A file is refused when it:
- names a source that isn’t registered, or declares the same policy twice;
- has a key TillArk doesn’t know, or a value outside what a setting takes;
- keeps nothing: every retention count at 0;
- gives a schedule that leaves longer between backups than the RPO objective allows. Changing an existing policy’s schedule? Give
rpowith it.
Every change or none
- Planned again on apply. TillArk plans the file again against the policies as they are when you apply, in one transaction. If a policy the plan acts on has changed since, say someone edited it on the dashboard, nothing is applied and you are asked to plan again.
- Reviewed plans stay reviewed.
plan -o plan.jsonsaves the plan with its file.apply plan.jsonapplies exactly that, without a prompt, and only while nothing it acts on has changed. - Audited. Each change is an entry in the hash-chained audit log, with the settings it changed and the plan it came from.
- Who can apply. Anyone in the workspace can plan. On the dashboard, applying needs an owner or admin. With a
tark_token,operatorcan create policies from a file, and changing or removing them needsadmin.
# In review: record the plan with the change.
tilldev ark plan policies.yaml -o plan.json --detailed-exitcode
# exit 0: nothing to change · 2: changes · 1: a problem in the file
# After approval: apply exactly the plan that was reviewed.
tilldev ark apply plan.json--prune (or tick the box on the dashboard). Prune only removes policies on the sources the file names, and a removed policy’s backups stay restorable.Settings
Every setting a policy takes, and where a new policy starts when the file leaves it out. A new policy’s rpo follows its schedule: the longest gap between backups plus time for the backup to run (1h15m for hourly), or 5m when the source streams WAL.
| Setting | Meaning | New policy |
|---|---|---|
enabled | false stops this policy’s backups and verifications; backups already taken are kept. | true |
schedule | When backups run: a 5-field cron, in UTC. | 0 * * * * |
retention.last | The most recent backups kept. | 7 |
retention.hourly | One backup per hour, for this many hours. | 24 |
retention.daily | One backup per day, for this many days. | 7 |
retention.weekly | One backup per week, for this many weeks. | 4 |
retention.monthly | One backup per month, for this many months. | 12 |
retention.yearly | One backup per year, for this many years. | 3 |
placement.different_provider | Keep copies with a provider other than the source’s. | true |
placement.different_region | Keep copies in a region other than the source’s. | true |
placement.residency | Where copies may be kept: a residency zone such as eu, or global. | global |
placement.copies | Copies of each backup, each in its own failure domain. | 1 |
placement.separate_credentials | Keep each copy under a different target credential domain. | false |
rpo | The most data a restore may lose. It must cover the schedule’s longest gap, unless the source streams WAL. 0 sets no objective. | from the schedule |
rto | How long a restore may take. | 1h |
verify.cadence | When restores are verified: a 5-field cron, in UTC, or off. | 0 6 * * * |
verify.spread | How long after each slot verifications may start; each policy keeps its place. | 1h |
verify.older_every | Every nth scheduled verification proves the older kept backup proven longest ago; 0 never. | 4 |
verify.read_parts | Parts a restic repository’s data is read in, one per scheduled verification; 0 never. | 30 |
incremental | Between full backups, pgBackRest and WAL-G sources take differential (changes since the last full) or incremental (changes since the last backup) ones; off takes a full every time. restic always stores only what changed. | off |
full_every | How often a policy with incremental on takes a full backup. | 7d |
pause_on_alarm | Pause the source when its ransomware canary raises an alarm. | false |
From your own tooling
The CLI and dashboard use these endpoints. They take the file as JSON, up to 1 MB; send the digest of the plan you reviewed so apply refuses anything else.
GET /api/ark/policies/export → { "document": "<policies.yaml>" }
POST /api/ark/policies/plan { "document": {…}, "prune": false }
→ { "plan": { changes, unlisted, summary, issues, digest } }
POST /api/ark/policies/apply { "document": {…}, "prune": false, "digest": "sha256:…" }
→ { "applied": [...], "plan": {…} }Request and response shapes are in the API reference.
See Backups & retention for what each setting does, and the CLI reference for the per-policy commands. Back to the TillArk overview.