Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

README.md

Diffinery

A git forge for teams of people and AI agents, running entirely on Cloudflare. Git hosting, pull requests and reviews, issues, GitHub-Actions-compatible CI, an MCP server for agents, and sandboxes, with no servers to run.

  • Drop-in for GitHub workflows. .github/workflows/*.yml runs as-is: same triggers, expressions, matrices, needs, outputs, caches, artifacts, secrets, OIDC, actions/checkout, setup-node/python/go, and remote JavaScript and composite actions.
  • Built for agents. Agents are first-class accounts owned by a person or team, with their own tokens and limits. They can't push or merge to protected branches, and their pull requests always need a human approval. Every commit they make is stamped with who they act for. They work through MCP (/mcp) or the REST API, and can start sandboxes to build and test.
  • Secure by default. People sign in with passkeys, with no passwords to leak or phish. Invites, roles and branch protection decide what they can do. Pushes are scanned for leaked credentials. Secrets use envelope encryption, and every sensitive action goes to an append-only audit log.

How it fits on Cloudflare

PieceCloudflare product
HTTP, git smart HTTP, REST API, MCP, web UIWorkers (Hono)
One repository: refs, objects, PRs, issues, reviews, checks, runsDurable Object with SQLite (RepoDO), one per repository
Large objects, Git LFS, logs, caches, artifacts, event archiveR2
Accounts, orgs, tokens, invites, notifications, audit logD1
Webhooks, notifications, audit fan-outQueues (with a dead-letter queue)
Workflow runs (durable, survive restarts, 6 h job timeout)Workflows
Job runners and agent sandboxesContainers (Ubuntu 24.04 image in containers/runner)
Scheduled workflows (on: schedule)Cron Triggers
Live run logs and PR updatesDurable Object WebSockets (hibernation)
Sign-inPasskeys (WebAuthn) and sessions in D1. Cloudflare Access guards only /auth/access, the email recovery path
apps/diffinery        the Worker (src/index.ts), D1 migrations, wrangler config, e2e test
apps/cli              the `diffinery` command-line client
containers/runner     runner agent + sandbox server, and the container image
packages/git-core     git objects, packfiles, deltas, diff/merge (pure TypeScript)
packages/workflow-yaml  GitHub Actions workflow parser, expressions, matrix, cron
packages/shared       types shared by the Worker and the runner
scripts               bootstrap.sh, ensure-secrets.sh, render-config.mjs

Deploying

Deploys run from GitHub Actions on every push to main (.github/workflows/deploy.yml). The workflow needs two repository secrets:

  • CLOUDFLARE_API_TOKEN: account permissions Workers Scripts, D1, R2, Queues, Workflows, Containers and Access: Apps and Policies (all Edit), and zone permission Workers Routes Edit on diffinery.com.
  • CLOUDFLARE_ACCOUNT_ID

On each deploy, scripts/bootstrap.sh creates anything that is missing: the D1 database, R2 bucket, queues and one Access application that covers only /auth/access (email-based account recovery). It then applies D1 migrations. wrangler deploy builds and pushes the runner image. On the first deploy, ensure-secrets.sh generates MASTER_KEY and the OIDC signing key inside the job, so neither value is ever printed.

If the token can't read your Zero Trust organization, set a repository variable ACCESS_TEAM_DOMAIN (for example codician.cloudflareaccess.com).

Email recovery uses Access's email one-time PIN, which works without any setup. You can also add GitHub as a login method under Zero Trust → Settings → Authentication.

Signing in and inviting people

Everyone signs in with a passkey. There are no passwords.

  1. First sign-in for admins. The emails in BOOTSTRAP_ADMINS (in apps/diffinery/wrangler.jsonc) become site admins. Open https://diffinery.com/auth/access, confirm your email with the one-time code, and add a passkey on the page that follows.
  2. Inviting someone. An admin or org owner creates an invite from Settings → Site administration, an organization's People tab, or diffinery invite friend@example.com --org codician. The person opens the link, picks a username and creates a passkey, and they're in.
  3. Lost passkey. A site admin clicks Sign-in link next to the person in Settings → Site administration and sends them the one-time link, which works for 24 hours. People can also recover through /auth/access by email.
  4. More devices. Add passkeys under Settings → Passkeys and sessions, or keep one in a password manager that syncs. The same page signs out other browsers.
  5. Repository and org roles (read, triage, write, maintain, admin) decide what each person can do.

Nobody needs a Cloudflare account. Git and the CLI use a personal token from Settings → Tokens.

git clone https://diffinery.com/codician/website.git   # username: anything, password: your dfy_ token
diffinery login && diffinery auth setup-git             # or let the CLI handle credentials

Agents

Create an agent under Settings → Agents or with diffinery agent create reviewer-bot --org codician. Then point any MCP client at it:

{ "mcpServers": { "diffinery": { "type": "http", "url": "https://diffinery.com/mcp", "headers": { "Authorization": "Bearer dfy_…" } } } }

Tools include get_file, list_tree, commit_files (no git client needed), create_branch, create_pull_request (with an intent for reviewers), review_pull_request, merge_pull_request (policy-guarded), issues, list_workflow_runs and get_job_logs, rerun_workflow, and sandbox_create/sandbox_exec for running builds and tests.

Agent guardrails apply everywhere: git, REST, MCP and the web UI.

  • An agent's role is capped at write, and never exceeds its owner's role.
  • Agents can't push to the default branch or protected branches, and can't merge there. Turn this on per branch with agentsMayPush or agentsMayMerge.
  • A pull request opened by an agent needs at least one human approval, and approvals from agents don't count toward it.
  • Agents can't change branch protection or read or set secrets.

Workflows

Put workflow files in .github/workflows/ (or .diffinery/workflows/). Supported triggers are push, pull_request, workflow_dispatch (with inputs) and schedule, with branch, tag and path filters. Supported workflow features:

  • needs and job outputs, if with success(), failure(), always() and cancelled(), strategy.matrix with include and exclude, fail-fast and max-parallel, concurrency with cancel-in-progress, and timeout-minutes.
  • continue-on-error, defaults.run, env at every level, $GITHUB_OUTPUT, $GITHUB_ENV, $GITHUB_PATH, $GITHUB_STEP_SUMMARY, and masking of secrets and ::add-mask:: values.
  • secrets.*, vars.*, GITHUB_TOKEN (a per-job token), and permissions: id-token: write for OIDC (issuer https://diffinery.com).
  • runs-on: ubuntu-latest (and any ubuntu-* or linux label) runs in a Cloudflare Container. runs-on: [self-hosted, …] jobs wait for a runner you start with diffinery runner -R owner/repo --labels gpu,macos. Use that for macOS, GPUs or private networks.
  • Reusable workflows: jobs.<id>.uses: ./.github/workflows/x.yml from the same commit, or owner/repo/.github/workflows/x.yml@ref from another repository on this instance (public or internal ones, or private ones with the same owner). with, secrets (a mapping or inherit), on.workflow_call outputs and nesting up to four levels deep all work. A matrix on the calling job isn't supported yet.

Not supported yet: job container: and services:, Docker container actions, actions/github-script, Windows and macOS hosted runners, pull_request_target, merge_group, and deepen-not / partial clone filters. Pull request runs use the head commit rather than a test merge commit. A job that uses an unsupported feature fails with a clear message instead of misbehaving.

Developing

pnpm install
pnpm -r build && pnpm -r typecheck && pnpm -r test
pnpm --filter @diffinery/app db:migrate:local
pnpm dev                                  # http://localhost:8787, signed in as dev@diffinery.local
pnpm --filter @diffinery/app e2e          # wrangler dev + real git + the runner agent + Chromium

The end-to-end test does the following:

  1. Pushes a repository with real git.
  2. Runs its CI through the actual runner agent.
  3. Checks secret scanning and branch protection.
  4. Has an agent open a pull request that needs a human approval and a green check, then merges it.
  5. Clones shallow, then deepens the clone.
  6. Drives the MCP server.
  7. In Chromium with a virtual authenticator, joins from an invite with a new passkey, adds and removes passkeys, signs out and in, and uses an admin sign-in link.

Team preferences for product and design decisions are in docs/PREFERENCES.md.