Security
What Fwozen can and cannot see.
Fwozen never reads your code. Here is every permission we ask for, what it lets us do, and what it does not.
This page is written to be forwarded to a security reviewer. If something here is not checkable, tell us and we will fix the page: [email protected].
Permission by permission
| Permission | Level | Why we need it | What this does not allow |
|---|---|---|---|
| checks | write | Create and update the fwozen check run that blocks the merge. | Reading file contents, or writing anything except our own check runs. |
| statuses | write | The legacy fwozen/status mode, for orgs still on classic branch protection. | Reading code, or modifying another app’s statuses. |
| pull_requests | read | List open pull requests so we know which head SHAs to write to. | Reading diffs. We do not request patch contents. |
| contents | read | GitHub requires it to deliver push events and to read the default branch ref. | We never call the contents API for file data. This permission is the single most common misreading of a GitHub App’s scope — see the note below. |
| metadata | read | Repository names and visibility. Mandatory for every GitHub App. | — |
| merge_queues | read | Receive merge_group events so a queued pull request cannot slip through a freeze. | Modifying the queue. |
| Organization members | read | Verify that the person clicking Freeze is actually in your org. | Reading emails or profile data beyond membership. |
What we never ask for
contents: writeNever, under any circumstance. Fwozen does not write to your repositories.
administration: writeNot in the core set. One-click ruleset setup uses your own admin authorization, which is kept encrypted so the rule can be repaired, and which you can revoke in GitHub at any time.
Slack user-token scopesNone. The Slack app is bot-only.
Slack message historyNone. Fwozen reads its own slash commands and nothing else.
issues, workflows, deployments, secrets, packages, actionsNone of them.
The comparison
MergeFreeze requires Administration: read & write for its branch-protection freeze method. We don’t — administration is not in our core permission set at all, and one-click ruleset setup uses your own authorization rather than the app’s.
Fwozen requests
- checkswrite
- statuseswrite
- pull_requestsread
- contentsread
- metadataread
- merge_queuesread
- Organization membersread
- No
administration, nocontents: write.
MergeFreeze requests
- Administrationread & write
- Metadataread
- Pull requestsread
- Commit statusesread & write
- Organization membersread
- Administration: read & write is required for their branch-protection freeze method.
Why contents: read looks scarier than it is
This is the single most common misreading of a GitHub App’s scope, so it gets its own section. GitHub bundles push event delivery behind contents: read. We need push events to know when a pull request’s head commit moved, because that is the commit the fwozen check has to be written to.
The permission would also allow reading file contents. We do not do that. Rather than ask you to take that on faith, here is the complete list of GitHub endpoints Fwozen calls — short enough to audit, and there is nothing on it that returns a byte of your code.
If a future feature ever needed a content endpoint, it would appear in this table and in the changelog before it shipped.
| Endpoint | Why |
|---|---|
| POST /repos/{owner}/{repo}/check-runs | Write the fwozen check run |
| PATCH /repos/{owner}/{repo}/check-runs/{id} | Update it on thaw or exception |
| GET /repos/{owner}/{repo}/pulls | Enumerate open pull requests |
| GET /repos/{owner}/{repo}/rules/branches/{branch} | Read the effective rules to verify enforcement |
| GET /installation/repositories | Repository names and visibility |
| GET /orgs/{org}/members/{username} | Authorization check |
| POST /repos/{owner}/{repo}/statuses/{sha} | Legacy status mode only, when the org selects it |
If Fwozen goes down
Yes, and that’s the design. The fwozen check is required, so if we can’t report a result the check stays pending and the merge waits. A freeze app that failed open would be worthless.
What we owe you instead is not going down: a reconciler that re-asserts state every few minutes, a public status page, and freeze propagation that is typically under a minute — we publish the measured number rather than a target we haven’t load-tested.
If you’d rather fail open during an outage, remove the required check. One setting, your call.
What we store, and what we don’t
Stored
- Repository ids, names, and visibility
- Branch names
- Pull request numbers and head commit SHAs
- GitHub logins of the people who act in Fwozen
- Freeze reasons, exception reasons, and schedule names — all user-authored text
- Slack workspace, channel, and user ids
Freeze and exception reasons are text your team writes, and they are shown on pull requests and in Slack. Don’t put secrets in them.
Not stored
- Source code, in any form
- Diffs or patch contents
- Commit messages
- Pull request titles or bodies
- Committer email addresses
- Any token in plaintext
This is enforced at ingest rather than promised: webhook payloads are projected the moment they arrive, commits are reduced to SHAs, and titles, bodies, and email addresses are dropped before anything is persisted. A unit test over real GitHub push and pull_request fixtures asserts those keys are absent.
Where
Railway, US region. Postgres 17, encrypted at rest; TLS in transit. No production data on laptops.
How long
Freeze history follows your plan: 7 days on Free, 90 on Team, unlimited on Business. Retention is a read filter, not a delete — nothing is destroyed, and upgrading restores the full history.
Deletion
Delete your organisation in the app and the data goes. After cancellation, private data is purged within 30 days. On request, sooner.
Subprocessors
The same table appears in the privacy policy, rendered from the same source, so the two cannot drift.
| Subprocessor | What it does | What it sees |
|---|---|---|
| Railway | Application hosting and Postgres, US region | Everything Fwozen stores |
| WorkOS | Authentication, sessions, SSO and directory sync | Name, email, GitHub login, org membership |
| Stripe | Payments, invoices, tax | Billing contact, address, card token (Stripe holds the card, we never see it) |
| Slack | The Slack app and freeze notifications | Workspace and channel ids, freeze reasons posted to your channel |
| GitHub | The product itself | Repository, branch, and pull request metadata; check run results |
| PostHog | Product analytics on explicit events, autocapture off | Anonymous usage events, org id, no repository names |
Compliance, honestly
SOC 2 Type I is planned for Q4 2026. We are not SOC 2 certified today and won’t say otherwise.
If your procurement process requires a report today, we are the wrong choice today, and we would rather you found that out on this page than in week three of an evaluation.
What we do have
- Encryption in transit and at rest
- Least-privilege GitHub scopes, itemised above
- An append-only audit log with a real actor on every row
- Tenant isolation with org-scoped queries, verified by a generated cross-tenant test suite
- Read-only, recorded support impersonation
- No production data on developer machines
Reporting a vulnerability
Mail [email protected]. We acknowledge within 72 hours, we will not threaten you with lawyers, and we will credit you unless you ask us not to.
A PGP key will be published at /.well-known/security.txt before launch. If you need it sooner, ask and we will send it.