gitfor
A git forge built entirely on Cloudflare: repositories live in Artifacts, and everything else runs on Workers, D1, KV, R2, Queues, Workflows, Durable Objects and Containers.
Features
- Repositories: create, import from HTTPS remotes, fork, archive, delete, public/private visibility
- Browsing: trees, syntax-highlighted files, raw files, README rendering, commit history, commit diffs, branches, tags,
.tar.gzarchives - Git over HTTPS: clone/fetch/push through a Worker proxy authenticated with personal access tokens
- Web editing: create, edit, rename and delete files, optionally on a new branch
- Branch protection: required pull requests, approvals and status checks, deletion protection, admin bypass
- Issues: labels, assignees, comments, locking, closing keywords (
fixes #12), full-text search - Pull requests: cross-fork PRs, inline review threads, approvals and change requests, review requests, drafts, merge/squash/rebase, mergeability checks
- Actions: GitHub-Actions-style workflows in
.gitfor/workflows/*.ymlwithneeds, matrices,if:expressions, secrets, artifacts and live logs - Runners: Cloudflare Containers by default, plus self-hosted runners (
gfd runner, a container per job) - Releases with R2-backed assets, webhooks with HMAC signatures and redelivery, notifications, activity feeds, organizations
Architecture
| Concern | Cloudflare primitive |
|---|---|
| Git objects and refs | Artifacts (Workers binding + smart HTTP) |
| Users, repos, issues, PRs, CI state | D1 (with FTS5 search) |
| Sessions, refs/object/diff caches, token cache | KV |
| Logs, CI artifacts, release assets | R2 |
| Webhook fan-out and delivery, notifications | Queues (with retries and a DLQ) |
| Post-push pipeline, CI run orchestration | Workflows (PushWorkflow, CiRunWorkflow) |
| Job execution, log streaming, merges | Durable Object Runner with a Container |
| Push events from Artifacts | triggers.events (cf.artifacts.repo.pushed) |
Key code paths:
src/server.ts— Worker entry: git proxy, runner API, raw/archive/asset routes, queue and cron handlerssrc/server/git/— pkt-line, refs, packfile writer, receive-pack client, tree diff, merge-basesrc/server/ci/— workflow schema, expressions, compiler, bash job script, runner DO, schedulersrc/fns/— TanStack Start server functions used by the UImigrations/— D1 schema
Setup
Requirements: Bun, Docker (for building the runner image), a Cloudflare account on the Workers Paid plan with Artifacts enabled.
bun install
Create resources and put their IDs in wrangler.jsonc (also set account_id if your login has several accounts):
bunx wrangler d1 create gitfor
bunx wrangler kv namespace create gitfor
bunx wrangler r2 bucket create gitfor
bunx wrangler queues create gitfor-events
bunx wrangler queues create gitfor-events-dlq
bunx wrangler secret put SECRETS_KEY
The Artifacts namespace (gitfor) is referenced by the binding in wrangler.jsonc; check it with bunx wrangler artifacts namespaces get gitfor.
Set PUBLIC_URL in wrangler.jsonc to your deployment URL. Optional: GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET secrets enable GitHub login.
Develop
echo "SECRETS_KEY=dev-secret" > .dev.vars
bun run db:migrate
bun run dev
The Artifacts binding uses remote: true, so local development talks to your real Artifacts namespace.
Deploy
bun run deploy
The first account to sign up becomes the instance administrator. Set SIGNUPS_ENABLED to "false" to close registration afterwards.
Using git
Create a token under Settings → Access tokens, then use your username and the token as the password:
git clone https://gitfor.dev/alice/project.git
Workflows
# .gitfor/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: cloudflare
strategy:
matrix:
node: [22, 24]
steps:
- uses: actions/checkout@v4
- run: bun install && bun test
- uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node }}
path: coverage
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: [self-hosted, linux]
steps:
- run: ./deploy.sh
env:
TOKEN: ${{ secrets.DEPLOY_TOKEN }}
Supported triggers: push (branches, tags, paths filters), pull_request (types, branches, paths), release, workflow_dispatch (with inputs).
Steps are run scripts (bash, sh, python, node, bun) plus checkout, upload-artifact and setup actions treated as no-ops; third-party actions are not supported.
Job status is published as commit statuses named <workflow> / <job>, which branch protection can require.
Self-hosted runners
Create a registration token under Repository settings → Runners (or /orgs/<owner>/runners for owner-wide runners), choosing the labels and the isolation, then on the host (needs Docker or Podman):
gfd runner register --url https://gitfor.dev --token gitfor_reg_...
gfd runner run
Each job runs in a fresh, non-root container with a read-only root filesystem and an egress firewall that blocks cloud metadata and private networks.
Fork pull request jobs never reach self-hosted runners unless a repository admin opts in, and then only runners whose container isolation the admin declared on the token.
See the gfd README for what is and isn't isolated. The old runner/gitfor-runner.ts is retired.
Pages
Static sites are served from <owner>.<PAGES_DOMAIN>/<repo>/ (a repo named <owner>.<PAGES_DOMAIN> is served at the root) and pull request previews from /_pr/<number>/. PAGES_DOMAIN must be a separate registrable domain from the forge so user content can never read forge cookies.
- Add Worker routes for
*.<PAGES_DOMAIN>/*(with a wildcard DNS record and certificate). - Custom domains use Cloudflare for SaaS: set
PAGES_ZONE_IDto the SaaS zone (with a fallback origin and a*/*Worker route) and add aCF_API_TOKENsecret with "SSL and Certificates: Edit" on that zone. - Deploys copy files into content-addressed R2 objects (
pages/objects/<git blob sha>), publish a manifest to KV and flip a KV pointer, so rollbacks are instant.
Scripts
| Command | Purpose |
|---|---|
bun run dev |
Local dev server |
bun run build |
Production build and type-check |
bun run deploy |
Apply D1 migrations and deploy |
bun run cf-typegen |
Regenerate worker-configuration.d.ts |
bun run lint / bun run format |
oxlint / oxfmt |
License
Released under the 0BSD license.