- TypeScript 96.4%
- JavaScript 3.3%
- Dockerfile 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Deploy / deploy (push) Successful in 1m59s
Reviewed-on: #2 |
||
| .forgejo/workflows | ||
| docs | ||
| mail-bridge | ||
| skills | ||
| .env.example | ||
| .gitignore | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| README.md | ||
| renovate.json | ||
paperclip
Paperclip at https://team.bobparsons.dev.
Official image, shared pg18 for the database, Traefik for routing (behind the
Cloudflare tunnel), secrets from Infisical. Push to main deploys.
Runs authenticated + private so the agents can sign in with your AI
subscriptions in the browser, which Paperclip turns off for public. The
internet is kept out by Cloudflare Access (owner email only) plus a Traefik
IP allowlist that stops anyone going around Access via the WAN IP.
One-time setup
- Database on the shared pg18:
Paperclip runs its migrations on startup.ssh server 'docker exec -i postgres18 psql -U postgres' <<'SQL' CREATE USER paperclip WITH ENCRYPTED PASSWORD '<pw>'; CREATE DATABASE paperclip OWNER paperclip; SQL - Infisical: create a project with a
prodenv, grant the deploy machine identity read access, and add the keys from.env.example(DATABASE_URL=postgres://paperclip:<pw>@postgres18:5432/paperclip). - Forgejo: add the repo variable
INFISICAL_PROJECT_ID. - Cloudflare: add a proxied CNAME
teampointing at the tunnel (0f3f1dd3-e3ec-42b3-835c-b05dcc6c2354.cfargotunnel.com). The tunnel's*.bobparsons.devroute already sends traffic to Traefik.
First admin
With private exposure you can also claim the instance from the setup screen
in the browser. Otherwise the admin comes from a CLI invite. The CLI refuses to run without a config file, but the
server is configured only through env. Give the CLI a throwaway config in
/tmp. Don't write one to /paperclip, because the server reads it too.
ssh server 'docker exec -u node -w /app paperclip sh -c '"'"'
cat > /tmp/boot.json <<EOF
{"\$meta":{"version":1,"updatedAt":"2026-01-01T00:00:00Z","source":"onboard"},
"database":{"mode":"postgres"},"logging":{"mode":"file"},
"server":{"deploymentMode":"authenticated","exposure":"public"},
"auth":{"baseUrlMode":"explicit","publicBaseUrl":"https://team.bobparsons.dev"}}
EOF
pnpm --silent paperclipai auth bootstrap-ceo --config /tmp/boot.json \
--expires-hours 24 --base-url https://team.bobparsons.dev; rm /tmp/boot.json'"'"''
Add --force to issue a new invite after an admin already exists. Open the
invite URL while signed in. Then set PAPERCLIP_AUTH_DISABLE_SIGN_UP=true in
Infisical and redeploy.
The startup banner shows Agent JWT missing. Ignore it: agent JWTs fall back to
BETTER_AUTH_SECRET, and only the banner checks for the dedicated variable.
AI subscription logins
When Paperclip shows a login command (for example codex ... login --device-auth),
run it inside the container as node. The credentials have to land in the
server's /paperclip volume:
ssh -t server docker exec -it -u node paperclip sh -c '<command from the UI>'
Approve the device code in your browser, then return to the Paperclip screen. It
imports the login and deletes the staging directory under
ai-local-logins/, so seeing that directory gone afterwards is normal.
Data
The paperclip-data volume (/paperclip) holds uploads, agent workspaces,
agent CLI logins and the secrets master key. Back it up together with the
paperclip database, because stored secrets can't be decrypted without the
key.
Mail bridge
The mail bridge watches Purelymail inboxes, wakes the
owning Paperclip agent through a routine webhook, and exposes mailbox-scoped MCP
tools for reading, triage, and approved sends. It keeps triage and send state in
the mail-bridge-data volume (/data); message bodies are fetched live.
To add a mailbox, follow docs/mail-bridge-runbook.md. It has copy-paste console snippets and a troubleshooting table.
For older mail, run a bounded backfill. For an uncertain send, check the
recipient side for its stored rfcMessageId before resolving it. Never resend
under a new key without that check.
docker exec mail-bridge node src/cli.ts backfill <mailbox> <YYYY-MM-DD> [max=50]
docker exec mail-bridge node src/cli.ts resolve-send <mailbox> <idempotencyKey> sent|failed
A push that changes only mail-bridge/ rebuilds only the bridge. Changes to the
paperclip service block, including this first rollout's network addition,
recreate Paperclip and interrupt agent runs. Push those while no agents are
running.