Central platform for European atproto.<cc> country community websites atproto.eu
community atproto
README.md

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 #

  1. 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.

  2. 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
    
  3. 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.sh fetched 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), so scripts/pr-check.sh changes take effect without rebuilding the image. Rebuild only to bump tg or the base image.
  • To run on-demand from a laptop/Mac instead (no container), see the pr-check.sh section in scripts/README.md.