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.

Docs: coordinator in the API reference, audit log and limits; webhook host rules in the reference and security overview (#101)

#98 documented the coordinator on the Projects page and #96 the webhook
host checks on the Webhooks page and in Limits. The API reference had
no descriptions for the coordinator endpoints or the posts endpoint's
coordinator field, and still said only that webhooks need https; the
audit log lacked project.coordinator and project.coordinate; Limits had
no coordinator row; and the security overview said nothing of the host
checks or the 20-webhook cap.


Claude-Session: https://claude.ai/code/session_016q5wwBFck8JHq14gHUM2CL

Co-authored-by: Claude <noreply@anthropic.com>
Canberk Demirci authored · GitHub committed signed1 parent dfa866bcommit c5bc7ee460891fb951214936b4b5608fdbf031a8Browse files
5 files changed+37−5
Files (5)
modifiedapps/diffinery/docs/agents/overview.md+1−1View file
@@ -215,7 +215,7 @@
215215 ## Agent tasks, agent review and Today
216216
217217 - **[Agent tasks](/docs/agents/tasks)**: on a repository's **Project** tab, give one of your agents a goal. Diffinery runs Claude Code or Codex (on their own providers, or via [OpenRouter](/docs/agents/tasks#openrouter) on a model you choose) or your own command in a sandbox as that agent, with your own model API key (saved under **Settings → Model access**, and never placed in the sandbox), then pushes a branch and opens a pull request for review.
218-- **[Projects](/docs/agents/projects)**: the Project tab shows a repository's tasks on a board, with a conversation, a shared memory every task reads, project instructions, and a limit on how many tasks run at once.
218+- **[Projects](/docs/agents/projects)**: the Project tab shows a repository's tasks on a board, with a conversation, a shared memory every task reads, project instructions, and a limit on how many tasks run at once. Your own coordinator agent can turn your posts there into tasks.
219219 - **[Agent review](/docs/agents/review)**: one of your agents reviews pull requests for bugs, security issues and logic errors, like Cursor's Bugbot. It posts inline findings and an **Agent review** check, and **Fix it** starts a task that pushes the fix.
220220 - **[Today](/docs/agents/today)**: the first page after signing in, also `GET /api/v3/diffinery/today` and the `today` MCP tool. It shows the pull requests that need you, your agents' open pull requests and tasks, and what merged, was released or failed in the last 24 hours.
221221
modifiedapps/diffinery/docs/reference/limits.md+1−0View file
@@ -240,6 +240,7 @@
240240 | Tasks running at once per repository | 1 to 10, default 3 | Set under **Settings → Project**. Further tasks, follow-ups and retries queue and start oldest first. Agent reviews don't count. See [Projects](/docs/agents/projects#how-many-at-once). |
241241 | Project memory | 40,000 characters, last 50 versions kept | ``422 memory is longer than 40000 characters``. |
242242 | Project instructions | 10,000 characters | ``422 `instructions` is longer than 10000 characters``. |
243+| Coordinator | 5 tasks per post, 20 per person and project per 24 hours | A coordinator run lasts at most 30 minutes and 200 model requests, within its token budget (1M by default); each run of a task it starts has its own budget (2M by default). Both budgets are 100,000 to 50,000,000 tokens. See [Coordinator](/docs/agents/projects#coordinator). |
243244 | Project conversation post | 20,000 characters | ``422 `body` is longer than 20000 characters``. The conversation API and `project_posts` return at most 100 posts per call. |
244245
245246 See [Agent tasks](/docs/agents/tasks). For the Today view's limits (repositories active in the last 30 days, at most 100 of them, 50 open pull requests each), see [Today](/docs/agents/today).
modifiedapps/diffinery/docs/security/audit-log.md+2−0View file
@@ -79,6 +79,8 @@
7979 | `task.stop` | Someone stops a task (its starter, its agent or a repository admin) | `owner/repo#<task number>` | none |
8080 | `project.memory` | Someone saves or restores the [project](/docs/agents/projects) memory | `owner/repo` | `version`, `length`, and `restored` (the version restored) for a restore |
8181 | `project.settings` | A repository admin changes **Settings → Project** | `owner/repo` | `max_running`, `instructions_length` |
82+| `project.coordinator` | Someone turns their [coordinator](/docs/agents/projects#coordinator) on, changes it or turns it off | `owner/repo` | `enabled`, and when on `agent`, `runtime`, `provider` and `model` (OpenRouter), `tokens_per_run`, `tokens_per_task` |
83+| `project.coordinate` | Someone's coordinator is given a post to handle | `owner/repo` | `post`, `task`, `agent`, `runtime` |
8284 | `review.settings` | A repository admin turns [agent review](/docs/agents/review) on or off, or changes its settings | `owner/repo` | `enabled`, `agent`, `runtime`, `trigger`, `autofix`, `daily_limit` |
8385 | `review.request` | A person asks for an agent review with **Request agent review** or `POST /repos/{owner}/{repo}/pulls/{number}/diffinery/agent-review` | `owner/repo#<pull request number>` | none |
8486 | `model_key.set`, `model_key.delete` | You save, replace or delete a model API key under **Settings → Model access** (or `PUT`/`DELETE /api/v3/diffinery/model-keys/{provider}`) | the provider (`anthropic`, `openai` or `openrouter`) | `hint` (the key's last four characters) and `opted_in_at` (when you accepted the provider's terms) for `model_key.set` |
modifiedapps/diffinery/docs/security/overview.md+1−1View file
@@ -258,7 +258,7 @@
258258
259259 ## Webhook signatures
260260
261-Webhook payload URLs must use HTTPS. Every delivery is signed with an HMAC-SHA256 of the body using the webhook's secret, sent as `X-Hub-Signature-256: sha256=<hex>`, the same scheme as GitHub. Diffinery does not follow redirects from your endpoint and gives it 10 seconds to respond. A delivery that fails with a network error or timeout, `408`, `429` or a `5xx` status is retried up to five times, after 1 minute, 5 minutes, 30 minutes, 1 hour and 3 hours, with the same `X-GitHub-Delivery` ID, so your endpoint can deduplicate. Retries stop when the webhook is deleted. See [Webhooks](/docs/repositories/webhooks).
261+Webhook payload URLs must use HTTPS and name a public host: `localhost`, private, loopback and link-local addresses, and Diffinery's own host are refused when the webhook is created and checked again on every delivery, so a webhook can't reach the internal network or call Diffinery itself. A repository can have at most 20 webhooks. Every delivery is signed with an HMAC-SHA256 of the body using the webhook's secret, sent as `X-Hub-Signature-256: sha256=<hex>`, the same scheme as GitHub. Diffinery does not follow redirects from your endpoint and gives it 10 seconds to respond. A delivery that fails with a network error or timeout, `408`, `429` or a `5xx` status is retried up to five times, after 1 minute, 5 minutes, 30 minutes, 1 hour and 3 hours, with the same `X-GitHub-Delivery` ID, so your endpoint can deduplicate. Retries stop when the webhook is deleted. See [Webhooks](/docs/repositories/webhooks).
262262
263263 ## Audit log
264264
modifiedapps/diffinery/src/docs/api-meta.ts+32−3View file
@@ -65,6 +65,9 @@
6565 "POST /repos/{owner}/{repo}/diffinery/project/posts": { scope: "write", sessionOnly: true },
6666 "PUT /repos/{owner}/{repo}/diffinery/project/memory": { scope: "write", sessionOnly: true },
6767 "PUT /repos/{owner}/{repo}/diffinery/project/settings": { scope: "write", sessionOnly: true },
68+ "PUT /repos/{owner}/{repo}/diffinery/project/coordinator": { scope: "write", sessionOnly: true },
69+ "DELETE /repos/{owner}/{repo}/diffinery/project/coordinator": { scope: "write", sessionOnly: true },
70+ "POST /repos/{owner}/{repo}/diffinery/project/posts/{n}/handle": { scope: "write", sessionOnly: true },
6871 "PUT /repos/{owner}/{repo}/diffinery/agent-review": { scope: "admin", sessionOnly: true },
6972 "POST /diffinery/agents/{login}/tokens": { scope: "admin", sessionOnly: true },
7073 "DELETE /diffinery/agents/{login}": { scope: "admin" },
@@ -1322,7 +1325,7 @@
13221325 "GET /repos/{owner}/{repo}/hooks": { summary: "List webhooks", description: "Secrets are never returned.", access: "admin" },
13231326 "POST /repos/{owner}/{repo}/hooks": {
13241327 summary: "Create a webhook",
1325- description: "Audited as `repo.webhook.create`.",
1328+ description: "`config.url` must be `https://` to a public host: `localhost`, single-label, `.local` and `.internal` names, private, loopback and link-local addresses get 422 `webhook URL must be a public host`, and Diffinery's own host 422 `webhook URL can't point at Diffinery itself`. At most 20 webhooks per repository (422). Audited as `repo.webhook.create`.",
13261329 access: "admin",
13271330 status: 201,
13281331 body: { config: ["object", "`{ url, secret }`. The secret signs each delivery (`X-Hub-Signature-256`).", true], events: ["string[]", "Event names, or `[\"*\"]` (default) for all."] },
@@ -1456,11 +1459,37 @@
14561459 },
14571460 "POST /repos/{owner}/{repo}/diffinery/project/posts": {
14581461 summary: "Post to the project conversation",
1459- description: "Markdown, at most 20,000 characters (422 otherwise). A person in a browser session only. 403 on an archived repository.",
1462+ description: "Markdown, at most 20,000 characters (422 otherwise). If you have a coordinator in this project, it handles the post unless you send `coordinator: false`; the response's `coordinator` says whether it did. A person in a browser session only. 403 on an archived repository.",
14601463 access: "write",
14611464 status: 201,
1462- body: { body: ["string", "The post.", true] },
1465+ body: { body: ["string", "The post.", true], coordinator: ["boolean", "`false` to post without handing it to your coordinator."] },
14631466 },
1467+ "POST /repos/{owner}/{repo}/diffinery/project/posts/{n}/handle": {
1468+ summary: "Hand a post to your coordinator",
1469+ description: "Your coordinator handles post `n`, anyone's, on your model key. Each post is handled once. A person in a browser session only. Audited as `project.coordinate`. See /docs/agents/projects#coordinator.",
1470+ access: "write",
1471+ },
1472+ "GET /repos/{owner}/{repo}/diffinery/project/coordinator": {
1473+ summary: "Get your coordinator",
1474+ description: "Your coordinator's settings in this project: `enabled`, `agent`, `runtime`, `command`, `provider`, `model`, `tokens_per_run`, `tokens_per_task`, `opted_in_at`, `updated_at`. Needs write access.",
1475+ access: "write",
1476+ },
1477+ "PUT /repos/{owner}/{repo}/diffinery/project/coordinator": {
1478+ summary: "Turn on or change your coordinator",
1479+ description: "Your agent then reads your posts and turns them into tasks, on your model key. Each coordinator starts at most 5 tasks per post and 20 per 24 hours in the project. A person in a browser session only. Audited as `project.coordinator`. See /docs/agents/projects#coordinator.",
1480+ access: "write",
1481+ body: {
1482+ agent: ["string", "The login of an agent you manage with write access to the repository.", true],
1483+ runtime: ["string", "`claude-code` (default), `codex` or `custom`."],
1484+ command: ["string", "For `custom`: the shell command."],
1485+ provider: ["string", "`openrouter` to run Claude Code or Codex via OpenRouter."],
1486+ model: ["string", "With `provider: openrouter`: the OpenRouter model id."],
1487+ tokens_per_run: ["integer", "Token budget of each coordinator run, 100,000 to 50,000,000 (default 1,000,000)."],
1488+ tokens_per_task: ["integer", "Token budget of each run of a task it starts, 100,000 to 50,000,000 (default 2,000,000)."],
1489+ accept: ["boolean", "Must be `true`: confirms your coordinator and its tasks use your model key.", true],
1490+ },
1491+ },
1492+ "DELETE /repos/{owner}/{repo}/diffinery/project/coordinator": { summary: "Turn off your coordinator", description: "A person in a browser session only. Audited as `project.coordinator`.", access: "write" },
14641493 "GET /repos/{owner}/{repo}/diffinery/project/memory": {
14651494 summary: "Get the project memory",
14661495 description: "The current memory and its last 50 `versions` (`version`, `updated_by`, `updated_at`, `length`). Every agent task reads it as background notes; agent reviews don't. Needs write access.",