Files
quicproquo/docs/operations/backup-restore.md
Christian Nennemann 2e081ead8e chore: rename quicproquo → quicprochat in docs, Docker, CI, and packaging
Rename all project references from quicproquo/qpq to quicprochat/qpc
across documentation, Docker configuration, CI workflows, packaging
scripts, operational configs, and build tooling.

- Docker: crate paths, binary names, user/group, data dirs, env vars
- CI: workflow crate references, binary names, artifact names
- Docs: all markdown files under docs/, SDK READMEs, book.toml
- Packaging: OpenWrt Makefile, init script, UCI config (file renames)
- Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team
- Operations: Prometheus config, alert rules, Grafana dashboard
- Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths
- Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
2026-03-21 19:14:06 +01:00

5.5 KiB

Backup and Restore Procedures

This document covers backup and restore for all quicprochat server data stores.

Data Inventory

Data Location Backend Contains
SQLCipher DB QPC_DB_PATH (default data/qpc.db) store_backend=sql Users, key packages, delivery queues, sessions, KT log, OPAQUE setup, blobs metadata, moderation
File store QPC_DATA_DIR (default data/) store_backend=file Bincode-serialized key packages, delivery queues, server state
Blob storage QPC_DATA_DIR/blobs/ Filesystem Uploaded file transfer blobs
TLS certificates QPC_TLS_CERT, QPC_TLS_KEY DER files Server identity
OPAQUE ServerSetup Inside DB or file store Persisted OPAQUE credential state (critical for auth)
Server signing key Inside DB or file store Persisted Ed25519 key for delivery proofs
KT Merkle log Inside DB or file store Persisted Key transparency audit log

SQLCipher Backup

Hot Backup (Online)

SQLCipher supports the .backup command while the server is running (WAL mode allows concurrent readers).

# 1. Open the encrypted database with the same key
sqlite3 data/qpc.db

# 2. At the sqlite3 prompt, set the encryption key
PRAGMA key = 'your-db-key-here';

# 3. Perform an online backup
.backup /backups/qpc-$(date +%Y%m%d-%H%M%S).db

.quit

Scripted Hot Backup

#!/bin/bash
set -euo pipefail

BACKUP_DIR="/backups/qpc"
DB_PATH="${QPC_DB_PATH:-data/qpc.db}"
DB_KEY="${QPC_DB_KEY}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/qpc-${TIMESTAMP}.db"

mkdir -p "$BACKUP_DIR"

sqlite3 "$DB_PATH" <<EOF
PRAGMA key = '${DB_KEY}';
.backup ${BACKUP_FILE}
EOF

# Verify the backup is readable
sqlite3 "$BACKUP_FILE" "PRAGMA key = '${DB_KEY}'; PRAGMA integrity_check;" \
  | grep -q "ok" && echo "Backup verified: $BACKUP_FILE" \
  || { echo "ERROR: backup verification failed"; exit 1; }

# Retain last 7 daily backups
find "$BACKUP_DIR" -name 'qpc-*.db' -mtime +7 -delete

Cold Backup (Offline)

# 1. Stop the server
systemctl stop qpc-server   # or docker compose stop server

# 2. Copy the database file
cp data/qpc.db /backups/qpc-$(date +%Y%m%d).db

# 3. Copy the WAL and SHM files if they exist
cp data/qpc.db-wal /backups/ 2>/dev/null || true
cp data/qpc.db-shm /backups/ 2>/dev/null || true

# 4. Restart the server
systemctl start qpc-server

File Backend Backup

When using store_backend=file, data is stored as bincode files under QPC_DATA_DIR.

# Full directory backup
tar czf /backups/qpc-data-$(date +%Y%m%d-%H%M%S).tar.gz \
  -C "$(dirname "${QPC_DATA_DIR:-data}")" \
  "$(basename "${QPC_DATA_DIR:-data}")"

Blob Storage Backup

Blobs are stored in QPC_DATA_DIR/blobs/. These are immutable once written.

# Incremental rsync (blobs are write-once, ideal for rsync)
rsync -av --progress data/blobs/ /backups/blobs/

TLS Certificate Backup

# Back up TLS certificates (store separately from DB backups)
cp data/server-cert.der /backups/tls/server-cert.der
cp data/server-key.der /backups/tls/server-key.der

# Federation certs (if federation is enabled)
cp data/federation-cert.der /backups/tls/federation-cert.der 2>/dev/null || true
cp data/federation-key.der /backups/tls/federation-key.der 2>/dev/null || true
cp data/federation-ca.der /backups/tls/federation-ca.der 2>/dev/null || true

Restore Procedures

Restore SQLCipher Database

# 1. Stop the server
systemctl stop qpc-server

# 2. Move the current (corrupt/lost) database aside
mv data/qpc.db data/qpc.db.broken 2>/dev/null || true
rm -f data/qpc.db-wal data/qpc.db-shm

# 3. Copy the backup in place
cp /backups/qpc-20260304.db data/qpc.db

# 4. Verify integrity
sqlite3 data/qpc.db "PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check;"

# 5. Start the server (migrations will apply automatically if needed)
systemctl start qpc-server

Restore File Backend

# 1. Stop the server
systemctl stop qpc-server

# 2. Replace the data directory
mv data data.broken 2>/dev/null || true
tar xzf /backups/qpc-data-20260304.tar.gz -C .

# 3. Restore TLS certs if not included in the data backup
cp /backups/tls/server-cert.der data/server-cert.der
cp /backups/tls/server-key.der data/server-key.der

# 4. Start the server
systemctl start qpc-server

Restore Blobs Only

rsync -av /backups/blobs/ data/blobs/

Backup Schedule Recommendations

Frequency What Method
Every 6 hours SQLCipher database Hot backup script via cron
Daily File backend / full data dir tar + offsite copy
Continuous Blobs rsync (incremental)
On change TLS certificates Manual + secret manager

Cron Example

# SQLCipher hot backup every 6 hours
0 */6 * * * /opt/qpc/scripts/backup-db.sh >> /var/log/qpc-backup.log 2>&1

# Full data directory daily at 02:00
0 2 * * * tar czf /backups/qpc-data-$(date +\%Y\%m\%d).tar.gz -C /var/lib quicprochat

# Blob sync every hour
0 * * * * rsync -a /var/lib/quicprochat/blobs/ /backups/blobs/

# Prune backups older than 30 days
0 3 * * 0 find /backups -name 'qpc-*' -mtime +30 -delete

Verification

Always verify backups after creation:

# SQLCipher integrity check
sqlite3 /backups/qpc-latest.db \
  "PRAGMA key = '${QPC_DB_KEY}'; PRAGMA integrity_check; SELECT count(*) FROM users;"

# File backend: check the archive is valid
tar tzf /backups/qpc-data-latest.tar.gz > /dev/null

# TLS cert: check it parses and is not expired
openssl x509 -inform DER -in /backups/tls/server-cert.der -noout -dates