PR-checker container (always-on, NAS) #
Runs the advisory community-PR checker (scripts/pr-check.sh) on a schedule from the NAS. The
Synology host has no tg/node/git, so the toolchain lives in this image; the untrusted PR checks
run in a separate sibling container (spawned via the mounted docker socket) so PR code never runs
alongside the tg credentials.
What it does #
Every run: for each OPEN pull request, apply it onto current main, and either
- comment "needs rebase" if it no longer applies cleanly, or
- run tests + copy-voice in a sandboxed sibling container and comment pass/fail.
One comment per PR round (idempotent via <rkey>:<updatedAt>), so a 30-minute schedule never spams.
Advisory only: Tangled has no required-status-check, so it informs; it cannot block a merge.
One-time setup #
-
Bot account + app-password. Use a DEDICATED account for the comments (recommended: its only power is commenting, so a compromised token is low-value). Create an atproto app-password for it (Bluesky Settings -> App Passwords, or the account's PDS). @gui.do works too, but a bot limits blast radius: the untrusted checks run in a sibling with no creds, but defense in depth is cheap.
-
Store the secret on the NAS, not in the repo. e.g. a root-only env file:
sudo install -m 600 /dev/stdin /volume1/docker/pr-check/env <<'EOF' TG_HANDLE=your-bot.handle TG_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx EOF sudo mkdir -p /volume1/docker/pr-check/work -
Build the image on the NAS (native amd64, no emulation):
cd /volume1/docker/pr-check git clone https://tangled.org/atcommons.eu/website src # or copy docker/pr-check/ over sudo /usr/local/bin/docker build -t atproto-pr-check:latest src/docker/pr-check
Run (dry run first) #
sudo /usr/local/bin/docker run --rm \
--env-file /volume1/docker/pr-check/env \
-e PR_CHECK_DIR=/volume1/docker/pr-check/work \
-e DRY_RUN=1 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /volume1/docker/pr-check:/volume1/docker/pr-check \
atproto-pr-check:latest
DRY_RUN=1 checks + logs but posts NO comments. When it looks right, drop DRY_RUN.
The two volume mounts are load-bearing: the docker socket lets the orchestrator spawn the sandbox
sibling, and bind-mounting /volume1/docker/pr-check at the SAME path (so PR_CHECK_DIR is identical
inside the container and on the host) is what lets the sandbox sibling's -v <checkout>:/app resolve
on the host.
Schedule (every 30 min) #
Add to the NAS scheduler (DSM Task Scheduler or cron) the run command above WITHOUT DRY_RUN. It
locks (flock) so overlapping ticks are safe, and only comments once per PR round.
Security notes #
- The orchestrator holds the tg credentials and the docker socket (trusted: it runs only our script +
tg). The untrusted PR code runs only in the sandbox sibling (
node:24-alpine, no socket, no creds, no host mounts beyond the checkout), so a malicious PR cannot reach the token or the host. - The docker socket in the orchestrator is root-on-host-equivalent; keep this image's contents trusted
(it runs only
tg+pr-check.shfetched from our own Tangled raw). - Prefer a dedicated bot account so the worst case of any slip is comment-only.
Notes #
- The checker logic is fetched from Tangled raw at run time (
entrypoint.sh), soscripts/pr-check.shchanges take effect without rebuilding the image. Rebuild only to bumptgor the base image. - To run on-demand from a laptop/Mac instead (no container), see the
pr-check.shsection inscripts/README.md.