Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/docs/docs/guides/existing-repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ Connect the repo to a project. The platform detects vendored facility files
and records the current state as the fingerprint baseline (**adopt**), so
integrity checking starts from reality, not from an ideal.

An adopted repository has been running agents without Facility's assumptions
being checked. Verify the
[repository settings that back the gates](../reference/security#repository-settings-that-back-the-gates)
now: fingerprinting records what the repository is, not whether its human gates
can actually hold.

## Step 2 — money first

Switch the repo's provider secrets to gateway virtual keys
Expand Down
9 changes: 9 additions & 0 deletions apps/docs/docs/guides/kickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ From zero to a working factory:
`facility/kickstart` and opens a pull request. Manual steps that only you
can do are in the PR body: create the agent token secret, protect the
default branch, confirm App permissions, and complete preview configuration.
Work through the
[repository settings that back the gates](../reference/security#repository-settings-that-back-the-gates)
while you are there — several of the human gates are held up by repository
configuration rather than by Facility.
6. **Configure a live PR preview.** Choose a Facility-owned preview or an
external deployment adapter. For a native preview, provide an immutable
image, optional command, internal port, readiness path, and TTL. Add
Expand All @@ -33,6 +37,11 @@ From zero to a working factory:
preview, review the PR, and squash-merge it in GitHub. On merge the
fingerprint baseline is recorded and the project reports **system ok**.

Confirm the
[repository settings that back the gates](../reference/security#repository-settings-that-back-the-gates)
are in place before this point: the gates have to exist before the first agent
run, not after the first surprise.

Now open an issue and comment `/architect`. The agent's task-specific checklist
and final plan appear in one comment. Continue entirely from GitHub: comment
`/builder` to approve that plan, or `/architect <feedback>` to request another
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ Actions in the repository lane and by Facility in the platform lane.

Run `facility doctor --run-guards --github` after committing. It checks the
manifest, generated workflows, configured agent models and authentication,
preview variables/secrets, deterministic guards, and branch protection.
preview variables/secrets, deterministic guards, and branch protection. It does
not check every setting the human gates depend on — see
[repository settings that back the gates](../reference/security#repository-settings-that-back-the-gates)
for the full list and for what the command does not yet cover.

## Platform lane

Expand Down
65 changes: 65 additions & 0 deletions apps/docs/docs/reference/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,69 @@ Agents never approve, never merge, never push to protected branches. Every
outward action carries a named principal. Every merge carries a human
decision.

## Repository settings that back the gates

Facility enforces part of the paragraph above — generated workflows declare their
own least-privilege `permissions:` block, refuse bot-authored events, skip fork
heads, and pin every action to a SHA. The rest is GitHub repository
configuration, which Facility cannot enforce from inside a workflow. The split is
invisible: a repository can run the whole loop, produce plans and pull requests
and reviews, look completely correct, and still let an automated actor satisfy a
human gate. Configure these before the first agent run.

| setting | required value | enforced by | what skipping it costs |
|---|---|---|---|
| **Allow GitHub Actions to create and approve pull requests** (organization and repository) | disabled | the setting | Breaks *agents never approve*. The review workflow only comments — but that is a prompt, and the job runs with `pull-requests: write`. With this enabled, `GITHUB_TOKEN` can submit an approving review and satisfy the required human approval on its own pull request. |
| **Workflow permissions** — the default `GITHUB_TOKEN` scope | read repository contents and packages permissions | the setting, for workflows you add later; Facility, for the ones it generates | Every generated workflow declares an explicit job-level `permissions:` block, so this default does not change what the crew receives. It governs the next workflow added without one, which otherwise gets write access to everything. |
| **Default branch protected, pull request required** | enabled | the setting | Breaks *agents never push to protected branches* by making the sentence vacuous. Facility commits kickstart to `facility/kickstart` and builders push semantic branches, but the address-review and doctor workflows hold `contents: write`. Nothing structural stops a push to an unprotected default branch. |
| **Required approvals** | at least 1 | the setting | Breaks *every merge carries a human decision*. The count alone is not enough: pair it with the Actions-approval setting above, or a token can satisfy it. |
| **Dismiss stale approvals when new commits are pushed** | enabled | the setting | The address-review agent pushes commits to the pull request branch after a human reviews it. Without dismissal, an approval given for one diff silently covers code the approver never read. |
| **Required status checks**, with branches up to date (or a merge queue) | the repository's own checks, plus the guards runner | Facility runs them; the setting makes them blocking | The agent verified the change against these checks. Unless they are required, nothing stops a merge that ignores that verification — and a green check against a stale base is not evidence about what lands. |
| **Restrict who can push, and who can bypass required pull requests** | humans and teams only — never the Facility GitHub App | the setting | Breaks *agents never merge*. The App installation holds `Contents: Read and write`; a bypass entry converts that permission into merge authority over the default branch. |
| **Do not allow bypassing the above settings** | enabled | the setting | An administrator, or an App acting with administrative rights, otherwise skips every row in this table. |

Rulesets are the modern equivalent of branch protection and satisfy the same
rows. Audit their **bypass list** with equal suspicion: a bypass entry is the
setting saying "except for this actor".

### Checking them through the API

| setting | endpoint and field | needs |
|---|---|---|
| Actions may approve pull requests; default `GITHUB_TOKEN` scope | `GET /repos/{owner}/{repo}/actions/permissions/workflow` → `can_approve_pull_request_reviews` must be `false`, `default_workflow_permissions` must be `"read"` | repository admin |
| The same two, organization-wide | `GET /orgs/{org}/actions/permissions/workflow` | organization admin, and an `admin:org` token scope |
| Approvals, stale dismissal, required checks, push restrictions, admin enforcement | `GET /repos/{owner}/{repo}/branches/{branch}/protection` → `required_pull_request_reviews.required_approving_review_count`, `.dismiss_stale_reviews`, `required_status_checks.contexts` and `.strict`, `restrictions`, `enforce_admins` | repository admin |
| The rules actually in force on a branch, including rulesets | `GET /repos/{owner}/{repo}/rules/branches/{branch}` | read access — no admin required |

The organization setting constrains the repository one: if the organization
disables Actions approving pull requests, a repository cannot re-enable it. Check
both before concluding a repository is safe.

Two `404` responses from the protection endpoint mean different things, and the
message body is the difference: `Branch not protected` is an answer, `Not Found`
means the caller is not an admin and learned nothing.

### What `facility doctor --github` covers today

The command verifies that the required secrets and variables exist, and that the
default branch returns a branch-protection response at all. It does not read that
response. Worth adding, in rough priority order — each is a field in a call the
command already makes, or one extra call:

- `can_approve_pull_request_reviews` and `default_workflow_permissions`, from a
single `actions/permissions/workflow` call. Neither is checked today, and the
first is the setting that most directly defeats an advertised gate.
- The organization-level values of those two fields when the token carries
`admin:org`, reported as unknown rather than as a pass when it does not.
- `required_approving_review_count`, `dismiss_stale_reviews`, the required check
contexts, `strict`, `restrictions`, and `enforce_admins` — all already present
in the protection response the check fetches and discards.
- `GET /repos/{owner}/{repo}/rules/branches/{branch}` as a fallback: a branch
protected purely by a ruleset returns `404` from the classic endpoint, so the
command currently reports a correctly protected repository as a failure.
- Distinguishing `Branch not protected` from `Not Found`, so a non-administrator
does not get the same failure as a genuinely unprotected branch.

---

Report vulnerabilities per [SECURITY.md](https://github.com/theam/facility/blob/main/SECURITY.md).