#!/usr/bin/env bash # Rebuild and run the personal data server in the foreground. # # This terminal shows registrations and lifecycle events. # # Foreground on purpose. The log is the point, and a backgrounded process # writing to a file is a log nobody is looking at. set -euo pipefail cd "$(dirname "$0")/.." # The port, the zone and the state directory. See dev-profile.sh. . "$(dirname "$0")/dev-profile.sh" . "$(dirname "$0")/dev-watch.sh" . "$(dirname "$0")/dev-pidfile.sh" . "$(dirname "$0")/dev-build.sh" # Before anything else reads them, so `--no-test` reaches neither the watch # loop nor the server. read_build_flags "$@" set -- "${DIDBOT_ARGV[@]}" PORT="$DIDBOT_PDS_PORT" ZONE="$DIDBOT_ZONE" # Agents get names here, because the dev stack is where anyone finds out what # the feature does. `NAMES=hostname ./scripts/dev-pds.sh` turns it off; any # spec works, so `NAMES=cybernetic+chaos` changes what the swarm sounds like. NAMES="${NAMES:-mineral+creature}" # The stack is restarted many times a day, and every restart of a server with # nothing on disk takes the accounts of whatever was talking to it — an agent # that signed in this morning is signed in to a server that no longer knows it. # So it keeps its state by default, per user rather than per checkout: the # agents on this machine address one server on one port, and a second checkout # on that port is refused by `pds.lock` before it can write. Empty is how a run # asks for a clean slate; dev-profile.sh says why the default is spelled so # that empty stays empty. DATA="$DIDBOT_PDS_DATA" say() { printf '\033[1m%s\033[0m\n' "$*"; } # Watching a server with nothing to keep is not like watching the others. # # The swarm holds nothing: restarting it costs the build and nothing else. This # holds every agent account, every record and every name hold, and a run that # asked for a store in memory rebuilds all of it on every save — so a watch # there factory-resets the deployment each time a file moves, and the first # symptom is sessions whose accounts have silently stopped existing. # # It only reaches this with `DIDBOT_PDS_DATA=` above, so the refusal is for a # combination somebody typed rather than for a default. if [ -z "$DATA" ] && [ "${DIDBOT_WATCH_EPHEMERAL:-0}" != "1" ]; then for arg in "$@"; do [ "$arg" = "--watch" ] || continue say "refusing to watch a server with nothing to keep" echo " DIDBOT_PDS_DATA is empty, so this server is in memory and every restart" echo " is a factory reset: every account, record and name hold goes, and live" echo " sessions find their accounts gone." echo echo " ./scripts/dev-pds.sh --watch # keep state, the default" echo " DIDBOT_WATCH_EPHEMERAL=1 ./scripts/dev-pds.sh --watch # if a clean" echo " # slate each time is the point" exit 1 done fi # `--watch` reruns this script whenever the source moves. It never returns. watch_or_continue "$@" # A previous run holding the port makes the new one fail with "address already # in use", which reads as a port conflict with something else entirely. By # pidfile rather than by pattern: see dev-pidfile.sh for what a pattern matches # that it should not. claim_port pds "${PORT}" didbot-pds build_before_running -p didbot-pds -p didbot-serve # The server is run straight out of the target directory. Nothing # invokes it by name, so installing it would only add a second full build and a # second copy to go stale. say "personal data server on port ${PORT}, zone ${ZONE}, names ${NAMES}" echo " stats curl -s http://localhost:${PORT}/xrpc/bot.did.stats" echo DATA_ARGS=() if [[ -n "$DATA" ]]; then DATA_ARGS=(--data "$DATA") echo " data ${DATA} (accounts and records survive a restart)" else echo " data in memory (DIDBOT_PDS_DATA is empty; a restart is a factory reset)" fi exec cargo run --quiet -p didbot-serve --bin didbot-pds -- \ --port "$PORT" --zone "$ZONE" --names "$NAMES" "${DATA_ARGS[@]}" "$@"