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.

189 lines · 9.9 KBRaw History
1# Diffinery
2
3A git forge for teams of people and AI agents, running entirely on Cloudflare.
4Git hosting, pull requests and reviews, issues, GitHub-Actions-compatible CI,
5an MCP server for agents, and sandboxes, with no servers to run.
6
7- **Drop-in for GitHub workflows.** `.github/workflows/*.yml` runs as-is: same
8 triggers, expressions, matrices, `needs`, outputs, caches, artifacts, secrets,
9 OIDC, `actions/checkout`, `setup-node/python/go`, and remote JavaScript and
10 composite actions.
11- **Built for agents.** Agents are first-class accounts owned by a person or
12 team, with their own tokens and limits. They can't push or merge to protected
13 branches, and their pull requests always need a human approval. Every commit
14 they make is stamped with who they act for. They work through MCP
15 (`/mcp`) or the REST API, and can start sandboxes to build and test.
16- **Secure by default.** People sign in with passkeys, with no passwords to
17 leak or phish. Invites, roles and branch protection decide what they can do. Pushes are scanned for
18 leaked credentials and blocked if they contain any; nothing from a blocked
19 push is kept. A repository admin (a person, never an agent) can push anyway
20 with `git push -o allow-secrets=<reason>`, which is audited. A file can opt
21 out with a `diffinery:allow-secret` comment, for test fixtures. Secrets use
22 envelope encryption, and every sensitive action goes to an append-only audit log.
23
24## How it fits on Cloudflare
25
26| Piece | Cloudflare product |
27|---|---|
28| HTTP, git smart HTTP, REST API, MCP, web UI | Workers (Hono) |
29| One repository: refs, objects, PRs, issues, reviews, checks, runs | Durable Object with SQLite (`RepoDO`), one per repository |
30| Large objects, Git LFS, logs, caches, artifacts, event archive | R2 |
31| Accounts, orgs, tokens, invites, notifications, audit log | D1, read from nearby replicas through the Sessions API (each browser keeps a bookmark so it reads its own writes) |
32| Webhooks, notifications, audit fan-out | Queues (with a dead-letter queue) |
33| Workflow runs (durable, survive restarts, 6 h job timeout) | Workflows |
34| Job runners and agent sandboxes | Containers (Ubuntu 24.04 image in `containers/runner`) |
35| Scheduled workflows (`on: schedule`) | Cron Triggers |
36| Live run logs and PR updates | Durable Object WebSockets (hibernation) |
37| Sign-in | Passkeys (WebAuthn) and sessions in D1. Cloudflare Access guards only `/auth/access`, the email recovery path |
38
39```
40apps/diffinery the Worker (src/index.ts), D1 migrations, wrangler config, e2e test
41apps/cli the `diffinery` command-line client
42containers/runner runner agent + sandbox server, and the container image
43packages/git-core git objects, packfiles, deltas, diff/merge (pure TypeScript)
44packages/workflow-yaml GitHub Actions workflow parser, expressions, matrix, cron
45packages/shared types shared by the Worker and the runner
46scripts bootstrap.sh, ensure-secrets.sh, render-config.mjs
47```
48
49## Deploying
50
51Deploys run from GitHub Actions on every push to `main` (`.github/workflows/deploy.yml`).
52The workflow needs two repository secrets:
53
54- `CLOUDFLARE_API_TOKEN`: account permissions Workers Scripts, D1, R2,
55 Queues, Workflows, Containers and Access: Apps and Policies (all Edit), and
56 zone permission Workers Routes Edit on `diffinery.com`.
57- `CLOUDFLARE_ACCOUNT_ID`
58
59On each deploy, `scripts/bootstrap.sh` creates anything that is missing: the D1
60database, R2 bucket, queues and one Access application that covers only
61`/auth/access` (email-based account recovery). It then applies D1 migrations. `wrangler deploy`
62builds and pushes the runner image. On the first deploy, `ensure-secrets.sh`
63generates `MASTER_KEY` and the OIDC signing key inside the job, so neither
64value is ever printed.
65
66If the token can't read your Zero Trust organization, set a repository
67**variable** `ACCESS_TEAM_DOMAIN` (for example `codician.cloudflareaccess.com`).
68
69Email recovery uses Access's email one-time PIN, which works without any setup.
70You can also add GitHub as a login method under Zero Trust → Settings → Authentication.
71
72## Signing in and inviting people
73
74Everyone signs in with a passkey. There are no passwords.
75
761. **First sign-in for admins.** The emails in `BOOTSTRAP_ADMINS` (in
77 `apps/diffinery/wrangler.jsonc`) become site admins. Open
78 `https://diffinery.com/auth/access`, confirm your email with the one-time
79 code, and add a passkey on the page that follows.
802. **Inviting someone.** An admin or org owner creates an invite from
81 *Settings → Site administration*, an organization's *People* tab, or
82 `diffinery invite friend@example.com --org codician`. The person opens the
83 link, picks a username and creates a passkey, and they're in.
843. **Lost passkey.** A site admin clicks *Sign-in link* next to the person in
85 *Settings → Site administration* and sends them the one-time link, which
86 works for 24 hours. People can also recover through `/auth/access` by email.
874. **More devices.** Add passkeys under *Settings → Passkeys and sessions*, or
88 keep one in a password manager that syncs. The same page signs out other
89 browsers.
905. Repository and org roles (read, triage, write, maintain, admin) decide what
91 each person can do.
92
93Nobody needs a Cloudflare account. Git and the CLI use a personal token from
94*Settings → Tokens*.
95
96```sh
97git clone https://diffinery.com/codician/website.git # username: anything, password: your dfy_ token
98diffinery login && diffinery auth setup-git # or let the CLI handle credentials
99```
100
101## Agents
102
103Create an agent under *Settings → Agents* or with `diffinery agent create reviewer-bot --org codician`.
104Then point any MCP client at it:
105
106```json
107{ "mcpServers": { "diffinery": { "type": "http", "url": "https://diffinery.com/mcp", "headers": { "Authorization": "Bearer dfy_…" } } } }
108```
109
110Tools include `get_file`, `list_tree`, `commit_files` (no git client needed),
111`create_branch`, `create_pull_request` (with an `intent` for reviewers),
112`review_pull_request`, `merge_pull_request` (policy-guarded), issues,
113`list_workflow_runs` and `get_job_logs`, `rerun_workflow`, and
114`sandbox_create`/`sandbox_exec` for running builds and tests.
115
116Agent guardrails apply everywhere: git, REST, MCP and the web UI.
117
118- An agent's role is capped at write, and never exceeds its owner's role.
119- Agents can't push to the default branch or protected branches, and can't
120 merge there. Turn this on per branch with `agentsMayPush` or `agentsMayMerge`.
121- A pull request opened by an agent needs at least one human approval, and
122 approvals from agents don't count toward it.
123- Agents can't change branch protection or read or set secrets.
124
125## Workflows
126
127Put workflow files in `.github/workflows/` (or `.diffinery/workflows/`).
128Supported triggers are `push`, `pull_request`, `workflow_dispatch` (with
129inputs), `schedule` and `workflow_run` (up to three levels deep, like GitHub),
130with branch, tag and path filters. Supported workflow features:
131
132- `needs` and job outputs, `if` with `success()`, `failure()`, `always()` and
133 `cancelled()`, `strategy.matrix` with include and exclude, `fail-fast` and
134 `max-parallel`, `concurrency` with `cancel-in-progress`, and `timeout-minutes`.
135- `continue-on-error`, `defaults.run`, `env` at every level, `$GITHUB_OUTPUT`,
136 `$GITHUB_ENV`, `$GITHUB_PATH`, `$GITHUB_STEP_SUMMARY`, and masking of
137 secrets and `::add-mask::` values.
138- `secrets.*`, `vars.*`, `GITHUB_TOKEN` (a per-job token), and
139 `permissions: id-token: write` for OIDC (issuer `https://diffinery.com`).
140- `runs-on: ubuntu-latest` (and any `ubuntu-*` or `linux` label) runs in a
141 Cloudflare Container. `runs-on: [self-hosted, …]` jobs wait for a runner you
142 start with `diffinery runner -R owner/repo --labels gpu,macos`. Use that for
143 macOS, GPUs or private networks.
144- Reusable workflows: `jobs.<id>.uses: ./.github/workflows/x.yml` from the same
145 commit, or `owner/repo/.github/workflows/x.yml@ref` from another repository
146 on this instance (public or internal ones, or private ones with the same
147 owner). `with`, `secrets` (a mapping or `inherit`), `on.workflow_call`
148 outputs and nesting up to four levels deep all work. A matrix on the calling
149 job isn't supported yet.
150- Docker, on hosted runners and on self-hosted runners with Docker installed:
151 a job `container:` (steps and JavaScript actions run inside it), `services:`
152 (reachable by name from the job container, or on `localhost` through their
153 mapped ports, with health checks awaited), Docker container actions
154 (`docker://image` steps and actions with `runs.using: docker`, built from
155 their Dockerfile), and plain `docker build` / `docker run` / `docker compose`
156 in steps. Hosted runners start the Docker daemon the first time a job uses
157 it and pull Docker Hub images through `mirror.gcr.io`.
158
159**Not supported yet:** `actions/github-script`,
160Windows and macOS hosted runners, `pull_request_target`, `merge_group`, and
161`deepen-not` / partial clone filters. Pull request runs use the head commit
162rather than a test merge commit. A job that uses an unsupported feature fails
163with a clear message instead of misbehaving.
164
165## Developing
166
167```sh
168pnpm install
169pnpm -r build && pnpm -r typecheck && pnpm -r test
170pnpm --filter @diffinery/app db:migrate:local
171pnpm dev # http://localhost:8787, signed in as dev@diffinery.local
172pnpm --filter @diffinery/app e2e # wrangler dev + real git + the runner agent + Chromium
173```
174
175The end-to-end test does the following:
176
1771. Pushes a repository with real `git`.
1782. Runs its CI through the actual runner agent.
1793. Checks secret scanning and branch protection.
1804. Has an agent open a pull request that needs a human approval and a green
181 check, then merges it.
1825. Clones shallow, then deepens the clone.
1836. Drives the MCP server.
1847. In Chromium with a virtual authenticator, joins from an invite with a new
185 passkey, adds and removes passkeys, signs out and in, and uses an admin
186 sign-in link.
187
188Team preferences for product and design decisions are in
189[docs/PREFERENCES.md](docs/PREFERENCES.md).