Skip to content

State Files

No database, and no Docker named volumes. JSON files in ./config/state, written with write-to-temp plus atomic rename, mutated under an exclusive flock. jq works on all of it, from the host, with no container in the way.

Files

File Contents
subscribers.json The roster, including pending and banned entries.
pending.json Requests held for approval.
room.json The live room. Absent means nothing is up.
runtime.json Pause switch, close request.
.lock The advisory lock both binaries take.

Everything lives in this directory

Every persistent thing the deployment has is a file here. A named volume is invisible to tar, easy to forget, and turns "move to another machine" into an archaeology exercise.

Directory Holds Replaceable?
state/ Roster, quotas, live room, pause switch No. Irreplaceable.
signal-cli/ Signal linked-device identity No, without re-registering
tor-party-line/ Party line checkout + Tor data Yes (git clone)
.cache/, target/ Cargo registry and build artifacts Yes (regenerated on build)

state/ and signal-cli/ are in .gitignore: they travel with the directory, not with the repository.

Container UIDs

The containers run as HOST_UID:HOST_GID from .env, so every file they write belongs to you rather than root. Without those set, the containers run as root and you need sudo to read your own roster. See .env.example.

Moving to another machine

docker compose down
tar czf partylinepager.tar.gz \
    --exclude=./.cache --exclude=./target \
    --exclude=./.git .
# copy to new host, unpack, then:
printf 'HOST_UID=%s\nHOST_GID=%s\nDOCKER_GID=%s\n' \
    "$(id -u)" "$(id -g)" \
    "$(getent group docker | cut -d: -f3)"
# update .env with the output above
docker compose up -d
docker compose exec partylinepagerd partylinepagerctl who

The two excludes are build caches. The .env UIDs are the only per-machine value: a stale one shows up as permission errors on state/.

Nothing else is left behind except rebuildable Docker images.

Identity model

A subscriber is an address on a service. There is no person model and no account linking: telegram:123 and email:partylinepager@example.org are two unrelated subscribers even if the same human owns both. They get two quotas. Policing that is the admin's job. There is no way to verify a human identity across networks, and pretending otherwise would be theatre.