Files
Paul HittandClaude Opus 5.5 0cb2d5dd2e Bridge inventory: obfs4_bridges table, flamenet-relay bridges, Scripts/bridges.sh
Until now a bridge line existed in one place, the bridge container's
stdout. Doc 27 Milestone 1 needs a list: what is handed out, since when,
and what is retired.

- obfs4_bridges: host, port, fingerprint (40-hex obfs4 node ID, unique),
  line, added_at, retired_at. Additive migration, appended last.
- The line is derived from the bridge's own state file, as the bridge
  prints it. The private key is read from stdin, used for the X25519
  public key, and dropped; no row holds it. Checked against RFC 7748 here,
  and against the live bridge on the box (derived cert == printed cert).
- A retired identity is never listed again.
- No route reads the table. flamenet-relay bridges list|add|retire is
  dispatched in main BEFORE configure, so it opens the database and does
  not migrate, sweep, drain or boot lifecycle handlers beside the live
  relay. Scripts/bridges.sh wraps it on the box, like suspend.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:43:22 -04:00

69 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# The obfs4 bridge inventory: which bridges this operator runs, since when,
# and which are retired. One row per bridge identity, in the relay's database
# (table obfs4_bridges). No route reads it; this script is the way in.
#
# Scripts/bridges.sh list [--all] bridges in service (--all: retired ones too)
# Scripts/bridges.sh add <host> <port> [state] list a bridge (state: this box's bridge by default)
# Scripts/bridges.sh retire <fingerprint> take one out of service; the row stays
#
# Run on the box with sudo, like suspend.sh.
#
# `add` derives the line from the bridge's own state file, the same file the
# bridge reads at start ({"nodeID": ..., "privateKey": ...}). The file goes to
# the relay container on stdin; the relay computes the public key from it and
# keeps only the public parts. Nothing writes the private key anywhere. For a
# bridge on another machine, pipe its state file in and pass `-`:
#
# ssh bridge2 sudo cat /var/lib/obfs4/state.json | sudo Scripts/bridges.sh add 203.0.113.9 443 -
#
# `<host>` is the address clients dial, not the container's. `<port>` is the
# published port (8443 for the bridge beside this relay).
#
# `list` prints bridge lines. Each one is what a client is given: hand them
# out one at a time, and never paste the list into a ticket, a chat or a doc.
# A retired identity is never listed again; give a burned bridge a new state
# file rather than reusing the old one.
set -euo pipefail
cd /srv/prod/messenger
relay() { docker compose exec -T relay flamenet-relay bridges "$@"; }
# Where this box's bridge keeps its identity.
local_state() {
local mp
mp="$(docker volume inspect messenger_obfs4-state --format '{{.Mountpoint}}')"
printf '%s/state.json' "$mp"
}
usage() {
sed -n '6,8p' "$0" | sed 's/^# *//' >&2
exit 64
}
case "${1:-}" in
list)
shift
relay list "$@"
;;
add)
host="${2:-}"; port="${3:-}"
[ -n "$host" ] && [ -n "$port" ] || usage
src="${4:-$(local_state)}"
if [ "$src" = "-" ]; then
relay add --host "$host" --port "$port" --state -
else
[ -r "$src" ] || { echo "cannot read $src (run with sudo)" >&2; exit 66; }
relay add --host "$host" --port "$port" --state - < "$src"
fi
;;
retire)
[ -n "${2:-}" ] || usage
relay retire "$2"
;;
*)
usage
;;
esac