Files
archy/tests/apps/podsteadr/run-disposable.sh

133 lines
6.5 KiB
Bash

#!/usr/bin/env bash
# Actual packaged HTTP lifecycle; synthetic-only isolated storage and identities.
set -euo pipefail
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)
image=${PODSTEADR_FIXTURE_IMAGE:?Immutable local candidate image ID required}
[[ "$image" =~ ^(sha256:)?[a-f0-9]{64}$ ]] || exit 2
bootstrap=${PODSTEADR_KEY_BOOTSTRAP:-0}
[[ "$bootstrap" == 0 || "$bootstrap" == 1 ]] || exit 2
name="archy-podsteadr-contract-$$"
volume="$name-data"
work=$(mktemp -d)
cleanup() {
result=$?; trap - EXIT
for container in "$name" "$name-key" "$name-invalid" "$name-bootstrap"; do
if podman container exists "$container"; then
timeout 15 podman rm -f --time 0 "$container" >/dev/null 2>&1 || true
fi
if podman container exists "$container"; then result=1; fi
done
podman volume rm -f "$volume" >/dev/null 2>&1 || true
if podman volume exists "$volume"; then result=1; fi
rm -rf -- "$work"
exit "$result"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
[[ $(podman image inspect "$image" --format '{{.Config.User}}') == 1000:1000 ]]
common=(--pull=never --network=none --pid=private --ipc=private --http-proxy=false
--read-only --cap-drop=all --security-opt=no-new-privileges
--cpus=1 --memory=512m --memory-swap=512m --pids-limit=128 --timeout=180
--tmpfs /tmp:rw,notmpcopyup,nosuid,nodev,size=32m)
keys=$(podman run --rm --name "$name-key" "${common[@]}" --entrypoint node "$image" --input-type=module -e \
'import {getPublicKey} from "nostr-tools/pure";console.log([1,3].map(n=>getPublicKey(new Uint8Array(32).fill(n))).join(" "))')
read -r original replacement <<< "$keys"
[[ "$original" =~ ^[a-f0-9]{64}$ && "$replacement" =~ ^[a-f0-9]{64}$ ]]
podman volume create "$volume" >/dev/null
expected_server_key=''
if [[ "$bootstrap" == 1 ]]; then
# Only this owned synthetic volume is initialized; no live bind or secret export.
podman run --rm --name "$name-bootstrap" "${common[@]}" \
--volume "$volume:/data:U" --entrypoint node "$image" \
dist/provision-server-key.js /data > "$work/bootstrap.json"
podman run --rm --name "$name-bootstrap" "${common[@]}" \
--volume "$volume:/data:ro" --entrypoint node "$image" \
dist/provision-server-key.js /data > "$work/bootstrap-repeat.json"
cmp "$work/bootstrap.json" "$work/bootstrap-repeat.json"
expected_server_key=$(python3 - "$work/bootstrap.json" <<'PY'
import json,re,sys
value=json.load(open(sys.argv[1]))
assert set(value)=={'version','publicKey'} and value['version']==1
assert re.fullmatch('[a-f0-9]{64}',value['publicKey'])
print(value['publicKey'])
PY
)
fi
start() {
podman run -d --name "$name" "${common[@]}" \
--volume "$volume:/data:U" \
--volume "$repo/tests/apps/podsteadr/protocol.mjs:/app/qualification-protocol.mjs:ro" \
--env "PODSTEADR_ADMIN_PUBKEYS=$1" --env PUBLIC_URL=http://127.0.0.1:8095 \
--env NOSTR_RELAYS= --env CASHU_MINT_URL_DEFAULT=http://127.0.0.1:9996 \
--env MEDIAMTX_API_URL=http://127.0.0.1:9997 \
--env BLOSSOM_URL_DEFAULT=http://127.0.0.1:8098 "$image" >/dev/null
for _ in $(seq 1 30); do
if timeout 3 podman exec "$name" node -e 'fetch("http://127.0.0.1:8095/api/health").then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))' >/dev/null 2>&1; then
podman healthcheck run "$name" >/dev/null
if [[ -n "$expected_server_key" ]]; then
podman exec --env "EXPECTED_KEY=$expected_server_key" "$name" node --input-type=module -e '
const response=await fetch("http://127.0.0.1:8095/api/settings/public");
if(!response.ok || (await response.json()).serverPubkey!==process.env.EXPECTED_KEY) process.exit(1);
'
fi
return
fi
sleep 0.2
done
podman logs "$name" >&2
return 1
}
start "$original"
timeout 45 podman exec --env PODSTEADR_FIXTURE_PHASE=seed "$name" node /app/qualification-protocol.mjs
podman stop --time 5 "$name" >/dev/null
[[ $(podman inspect "$name" --format '{{.State.ExitCode}}') == 0 ]] || {
echo 'Packaged server did not close cleanly on SIGTERM' >&2; exit 1;
}
podman rm "$name" >/dev/null
start "$replacement"
timeout 45 podman exec --env PODSTEADR_FIXTURE_PHASE=verify-preserved "$name" node /app/qualification-protocol.mjs
# Also qualify abrupt process loss, without deleting or repairing the database.
podman kill --signal KILL "$name" >/dev/null
podman rm "$name" >/dev/null
start "$replacement"
# The final verify phase deliberately logs out the saved cookie. Defer that
# logout until after the additional missing-key/restoration check in this mode.
final_phase=verify
if [[ "$bootstrap" == 1 ]]; then final_phase=verify-preserved; fi
timeout 45 podman exec --env "PODSTEADR_FIXTURE_PHASE=$final_phase" "$name" node /app/qualification-protocol.mjs
if [[ "$bootstrap" == 1 ]]; then
podman stop --time 5 "$name" >/dev/null
[[ $(podman inspect "$name" --format '{{.State.ExitCode}}') == 0 ]]
podman rm "$name" >/dev/null
for phase in hold verify-restore; do
if [[ "$phase" == verify-restore ]]; then
if timeout 15 podman run --rm --name "$name-invalid" "${common[@]}" \
--volume "$volume:/data" --env "PODSTEADR_ADMIN_PUBKEYS=$replacement" \
--env PUBLIC_URL=http://127.0.0.1:8095 --env NOSTR_RELAYS= "$image" > "$work/key-loss.log" 2>&1; then
echo 'Missing established key unexpectedly started or regenerated' >&2; exit 1
else
status=$?
[[ "$status" == 1 ]] || { echo 'Missing-key check did not prove application refusal' >&2; exit 1; }
grep -q 'Server identity unavailable' "$work/key-loss.log"
fi
fi
podman run --rm --name "$name-bootstrap" "${common[@]}" \
--volume "$volume:/data" \
--volume "$repo/tests/apps/podsteadr/key-loss.mjs:/app/qualification-key-loss.mjs:ro" \
--entrypoint node "$image" /app/qualification-key-loss.mjs "$phase"
done
start "$replacement"
timeout 45 podman exec --env PODSTEADR_FIXTURE_PHASE=verify "$name" node /app/qualification-protocol.mjs
fi
# No default administrator binding may silently become first-login admin.
if timeout 15 podman run --rm --name "$name-invalid" "${common[@]}" "$image" >"$work/invalid.log" 2>&1; then
echo 'Missing identity binding unexpectedly started' >&2; exit 1
else
status=$?
[[ "$status" == 1 ]] || { echo 'Invalid-binding check did not prove application refusal' >&2; exit 1; }
grep -q 'PODSTEADR_ADMIN_PUBKEYS' "$work/invalid.log"
fi
if [[ "$bootstrap" == 1 ]]; then echo 'PASS key-only bootstrap, readonly repeat and same public identity across normal/crash recreation'; fi
echo 'PASS packaged HTTP recreation/preservation; not manager install/update/uninstall, actual media, financial or provenance acceptance.'