Implement Phase 1: core framework, operational tooling, and runbook
Core packages: crypto (Argon2id/AES-256-GCM), config (TOML/viper), db (SQLite/migrations), barrier (encrypted storage), seal (state machine with rate-limited unseal), auth (MCIAS integration with token cache), policy (priority-based ACL engine), engine (interface + registry). Server: HTTPS with TLS 1.2+, REST API, auth/admin middleware, htmx web UI (init, unseal, login, dashboard pages). CLI: cobra/viper subcommands (server, init, status, snapshot) with env var override support (METACRYPT_ prefix). Operational tooling: Dockerfile (multi-stage, non-root), docker-compose, hardened systemd units (service + daily backup timer), install script, backup script with retention pruning, production config examples. Runbook covering installation, configuration, daily operations, backup/restore, monitoring, troubleshooting, and security procedures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
deploy/docker/docker-compose.yml
Normal file
24
deploy/docker/docker-compose.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
services:
|
||||
metacrypt:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: Dockerfile
|
||||
container_name: metacrypt
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8443:8443"
|
||||
volumes:
|
||||
- metacrypt-data:/data
|
||||
# To populate /data before first run, use an init container or
|
||||
# bind-mount a host directory instead of a named volume:
|
||||
# volumes:
|
||||
# - ./data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "metacrypt", "status", "--addr", "https://localhost:8443", "--ca-cert", "/data/certs/ca.crt"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
metacrypt-data:
|
||||
22
deploy/examples/metacrypt-docker.toml
Normal file
22
deploy/examples/metacrypt-docker.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
# Metacrypt configuration for Docker deployment.
|
||||
# Place this file at /data/metacrypt.toml inside the container volume.
|
||||
|
||||
[server]
|
||||
listen_addr = ":8443"
|
||||
tls_cert = "/data/certs/server.crt"
|
||||
tls_key = "/data/certs/server.key"
|
||||
|
||||
[database]
|
||||
path = "/data/metacrypt.db"
|
||||
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
# ca_cert = "/data/certs/mcias-ca.crt"
|
||||
|
||||
[seal]
|
||||
# argon2_time = 3
|
||||
# argon2_memory = 131072
|
||||
# argon2_threads = 4
|
||||
|
||||
[log]
|
||||
level = "info"
|
||||
38
deploy/examples/metacrypt.toml
Normal file
38
deploy/examples/metacrypt.toml
Normal file
@@ -0,0 +1,38 @@
|
||||
# Metacrypt production configuration
|
||||
# Copy to /etc/metacrypt/metacrypt.toml and adjust for your environment.
|
||||
|
||||
[server]
|
||||
# Address to listen on. Use "0.0.0.0:8443" to listen on all interfaces.
|
||||
listen_addr = ":8443"
|
||||
|
||||
# TLS certificate and key. Metacrypt always terminates TLS.
|
||||
tls_cert = "/etc/metacrypt/certs/server.crt"
|
||||
tls_key = "/etc/metacrypt/certs/server.key"
|
||||
|
||||
[database]
|
||||
# SQLite database path. Created automatically on first run.
|
||||
# The directory must be writable by the metacrypt user.
|
||||
path = "/var/lib/metacrypt/metacrypt.db"
|
||||
|
||||
[mcias]
|
||||
# MCIAS server URL for authentication.
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
|
||||
# CA certificate for verifying the MCIAS server's TLS certificate.
|
||||
# Omit if MCIAS uses a publicly trusted certificate.
|
||||
# ca_cert = "/etc/metacrypt/certs/mcias-ca.crt"
|
||||
|
||||
[seal]
|
||||
# Argon2id parameters for key derivation.
|
||||
# These are applied during initialization and stored alongside the encrypted
|
||||
# master key. Changing them here after init has no effect.
|
||||
#
|
||||
# Defaults are tuned for server hardware (3 iterations, 128 MiB, 4 threads).
|
||||
# Increase argon2_memory on machines with more RAM for stronger protection.
|
||||
# argon2_time = 3
|
||||
# argon2_memory = 131072 # KiB (128 MiB)
|
||||
# argon2_threads = 4
|
||||
|
||||
[log]
|
||||
# Log level: debug, info, warn, error
|
||||
level = "info"
|
||||
23
deploy/scripts/backup.sh
Executable file
23
deploy/scripts/backup.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Create a timestamped Metacrypt database backup and prune old ones.
|
||||
#
|
||||
# Usage: ./backup.sh [retention_days]
|
||||
# retention_days: number of days to keep backups (default: 30)
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
CONFIG="${METACRYPT_CONFIG:-/etc/metacrypt/metacrypt.toml}"
|
||||
BACKUP_DIR="${METACRYPT_BACKUP_DIR:-/var/lib/metacrypt/backups}"
|
||||
RETENTION_DAYS="${1:-30}"
|
||||
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
BACKUP_FILE="${BACKUP_DIR}/metacrypt-${TIMESTAMP}.db"
|
||||
|
||||
echo "==> Creating backup: ${BACKUP_FILE}"
|
||||
metacrypt snapshot --config "$CONFIG" --output "$BACKUP_FILE"
|
||||
|
||||
echo "==> Pruning backups older than ${RETENTION_DAYS} days"
|
||||
find "$BACKUP_DIR" -name 'metacrypt-*.db' -mtime "+${RETENTION_DAYS}" -delete -print
|
||||
|
||||
echo "==> Done"
|
||||
ls -lh "$BACKUP_DIR"/metacrypt-*.db 2>/dev/null | tail -5
|
||||
56
deploy/scripts/install.sh
Executable file
56
deploy/scripts/install.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install Metacrypt on a systemd-based Linux system.
|
||||
#
|
||||
# Usage: sudo ./install.sh /path/to/metacrypt
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
BINARY="${1:?Usage: $0 /path/to/metacrypt}"
|
||||
INSTALL_DIR="/usr/local/bin"
|
||||
CONFIG_DIR="/etc/metacrypt"
|
||||
DATA_DIR="/var/lib/metacrypt"
|
||||
BACKUP_DIR="${DATA_DIR}/backups"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
DEPLOY_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
echo "==> Creating metacrypt user and group"
|
||||
if ! getent group metacrypt >/dev/null 2>&1; then
|
||||
groupadd --system metacrypt
|
||||
fi
|
||||
if ! getent passwd metacrypt >/dev/null 2>&1; then
|
||||
useradd --system --gid metacrypt --home-dir "$DATA_DIR" --shell /usr/sbin/nologin metacrypt
|
||||
fi
|
||||
|
||||
echo "==> Installing binary"
|
||||
install -m 0755 "$BINARY" "$INSTALL_DIR/metacrypt"
|
||||
|
||||
echo "==> Creating directories"
|
||||
install -d -m 0750 -o metacrypt -g metacrypt "$CONFIG_DIR"
|
||||
install -d -m 0750 -o metacrypt -g metacrypt "$CONFIG_DIR/certs"
|
||||
install -d -m 0700 -o metacrypt -g metacrypt "$DATA_DIR"
|
||||
install -d -m 0700 -o metacrypt -g metacrypt "$BACKUP_DIR"
|
||||
|
||||
echo "==> Installing configuration"
|
||||
if [ ! -f "$CONFIG_DIR/metacrypt.toml" ]; then
|
||||
install -m 0640 -o metacrypt -g metacrypt "$DEPLOY_DIR/examples/metacrypt.toml" "$CONFIG_DIR/metacrypt.toml"
|
||||
echo " Installed default config to $CONFIG_DIR/metacrypt.toml"
|
||||
echo " >>> Edit this file before starting the service <<<"
|
||||
else
|
||||
echo " Config already exists at $CONFIG_DIR/metacrypt.toml — skipping"
|
||||
fi
|
||||
|
||||
echo "==> Installing systemd units"
|
||||
install -m 0644 "$DEPLOY_DIR/systemd/metacrypt.service" /etc/systemd/system/
|
||||
install -m 0644 "$DEPLOY_DIR/systemd/metacrypt-backup.service" /etc/systemd/system/
|
||||
install -m 0644 "$DEPLOY_DIR/systemd/metacrypt-backup.timer" /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
|
||||
echo "==> Done"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Place TLS cert and key in $CONFIG_DIR/certs/"
|
||||
echo " 2. Edit $CONFIG_DIR/metacrypt.toml"
|
||||
echo " 3. Initialize: metacrypt init --config $CONFIG_DIR/metacrypt.toml"
|
||||
echo " 4. Start: systemctl enable --now metacrypt"
|
||||
echo " 5. Backups: systemctl enable --now metacrypt-backup.timer"
|
||||
15
deploy/systemd/metacrypt-backup.service
Normal file
15
deploy/systemd/metacrypt-backup.service
Normal file
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Metacrypt database backup
|
||||
After=metacrypt.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=metacrypt
|
||||
Group=metacrypt
|
||||
ExecStart=/usr/local/bin/metacrypt snapshot --config /etc/metacrypt/metacrypt.toml --output /var/lib/metacrypt/backups/metacrypt-%i.db
|
||||
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/metacrypt
|
||||
10
deploy/systemd/metacrypt-backup.timer
Normal file
10
deploy/systemd/metacrypt-backup.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Daily Metacrypt database backup
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 02:00:00
|
||||
Persistent=true
|
||||
RandomizedDelaySec=300
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
45
deploy/systemd/metacrypt.service
Normal file
45
deploy/systemd/metacrypt.service
Normal file
@@ -0,0 +1,45 @@
|
||||
[Unit]
|
||||
Description=Metacrypt cryptographic service
|
||||
Documentation=https://git.wntrmute.dev/kyle/metacrypt
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=metacrypt
|
||||
Group=metacrypt
|
||||
|
||||
ExecStart=/usr/local/bin/metacrypt server --config /etc/metacrypt/metacrypt.toml
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictSUIDSGID=true
|
||||
RestrictNamespaces=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
RestrictRealtime=true
|
||||
|
||||
# Allow write access to the database directory and log
|
||||
ReadWritePaths=/var/lib/metacrypt
|
||||
|
||||
# Limit file descriptor count
|
||||
LimitNOFILE=65535
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=metacrypt
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user