#!/usr/bin/env bash # Build maven-evidence.zip: the architecture package plus the source seams a # reviewer needs to test its claims, and nothing else. # # sh docs/architecture/pack_evidence.sh # # Three rules this script exists to enforce: # # 1. Whole files, never snippets. A cut-down file loses the call path that # makes a claim checkable, which is the whole point of sending source. # 2. Allowlist, not denylist. Paths are named one by one below. A denylist # ships whatever nobody thought to exclude, and this tree has a database # key in it. # 3. Refuse rather than warn. The scan at the end aborts on a hit instead of # printing something a tired person scrolls past. # # The one file that is not verbatim is docker-compose.yml. It carries a live # uptime-kuma API key, so a redacted copy goes in its place and the redaction is # recorded in architecture-evidence.txt and printed here. set -euo pipefail here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) root=$(CDPATH= cd -- "$here/../.." && pwd) cd "$root" out=maven-evidence.zip stage=$(mktemp -d) trap 'rm -rf "$stage"' EXIT echo "== regenerating the architecture package" python3 "$here/build_inventory.py" python3 "$here/verify_anchors.py" # exits 1 if any claim no longer resolves python3 "$here/build_evidence.py" python3 "$here/build_viewer.py" echo "== structural context" # `tree` here is an eza alias in the owner's shell and absent in a plain sh, so # the listing is generated with find and does not depend on either. { echo "# find -L internal cmd -maxdepth 3 -type d" echo find internal cmd -maxdepth 3 -type d | sort echo echo "# go files per package" echo find internal cmd -name '*.go' ! -name '*_test.go' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn echo echo "# test files per package" echo find internal cmd -name '*_test.go' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn } > "$here/tree.txt" echo "== redacting the one credential in docker-compose.yml" sed 's/"uk5_[^"]*"/""/' docker-compose.yml \ > "$here/docker-compose.redacted.yml" if grep -q 'uk5_' "$here/docker-compose.redacted.yml"; then echo "pack_evidence.sh: redaction failed, refusing to build" >&2; exit 1 fi diff <(sed 's/"uk5_[^"]*"/X/' docker-compose.yml) \ <(sed 's/""/X/' "$here/docker-compose.redacted.yml") \ >/dev/null || { echo "pack_evidence.sh: redacted copy differs by more than the key" >&2; exit 1; } # ---- the allowlist ------------------------------------------------------- # Requested and present. internal/session, internal/db and tests/ are absent # from this repo; architecture-evidence.txt says where their contents live. paths=( docs/architecture CLAUDE.md docs/CLAUDE.md go.mod deploy/mavend.json # ${VAR} placeholders only; 16 off-claims read it cmd/mavend internal/auth internal/tool internal/claim internal/modes internal/router internal/voice internal/ipc # the boundary auth.Can actually runs on internal/store internal/dialogue # clarify + session state the turn path parks in internal/decision # the arbitration record internal/delivery/channel.go internal/loop internal/webauthn # the other half of the auth story cmd/mavwaked/main.go # the client that sends Surface cmd/mavweb/main.go # the six unguarded surfaces ) echo "== staging" for p in "${paths[@]}"; do if [ ! -e "$p" ]; then echo " MISSING $p (skipped)"; continue; fi mkdir -p "$stage/$(dirname "$p")" cp -r "$p" "$stage/$(dirname "$p")/" done # Generated-in-place files that must not travel, and anything that is a secret, # a model, a database or a build artefact regardless of how it got staged. # # The name filters skip .go on purpose: internal/router/singletoken.go matched # '*token*' and was deleted out of the first build of this pack. That is exactly # the silent hole an allowlist exists to prevent, and a Go source file is never # the thing this clause is for. find "$stage" ! -name '*.go' \( \ -name '*.db' -o -name '*.sqlite*' -o -name '*.enc' \ -o -name '*.pem' -o -name '*.key' -o -name '*.crt' -o -name '*.p12' \ -o -name '.env*' -o -name '*.token' -o -name '*.secret' -o -name '*.password' \ -o -name '*.onnx' -o -name '*.gguf' -o -name '*.bin' -o -name '*.wav' \ -o -name '*.zip' -o -name '*.log' -o -name '.git' \ \) -print -exec rm -rf {} + 2>/dev/null || true echo "== scanning the staged tree" # A value-shaped assignment: a credential word, a delimiter, then twelve or more # characters of value. The value must NOT begin with a slash or a dot, because a # docker volume line pairs a host path with a container path and both halves end # in the same secret-sounding filename while containing no secret. Three of those # in docker-compose.yml tripped the first version of this scan. hits=$(grep -rInE '(api[_-]?key|secret|passwo?r?d|bearer|token)["'"'"' ]*[:=]["'"'"' ]*[A-Za-z0-9+_-][A-Za-z0-9/+_-]{11,}' "$stage" \ | grep -vE '\$\{|&2 echo "$hits" >&2 exit 1 fi echo "== building $out" rm -f "$out" ( cd "$stage" && zip -qr "$root/$out" . ) echo printf '%s %s %s files\n' "$out" \ "$(du -h "$out" 2>/dev/null | cut -f1)" \ "$(unzip -l "$out" | tail -1 | awk '{print $2}')" echo echo "redacted: docker-compose.yml -> docs/architecture/docker-compose.redacted.yml" echo " one uptime-kuma api key, nothing else" echo "excluded: .git, deploy/telegram.env, deploy/db_key.env, models, deps, databases"