Docs

Gate

Org Policies & Freezes ​

Set one gate policy for every repository, lock the keys repositories may only tighten, and freeze releases on a schedule or on demand.

Each repository setting its own rules leaves gaps an auditor will find; org policies set one floor for the whole workspace, for platform and security teams.

Beta, early access. A workspace without access gets 403 FEATURE_EARLY_ACCESS and a link to request it.

An org policy is one gate policy for your whole workspace. The Release Gate merges it with each repository's bumpkit.yml. You can lock keys so a repository can only make them stricter. Change freezes and release windows stop releases at set times, and the version consistency check catches manifests and tags that disagree with the version being released.

This page covers four features. Three of them are behind their own flag:

Feature Flag
Org-wide policies org_policies
Release windows and change freezes change_freezes
Version consistency version_consistency
Replay-safe checks none

Org-wide policies ​

Beta, early access. A workspace without access gets 403 FEATURE_EARLY_ACCESS and a link to request it. Flag: org_policies.

An org policy is written exactly like a bumpkit.yml, with one extra top-level key: locked, a list of gate keys that repositories may only tighten.

YAML
locked:
  - block_on
  - require_checks
  - on_error
gate:
  block_on: [secret, data_loss, breaking]
  require_checks: [ci/test]
  on_error: fail
  security_alerts: high

You can lock any of these keys:

block_on, require_approval, on_error, require_checks, require_check_apps, security_alerts, vulnerabilities, break_glass, hotfix, blast_radius, deploy, cross_repo, timezone, windows, outside_windows, freezes, quality, exports, behavior, fix_tasks, stacks

An unknown key in locked is rejected when you save the policy. For the meaning of each key, see the bumpkit.yml Reference.

Where the org policy lives ​

You store the org policy in one of two ways:

  • In Bumpkit. You upload the policy text. Every change is saved as a new numbered version with a message, a diff and an audit record. The policy must be under 64 KB.
  • In a config repository. You point Bumpkit at a repository, such as acme/.github. The gate reads that repository's bumpkit.yml from its default branch every time it runs. The Bumpkit GitHub App must be installed on it, and the installation must belong to the same workspace.

If Bumpkit can't read or parse the org policy, the gate fails closed. The API returns 503 with code ORG_POLICY_UNAVAILABLE, and the GitHub check run fails with "Couldn't read the org policy".

When the org_policies flag is off, the gate ignores any stored org policy and uses only the repository's bumpkit.yml.

How the org policy and bumpkit.yml combine ​

For each key:

  • If the repository's bumpkit.yml doesn't set the key, the org policy's value applies.
  • If the key isn't locked, the repository's value replaces the org value.
  • If the key is locked, the stricter of the two applies. A value that would loosen the policy is ignored, and the gate decision says why with a reason named org_policy: <key> is locked.

How "stricter" works for each locked key:

Key Result when locked
block_on Union of both lists. The repository can add kinds, not remove them.
require_checks Union of both lists.
require_check_apps The org's issuers always apply. The repository can add checks with their own Apps, but can't drop a check or point it at another App; the decision says "bumpkit.yml can't remove or replace trusted check issuers; the org policy keeps them."
require_approval Org rules plus any extra rules from the repository.
on_error If the org sets fail, it stays fail.
security_alerts, vulnerabilities The repository can lower the severity threshold, not raise it.
break_glass If the org disallows overrides, the repository can't allow them.
hotfix The stricter of each setting: union of block_on, the smaller max_files, the shorter review_within, the stricter backports.
blast_radius Union of approve_areas. The org's max_depth always applies; a different value in bumpkit.yml is ignored with the note "bumpkit.yml can't loosen blast_radius.max_depth; the org policy's 2 applies."
timezone Must match the org's. A different value is ignored.
windows The repository can't change the org's release windows. Add freezes to narrow them. If the org sets no windows, the repository's apply.
outside_windows If the org sets block, it stays block.
freezes Org freezes plus any extra freezes from the repository.
fix_tasks The repository can only tighten it: off < approve < block. Unset counts as block.
deploy, cross_repo, quality, exports, behavior Must match the org's exactly. A different value is ignored.

See the effective policy ​

Run this in a repository to see the policy the gate applies there and where each value came from:

Terminal
bumpkit policy show --effective
Text
Org policy v4, merged with this repository's bumpkit.yml
KEY              VALUE                        FROM
block_on         - secret - data_loss - breaking - version_mismatch  org+repo (locked)
require_checks   - ci/test                    org (locked)
on_error         fail                         org (locked)
security_alerts  high                         repo
! bumpkit.yml can't remove breaking from block_on; the org policy keeps them.

The FROM column is one of default, org, repo, org+repo or not set. The command reads bumpkit.yml as it is at --base (default HEAD), the same way the gate reads it at a pull request's base. Use --file PATH to test an unsaved file, and --repository owner/name to override the repository detected from the origin remote.

The same merge is available from the API:

Terminal
curl -X POST https://bumpkit.dev/v1/policy/effective \
  -H "x-api-key: $BUMPKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"repository": "acme/api", "policy": "gate:\n  on_error: pass\n"}'
JSON
{
  "org_version": 4,
  "policy": "gate:\n  on_error: fail\n",
  "sources": [
    {"key": "on_error", "value": "fail", "from": "org", "locked": true, "note": "bumpkit.yml can't loosen on_error; the org policy's fail applies."}
  ]
}

org_repository is also set when the org policy is read from a config repository.

Change the org policy ​

Only workspace owners and admins can change the org policy. Anyone in the workspace can read it.

Terminal
bumpkit policy set org-policy.yml --message "Require CI everywhere"
bumpkit policy set --repository acme/.github
bumpkit policy set --clear

bumpkit policy set validates the file, shows the diff against the current version and asks you to confirm. Pass -y to skip the prompt. Pass exactly one of a file, --content YAML, --repository or --clear. Clearing saves a new, empty version, so the history is kept.

To review changes:

Terminal
bumpkit policy show
bumpkit policy history --limit 50
bumpkit policy diff v3 v4

bumpkit policy diff FROM [TO] compares two versions. TO defaults to the latest version.

You can also edit the org policy on the Policy page of the dashboard, under the Policy and freezes tab.

Every gate decision made under an org policy records the version it used. The POST /v1/gate response includes it as org_policy_version.

Release windows and change freezes ​

Beta, early access. A workspace without access gets 403 FEATURE_EARLY_ACCESS and a link to request it. Flag: change_freezes.

Release windows say when releases may ship. Change freezes say when they may not. You declare both under gate: in bumpkit.yml or in the org policy, and you can declare a freeze on demand from the CLI, the API or the dashboard.

Release windows ​

YAML
gate:
  timezone: America/New_York
  windows:
    - days: [mon-thu]
      from: "09:00"
      to: "17:00"
  outside_windows: approve
Key Description
timezone IANA timezone name for windows and freezes that don't set their own. Defaults to UTC.
windows[].days Days such as mon, tue, or a range such as mon-fri. Full names like monday also work. Omit for every day.
windows[].from, windows[].to Times like 09:00. from defaults to 00:00, to to 24:00.
windows[].timezone Overrides timezone for this window.
outside_windows approve (the default) asks for an approval outside the windows. block fails the gate.

Hotfixes may always ship outside the windows. See Hotfixes & Break Glass.

Scheduled freezes ​

Each freeze needs a name and one of three shapes: a date range, a list of dates, or a weekly schedule.

YAML
gate:
  timezone: America/New_York
  freezes:
    - name: Holiday freeze
      start: 2026-12-20
      end: 2027-01-02
    - name: Black Friday
      dates: [2026-11-27, 2026-11-30]
      mode: approve
    - name: Friday afternoons
      days: [fri]
      from: "15:00"
      hotfix: allow
Key Description
name Required. Shown on every gated pull request.
start, end A date range, both required. Dates like 2026-12-24 or times like 2026-12-24T18:00. A date-only end includes that whole day.
dates Individual days that are frozen all day.
days, from, to A weekly freeze, written like a window.
timezone Overrides the policy's timezone.
mode block (the default) fails the gate. approve asks for an approval.
hotfix What hotfixes get: allow, approve (the default) or block.

For the full schema, see the bumpkit.yml Reference.

What the gate does during a freeze ​

Situation Outcome
Freeze with mode: block Fails. Only a break-glass override lets the commit through.
Freeze with mode: approve Needs an approval. Passes once approved.
Hotfix, freeze hotfix: allow Passes.
Hotfix, freeze hotfix: approve Needs an approval.
Hotfix, freeze hotfix: block Fails. Only a break-glass override lets it through.
Outside every window, outside_windows: approve Needs an approval.
Outside every window, outside_windows: block Fails.
Hotfix outside every window Passes.

A break-glass override still works during a freeze when the repository allows overrides.

Declare a freeze on demand ​

Declare a freeze during an incident or over the holidays:

Terminal
bumpkit freeze start --reason "Incident 42: payments degraded" --for 4h
bumpkit freeze start --reason "Year-end holiday freeze" --starts 2026-12-20T00:00:00Z --until 2027-01-04T00:00:00Z --mode approve
bumpkit freeze start --reason "Database migration window" --repository acme/api --starts 2027-01-12T06:00:00Z --for 2h
Flag Default Description
--reason required Why releases are frozen, 10 to 500 characters. Shown on gated pull requests and kept in the audit log.
--repository every repository owner/name to freeze.
--mode block block, or approve to ask for an approval instead.
--hotfix approve What hotfixes get: allow, approve or block.
--starts now When it starts, RFC 3339.
--until until ended When it ends, RFC 3339.
--for none How long it lasts, such as 4h. Use --until or --for, not both.

List and end freezes:

Terminal
bumpkit freeze list
bumpkit freeze end 3f2b9c1e-7a4d-4e0b-9a51-2d6c8e1f0a77

bumpkit freeze list shows each freeze as active, upcoming or over. Owners, admins and policy admins can declare one. Ending it early takes one of them signed in to the dashboard, so an API key, the CLI or an agent can't lift a freeze. Anyone else gets 403 with "Only owners, admins and policy admins can declare or end a change freeze." Declaring and ending freezes are both audited, and so are refused attempts (freeze.declare.denied, freeze.end.denied).

You can also declare and end freezes on the Policy page of the dashboard, under the Policy and freezes tab. The page shows those controls only to the roles that can use them.

Version consistency ​

Beta, early access. A workspace without access gets 403 FEATURE_EARLY_ACCESS and a link to request it. Flag: version_consistency.

When the flag is on, every check compares the version Bumpkit decides for the release with the versions declared in your repository and its tags. Each mismatch is a version_mismatch finding with the file and line.

Bumpkit reads these files at the root of the repository:

File What it checks
package.json The top-level version.
pyproject.toml version under [project] or [tool.poetry].
Cargo.toml version under [package].
VERSION The first non-empty line.
go.mod The module path has the /vN suffix that major version 2 and up need, and no suffix for v0 and v1.
CHANGELOG.md The newest #, ## or ### version heading.

A manifest may still carry the previous version until the release bumps it. Bumpkit reports:

  • A manifest that names neither the release's version nor the previous one.
  • A manifest left behind when another one was already bumped, so the published packages would disagree.
  • A tag that already exists for this version, or a newer tag than this release.
  • A go.mod module path with the wrong major version suffix.
  • A CHANGELOG.md whose newest heading is neither this release nor the previous one.

The CLI sends the manifests and tags from your local checkout. For a check against a GitHub repository, Bumpkit reads them from GitHub at the head commit.

version_mismatch findings are high severity, so they show on every check. To fail the gate on them, add the kind to block_on:

YAML
gate:
  block_on: [secret, data_loss, version_mismatch]

See block_on.

Replay-safe checks ​

Beta. Replay-safe requests are on for every workspace, with no flag to enable.

POST /v1/checks and POST /v1/gate are replay-safe. If a CI job retries after a timeout, or the same request arrives twice, you get the first decision back instead of a second, possibly different, one.

A request is replayed in two cases:

  • Pinned requests. A request that sends a diff, or sends base and head as full 40-character commit SHAs, is identified by its content. Sending the same request again returns the stored result, as long as the workspace's enabled features are unchanged. For the gate, the approvals, the org policy, and any active freezes must also be unchanged. No header is needed.
  • Idempotency keys. Any request with an Idempotency-Key header is identified by that key. The key is 1 to 200 letters, digits, dots, colons, dashes or underscores.
Terminal
curl -X POST https://bumpkit.dev/v1/gate \
  -H "x-api-key: $BUMPKIT_API_KEY" \
  -H "Idempotency-Key: ci-run-8812-attempt" \
  -H "Content-Type: application/json" \
  -d @gate-request.json
Situation Response
Same request again 200 with the stored result and "replayed": true.
Same key, different request body 422 with code IDEMPOTENCY_KEY_REUSED.
Same request still running Waits for it, up to 2 minutes, then 409 with code REQUEST_IN_PROGRESS.
Invalid key 400 with code INVALID_IDEMPOTENCY_KEY.

Replayed responses don't count toward your rate limit. Bumpkit keeps stored results for one day.

The CLI sends bumpkit check and bumpkit gate up to three times in all, retrying on 502, 503, 504 and REQUEST_IN_PROGRESS, with the same key on every attempt. It generates a key for each run. To reuse a key across CI job retries, set it yourself:

Terminal
export BUMPKIT_IDEMPOTENCY_KEY="$GITHUB_RUN_ID-$GITHUB_JOB"
bumpkit gate

API endpoints ​

All endpoints take the x-api-key header. While a feature is in early access and your workspace isn't on it yet, its endpoints return 403 with code FEATURE_EARLY_ACCESS; when the feature is off, they return 404.

Method Path Who Description
GET /v1/policy/org any role The current org policy. 404 if there is none.
PUT /v1/policy/org owners, admins Save a new version. Send one of content, repository or clear, plus an optional message. Returns the new version and its diff.
GET /v1/policy/org/versions any role Version history. ?limit= from 1 to 200, default 50.
GET /v1/policy/org/versions/{version} any role One version.
POST /v1/policy/effective any key Merge the org policy with a bumpkit.yml. Send repository and policy.
GET /v1/freezes any role Current, upcoming and recent declared freezes.
POST /v1/freezes owners, admins, policy admins Declare a freeze. Send reason, and optionally repository, mode, hotfix, starts_at and ends_at (RFC 3339). Returns 201.
POST /v1/freezes/{id}/end owners, admins, policy admins End a freeze early.

A freeze object looks like this:

JSON
{
  "id": "3f2b9c1e-7a4d-4e0b-9a51-2d6c8e1f0a77",
  "repository": "acme/api",
  "reason": "Incident 42: payments degraded",
  "mode": "block",
  "hotfix": "approve",
  "starts_at": "2026-10-22T14:00:00Z",
  "ends_at": "2026-10-22T18:00:00Z",
  "actor_email": "oncall@acme.dev",
  "active": true,
  "created_at": "2026-10-22T14:00:00Z"
}

repository is omitted for a workspace-wide freeze. ended_at and ended_by are set once a freeze is ended early.

Agents use the MCP tools policy_show, policy_history, policy_diff, policy_set, freeze_list, freeze_start and freeze_end. For the full command and endpoint references, see the CLI Reference and the API Reference.

↑↓ to move, Enter to open