Self-hosting and custom hosts live
All of Gitstalk's source is public, and the whole forge deploys to one Cloudflare account with a handful of commands. Run staging and production side by side, put it on your own domain, and set your own limits.
What you get and what it needs
You get the same forge as the hosted service: the git proxy and engine, pre-land check sandboxes, GitHub Actions and automations, the web app, the MCP server for agents and these docs. You own the Cloudflare resources.
-
A Cloudflare account on Workers Paid, with Containers, Durable
Objects and Artifacts, and Workers AI for Ask and automations. Workers AI calls go
through an AI Gateway named
default: provision creates it when your API token may, and otherwise tells you to create it under AI, AI Gateway. - On your machine: Node 24 or later, pnpm 11, stable Rust, and Docker (it builds the container images).
What you will own
- People and agentsbrowser, git, MCP
- web, mcp, site, sshWorkers
- gatewayengine in Durable Objects, runner containers
- StorageArtifacts, D1, KV, R2, Queues
| Piece | Cloudflare product |
|---|---|
| Gateway: git proxy, engine, registry, repository RPC | Workers, Durable Objects (SQLite) |
| Runner: squash, merge, revert, run the suite (the check sandboxes) | Containers (Rust) |
| Actions executor: one container per workflow job | Workers, Containers |
| Git storage | Artifacts |
| Accounts, repositories, access, the activity index | D1 |
| OAuth grants; public repositories' files and diffs cached at the edge | KV, Cache API |
| Repository events to the index | Queues |
| Actions logs and dependency snapshots | R2 |
| Web app (Next.js App Router on vinext), site and docs | Workers |
| MCP server | Workers, Workers AI |
| SSH (not offered publicly yet) | Workers, Durable Objects, Containers, Spectrum |
Deploy
The repository holds no account's ids. Each package's wrangler.jsonc is a
template, and each deployment you run is an environment: one directory,
environments/<name>/, with your account, a name suffix and the
packages to run. Never wrangler deploy a template; the package's
deploy script refuses on purpose.
-
Create the environment
Copy the example and fill in your account.
Terminal pnpm install pnpm -F @gitstalk/gateway exec wrangler login cp -r environments/example environments/stagingEdit
environments/staging/env.jsonc. Find your account id withpnpm -F @gitstalk/gateway exec wrangler whoami. Wrangler is a dependency of each package, not of the repository root, so a barenpx wranglerthere downloads whatever version is newest instead. -
Provision
Creates what the environment needs if it is missing (D1 databases, the KV namespaces for OAuth grants and the edge cache, R2 buckets, the events queue and the Artifacts namespace), records their ids in
resources.json, generates the Workers' secrets intosecrets/(never printed, never committed) and applies the D1 migrations. Run it again after an update that adds a resource or a migration.Terminal pnpm env:provision staging -
Push the secrets
Sends the generated values to the Workers. They are never printed. Before the first deploy there are no Workers yet, so it only says so: deploy uploads each Worker's secrets with its first version.
Terminal pnpm env:secrets staging -
Deploy
Stops if a required secret is missing, then deploys the Actions executor, the gateway with its runner container, the MCP server, the site, the web app and SSH, in that order. Add
--dry-runto check without uploading, or--only mcpto deploy one package.Terminal pnpm env:deploy staging -
Smoke test
Checks health, the sign-up and sign-in pages, MCP metadata,
robots.txtand the docs on the live origins. SetGITSTALK_TOKENto a personal token and it also checkswhoamiand a clone.Terminal pnpm env:smoke staging
Every Worker and resource is named
<prefix>-<name><suffix>: the prefix defaults to
gitstalk, and with the suffix -staging you get
gitstalk-gateway-staging. An empty suffix keeps the base names. D1, KV, R2,
queues, Artifacts namespaces, Analytics Engine datasets and rate-limit namespaces all
belong to the account, so on an account that already runs Gitstalk give the new
environment its own prefix or suffix, and its own namespace_id for the
web's SIGNIN_RATE_LIMIT (the example's
overrides.web.ratelimits). A kill switch for benchmark races:
POST /v1/admin/halt with the admin token refuses new race runs and starts
(DELETE on the same path resumes them). It does not stop repositories. The
gateway also has an hourly cron trigger that deletes race repositories older than a day;
it is part of the gateway's config and needs nothing provisioned.
The environment file
prefix renames the Workers, packages lists what to run, and
overrides merges into each package's generated config, such as smaller
container pools for a test stack. Search engines stay out unless you say otherwise:
every environment answers /robots.txt with Disallow: / and
marks its pages X-Robots-Tag: noindex. For the one deployment that should
be found, set "search_indexing": true.
Your own domain and custom host
Without urls, every Worker answers on its workers.dev address.
To use your own hostnames, put the zone in the same Cloudflare account and name one
origin per package. Each becomes a Workers Custom Domain of that Worker; Cloudflare
creates the DNS record and the certificate on deploy. The hostnames must not have DNS
records already. Do not look them up before the first deploy: a resolver that answered
"no such name" keeps that answer for up to 30 minutes, so the smoke test fails with
fetch failed on your machine while the hosts already work elsewhere.
"urls": {
"web": "https://git.example.com",
"site": "https://git.example.com",
"mcp": "https://mcp.git.example.com",
"gateway": "https://api.git.example.com",
"ssh": "https://ssh.git.example.com"
}
What changes when you set urls
| What | Becomes |
|---|---|
| Clone URL |
https://git.example.com/<owner>/<repo>.git (the web
origin)
|
| MCP URL | https://mcp.git.example.com/mcp |
| OAuth issuer and Actions OIDC issuer | The MCP and gateway origins |
| Docs link | The site origin, /docs/ |
| Passkeys | Belong to the web app's hostname. Pick it before people sign up; changing it later means people register passkeys again. |
| Edge cache |
The web app keeps public repositories' files and diffs in each data centre's
cache too. That cache does nothing on workers.dev, where only the
global KV copy is used.
|
When site and web share an origin, the web app hands the
site's paths (/docs/, its files, and / for someone signed out)
to the site Worker over a service binding.
Point agents at your host
The plugin's marketplace entry points at the hosted MCP server, so on
your own host add the server directly with your MCP URL. Your deployment's
/signup/agent page prints these lines for you.
claude mcp add --transport http gitstalk https://mcp.git.example.com/mcp && claude mcp login gitstalk
codex mcp add gitstalk --url https://mcp.git.example.com/mcp && codex mcp login gitstalk
curl -fsSL https://git.example.com/setup.sh | sh -s -- detect
A script served by your deployment already points at it. For a copy you downloaded
elsewhere, set GITSTALK_WEB=https://git.example.com.
Secrets each Worker needs
Names only. Provision generates them, and never replaces one a Worker already has.
| Worker | Required secrets |
|---|---|
| Gateway |
ADMIN_TOKEN, RUN_TOKEN_SECRET,
ACTIONS_SECRETS_KEY, OIDC_REQUEST_SECRET,
OIDC_SIGNING_KEYS
|
| Actions executor | ADMIN_TOKEN |
| Web app |
DEMO_PASSWORD, EMAIL_SIGNIN_KEY (optional:
TURNSTILE_SECRET_KEY)
|
| SSH | SSH_HOST_KEY |
| MCP server, site | none |
Email sign-in codes need EMAIL_SENDER_DOMAIN in the web package's
overrides.web.vars: a zone in your account onboarded to Email Sending. Left
empty, sign-in is passkeys only. PLATFORM_ADMINS (comma-separated handles)
sets who sees the admin area; by default nobody does.
Limits you set
The hosted beta's limits are these defaults; see Limits and plans. When you self-host, you set every one.
| Setting | Default | Where |
|---|---|---|
| Pre-land checks at once per repository (one sandbox each) | 32 (1–64) |
operator setting preland_sandboxes on a repository's engine (not an
env.jsonc field)
|
| Validations at once | 2 (1–16) | operator setting ci_slots on a repository's engine |
| Runner containers in the shared pool | 48 |
max_instances and RUNNER_MAX_INSTANCES in the
gateway's config
|
| Suite time limit | 300 s | timeout-minutes in checks.yml |
| Actions minutes per repository per month | 100 | ACTIONS_MONTHLY_MINUTES |
| Actions job timeout | 60 min | ACTIONS_JOB_TIMEOUT_MINUTES |
| Concurrent Actions jobs | 4 | ACTIONS_CONCURRENT_JOBS |
Keep your environments in a private fork
The public repository ignores environments/ except the example. Keep yours
in a private repository with the public one as upstream:
git clone https://github.com/coop-collective/gitstalk.git my-gitstalk
cd my-gitstalk
git remote rename origin upstream
git remote add origin <your private repository>
git add -f environments/staging/env.jsonc environments/staging/resources.json
git commit -m "environments: staging"
git push -u origin main
git fetch upstream && git merge upstream/main # later: stay in sync
Your fork only adds files under environments/, which upstream never writes,
so the merge does not conflict. Never add secrets/. A scheduled workflow
can do the merge for you: fetch upstream, merge (a pull request when it conflicts),
push.
Deploying from CI
The deploy workflow in .github/workflows/ deploys staging on
every push to main, and production only when you run it by hand or push a
production-* tag, through the GitHub environment production,
where you can require a reviewer. Each run checks the code, provisions, pushes the
secrets, deploys and smoke-tests. It does nothing in a repository without
environments/<name>/env.jsonc (or a
GITSTALK_ENV_CONFIG variable on the GitHub environment). Each GitHub
environment needs two secrets:
-
CLOUDFLARE_API_TOKEN: an account token with edit on Workers Scripts, D1, Workers KV Storage, Workers R2 Storage, Queues, Containers, Artifacts and AI Gateway, read on Account Settings, and for your zones edit on Workers Routes, DNS and SSL and Certificates, read on Zone. -
GITSTALK_SECRETS_JSON: set withenv:secrets --to-github.
Run a race against your own deployment
The race harness can rerun the benchmark against your account. Replay agents use no
model tokens; real agents need the claude or codex CLI. Each
race deletes its repository when it ends (unless you keep it) and prints the
infrastructure cost it metered: $0.35 to $0.42 for each of three 12-agent Gitstalk
races, plus $4 to $6 of model spend for the agents. Commands are in the research docs
for
races on Cloudflare
and
token-free replay. This is a benchmark, not a user feature; see
Cloud agents and sandboxes.
Licence and name
Apache-2.0: you may use, modify and redistribute it, including running it as a service, under the licence's terms. The Gitstalk name and logos are not licensed: a copy you run for others needs its own name and logo (trademarks).