open-standards/openpay/docs/deploy/backup.md
Transaction Science 70ecd6caa0 Initial import — three open standards under one shelf
Transaction Science open standards monorepo. Each subdirectory is a
self-contained reference protocol with its own README and licence.

- openpay/    Apache-2.0  payment-acceptance stack (Rust, 26 crates)
- smart-byte/ CC-BY-4.0   content-addressed value carrier (spec)
- eoc/        CC-BY-4.0   energy-optimised compute (spec)

The wire format and the right to fork are public. The optional hosted
services are the offer; the protocols are not.
2026-05-23 07:58:09 -04:00

118 lines
4.2 KiB
Markdown

# OpenPay — backup and restore
The entire deployment — refunds, disputes, settlement batches,
ledger, reconciliation, webhooks, subscriptions, idempotency cache
— lives in **one file**: the `.graph` Minigraf database pointed at
by `AppState::with_graph_path`. Backup is "copy that file";
restore is "put it back."
This is the single biggest win of the Phase 26 single-file
persistence model (`docs/26-graph-persistence-progress.md`): no
multi-table dump, no schema-aware dump tool, no per-store backup
job. One file. Atomic.
## What you're backing up
By default the systemd unit puts the file at
`/var/lib/openpay/data.graph`. (See `OP_GRAPH_PATH` in
`openpay.env.sample`.) If you configured a different path, that's
the one.
## The honest caveat: hot copies can miss recent writes
Minigraf holds in-memory state that the on-disk file lags slightly
behind. A `cp` / `rsync` of the file **while op-server is serving
traffic** will get you something close-but-not-equal to the
in-memory truth — the last few seconds of writes may be missing,
and depending on what's mid-flush you may copy a partial page.
Two ways to get a clean snapshot:
### Option A — stop the service briefly (simplest, recommended)
```bash
sudo systemctl stop openpay
sudo rsync -av /var/lib/openpay/data.graph \
backup-host:/snapshots/openpay-$(date +%F-%H%M).graph
sudo systemctl start openpay
```
The service shutdown handler (SIGTERM → `shutdown_signal` in
`crates/op-server/src/main.rs`) drains in-flight requests cleanly,
and `Drop` on `GraphHandle` flushes pending writes. Typical
service downtime: under five seconds for graphs in the
single-digit-GB range.
### Option B — call `compact()` first, then copy
`GraphHandle::compact()` (see `crates/op-graph/src/graph.rs:239`)
flushes pending writes and rewrites the file in compact form.
Right after `compact()` returns, the on-disk state is consistent
with what was in memory up to that moment. There's no shipped HTTP
endpoint for this — you'd add one to your patched `main.rs` (an
operator-only path on a local-only socket, ideally) or trigger it
from a maintenance binary that opens the same graph path.
This still doesn't capture writes that arrive *between* `compact()`
and the copy. For that you want Option A.
## restic example (recommended for offsite)
```bash
restic -r s3:s3.amazonaws.com/openpay-backups init # first time only
sudo systemctl stop openpay
sudo restic -r s3:s3.amazonaws.com/openpay-backups \
--tag openpay --tag $(hostname) \
backup /var/lib/openpay/data.graph
sudo systemctl start openpay
# Verify periodically:
sudo restic -r s3:s3.amazonaws.com/openpay-backups check
```
restic deduplicates across snapshots — a daily backup of a
mostly-static graph file is cheap. Pair with `restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12`
to age out old snapshots.
## Restore
Stop the service, drop the file in place, start the service.
Minigraf opens it on startup; every store recovers automatically.
```bash
sudo systemctl stop openpay
sudo cp /snapshots/openpay-2026-05-20-0300.graph \
/var/lib/openpay/data.graph
sudo chown openpay:openpay /var/lib/openpay/data.graph
sudo systemctl start openpay
./target/release/op readiness
# Stores should all answer ready.
```
No migrations to run. No "reseed the index." The audit-report
endpoint (`GET /v1/audit/report`) will reflect the restored state.
## What about the in-memory graph?
`AppState::new_in_memory()` keeps everything in RAM and drops it on
restart. There is nothing to back up; there is also nothing to
recover. If you're running on the in-memory graph, you accepted
that. (Suitable for: tests, demos, ephemeral kiosks. Not for:
anything you want to see tomorrow.)
## Cross-host restore
The `.graph` file is portable across hosts of the same Rust target
triple. Copy it to a new machine, install the matching `op-server`
binary, point `OP_GRAPH_PATH` at the file, start the service —
done. No external dependencies, no DB cluster to migrate.
## What about bi-temporal history?
The ledger's bi-temporal history (`op_ledger::LedgerHistory`) is
stored in the same `.graph` file. Restoring the file restores the
history. Time-travel queries (`balance_as_of`, the audit report's
`as_of` parameter, etc.) work against the restored state with no
extra work.