# 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 `:`), 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: ```sh 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): ```sh 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) ```sh 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 :/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`.