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
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/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, Zabbix freshness trapper.
|
||||
# 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,
|
||||
@@ -17,9 +17,6 @@
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="/mnt/backups/kanboard"
|
||||
ZABBIX_TOKEN_FILE="/home/alvis/.zabbix_token"
|
||||
ZABBIX_URL="http://192.168.1.4:81/api_jsonrpc.php"
|
||||
ZABBIX_ITEM_ID="70605" # kanboard.backup.ts on host AgapHost (10776)
|
||||
|
||||
DATE=$(date '+%Y%m%d-%H%M')
|
||||
DEST="$BACKUP_DIR/$DATE"
|
||||
@@ -48,22 +45,9 @@ docker run --rm --user 1000:1000 -v kanboard_plugins:/plugins:ro -v "$DEST":/des
|
||||
|
||||
echo "$(date): Backup complete: $DEST"
|
||||
ls -la "$DEST/"
|
||||
|
||||
# Notify Zabbix (trapper item kanboard.backup.ts, unixtime) -- pushes a real epoch
|
||||
# timestamp, unlike vaultwarden.backup.ts which (kb#158 finding) pushes a formatted
|
||||
# date STRING into a numeric item and has therefore never recorded a valid value.
|
||||
if [[ -f "$ZABBIX_TOKEN_FILE" ]]; then
|
||||
ZABBIX_TOKEN=$(cat "$ZABBIX_TOKEN_FILE")
|
||||
NOW_EPOCH=$(date '+%s')
|
||||
env -u HTTPS_PROXY -u HTTP_PROXY -u ALL_PROXY -u https_proxy -u http_proxy -u all_proxy \
|
||||
curl -s -X POST "$ZABBIX_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $ZABBIX_TOKEN" \
|
||||
-d "{\"jsonrpc\":\"2.0\",\"method\":\"history.push\",\"id\":1,\"params\":{\"itemid\":\"$ZABBIX_ITEM_ID\",\"value\":$NOW_EPOCH}}" > /dev/null \
|
||||
&& echo "Zabbix notified (kanboard.backup.ts=$NOW_EPOCH)."
|
||||
else
|
||||
echo "WARNING: $ZABBIX_TOKEN_FILE not found -- skipped Zabbix freshness push." >&2
|
||||
fi
|
||||
# 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
|
||||
|
||||
82
kanboard/restore.sh
Executable file
82
kanboard/restore.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Kanboard restore — companion to backup.sh (kb#192).
|
||||
#
|
||||
# Restores a snapshot produced by backup.sh (db.sqlite + optional plugins.tar.gz)
|
||||
# into a running Kanboard container. Defaults to the live "kanboard" container/
|
||||
# volumes, but every target is overridable via env vars so the exact same script
|
||||
# can be pointed at a disposable/throwaway container for a dry-run restore test
|
||||
# (see kb#192 runbook for the recommended throwaway-container recipe).
|
||||
#
|
||||
# Usage:
|
||||
# ./restore.sh /mnt/backups/kanboard/<snapshot-dir>
|
||||
#
|
||||
# Env overrides (defaults = live service):
|
||||
# CONTAINER=kanboard # target container name
|
||||
# DATA_PATH=/var/www/app/data # data dir inside the container
|
||||
# PLUGINS_PATH=/var/www/app/plugins # plugins dir inside the container
|
||||
#
|
||||
# WARNING: this overwrites the target container's live database. Never run
|
||||
# against the "kanboard" container name unless you intend a real disaster
|
||||
# recovery — for testing, point CONTAINER at a throwaway container instead.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CONTAINER="${CONTAINER:-kanboard}"
|
||||
DATA_PATH="${DATA_PATH:-/var/www/app/data}"
|
||||
PLUGINS_PATH="${PLUGINS_PATH:-/var/www/app/plugins}"
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <path-to-backup-snapshot-dir>" >&2
|
||||
echo " e.g. $0 /mnt/backups/kanboard/20260728-0300" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SRC="$(realpath "$1")"
|
||||
DB_FILE="$SRC/db.sqlite"
|
||||
PLUGINS_FILE="$SRC/plugins.tar.gz"
|
||||
|
||||
if [ ! -f "$DB_FILE" ]; then
|
||||
echo "Error: $DB_FILE not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker inspect "$CONTAINER" > /dev/null 2>&1; then
|
||||
echo "Error: container '$CONTAINER' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Restoring into container '$CONTAINER' from $SRC"
|
||||
|
||||
# Stop the app so the sqlite file isn't being written to concurrently.
|
||||
docker stop "$CONTAINER" > /dev/null
|
||||
|
||||
# Replace the database file.
|
||||
docker cp "$DB_FILE" "$CONTAINER:$DATA_PATH/db.sqlite"
|
||||
|
||||
# Restore plugins, if present in the snapshot.
|
||||
if [ -f "$PLUGINS_FILE" ]; then
|
||||
docker cp "$PLUGINS_FILE" "$CONTAINER:/tmp/plugins.tar.gz"
|
||||
docker start "$CONTAINER" > /dev/null
|
||||
# Extract inside the container so ownership matches what the app expects.
|
||||
docker exec "$CONTAINER" sh -c "rm -rf '$PLUGINS_PATH'/* && tar -xzf /tmp/plugins.tar.gz -C '$PLUGINS_PATH' && rm -f /tmp/plugins.tar.gz"
|
||||
else
|
||||
echo "Note: no plugins.tar.gz in snapshot, skipping plugin restore"
|
||||
docker start "$CONTAINER" > /dev/null
|
||||
fi
|
||||
|
||||
echo "Waiting for Kanboard to come up..."
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec "$CONTAINER" php -r 'exit(file_exists("'"$DATA_PATH"'/db.sqlite") ? 0 : 1);' > /dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Verifying restored database..."
|
||||
docker exec "$CONTAINER" php -r '
|
||||
$db = new PDO("sqlite:'"$DATA_PATH"'/db.sqlite");
|
||||
$count = $db->query("SELECT COUNT(*) FROM tasks")->fetchColumn();
|
||||
echo "tasks table row count: $count\n";
|
||||
'
|
||||
|
||||
echo "Restore complete: $CONTAINER"
|
||||
Reference in New Issue
Block a user