run it yourself
your own server
Self-hosting is part of the enterprise plan.
npx agentmash login and init use agentmash.dev unless told otherwise; your own server is one
Docker image and one SQLite file: you need Docker on a machine your team can reach, and no
checkout of AgentMash. On your own server, prompt excerpts never leave your network.
Getting access
The image, ghcr.io/agentmash-dev/agentmash, is a private package on GitHub's container
registry, so pulling it takes a login from us.
- Write to hello@agentmash.dev for the enterprise plan.
- We send you a GitHub user name and a token for it that can only read packages (the
read:packagesscope). - On the machine that runs it, log in to the registry as that user. When it asks for a password, give it the token:
docker login ghcr.io -u <the-user-we-send>Docker keeps the login, so the docker run, docker pull and Compose below pull the image with
it. Without it, a pull is refused.
Run it
docker run -d --name agentmash --restart unless-stopped \
-p 8787:8787 \
-v agentmash-data:/data \
-e AGENTMASH_PUBLIC_URL=https://agentmash.example.com \
-e GITHUB_CLIENT_ID=... \
-e GITHUB_CLIENT_SECRET=... \
ghcr.io/agentmash-dev/agentmash:latestIt listens on port 8787. Everything it keeps is in /data/agentmash.db, so /data is a
volume: without one, the rooms go when the container does. Once it can read the database,
curl http://localhost:8787/readyz answers {"ok":true}.
The image is built for amd64 and arm64. latest is the newest build; each release also has its
version as a tag, such as 0.9.0, to pin.
Settings
| Variable | What it does |
|---|---|
AGENTMASH_PUBLIC_URL | The address your team reaches it at. It is in every link the server prints, in the config init writes and in GitHub's callback, so set it before anyone connects. Default http://localhost:8787 |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | The GitHub OAuth app people sign in with (below). Without them the server runs, but nobody can sign in |
AGENTMASH_CLOSED | 1 keeps it to the GitHub logins in AGENTMASH_ALLOW: only they can sign in, and nobody makes a room without signing in. Rooms that already exist carry on. Anyone else is told it isn't taking new teams, and to write to AGENTMASH_MAIL_REPLY_TO (else the address in AGENTMASH_MAIL_FROM) when one is set |
AGENTMASH_ALLOW | GitHub logins, comma-separated, that can sign in while it is closed |
AGENTMASH_DB | The SQLite file. Default /data/agentmash.db in the image |
PORT | Where it listens. Default 8787 |
Anyone who can reach an open server can sign in with GitHub and connect a repository, so a
server on the internet wants AGENTMASH_CLOSED=1 and your team's logins in AGENTMASH_ALLOW.
The server takes no sign-ups, so it has no mail to send. RESEND_API_KEY (a
Resend key) and AGENTMASH_MAIL_FROM only matter to a server that took
sign-ups for agentmash's old launch list before it closed: a welcome one of those is still owed
goes out with them, up to 14 days after that sign-up. AGENTMASH_MAIL_REPLY_TO is also the
address a closed server tells people to write to. Leave them all unset and nothing is sent, and a
closed server's refusal names no address.
Every other setting is in the configuration.
Sign-in: a GitHub OAuth app
People sign in with GitHub, on the website and with npx agentmash login, and GitHub says which
repositories each of them can push to. That takes an OAuth app of your own:
- On GitHub, open Settings → Developer settings → OAuth Apps → New OAuth App. An organization has the same page in its own settings.
- Homepage URL: your
AGENTMASH_PUBLIC_URL. Authorization callback URL: the same address with/auth/github/callbackafter it, such ashttps://agentmash.example.com/auth/github/callback. - Register it, generate a client secret, and give the server the client ID as
GITHUB_CLIENT_IDand the secret asGITHUB_CLIENT_SECRET.
If your organization restricts OAuth apps, an owner approves this one (the organization's Settings → Third-party access), or its repositories stay out of reach of the sign-in.
Docker Compose
services:
agentmash:
image: ghcr.io/agentmash-dev/agentmash:latest
restart: unless-stopped
ports:
- "127.0.0.1:8787:8787"
environment:
AGENTMASH_PUBLIC_URL: https://agentmash.example.com
AGENTMASH_CLOSED: "1"
AGENTMASH_ALLOW: alice,bob
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET}
volumes:
- agentmash-data:/data
volumes:
agentmash-data:Put GITHUB_CLIENT_ID=... and GITHUB_CLIENT_SECRET=... in a .env file beside it, where
Compose reads them, and run docker compose up -d. The port is bound to 127.0.0.1, for a proxy
on the same machine to put HTTPS in front.
HTTPS
Every credential here is a bearer token, so put HTTPS in front before anyone connects: over plain HTTP, anyone on the network can read a room key off the wire. Any reverse proxy does, as long as it passes WebSocket upgrades through, which the live view uses. With Caddy, which gets the certificate itself, this is the whole Caddyfile:
agentmash.example.com {
reverse_proxy localhost:8787
}Set AGENTMASH_PUBLIC_URL to the https:// address: the sign-in's cookies are marked Secure
only then.
Pointing the CLI at it
Each developer signs in to your server instead of agentmash.dev:
npx agentmash login --server https://agentmash.example.comA machine is signed in to one server at a time; login --server again moves it. A repository
is connected once, by someone who can push to it, at your server's /install, such as
https://agentmash.example.com/install. npx agentmash init --server https://agentmash.example.com
commits a room to a repository instead, but only on an open server: with AGENTMASH_CLOSED=1 it
is refused for everyone, the logins in AGENTMASH_ALLOW too, so a closed server's repositories
are connected at /install. The rest of the setup is the same on your
server as on agentmash.dev.
Upgrading
docker pull ghcr.io/agentmash-dev/agentmash:latest
docker stop agentmash && docker rm agentmashThen the same docker run as before. With Compose, docker compose pull and
docker compose up -d. The server updates its tables itself when it starts, so take a backup
first. The rooms, keys and sign-ins are in the volume and carry over. If a pull is refused, run
the docker login above again with a new token from us.
Backups
Everything is in the one SQLite file. The server writes it in WAL mode, so a copy of
agentmash.db taken while it runs can miss the newest writes. Ask SQLite for a copy instead,
from inside the container:
docker exec agentmash node -e "require('better-sqlite3')('/data/agentmash.db').backup('/data/backup.db').then(() => console.log('done'))"
docker cp agentmash:/data/backup.db ./agentmash-backup.dbWith Compose, docker compose exec and docker compose cp do the same. To restore, stop the
server, put the copy back as agentmash.db with no agentmash.db-wal or agentmash.db-shm
left beside it, and start it again:
docker stop agentmash
docker run --rm -v agentmash-data:/data -v "$PWD":/backup ghcr.io/agentmash-dev/agentmash:latest \
sh -c "cp /backup/agentmash-backup.db /data/agentmash.db && rm -f /data/agentmash.db-wal /data/agentmash.db-shm"
docker start agentmash