Files
AgapHost/kanboard/backup.sh
alvis a27bae828a kb: batch from 2026-07-30 parallel run (#181 #183 #189 #192 #164 #128 #219)
Work produced by the /kb driver on 2026-07-30. Each change is recorded on its
Kanboard task; all remain Done-unverified or parked pending alvis's decisions.

#183 agap-mcp/src/gitea.js
  askpassScript() and giteaWikiWrite()'s wiki checkout both used
  /tmp/agap-mcp-wiki, so writing the askpass helper made the dir non-empty and
  git clone always failed. gitea_wiki_write had likely never succeeded in
  production. Askpass moved to its own dir.

#181 agap-mcp/src/server.js
  Initialise registeredToolCount at module load so /health reports the real
  count immediately instead of 0 until the first MCP request.

#189 kanboard/backup.sh, seafile/backup.sh, vaultwarden/backup.sh,
     users-backup.sh, openai/backup-{hindsight-adolf,llm-dbs}.sh
  Remove the dead *.ts Zabbix trapper pushes (never landed). users-backup.sh
  also pointed at localhost:81 instead of 192.168.1.4:81 and pushed a date
  string into a numeric item. Freshness monitoring now rides the .age items.

#192 RESTORE-RUNBOOK.md, {kanboard,seafile,vaultwarden}/restore.sh
  Restore path for the three services, verified in throwaway containers.
  Note: this work found Seafile backups have carried an empty ccnet_db.sql
  since 2026-07-07 -- filed as kb#222, not fixed here.

#164 openai/litellm-config.yaml
  Metered `judge` (anthropic/claude-haiku-4-5) entry removed per alvis's
  2026-07-30 decision. ANTHROPIC_API_KEY was never wired, so it could not spend.

#128 openai/agent_registry.py
  litellm_key_spec() now also grants the routing-mode aliases, gated by the
  same _reachable_tiers() check as raw grants, so a small-tier agent cannot
  acquire automatic routing that resolves to tier-large.

#219 openai/migrate-adolf-state.sh
  Migration script only; inert until run. Copies (never moves) the
  openai_adolf-state volume to /mnt/ssd/dbs/adolf, verifying a full sha256
  manifest before declaring success. Tested against a throwaway volume.

Deliberately NOT included, both awaiting alvis:
  agap-mcp/docker-compose.yml -- kb#174's contested BW_EMAIL revert (parked).
  openai/docker-compose.yml   -- kb#219's bind-mount switch; the target dirs
                                 under /mnt/ssd/dbs/adolf do not exist yet, so
                                 committing it would let a later `compose up`
                                 recreate Adolf against empty paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y5QPagv4iun1ghpwM96Ff
2026-07-30 15:06:09 +00:00

54 lines
2.6 KiB
Bash
Executable File

#!/bin/bash
# Kanboard backup — tier-0 hardening (kb#158, A2A-26, DESIGN-a2a-agents.md v2.1 §6c).
# Mirrors the vaultwarden backup.sh pattern (same repo, ~/agap_git/vaultwarden/backup.sh):
# scheduled dump -> /mnt/backups, retention of last 5. Backup-freshness monitored via .age items.
#
# Runs every 3 days via alvis's user crontab (NOT root crontab like vaultwarden's --
# /mnt/backups/kanboard was bootstrapped chown'd to alvis specifically so this backup,
# like the rest of the kanboard tooling, needs no root/sudo at all. alvis is in the
# `docker` group so `docker exec`/`docker cp` need no privilege escalation either).
#
# DB dump method: kanboard's container has no sqlite3 CLI and no PHP `sqlite3`
# extension (only pdo_sqlite) -- checked directly (kb#158). Instead we run SQLite's
# own `VACUUM INTO` via PDO, which is SQLite's supported way to take an atomic,
# consistent online snapshot of a live database (safe against concurrent writers,
# same safety property `vaultwarden backup` gives us for that service).
set -euo pipefail
BACKUP_DIR="/mnt/backups/kanboard"
DATE=$(date '+%Y%m%d-%H%M')
DEST="$BACKUP_DIR/$DATE"
TMP_NAME="backup_${DATE}.sqlite"
mkdir -p "$DEST"
# Online, consistent snapshot via SQLite's VACUUM INTO (PDO sqlite driver is present
# in the image; the sqlite3 CLI/extension is not, so this replaces the vaultwarden
# `docker exec vaultwarden /vaultwarden backup` equivalent for this service).
docker exec kanboard php -r '
$db = new PDO("sqlite:/var/www/app/data/db.sqlite");
$db->exec("VACUUM INTO \"/var/www/app/data/'"$TMP_NAME"'\"");
'
# Pull the snapshot out to the host, then remove the temp copy from the live data dir
# (mirrors vaultwarden's "move the file out of DATA_DIR" step).
docker cp "kanboard:/var/www/app/data/$TMP_NAME" "$DEST/db.sqlite"
docker exec kanboard rm -f "/var/www/app/data/$TMP_NAME"
# Plugins volume (PLUGIN_INSTALLER=true means plugins can be installed at runtime,
# not just baked into the image) -- back it up too so a restore doesn't silently
# drop installed plugins.
docker run --rm --user 1000:1000 -v kanboard_plugins:/plugins:ro -v "$DEST":/dest alpine \
sh -c 'cd /plugins && tar -czf /dest/plugins.tar.gz . 2>/dev/null || true'
echo "$(date): Backup complete: $DEST"
ls -la "$DEST/"
# Backup-freshness monitoring is now done via .age items (calculated fields showing
# age of the backup). The .ts (timestamp) trappers were unreliable (history.push not
# landing); removed in kb#189 in favor of .age overdue triggers.
# Rotate: keep last 5 backups
ls -1dt "$BACKUP_DIR"/[0-9]*-[0-9]* 2>/dev/null | tail -n +6 | xargs -r rm -rf