107 lines
4.5 KiB
Text
107 lines
4.5 KiB
Text
|
|
# Backup provider contract — the interface every backup/restore provider must implement.
|
||
|
|
# Consumed by op.nu to build and execute archive commands generically.
|
||
|
|
# Each provider declares its binary, features, environment requirements, and
|
||
|
|
# the exact subcommands + flags needed for each operation.
|
||
|
|
|
||
|
|
let _Features = {
|
||
|
|
tags | Bool | doc "Supports --tag key=value on snapshot create" | default = true,
|
||
|
|
ui | Bool | doc "Has built-in web UI (e.g. kopia server start --ui)" | default = false,
|
||
|
|
verify | Bool | default = true,
|
||
|
|
mount | Bool | doc "Supports FUSE mount of snapshots" | default = false,
|
||
|
|
encryption | Bool | doc "Encrypts snapshots end-to-end (non-negotiable for this stack)" | default = true,
|
||
|
|
compression | Bool | default = false,
|
||
|
|
dedup | [| 'none, 'per_repo, 'global |] | default = 'per_repo,
|
||
|
|
streaming | Bool | doc "Supports backup from stdin (pg_dump pipe, etc.)" | default = false,
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _Env = {
|
||
|
|
required | Array String | doc "Must be set before any operation",
|
||
|
|
optional | Array String | default = [],
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _Connection = {
|
||
|
|
required | Bool | doc "Must call connect before backup (kopia=true, restic=false)" | default = false,
|
||
|
|
status_subcmd | String | optional | doc "Subcommand to check if already connected",
|
||
|
|
connect_subcmd | String | optional | doc "Subcommand to establish connection to the repo",
|
||
|
|
state_file | String | optional | doc "Filename stored under ops_dir for per-workspace connection state (e.g. '.kopia-config')",
|
||
|
|
s3_flags | {
|
||
|
|
bucket | String | optional,
|
||
|
|
endpoint | String | optional,
|
||
|
|
prefix | String | optional,
|
||
|
|
} | default = {},
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _BackupCmd = {
|
||
|
|
subcmd | String | doc "Subcommand (may contain spaces, e.g. 'snapshot create')",
|
||
|
|
repo_flag | String | optional | doc "Flag for repository URL; absent if config-file based",
|
||
|
|
tag_flag | String | optional | doc "Flag for a single tag entry (repeated per tag)",
|
||
|
|
snapshot_id_regex | String | doc "Regex with named group 'id' to extract snapshot ID from stdout",
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _RestoreCmd = {
|
||
|
|
subcmd | String,
|
||
|
|
repo_flag | String | optional,
|
||
|
|
target_flag | String | doc "Flag for restore destination; empty string = positional arg",
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _ListCmd = {
|
||
|
|
subcmd | String,
|
||
|
|
repo_flag | String | optional,
|
||
|
|
tag_flag | String | optional,
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _ForgetCmd = {
|
||
|
|
subcmd | String | doc "Subcommand that removes old snapshots",
|
||
|
|
repo_flag | String | optional,
|
||
|
|
tag_flag | String | optional | doc "Scope forget to a specific tag (workspace isolation)",
|
||
|
|
keep_last_flag | String | optional | doc "Absent when retention is controlled via policy_subcmd",
|
||
|
|
keep_monthly_flag | String | optional,
|
||
|
|
keep_yearly_flag | String | optional,
|
||
|
|
extra_flags | Array String | default = [],
|
||
|
|
policy_subcmd | String | optional | doc "If set, run policy subcommand before forget (kopia model)",
|
||
|
|
policy_keep_flags | {
|
||
|
|
keep_latest | String | default = "--keep-latest",
|
||
|
|
keep_monthly | String | default = "--keep-monthly",
|
||
|
|
keep_annual | String | default = "--keep-annual",
|
||
|
|
} | optional,
|
||
|
|
} in
|
||
|
|
|
||
|
|
let _VerifyCmd = {
|
||
|
|
subcmd | String,
|
||
|
|
repo_flag | String | optional,
|
||
|
|
} in
|
||
|
|
|
||
|
|
# EncryptionRequired contract: provider features must include encryption=true.
|
||
|
|
# A BackupPolicy referencing a provider that does not encrypt fails at
|
||
|
|
# `nickel export` time, not at runtime — this is how the non-negotiable
|
||
|
|
# E2E encryption invariant is enforced.
|
||
|
|
let _EncryptionRequired = std.contract.from_validator (fun value =>
|
||
|
|
if value.features.encryption == true
|
||
|
|
then 'Ok
|
||
|
|
else 'Error {
|
||
|
|
message = "BackupProvider lacks 'encryption in features (E2E encryption is non-negotiable)",
|
||
|
|
}
|
||
|
|
) in
|
||
|
|
|
||
|
|
{
|
||
|
|
BackupProvider = {
|
||
|
|
name | String | doc "Provider identifier — must match the directory name under extensions/providers/backup/",
|
||
|
|
binary | String | doc "CLI binary invoked for all operations",
|
||
|
|
features | _Features | default = {},
|
||
|
|
env | _Env,
|
||
|
|
connection | _Connection | default = {},
|
||
|
|
mount_capable | Bool | doc "Convenience flag mirroring features.mount" | default = false,
|
||
|
|
streaming_capable | Bool | doc "Convenience flag mirroring features.streaming" | default = false,
|
||
|
|
commands = {
|
||
|
|
backup | _BackupCmd,
|
||
|
|
restore | _RestoreCmd,
|
||
|
|
list | _ListCmd,
|
||
|
|
forget | _ForgetCmd,
|
||
|
|
verify | _VerifyCmd | optional,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
|
||
|
|
# Apply this contract to provider definitions to enforce E2E encryption.
|
||
|
|
EncryptionRequired = _EncryptionRequired,
|
||
|
|
}
|