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: agent code per commit, protected concurrency, search and paging limits, encrypted webhook secrets, key rotation and image proxy in descriptions (#213)

* Docs: what the security audit's batches 3 and 4 changed, where agents look it up
test-bot authored 1 parent 1d727ebcommit 648c2d700ea2ee0be05c40eafa8309f231e0eafaBrowse files
10 files changed+12−9
Files (10)
modifiedapps/diffinery/docs/actions/overview.md+1−0View file
@@ -404,6 +404,7 @@
404404 - The group is evaluated when the run is created, with the `github`, `inputs` and `vars` contexts. Groups are scoped to the repository, so two workflows can share a group.
405405 - With `cancel-in-progress: true`, creating a run cancels every unfinished run (queued or in progress) in the same group, with the reason `cancelled by a newer run in the same concurrency group`.
406406 - Without it, a new run waits while another run in the group is in progress, checking once a minute. After 6 hours of waiting it starts anyway.
407+- A run or job on a protected branch is cancelled or replaced through its group only by runs on that same branch. A workflow on another branch that names the same group, `cancel-in-progress` or not, waits for it instead, so a branch can't cancel a protected branch's deploy.
407408
408409 Differences from GitHub: GitHub keeps at most one pending run per group and cancels older pending ones; Diffinery lets several runs wait and starts them as the group frees up. If the group expression fails to evaluate, the run has no concurrency group.
409410
modifiedapps/diffinery/docs/actions/secrets-and-variables.md+1−1View file
@@ -308,7 +308,7 @@
308308
309309 This is a difference from GitHub worth understanding. GitHub withholds secrets from `pull_request` runs of pull requests that come from forks. Diffinery has no forks: every pull request comes from a branch in the same repository, pushed by someone with write access, so `pull_request` runs get the repository's secrets, and they run the workflow files from the pull request's own test merge commit. The exception is a pull request [imported from GitHub](/docs/migrate-from-github) whose head was in a fork (its head shows as `owner:branch`): its runs get no secrets and a read-only token, as on GitHub. `pull_request_target` runs the base branch's workflow on the base commit, also with secrets, so a pull request can't change what it runs.
310310
311-Runs of code an agent wrote are the other exception. A run whose trigger was an agent, whose branch an agent pushed last, or whose pull request an agent opened or pushed to gets no repository or organization secrets, only its job token. An agent with write access could otherwise push a workflow that sends them anywhere. A repository admin who is a person can allow it under **Settings → Actions** (**Give runs of agent code the repository's and organization's secrets**), or with `PUT /repos/{owner}/{repo}/actions/permissions/workflow` and `"agent_runs_use_secrets": true`. The change is audited. Environment secrets follow the environment's own rules.
311+Runs of code an agent wrote are the other exception. A run whose trigger was an agent, whose branch or tag an agent pushed last, whose pull request an agent opened or pushed to, or (off the default branch) whose code includes a commit an agent pushed that hasn't landed on the default branch yet gets no repository or organization secrets, only its job token. A person pushing on top of an agent's commits, tagging them or dispatching a run on them doesn't change that; see [Runs whose code came from an agent](/docs/actions/workflow-permissions#runs-whose-code-came-from-an-agent). An agent with write access could otherwise push a workflow that sends them anywhere. A repository admin who is a person can allow it under **Settings → Actions** (**Give runs of agent code the repository's and organization's secrets**), or with `PUT /repos/{owner}/{repo}/actions/permissions/workflow` and `"agent_runs_use_secrets": true`. The change is audited. Environment secrets follow the environment's own rules.
312312
313313 This includes the default branch once agents land changes on it. When an agent's pull request lands through [auto-merge](/docs/collaboration/pull-requests#auto-merge), the merge is the agent's push, so the branch was last pushed by an agent: every run on it from then on is agent code until a person pushes or merges there. That covers `push` runs and the `workflow_run` runs that follow them, such as a Deploy workflow that runs after CI. Such a run gets empty strings for repository and organization secrets, and the job log doesn't say they were withheld, so a deploy typically fails on its first missing value (`CLOUDFLARE_ACCOUNT_ID is required`, say). If agents land on your default branch, choose one of these:
314314
modifiedapps/diffinery/docs/actions/workflow-permissions.md+3−2View file
@@ -308,8 +308,9 @@
308308 A run is treated as agent code when:
309309
310310 - an agent triggered it,
311-- an agent last pushed the branch it runs on (a merge that Diffinery lands for an agent through auto-merge counts as the agent's push), or
312-- for pull request and merge queue events, an agent opened the pull request or last pushed its head branch.
311+- an agent last pushed the branch or tag it runs on (a merge that Diffinery lands for an agent through auto-merge counts as the agent's push),
312+- for pull request and merge queue events, an agent opened the pull request or last pushed its head branch, or
313+- off the default branch, the code it runs includes a commit an agent pushed that hasn't landed on the default branch yet. A person pushing on top of an agent's commits, tagging them, making a release from them or dispatching a workflow on them doesn't make that code a person's. Once a person merges the commits into the default branch (or a person lands them there), they count as that person's, since the default branch's runs go by who moved it.
313314
314315 A re-run of such a run keeps this, whoever starts the re-run, and so does a `workflow_run` run that follows such a run, since it acts on what that run produced (its artifacts and outputs). The jobs of these runs act as an agent, so the [agent guardrails](/docs/agents/overview) hold for code an agent wrote, even when a person triggered the run:
315316
modifiedapps/diffinery/docs/actions/workflow-syntax.md+1−1View file
@@ -220,7 +220,7 @@
220220 cancel-in-progress: true
221221 ```
222222
223-`group` is evaluated when the run is created, with the `github`, `inputs` and `vars` contexts. `cancel-in-progress` is `true`, `false` (the default) or an expression evaluated with the same contexts. With `true`, a new run cancels every unfinished run in its group. With `false`, the new run waits while another run in the group is in progress. Groups are shared by all workflows of the repository. See [Concurrency](/docs/actions/overview#concurrency) for the differences from GitHub.
223+`group` is evaluated when the run is created, with the `github`, `inputs` and `vars` contexts. `cancel-in-progress` is `true`, `false` (the default) or an expression evaluated with the same contexts. With `true`, a new run cancels every unfinished run in its group. With `false`, the new run waits while another run in the group is in progress. Groups are shared by all workflows of the repository, but a run on a protected branch is only cancelled by runs on that same branch; runs from elsewhere wait for it. See [Concurrency](/docs/actions/overview#concurrency) for the differences from GitHub.
224224
225225 A value that is neither a string nor a mapping with `group` fails with `concurrency must be a group string or a mapping with 'group'`.
226226
modifiedapps/diffinery/docs/agents/overview.md+1−1View file
@@ -130,7 +130,7 @@
130130 | No variable changes | Creating, changing or deleting repository or environment Actions variables fails with `agents cannot manage variables`, whatever the agent's role. Agents with write or higher can still list and read variables. | None. |
131131 | No environment changes | Creating, changing or deleting an environment or its deployment branch policies fails with `only repository admins (people, not agents) can manage environments`. | None. |
132132 | Agent reports never satisfy required checks | Commit statuses and check runs an agent reports are shown on the commit, but never count toward a required status check, and aren't counted when a deployment checks the commit's statuses. A failure reported by the real source still fails. | None. Report required checks from workflows or from a person's token. |
133-| Workflow runs of agent code act as the agent | A run counts as coming from an agent when an agent triggered it, an agent pushed the branch it runs on last, or (for pull request events) an agent opened the pull request or last pushed its head branch. Re-runs of such a run keep this, whoever starts them, and so do `workflow_run` runs that follow it, since they act on what it produced. The run's jobs act as an agent: their `GITHUB_TOKEN` gets the guardrails in this table, so it can't push or merge to the default or protected branches even with `permissions: contents: write`, and their deployments count as an agent's. Agent-written code can't reach a protected branch through a workflow. | The same per-branch settings, **Allow agents to push directly** and **Allow agents to merge pull requests**. |
133+| Workflow runs of agent code act as the agent | A run counts as coming from an agent when an agent triggered it, an agent pushed the branch or tag it runs on last, (for pull request events) an agent opened the pull request or last pushed its head branch, or (off the default branch) its code includes a commit an agent pushed that a person hasn't merged into the default branch yet, whoever pushed, tagged or dispatched on top. Re-runs of such a run keep this, whoever starts them, and so do `workflow_run` runs that follow it, since they act on what it produced. The run's jobs act as an agent: their `GITHUB_TOKEN` gets the guardrails in this table, so it can't push or merge to the default or protected branches even with `permissions: contents: write`, and their deployments count as an agent's. Agent-written code can't reach a protected branch through a workflow. | The same per-branch settings, **Allow agents to push directly** and **Allow agents to merge pull requests**. |
134134 | No repository or organization secrets for agent runs | A run whose code came from an agent (see the row above) gets no repository or organization secrets, only its job token, so an agent can't push a workflow that sends them elsewhere. Once an agent lands a pull request on the default branch through auto-merge, runs on that branch are agent code too, so a deploy there gets no repository secrets. | A repository admin who is a person ticks **Give runs of agent code the repository's and organization's secrets** under **Settings → Actions** (`agent_runs_use_secrets`), or, tighter, keeps deploy credentials in an environment limited to protected branches (next row). See [Which runs receive secrets](/docs/actions/secrets-and-variables#which-runs-receive-secrets). |
135135 | No environment secrets for agent runs on open environments | A job with `environment:` in a run whose code came from an agent (see the row above) gets the environment's secrets only when the environment is limited to protected branches. Pull request runs never get environment secrets. | Limit the environment to **protected branches**. |
136136 | Agent tags don't match environment tag rules | A tag last pushed or moved by an agent never matches an environment's tag pattern, so it can't deploy to that environment. | Have a person push the tag. |
modifiedapps/diffinery/docs/reference/limits.md+1−0View file
@@ -53,6 +53,7 @@
5353 | Commits in a comparison or pull request | 250 in total, 100 per page | `GET /repos/{owner}/{repo}/compare/{base}...{head}` and `GET .../pulls/{n}/commits`. |
5454 | Refs per page | 100 by default | `GET .../git/refs` and `.../git/matching-refs/{ref}`. |
5555 | Environment variables per page | 10 by default, 100 at most | `GET .../environments/{env}/variables`. |
56+| Commits listed by paging | The first 10,000 commits from where the listing starts | `GET /repos/{owner}/{repo}/commits` answers `422 In order to keep the API fast for everyone, pagination is limited for this resource.` past that, as GitHub does, and the web commit list stops there too. To go further back, start a new listing at an older commit with `sha=`. |
5657 | Commits searched for a path | The latest 2,000 commits | `GET /repos/{owner}/{repo}/commits?path=...` and the history of a file in the web UI only look at the 2,000 most recent commits. |
5758 | Package versions per page | 30 by default, 100 at most | `GET .../packages/container/{name}/versions`. |
5859 | Repository lists | 500 repositories | `GET /user/repos`, `/users/{login}/repos`, `/orgs/{org}/repos` page over at most 500 repositories (most recently pushed first). |
modifiedapps/diffinery/docs/reference/rest-api.md+1−1View file
@@ -633,7 +633,7 @@
633633 | --- | --- |
634634 | Query length | 256 characters (`422 The search is longer than 256 characters.`). An empty `q` returns `422 Validation Failed`. |
635635 | Results you can page to | The first 1,000 (`422 Only the first 1000 search results are available`) |
636-| Repositories one issue search looks at | 50 readable repositories, taken from the up to 500 most recently pushed repositories you can see. When more matched, `incomplete_results` is `true`; narrow the search with `repo:` or `org:`. |
636+| Repositories one issue search looks at | 50 readable repositories (10 without signing in), taken from the up to 500 most recently pushed repositories you can see. When more matched, `incomplete_results` is `true`; narrow the search with `repo:` or `org:`. |
637637 | Repositories one repository search checks | 300 candidates, from the same up to 500. More sets `incomplete_results`. |
638638 | Issues scanned per repository | The 5,000 most recent issues and pull requests |
639639 | Comments read for `in:comments` | The 2,000 most recent comments per repository |
modifiedapps/diffinery/docs/repositories/webhooks.md+1−1View file
@@ -8,7 +8,7 @@
88 ## How webhooks work
99
1010 - Webhooks belong to one repository. You need the **admin** role on it to create, list or delete them.
11-- Each webhook has a payload URL (HTTPS only), a list of events, and a signing secret.
11+- Each webhook has a payload URL (HTTPS only), a list of events, and a signing secret. The secret is stored encrypted with a key of its own and is never shown again after creation.
1212 - When an event happens, Diffinery puts it on an internal queue. Within a few seconds the queue delivers it to every active webhook of the repository that subscribes to that event, in parallel.
1313 - Each delivery is signed with an HMAC-SHA256 of the body using the webhook's secret, in the `X-Hub-Signature-256` header, the same scheme GitHub uses.
1414 - A delivery that fails with a network error, a timeout, `408`, `429` or a `5xx` status is retried up to five times over about four and a half hours, with the same `X-GitHub-Delivery` id. See [Retries](#retries).
modifiedapps/diffinery/docs/security/overview.md+1−1View file
@@ -1,6 +1,6 @@
11 ---
22 title: Security overview
3-description: Diffinery's security model end to end: sign-in, sessions, tokens, agent guardrails, branch and push protection, secrets, Actions, webhooks and auditing.
3+description: Diffinery's security model: sign-in, sessions, tokens, agent guardrails, branch and push protection, secrets and key rotation, Actions, webhooks, the Markdown image proxy and auditing.
44 ---
55
66 This page explains how Diffinery protects accounts, code and secrets, from how people sign in to how workflow secrets are encrypted and how every sensitive action is recorded. Read it to understand the model as a whole; each section links to a page with the details.
modifiedapps/diffinery/docs/self-hosting/configuration.md+1−1View file
@@ -1,6 +1,6 @@
11 ---
22 title: Configuration
3-description: Every setting of a self-hosted Diffinery: Worker variables and secrets, bindings, the dev environment, and the secrets and variables the deploy workflows use.
3+description: Every setting of a self-hosted Diffinery: Worker variables and secrets, bindings, the dev environment, the deploy workflows' secrets, and rotating MASTER_KEY with MASTER_KEY_PREV.
44 ---
55
66 A Diffinery instance is configured in four places: Worker variables in `apps/diffinery/wrangler.jsonc`, Worker secrets set with `wrangler secret put`, the Cloudflare bindings in the same `wrangler.jsonc`, and the secrets and variables of the repository whose workflows deploy it. This page lists every setting, its default and what it changes. To deploy for the first time, follow [Deploy your own Diffinery](/docs/self-hosting/deploy).