# GitCafe

> GitCafe is a Git forge for hosting repositories and collaborating on code. It works on its own; a repository can also sync one- or two-way with GitHub, including issues, pull requests, reviews and comments.

Source: https://git.cafe/

# GitCafe — Your code. All together.

Git hosting, code review, and stacked pull requests. Use it on its own, or keep it in sync with GitHub. Work in the browser or with the Cafe CLI.

## See the whole change.

Browse files, compare diffs, and leave feedback alongside the code.

[Browse the Ashlar repository](https://git.cafe/versecafe/ashlar).

## Keep big changes reviewable.

Build on an open pull request. Review each change separately, then merge the stack in order.

## With GitHub, or without it.

Import from GitHub and keep code, issues, and pull requests in sync, one way or both.

[Import from GitHub](https://git.cafe/import/github), or see [what syncs](https://git.cafe/docs/integrations/github-sync.md).

## GitCafe, from your terminal.

Manage repositories, issues, and pull requests with the Cafe CLI. Add agent skills to give your coding assistant the same workflow.

```sh
bun install -g @gitcafe/cli
export CAFE_HOST="https://git.cafe/api"
cafe auth login
cafe auth http setup
```

Logins are stored per host, so keep `CAFE_HOST` set for later commands. The CLI's built-in default is git.cafe, where this setting can be omitted.

From your local repository:

```sh
cafe pr create
```

Optional: install coding agent skills.

```sh
cafe skills install
```

## Get started

- [Create a free account](https://git.cafe/auth/sign-up.md)
- [Sign in](https://git.cafe/auth/sign-in.md)
- [Pricing](https://git.cafe/pricing.md)
- [Documentation](https://git.cafe/docs/index.md)
- [API reference](https://git.cafe/api/openapi)
- [Legal policies](https://git.cafe/legal/index.md)

Backed by [Y Combinator](https://www.ycombinator.com/).


---

Source: https://git.cafe/legal

# Legal

Clear terms for using GitCafe and a plain account of how we handle information.

- [Terms of Service](https://git.cafe/legal/terms.md): The terms that govern use of GitCafe.
- [Privacy Policy](https://git.cafe/legal/privacy.md): How GitCafe collects, uses, shares, and protects information.
- [Acceptable Use Policy](https://git.cafe/legal/acceptable-use.md): Rules protecting GitCafe, its users, and the public from abuse.
- [Copyright Policy](https://git.cafe/legal/copyright.md): How GitCafe handles copyright notices and counter-notices.


---

Source: https://git.cafe/legal/acceptable-use

# Acceptable Use Policy

Effective August 6, 2026

GitCafe supports open-source development, private collaboration, and legitimate security research. These rules focus on harmful conduct rather than disfavored topics or tools.

## Safety and lawful use

Do not use GitCafe for unlawful activity, fraud, impersonation, harassment, credible threats, sexual exploitation, non-consensual intimate material, infringement, or disclosure of private or highly sensitive information without authorization.

## Security abuse

Do not conduct phishing, credential theft, unauthorized access, command-and-control activity, denial-of-service attacks, active malware campaigns, or unauthorized testing of GitCafe or another person's systems. Legitimate security tools, malware samples, exploit research, and authorized testing are not prohibited merely because they are dual-use.

## Platform and resource abuse

Do not send spam, evade quotas or rate limits, generate excessive automated traffic, perform cryptocurrency mining or proof-of-work, interfere with Service security, or use repositories, LFS, releases, or artifacts primarily as an unrelated bulk-file host or CDN.

## Personal information

Do not scrape or use public user information for spam, sale of personal data, credential attacks, or unauthorized surveillance. Do not place another person's sensitive information in a repository or collaboration feature without a lawful and appropriate reason.

## Enforcement and appeals

We may rate-limit, quarantine, disable, remove, or suspend content or accounts where reasonably necessary to stop abuse, comply with law, or protect the Service or others. We consider context, intent, harm, and recurrence. Except in urgent safety, security, or legal cases, we will provide notice and a way to appeal. Report abuse to abuse@git.cafe.

Questions about this policy may be sent to legal@git.cafe.


---

Source: https://git.cafe/legal/copyright

# Copyright Policy

Effective August 6, 2026

We respect copyright and respond to sufficiently detailed reports of alleged infringement. This policy describes our notice, counter-notice, and repeat-infringer process.

## Copyright notices

A notice should include:

- Identification of the copyrighted work claimed to be infringed.
- The exact GitCafe URL or repository path of the material.
- Your name and reliable contact information.
- A good-faith statement that the use is not authorized.
- A statement, under penalty of perjury, that the notice is accurate and you are authorized to act.
- Your physical or electronic signature.

Send notices to copyright@git.cafe.

## Our response

We may ask for missing information, forward the notice to the affected user, restrict access to identified material, and take other action required by law. Where appropriate, we provide the user an opportunity to respond before disabling an entire repository.

## Counter-notices

If you believe material was removed or disabled by mistake or misidentification, you may send a counter-notice identifying the material and its former location, explaining the basis for restoration, consenting to the jurisdiction required by applicable law, and including your contact information, required statements, and signature. We may forward it to the original reporter and restore the material when legally permitted.

## Repeat infringement and misrepresentation

We may terminate accounts of repeat infringers in appropriate circumstances. Knowingly making material misrepresentations in a notice or counter-notice may create legal liability. Copyright disputes can be complex; consider obtaining legal advice before submitting either.

Questions about this policy may be sent to legal@git.cafe.


---

Source: https://git.cafe/legal/privacy

# Privacy Policy

Effective August 6, 2026

This policy explains how GitCafe handles personal information when you use our hosted service. It does not govern independently operated deployments of GitCafe software.

## Information we collect

We collect information in the following categories:

- **Account and profile:** email address, password hash, handle, display name, avatar, and optional profile fields.
- **Authentication and security:** passkey metadata, public SSH and GPG keys, access-token and device authorization metadata, MFA state, sessions, IP addresses, user agents, and security and authorization audit events.
- **Content:** repositories and Git metadata, including commit author names and emails; issues, pull requests, reviews, comments, attachments, releases, LFS objects, CI configuration, logs, and artifacts.
- **Organizations and integrations:** memberships, roles, invitations, webhook and integration configuration, event payloads, and delivery diagnostics.
- **Billing:** plan, subscription, usage, and Stripe customer and subscription identifiers. Stripe collects and processes payment-card details under its own privacy policy.
- **Operational data:** requested paths, timestamps, device and network information, performance measurements, error reports, and rate-limit identifiers.

## How we use information

- Provide, maintain, and administer the Service.
- Authenticate users, enforce permissions, and secure accounts.
- Store and render repositories and collaboration content.
- Operate integrations, webhooks, automation, and CI features.
- Send verification, recovery, invitation, and security email.
- Measure reliability, diagnose errors, and improve performance.
- Administer plans, subscriptions, usage, and payments.
- Prevent abuse, enforce our policies, comply with law, and resolve disputes.

## Visibility and organizations

Public profiles and public repositories are available to anyone and may be indexed, scraped, cloned, cached, forked, or redistributed by others. Removing content from GitCafe cannot remove copies other people already made. Git author information and durable attribution may remain in repository history after an account changes or closes.

Private content is available to authorized collaborators, organization administrators, and the limited GitCafe personnel and providers described in these policies. Organization administrators may manage memberships, access, integrations, and audit information.

## Service providers and disclosure

We currently use the following providers to operate GitCafe:

- **Latitude** for compute and infrastructure.
- **Amazon Web Services** for infrastructure and object storage.
- **PlanetScale** for database services.
- **Vercel** for web and landing-page hosting.
- **Axiom** for operational telemetry and error diagnostics.
- **Resend** for transactional email.
- **Stripe** for subscriptions and payment processing.

We also disclose information according to repository visibility and organization permissions; to integrations and webhook destinations you select; to comply with valid legal process; to protect GitCafe, our users, or the public; and in connection with a merger, financing, acquisition, or sale of assets subject to appropriate confidentiality protections.

We do not sell personal information, share it for cross-context behavioral advertising, or use repository content to train generative AI models.

## Cookies and operational telemetry

GitCafe uses a secure, HTTP-only session cookie to keep signed-in users authenticated and may store user-requested interface preferences. These functions are necessary to provide the Service and remember choices you make.

We use a first-party `gc_otel_session` cookie to correlate operational traces across page visits and browser tabs. It lasts for up to seven days, refreshes with activity, and rotates after 30 minutes of inactivity while retaining the prior correlation ID. We send limited traces and error information to Axiom to diagnose failures and measure reliability. We do not use this data for advertising or to build marketing profiles.

## Retention and deletion

We retain information while needed to provide the Service and for security, fraud prevention, dispute resolution, legal compliance, and legitimate business records. Short-lived authentication records expire automatically. Operational telemetry and access logs are retained for limited periods appropriate to troubleshooting and security. Billing records may be retained for legally required accounting periods.

Account or content deletion removes information from active use, but may not immediately remove encrypted backups, legal holds, security records, audit history, stable attribution, or copies held by other users. We retain only the information reasonably necessary for those purposes and delete or de-identify it when the purpose ends.

## Your choices and rights

You may update most profile and account information through the Service. Depending on your location, you may also request access, correction, export, deletion, restriction, or objection concerning your personal information. Email privacy@git.cafe. We may verify your identity and may retain information where permitted or required by law. We will not discriminate against you for exercising a privacy right.

## International use and transfers

GitCafe is operated from the United States. You understand that information may be processed in the United States and other countries where our providers operate. Where applicable law requires additional safeguards for an international transfer, we will use an appropriate legal mechanism.

## Security

We use administrative, technical, and organizational safeguards designed to protect personal information, including access controls, credential protection, and security logging. No system can guarantee absolute security. Please report suspected vulnerabilities or account compromise promptly.

## Children

The Service is not directed to children under 13, and we do not knowingly collect their personal information. Contact privacy@git.cafe if you believe a child under 13 has provided personal information so we can investigate and take appropriate action.

## Changes and contact

We will update the effective date and provide reasonable advance notice of material changes. Questions, complaints, and privacy requests may be sent to privacy@git.cafe. GitCafe has not appointed a Data Protection Officer; this address is monitored by the team responsible for privacy matters.


---

Source: https://git.cafe/legal/terms

# Terms of Service

Effective August 6, 2026

These Terms govern your use of GitCafe's hosted git forge, websites, APIs, command-line tools, repository hosting, collaboration features, and related services.

## Agreement

By creating an account or using the Service, you agree to these Terms and our [Privacy Policy](https://git.cafe/legal/privacy.md), [Acceptable Use Policy](https://git.cafe/legal/acceptable-use.md), and [Copyright Policy](https://git.cafe/legal/copyright.md). If you use the Service for an organization, you represent that you have authority to bind it.

You must be at least 13 years old. If local law requires a higher age or parental consent for you to use the Service, that requirement also applies.

## Accounts and organizations

Provide accurate account information and protect your passwords, passkeys, recovery methods, access tokens, SSH keys, device authorizations, agents, and API credentials. Tell us promptly if you suspect unauthorized use.

Organization owners control memberships, roles, repositories, integrations, and billing for their organization. You are responsible for activity performed through credentials and integrations you authorize, except to the extent caused by GitCafe's breach of these Terms or applicable law.

## The Service and beta features

We may add, change, limit, or discontinue features. Beta, preview, and experimental features may be incomplete, may change without notice, and have no service-level commitment. Roadmaps describe current direction, not promised delivery dates.

Private repositories are free during the current beta. We will publish pricing and give reasonable advance notice before changing that arrangement.

## Your content and ownership

You retain ownership of code, repositories, issues, pull requests, comments, attachments, release assets, CI configuration and logs, and other content you submit. You represent that you have the rights needed to submit and use that content through the Service.

You grant GitCafe a non-exclusive, worldwide, royalty-free license to host, store, reproduce, transmit, display, index, back up, and technically modify your content only as needed to operate, secure, maintain, and provide the Service, comply with law, and provide features you enable. We do not use repository content to train generative AI models.

## Public and private repositories

Marking a repository public directs us to make it available on the public internet and enables others to view, clone, and fork it. Public availability alone does not grant intellectual-property rights beyond Service functionality; the repository's license and applicable law govern broader use.

We treat private repository content as confidential and use reasonable safeguards against unauthorized access. Access is limited to operating and securing the Service, automated security and abuse controls, support requested by an authorized owner, incident or policy investigations, approved service providers, and legal obligations.

## APIs, automation, and integrations

You may use documented APIs and first-party tools subject to published technical limits. Do not evade rate limits, probe credentials, scrape private information, or impair the Service.

Applications, agents, workflows, webhooks, and integrations may execute code or transmit data. You are responsible for reviewing the permissions and third-party terms of services you enable. GitCafe is not responsible for third-party services.

## Security features and backups

Secret scanning, signature verification, rulesets, status checks, and other security features reduce risk but cannot detect every issue. You remain responsible for reviewing code, protecting credentials, configuring access, responding to exposed secrets, and keeping independent copies of important repositories and artifacts. Platform backups support service recovery and are not a substitute for your own backups.

## Paid plans

Paid plans renew for the interval shown at checkout until canceled. You authorize applicable charges, taxes, and disclosed seat or usage charges. Cancellation takes effect at the end of the current paid period unless stated otherwise. Fees are non-refundable except where law requires, and we will disclose material price changes before the affected renewal.

After downgrade, expiration, or nonpayment, we may restrict paid features or writes after reasonable notice. We will not make a private repository public solely because a subscription ends.

## Suspension and termination

You may stop using the Service and close your account subject to organization ownership and billing obligations. We may restrict or terminate access for material or repeated violations, legal requirements, security threats, nonpayment, or material harm. Where practicable, we will provide notice, an opportunity to cure or appeal, and a reasonable opportunity to export content. Some attribution, audit, backup, fraud-prevention, and legally preserved records may remain after account closure as described in our Privacy Policy.

## Disclaimers and liability

To the extent permitted by law, the Service is provided “as is” and “as available,” without warranties of uninterrupted, error-free, or completely secure operation. Nothing in these Terms excludes rights that cannot lawfully be excluded.

To the extent permitted by law, neither party is liable for indirect, incidental, special, consequential, exemplary, or punitive damages, or lost profits, revenue, goodwill, or data. GitCafe's aggregate liability arising from the Service will not exceed the greater of $100 or the amount you paid GitCafe during the 12 months before the claim. These limits do not apply where prohibited by law.

## Law, trade controls, and changes

You may not use or provide the Service in violation of applicable export-control or sanctions laws. We may restrict access where required to comply with those laws.

We will announce material changes by email or prominent in-product notice before they take effect. These Terms, together with incorporated policies and any applicable order form, are the entire agreement concerning the Service. Contact legal@git.cafe with legal questions or notices.

Questions about this policy may be sent to legal@git.cafe.


---

Source: https://git.cafe/pricing

# Pricing

Start free. Private repositories are free during the public beta; paid plans add higher limits.

- **Free** — $0 per month. For public projects and trying GitCafe.
- **Pro** — $20 per month. For individuals with private work.
- **Team** — $40 per seat per month with 5× rate limits, or $80 per seat per month with 10×. For organizations, billed per seat.
- **Enterprise** — contact sales (enterprise@gitcafe.dev). Custom terms and invoicing.

[Create a free account](https://git.cafe/auth/sign-up.md), then upgrade from billing settings.

## Plan limits

|  | Free | Pro | Team 5× | Team 10× |
| --- | --- | --- | --- | --- |
| Private repositories | Free during public beta | Yes | Yes | Yes |
| Repository size | 1 GiB | 5 GiB | 10 GiB | 25 GiB |
| Largest Git object | 50 MiB | 100 MiB | 100 MiB | 250 MiB |
| LFS storage per repository | 1 GiB | 20 GiB | 100 GiB | 250 GiB |
| Largest LFS file | 512 MiB | 2 GiB | 5 GiB | 5 GiB |
| Account storage | 2 GiB | 50 GiB | 250 GiB + 25 GiB per seat | 1 TiB + 100 GiB per seat |
| Monthly outbound transfer | 10 GiB | 100 GiB | 500 GiB + 50 GiB per seat | 2 TiB + 200 GiB per seat |
| API rate limits | 1× | 2× | 5× | 10× |

Prices are in US dollars. Checkout shows the final price, seat quantity and taxes before you pay. See [billing](https://git.cafe/docs/account/billing.md) for upgrades and seats.


---

Source: https://git.cafe/docs

# GitCafe documentation

GitCafe is a home for your Git repositories, with pull requests, stacked
changes, issues and CI results in one place. These guides show you how to use
it.

New here? Follow the [quickstart](https://git.cafe/docs/getting-started/quickstart.md); no GitHub
account is needed. Already on GitHub?
[Import your projects and keep them in sync](https://git.cafe/docs/integrations/github-sync.md). Coding agents
and other tools can start with [What can I do with GitCafe?](https://git.cafe/docs/agent-workflows.md).

## Getting started

- [Quickstart](https://git.cafe/docs/getting-started/quickstart.md): account, first repository, first
  pull request.
- [Clone and push with Git](https://git.cafe/docs/getting-started/git-access.md) over HTTPS or SSH.
- [Install the CLI](https://git.cafe/docs/getting-started/cli.md).

## Repositories

- [Create or fork a repository](https://git.cafe/docs/repositories/repository-create.md).
- [Branches](https://git.cafe/docs/repositories/branches.md), [tags](https://git.cafe/docs/repositories/tags.md),
  [history and blame](https://git.cafe/docs/repositories/commits.md) and
  [code search](https://git.cafe/docs/repositories/code-search.md).
- [Access](https://git.cafe/docs/repositories/repository-access.md),
  [rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) and
  [settings](https://git.cafe/docs/repositories/repository-settings.md).
- [Mirror a branch with another Git server](https://git.cafe/docs/repositories/git-mirrors.md) and
  [stars](https://git.cafe/docs/repositories/repository-stars.md).

## Pull requests

- [Open](https://git.cafe/docs/pull-requests/open.md), [review](https://git.cafe/docs/pull-requests/review.md) and
  [merge](https://git.cafe/docs/pull-requests/merge.md) a pull request.
- [Stacked pull requests](https://git.cafe/docs/pull-requests/stacks.md).

## Issues

- [Track work with issues](https://git.cafe/docs/issues/issues.md),
  [pin and lock](https://git.cafe/docs/issues/pin-and-lock.md), [labels](https://git.cafe/docs/issues/labels.md) and
  [milestones](https://git.cafe/docs/issues/milestones.md).

## Your account

- [Account settings](https://git.cafe/docs/account/account-settings.md),
  [password recovery](https://git.cafe/docs/account/password-recovery.md),
  [profile](https://git.cafe/docs/account/profile.md) and [appearance](https://git.cafe/docs/account/appearance.md).
- [SSH keys](https://git.cafe/docs/account/ssh-keys.md), [GPG keys](https://git.cafe/docs/account/gpg-keys.md),
  [personal access tokens](https://git.cafe/docs/account/personal-access-tokens.md) and
  [CLI sign-in](https://git.cafe/docs/account/device-authorization.md).
- [Billing and plans](https://git.cafe/docs/account/billing.md).

## Organizations

- [Organizations](https://git.cafe/docs/organizations/organizations.md),
  [members](https://git.cafe/docs/organizations/organization-members.md),
  [teams](https://git.cafe/docs/organizations/teams.md) and
  [settings](https://git.cafe/docs/organizations/organization-settings.md).

## CI

- [Move CI from GitHub](https://git.cafe/docs/ci/migrate-from-github.md).
- [Run your GitHub Actions workflows](https://git.cafe/docs/ci/actions.md),
  [what's supported](https://git.cafe/docs/ci/workflow-syntax.md),
  [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md),
  [runner providers](https://git.cafe/docs/ci/runner-providers.md) and
  [secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md).
- [Connect Buildkite](https://git.cafe/docs/ci/buildkite.md) and
  [read CI results and logs](https://git.cafe/docs/ci/checks-and-logs.md).

## Integrations

- [Import and sync from GitHub](https://git.cafe/docs/integrations/github-sync.md).
- [Webhooks](https://git.cafe/docs/integrations/webhooks.md).
- [Install and manage apps](https://git.cafe/docs/integrations/apps.md) that act as their own bot, in
  an organization or [on your account](https://git.cafe/docs/integrations/apps.md#install-an-app-on-your-account).
- [Connect an MCP app](https://git.cafe/docs/account/mcp-connections.md) such as Claude or ChatGPT.
- [Sign in with GitCafe](https://git.cafe/docs/integrations/sign-in-with-gitcafe.md): OpenID Connect
  sign-in, with repository access in the same approval.

## Reference

- [CLI reference](https://git.cafe/docs/reference/cli.md), [REST API](https://git.cafe/docs/reference/api.md) and
  [rate limits](https://git.cafe/docs/reference/rate-limits.md).
- [Read GitCafe as Markdown](https://git.cafe/docs/reference/markdown.md).

## Search the docs

Use **Search GitCafe** in the docs header to find guides, people and, when
signed in, your repositories. To search inside a repository's code, use its
[code search](https://git.cafe/docs/repositories/code-search.md).

These guides use `git.cafe` in addresses and examples. If you use another
GitCafe server, replace it with that server's address.


---

Source: https://git.cafe/docs/getting-started/quickstart

# Quickstart

Create an account, push your first repository and open a pull request.

## 1. Create an account

Open [sign up](https://git.cafe/auth/sign-up) and choose a username, email address and
password, or choose **Sync with GitHub** to
[sign up with GitHub](https://git.cafe/docs/integrations/github-sync.md#create-an-account-with-github).
Open the link in the verification email: you need a verified address to create
repositories.

## 2. Install the CLI and sign in

The `cafe` CLI handles sign-in and Git credentials for you. It needs
[Bun](https://bun.sh) 1.2 or newer.

```sh
bun install -g @gitcafe/cli
cafe auth login
cafe auth http setup
```

`cafe auth login` opens your browser to [approve the
sign-in](https://git.cafe/docs/account/device-authorization.md); `cafe auth http setup` lets Git
use that login for HTTPS. To use SSH instead, add an
[SSH key](https://git.cafe/docs/account/ssh-keys.md). See [Git access](https://git.cafe/docs/getting-started/git-access.md) for both.

## 3. Create a repository

```sh
cafe repo create my-app --private
```

Or open `/new` in the browser; see [create a repository](https://git.cafe/docs/repositories/repository-create.md).

## 4. Push your code

From your project directory:

```sh
git init -b main
git add .
git commit -m "First commit"
git remote add origin https://git.cafe/<you>/my-app
git push -u origin main
```

## 5. Open a pull request

```sh
git switch -c add-readme
echo "# my-app" > README.md
git add README.md
git commit -m "Add a README"
git push -u origin add-readme
cafe pr create --title "Add a README"
```

Open the pull request on GitCafe to [review](https://git.cafe/docs/pull-requests/review.md) and
[merge](https://git.cafe/docs/pull-requests/merge.md) it.

## Next steps

- Bring existing projects over with [GitHub import](https://git.cafe/docs/integrations/github-sync.md).
- Invite collaborators in [repository access](https://git.cafe/docs/repositories/repository-access.md),
  or set up an [organization](https://git.cafe/docs/organizations/organizations.md).
- Protect `main` with a [ruleset](https://git.cafe/docs/repositories/repository-rulesets.md).


---

Source: https://git.cafe/docs/getting-started/git-access

# Clone and push with Git

GitCafe repositories work with any Git client over HTTPS or SSH. Copy a
repository's address from its **Code** button: the **HTTPS** tab gives
`https://git.cafe/<owner>/<repo>` and the **SSH** tab gives
`git.cafe:<owner>/<repo>`. The `.git` suffix is optional.

Public repositories can be cloned without signing in. Pushing, and cloning
private repositories, needs credentials.

## Use HTTPS with the CLI

The simplest way to authenticate is the [`cafe` CLI](https://git.cafe/docs/getting-started/cli.md):

```sh
cafe auth login
cafe auth http setup
git clone https://git.cafe/<owner>/<repo>
```

`cafe auth http setup` registers `cafe` as Git's credential helper for
GitCafe, so Git uses your CLI login. `cafe auth http status` shows whether it
is configured.

## Use HTTPS with a token

On machines without the CLI, such as CI jobs, create a
[personal access token](https://git.cafe/docs/account/personal-access-tokens.md) from the **CI
read-only** or **Push code** template. When Git asks for credentials, enter your
GitCafe handle as the username and the token as the password.

An installed app clones with a 15-minute installation token instead; see
[Clone as the app](https://git.cafe/docs/integrations/apps.md#clone-as-the-app).

## Use SSH

1. Add your public key in **Settings → SSH Keys** with **Authentication**
   usage, and choose what it may access. See [SSH keys](https://git.cafe/docs/account/ssh-keys.md).
2. Clone with the SSH address:

   ```sh
   git clone git.cafe:<owner>/<repo>
   ```

`cafe auth ssh setup` can find and register your key for you.

## Clone without every file version (partial clone)

For a large repository, a partial clone downloads all commits and trees but
only the file contents you check out; Git fetches other versions on demand,
for example when you run `git log -p`, `git blame` or switch branches:

```sh
git clone --filter=blob:none https://git.cafe/<owner>/<repo>
```

`--filter=blob:limit=<size>` (such as `1m`) leaves out only files above a
size, and `--filter=tree:0` fetches commits alone. Both work over HTTPS and
SSH and combine with `git sparse-checkout`. Later `git fetch` runs keep the
filter. A partial clone needs network access to GitCafe whenever Git reads a
file version it does not have yet.

## Use Git LFS

Repositories support [Git LFS](https://git-lfs.com) for large files. Install
Git LFS, track the file types you need, and push as usual; the same HTTPS
credentials are used.

```sh
git lfs install
git lfs track "*.psd"
git add .gitattributes
```

LFS files count against your organization's storage. When you delete a
repository, its LFS files are kept, and still count, for 30 days. After that
they are deleted, and only then is their storage freed. Deleting a repository
does not free LFS storage right away, and LFS files that a repository no longer
references still count while the repository exists.

## Download a snapshot

To get the files without Git history, choose **Download tar.gz** under the
**Code** button, or run `cafe repo archive branch main > main.tar.gz`.

## If a push is rejected

- **Permission denied**: you need write access to the repository, and your
  token or SSH key must grant it.
- **Protected branch**: a [ruleset](https://git.cafe/docs/repositories/repository-rulesets.md)
  may require a pull request or block force pushes. Push to a new branch and
  [open a pull request](https://git.cafe/docs/pull-requests/open.md).


---

Source: https://git.cafe/docs/getting-started/cli

# Install the CLI

`cafe` is GitCafe's command-line tool. It signs you in, sets up Git
credentials, and manages repositories, pull requests, stacks, issues and more
from the terminal. Every command is listed in the [CLI reference](https://git.cafe/docs/reference/cli.md).

## Install

`cafe` needs [Bun](https://bun.sh) 1.2 or newer; it does not run under Node.js.

```sh
bun install -g @gitcafe/cli
cafe version
```

To try it without installing, run `bunx @gitcafe/cli --help`. When a newer
release is out, `cafe` prints a one-line notice; upgrade with
`bun install -g @gitcafe/cli@latest`.

## Sign in

```sh
cafe auth login
cafe auth http setup
cafe auth status
```

`cafe auth login` shows a code and opens your browser to
[approve the sign-in](https://git.cafe/docs/account/device-authorization.md). The login lasts 90
days. `cafe auth http setup` lets Git use it for HTTPS remotes.

On a server without a browser, set `CAFE_TOKEN` to a
[personal access token](https://git.cafe/docs/account/personal-access-tokens.md) instead. It
takes precedence over a stored login.

## Pick the repository

Inside a clone, commands use that repository. Elsewhere, pass
`--repo owner/name` or set `CAFE_REPO`.

```sh
cafe issue list --repo acme/project
```

## Script with the CLI

- `--json` prints structured output; progress and errors go to stderr.
- `--no-input` fails instead of prompting; `--yes` confirms destructive actions.
- `cafe api <path>` sends an authenticated request to any
  [REST API](https://git.cafe/docs/reference/api.md) endpoint.
- `cafe completion zsh` (or `bash`, `fish`, `powershell`) prints a shell
  completion script.

## Use another GitCafe server

The CLI talks to `https://git.cafe/api` by default. For another server, set
`CAFE_HOST` to its API address or pass `--host` with every command. Logins are
stored per server.


---

Source: https://git.cafe/docs/agent-workflows

# What can I do with GitCafe?

This page is a map for coding agents and other tools: how to read GitCafe
without an account, how to authenticate, and where each workflow is
documented. Replace `<owner>`, `<repo>` and numbers with real values.

## Read public repositories without signing in

Start at `https://git.cafe/<owner>/<repo>/llms.txt`. It summarizes the
repository and links to its code, history, issues and pull requests as
Markdown. Any repository page can be read as Markdown by adding
`?format=markdown`:

```text
https://git.cafe/<owner>/<repo>/blob/main/README.md?format=markdown
https://git.cafe/<owner>/<repo>/issues/123?format=markdown
https://git.cafe/<owner>/<repo>/pull/123/diffs.md
```

See [Read GitCafe as Markdown](https://git.cafe/docs/reference/markdown.md) for the address patterns
and limits; follow the links in each document rather than guessing addresses.
Documents are bounded summaries, not exports: use `git clone` for the full
repository.

The MCP server at `https://git.cafe/mcp` also serves public repositories
without signing in; see [connect an MCP app](https://git.cafe/docs/account/mcp-connections.md).

## Use GitCafe from a browser agent

In a browser that supports [WebMCP](https://webmachinelearning.github.io/webmcp),
GitCafe pages offer the MCP server's tools to the browser's agent. They act
with the session of whoever is signed in to that tab, with that person's full
access, not a narrower grant. Signed out, a page offers only the read tools
(repositories, code, issues, pull requests and CI) over public repositories.
Signed in, it also offers the tools that create, edit, comment on, review and
merge, each marked as consequential so that agents honouring the mark ask you
first. A page embedded in another site offers no tools.

## Open an issue or pull request in a coding tool

The button next to an issue or pull request number hands it to a coding agent.
Its menu lists Amp, Claude Code, Codex, Cursor, OpenCode and T3 Code; the
button itself repeats your last choice, and starts as **Copy as prompt**.

The prompt names the item, links to it, and tells the agent to read the rest
with `cafe issue view` or `cafe pr view`; for a pull request it also names the
branch and how to check it out. Claude Code, Codex, Cursor and T3 Code open
with the prompt filled in, a long description shortened to fit the link; if
even that is too long, the prompt is copied instead. Amp and OpenCode cannot take a prompt from a link: the prompt is copied
to your clipboard (and Amp opens a new thread) for you to paste. A tool that is
not installed does nothing when chosen. On a pull request, **Copy checkout
command** copies `cafe pr checkout`.

## Choose how to authenticate

Each method issues credentials that work only for its own purpose: an MCP token
does not work for the REST API, and a REST token does not work for MCP.

| Method                                                              | Use it for                                                  | Lasts                                    |
| ------------------------------------------------------------------- | ----------------------------------------------------------- | ---------------------------------------- |
| [Cafe CLI](https://git.cafe/docs/getting-started/cli.md)                                  | Terminal work and Git over HTTPS                            | 90 days                                  |
| [MCP connection](https://git.cafe/docs/account/mcp-connections.md)                        | Tools in an MCP client such as Claude or ChatGPT            | Until revoked; ends after 90 days unused |
| [API OAuth](https://git.cafe/docs/account/mcp-connections.md#connect-an-api-client)       | An OAuth application calling the REST API                   | Until revoked; ends after 90 days unused |
| [auth.md](https://git.cafe/docs/account/mcp-connections.md#connect-an-agent-using-authmd) | An agent without a registered OAuth client, for one session | 1 hour                                   |
| [Personal access token](https://git.cafe/docs/account/personal-access-tokens.md)          | Scripts, CI and agents using the REST API or Git over HTTPS | 30 days to 1 year                        |

MCP and API OAuth cover all repositories you can access or only the ones you
select when connecting (connect again to add one to a selection); auth.md
covers the repositories you select. Grant the least access the task needs.

## Work from the terminal

Install the [CLI](https://git.cafe/docs/getting-started/cli.md) and sign in:

```sh
bun install -g @gitcafe/cli
cafe auth login
cafe auth http setup
```

A person approves the sign-in in a browser. In automation, set `CAFE_TOKEN` to a
personal access token instead, created from the **Agent / CLI** template:
pushing code alone does not allow `cafe pr create`. Read commands work outside a clone when you name
the repository:

```sh
cafe issue list --repo owner/repo
cafe pr diff 123 --repo owner/repo --patch
```

Commands that change things, such as `cafe issue create` or `cafe pr merge`,
need the matching role and token permission. Run them only when the user has
asked for that effect. Add `--json` for structured output.

`cafe skills install` installs ready-made skills that teach coding agents how
to use `cafe`.

## Repositories that also live on GitHub

Using GitHub is optional, but a repository imported from GitHub keeps syncing
with it. With two-way sync, what you write in GitCafe (pushes, merges,
comments, reviews, labels) also reaches GitHub, so treat it as visible there.
References like `#5` are written to GitHub as full GitCafe links; use full URLs
to refer to a GitHub issue. See
[what syncs](https://git.cafe/docs/integrations/github-sync.md#what-syncs).

## Retry without duplicating work

Some operations (merges, stack landing, branch deletion, mirror syncs) are
accepted first and finish later. If a response is lost, check the operation's
state before retrying, and retry with the same request ID: see
[retry safely](https://git.cafe/docs/reference/api.md#retry-safely). Multi-step commands such as
`cafe stack submit` can stop part-way; read what they report before running
them again.

## Find the right guide

- Stacks: [stacked pull requests](https://git.cafe/docs/pull-requests/stacks.md), and preview with
  `cafe stack submit --dry-run`.
- CI results: [read CI results and logs](https://git.cafe/docs/ci/checks-and-logs.md) and
  `cafe ci list`.
- GitHub: [import and sync from GitHub](https://git.cafe/docs/integrations/github-sync.md).
- Anything else: the [CLI reference](https://git.cafe/docs/reference/cli.md) and the
  [REST API](https://git.cafe/docs/reference/api.md).

## Discover the services

Clients that look for services before connecting can read the MCP server's
description from `/server.json`, `/mcp/server-card` or
`/.well-known/ai-catalog.json`. `/.well-known/api-catalog` lists the REST API
and the MCP server.


---

Source: https://git.cafe/docs/repositories/repository-create

# Create a repository

You need a verified email address to create a repository. To bring an existing
project from GitHub instead, see [GitHub import](https://git.cafe/docs/integrations/github-sync.md).

## Create a repository in the browser

1. Open `/new`.
2. Choose an owner: your account or an organization where you can create
   repositories.
3. Enter:
   - **Name**: unique within the owner.
   - **Visibility**: **Private** (the default) or **Public**.
   - **Default branch**: `main` unless you choose another name.
   - **Description**: optional.
4. Choose **Create**.

The new repository is empty. Its page shows the commands to push an existing
project or start a new one; see [Git access](https://git.cafe/docs/getting-started/git-access.md).
There are no README, license or `.gitignore` templates.

## Create a repository from the CLI

```sh
cafe repo create my-app --private --clone
```

`--org <slug>` creates it in an organization, `--default-branch` sets the
initial branch name, and `--clone` clones it into a new directory.

## Fork a repository

A fork is your own copy of another repository, linked to its parent so you can
propose changes back with a [pull request](https://git.cafe/docs/pull-requests/open.md#open-a-pull-request-from-a-fork).

1. Open the repository and choose **Fork**.
2. Choose the owner and, optionally, a new name.
3. Choose **Create fork**.

Or run `cafe repo fork owner/repo --clone`. Forking a private organization
repository requires the organization to allow it.

To bring your fork's default branch up to date with its parent, run
`cafe repo sync`; `cafe repo compare-upstream` shows how far ahead or behind
it is.

## Next steps

- Invite people in [access settings](https://git.cafe/docs/repositories/repository-access.md).
- Protect branches with [rulesets](https://git.cafe/docs/repositories/repository-rulesets.md).
- Change the description, visibility or default branch in
  [repository settings](https://git.cafe/docs/repositories/repository-settings.md).


---

Source: https://git.cafe/docs/repositories/branches

# Branches

Open a repository's **Branches** tab to browse its branches, start a pull
request or create and delete branches. You need read access to the repository.

## Browse branches

Each row shows the branch name, its latest commit and who committed it when.
The default branch is marked `default`, and a branch that is the source of an
open pull request links to it.

- **All branches** lists every branch; **Your branches** keeps the branches
  whose latest commit you authored (on the current page).
- Type in the search box to filter by name. Pages hold up to 100 branches.

The ref switcher on the **Code** and **Commits** pages also links here through
**View all branches**.

## Create a branch

You need write access.

1. Choose **New branch**.
2. Enter the branch name and a source: a branch, a full `refs/tags/` name or a
   full commit hash.
3. Choose **Load source** to resolve its exact commit, then **Create branch**.

An existing branch is never overwritten. Most branches are created by pushing
from your own checkout; see [Git access](https://git.cafe/docs/getting-started/git-access.md).

**Branch activity** (the history icon) lists your recent branch creations and
deletions. If a request's result is unknown, choose **Inspect** there instead
of submitting it again.

## Delete or restore a branch

With write access, delete a branch from its row and confirm, or run
`cafe branch delete <name> --yes`. The default branch and branches used by
open pull requests cannot be deleted, not even by `git push`, and
[rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) can protect others. Branches of a repository
synced with GitHub or in a fork network are protected too;
[disconnect it from GitHub](https://git.cafe/docs/integrations/github-sync.md#disconnect-from-github)
to delete them.

Deleted branches can be restored from the repository's
[settings](https://git.cafe/docs/repositories/repository-settings.md#restore-a-deleted-branch) or with
`cafe branch restore <name>`.

## Start a pull request

Choose **New pull request** on a non-default branch to
[open a pull request](https://git.cafe/docs/pull-requests/open.md) from it.


---

Source: https://git.cafe/docs/repositories/tags

# Tags

Open a repository's **Tags** tab to find a tagged version of its code. You need
read access to the repository.

Each row shows the tag name, whether it is annotated, the commit it points at
and who committed it when. Type in the search box to filter by name; pages hold
up to 100 tags. A signed annotated tag shows **Verified** or **Unverified**.

## Create a tag

Tags are created with Git and pushed:

```sh
git tag -a v1.0.0 -m "Version 1.0.0"
git push origin v1.0.0
```

## Delete a tag

With write access, delete a tag from its row and confirm, or run
`cafe tag delete <name> --yes`. [Rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) can protect
tags from deletion. **Deletion activity** (the history icon) shows the result
of your recent deletions.

## Download a tagged version

`cafe repo archive tag v1.0.0 > v1.0.0.tar.gz` downloads the tagged tree as a
tar.gz archive.


---

Source: https://git.cafe/docs/repositories/commits

# Browse history and blame

Read a repository's commit history, inspect a single commit, and see who last
changed each line of a file. You need read access to the repository.

## Browse commit history

Open **Commits** from the repository's **Code** toolbar. The ref switcher
chooses the branch or tag; without one, the default branch is shown. Each row
shows the commit summary, short hash, author and time. History follows first
parents. Choose **Load more commits** for older commits.

```sh
cafe repo code commits --ref main
```

## Inspect a commit

Select a commit's hash or summary. The commit page shows the full message, the
author and date, the committer when different, and links to each parent
commit. The code icon opens the file tree at that commit.

Below the header is the diff against the first parent, with unified or split
view, per-file collapse and a file tree. A root commit has no parent and shows
no diff. Very large commits may omit some file diffs; the page says so.

Commit hashes in history, on the branches and tags pages, and in the blame
gutter all link to the commit page.

## See who changed a line

Open a text file from the **Code** page and choose **Blame**. The gutter shows
which commit, author and date last changed each group of lines; select a hash
to open that commit. Markdown files offer **Preview / Code / Blame**; other
text files offer **Code / Blame**.

Blame covers the first 2,000 lines of a file. A notice appears when the file
is longer.

## Signed commits

A signed commit shows **Verified** or **Unverified** next to its summary.
Unsigned commits show no badge. To have your own commits verified, add your
signing key as an [SSH key](https://git.cafe/docs/account/ssh-keys.md) with signing usage or as a
[GPG key](https://git.cafe/docs/account/gpg-keys.md).


---

Source: https://git.cafe/docs/repositories/code-search

# Search code

Search the files of one branch or tag in a repository. Open the **Search** tab
in the repository header, or `/<owner>/<repo>/search`.

## Search in the browser

Type a pattern; results update as you type. The ref switcher picks the branch
or tag (the default branch unless you change it). Two toggles sit beside the
box:

- **Aa**: ignore letter case.
- **.\***: treat the pattern as a JavaScript regular expression.

Results are grouped by file, with three lines of context around each match.
Select the file name to open the file, or a line number to jump to that line.
The address keeps your query and ref, so you can share the results.

## Search from the CLI

```sh
cafe repo code search TODO --repo owner/repo --path src
```

`--path` limits the search to a file or directory (`--path src` includes
`src/main.ts` but not `src-other/main.ts`). `--regex` and `--ignore-case` match
the browser toggles, and `--ref` picks the branch or tag.

## Limits

- Only the files of the selected ref are searched, not history, commit
  messages, issues or pull requests.
- At most 100 matches are returned; the page says **Results truncated** when
  there are more. Narrow the pattern or the path.
- Regular-expression searches stop after 5,000 tree entries, 32 MiB of content
  or 4 seconds. Binary files and files over 1 MiB are skipped.
- Context lines are shown for the first 50 matching files.


---

Source: https://git.cafe/docs/repositories/repository-access

# Manage repository access

Repository admins decide who can work on a repository under
**Settings → Access**. Organization members also get the organization's
[default repository permission](https://git.cafe/docs/organizations/organization-settings.md).

## Roles

Roles are cumulative; each includes everything the one before it can do.

- **Reader**: read the code, issues and pull requests, open issues and comment.
- **Triager**: also triage issues and pull requests: labels, assignees,
  milestones, pinning and locking.
- **Writer**: also push branches and merge pull requests.
- **Maintainer**: also open the repository's **Settings**.
- **Admin**: also manage access, [rulesets](https://git.cafe/docs/repositories/repository-rulesets.md),
  visibility, transfer and deletion.

## Add a collaborator

1. Choose **Add collaborator**.
2. Search for the user by handle (at least two characters) and pick them.
3. Choose a role and confirm.

Change a collaborator's role from the menu in their row, or remove them with
the bin icon. From the terminal:

```sh
cafe repo collaborator add alice --role repo-writer
cafe repo collaborator remove alice
```

The CLI can grant `repo-reader`, `repo-triager` or `repo-writer`; use the
browser for higher roles.

## Give a team access

Repositories owned by an organization also have a **Teams** table listing every
team in the organization with its role on this repository, or **No access**.
Pick a role to grant access, change it in place, or use the bin icon to revoke
it. See [teams](https://git.cafe/docs/organizations/teams.md).


---

Source: https://git.cafe/docs/repositories/repository-rulesets

# Protect branches and tags with rulesets

A ruleset protects matching branches or tags: it can require pull requests,
reviews and passing checks, or block deletion and force pushes. Repository
admins manage rulesets under **Settings → Rulesets**. Organizations and teams
can also define rulesets that apply to their repositories.

## Create a ruleset

1. Open **Settings → Rulesets** and choose **New ruleset**.
2. Under **Basics**, enter a name, choose the target (branches or tags), the
   enforcement and one or more patterns. `*` and `?` are the only wildcards,
   for example `main` or `release/*`.
3. Under **Rules**, turn on the rules you want and fill in their settings
   (approval count, required check names and so on).
4. Under **Bypass actors**, optionally let an organization role or a user skip
   the rules, either always or only when merging pull requests.
5. Save.

Enforcement is one of:

- **Active**: the rules are enforced.
- **Evaluate**: the rules only produce warnings, so you can try them first.
- **Disabled**: the ruleset is kept but does nothing.

From the terminal, `cafe repo ruleset create`, `edit`, `list` and `delete`
manage rulesets, and `cafe repo ruleset effective main` shows the rules that
apply to a branch.

## See which rules apply

**Effective rules**, below the ruleset list, shows every ruleset that applies
to the repository, including those inherited from the
[organization](https://git.cafe/docs/organizations/organization-settings.md#rulesets) or a
[team](https://git.cafe/docs/organizations/teams.md#team-rulesets). Inherited rulesets are edited
where they are defined.

Anyone who can read the repository can read its rulesets, so the rulesets of
a public repository are public. Only admins can change them, and only admins
see a ruleset's bypass actors. The evaluated rules of a branch still tell
every reader whether they themselves may bypass.

## How rules affect pull requests

Anyone can still open a pull request against a protected branch. Merging
checks every active ruleset that matches the target branch:

- **Require pull request**: changes must arrive through a pull request with the
  required number of approvals. Each reviewer's latest approval or
  change request counts; the author's own review does not. Depending on the
  setting, a new push can make earlier approvals stale.
- **Require status checks**: each named check must pass on the exact commit
  being merged. With strict checks, the branch must also be up to date with
  the target. A check can be pinned to an app, so that only that app's
  [check run](https://git.cafe/docs/integrations/check-runs.md#require-a-check) satisfies it.
- **Require linear history**: merge commits are not allowed; use squash or
  rebase.
- **Code owners**: each changed path needs approval from one of its owners in
  `CODEOWNERS` (looked up in the repository root, `.gitcafe/`, `.github/`, then
  `docs/`, from the target branch). The last matching pattern wins. Owners are
  users or organization teams with write access.
- **Restrict pushes**: only admins, or users with a branch role that allows it,
  can update the branch, including by merging.

The pull request's merge area lists any requirement that is not met.

Required signatures, required deployments and restricted file paths cannot be
satisfied yet: they block matching merges unless a bypass applies.

## How rules affect direct pushes

Every pushed branch or tag update is checked. Creation, deletion and
force-push restrictions apply to matching refs. **Require pull request** blocks
direct pushes to matching branches. Rules that need checks or other evidence
(status checks, linear history, signatures, deployments, file paths) also
reject direct pushes to matching refs unless an **always** bypass applies.
Evaluate-only rulesets never block a push.


---

Source: https://git.cafe/docs/repositories/repository-settings

# Repository settings

Open **Settings → General** in a repository to change its details, visibility
and default branch, transfer it, restore deleted branches or delete it. The
**Settings** tab is shown to maintainers and admins.

## Edit the description and topics

Edit the description, homepage, topics and **Automatically delete head
branches**, then choose **Save changes**. **Discard** restores the saved
values.

```sh
cafe repo edit --description "A parser" --topics parser,rust
```

## Change visibility

Admins can switch a repository between public and private. Making it public
lets anyone read its code, issues and pull requests.

```sh
cafe repo edit --visibility public --yes
```

For a repository synchronized with GitHub, you must be able to renew its GitHub
authorization, and a private GitHub source must stay private. Forks and
generic Git mirrors cannot change visibility.

## Change the default branch

The default branch is what people see when they open or clone the repository,
and the base for new pull requests. Choose an existing branch in General
settings, or run:

```sh
cafe repo edit --default-branch trunk
```

New repositories start with `main` unless you chose another name. Forks
inherit their parent's default branch.

## Transfer a repository

Choose **Transfer repository**, enter the new owner, an optional new name and
the current full name to confirm. You need permission at both the current and
the new owner. Or run `cafe repo transfer owner/repo new-owner --yes`.

After a transfer:

- Update your Git remote to the new address:
  `git remote set-url origin https://git.cafe/new-owner/repo`.
- Review access: team access from the old organization is removed; direct
  collaborators keep theirs.
- Reconnect GitHub synchronization under the new owner; it is paused by the
  transfer.

## Restore a deleted branch

Expand **Deleted branches**, find the branch and choose **Restore**. It is
recreated at the commit it pointed to when it was deleted. You need write
access, and no branch with that name may exist. Or run
`cafe branch restore <name>`.

Tags and deleted repositories cannot be restored.

## Delete a repository

Only the owner can delete a repository.

1. Under **Delete this repository**, choose **Delete repository**.
2. Type the full `owner/name`.
3. Choose **I understand, delete this repository**.

The repository disappears immediately and cannot be restored.


---

Source: https://git.cafe/docs/repositories/git-mirrors

# Mirror a branch with another Git server

A Git mirror keeps one branch in sync with a branch on another Git server:
pull changes into GitCafe, push them out, or both. Maintainers set mirrors up
under **Settings → Git mirrors**.

Mirrors copy branch history only. Tags, Git LFS objects, issues and pull
requests are not copied. To synchronize with GitHub, including issues and pull
requests, use [GitHub sync](https://git.cafe/docs/integrations/github-sync.md) instead.

## Add a mirror

1. Choose **Add Git mirror**.
2. Enter the remote's public HTTPS URL ending in `.git`, for example
   `https://git.example.com/team/repository.git`. SSH URLs, custom ports and
   private network addresses are not supported.
3. Enter the GitCafe branch and the remote branch, such as `main`.
4. Choose a direction:
   - **Pull into GitCafe**: the remote branch updates the GitCafe branch.
   - **Push from GitCafe**: the GitCafe branch updates the remote branch.
   - **Two-way sync**: changes flow both ways. Choose which side wins when
     both have changed; a change on only one side always syncs.
5. Choose how often to check, from 5 to 1,440 minutes (default 5).
6. If the remote needs credentials, choose **Username and token** and enter
   them. Never put credentials in the URL.
7. Choose **Create mirror**.

The card then shows the latest sync and **Last synced** time. A repository can
have up to 16 mirrors, and each transfer is limited to 64 MiB.

## Manage a mirror

- **Sync now** checks for changes immediately.
- **Pause** stops scheduled syncs; **Resume** starts them again.
- **Edit** changes the mapping. Changing the URL requires entering its
  credentials again; otherwise saved credentials are kept.

Failed syncs stay visible with their error. **Awaiting confirmation** means a
sync may already have changed a branch; GitCafe checks the outcome before
running another. If a save or action reports an uncertain result, use
**Check status** or **Retry action** rather than creating a new mirror.


---

Source: https://git.cafe/docs/repositories/repository-stars

# Star a repository

Star a repository to bookmark it and show your appreciation. The **Star**
button and the star count are in every repository header.

- Choose **Star** to star the repository and **Starred** to remove your star.
  Signed-out visitors are asked to sign in.
- The people icon beside the button opens the list of everyone who starred the
  repository.
- Your starred repositories appear alongside your member repositories in the
  sidebar, ordered by most recent repository activity.

The `/root` repository list also shows the most recently active repositories
first. Successful pushes, repository edits, issue and pull-request changes,
comments, reviews, and star/unstar changes count as activity; browsing does not.
Activity is shared across users, not a personal browsing history. Background
processing updates recency after a write and refreshes lists for viewers following
that repository. Activity alone does not refresh every organization member's
list; other viewers see the latest ordering when they return to the tab or fetch
the list again. Repeated star/unstar requests that do not change anything do not
advance recency. Repository creation, removal and access changes retain their
normal directory notifications.

From the terminal: `cafe repo star owner/repo`, `cafe repo unstar owner/repo`,
and `cafe repo starred` to list your stars.


---

Source: https://git.cafe/docs/pull-requests/open

# Open a pull request

A pull request proposes merging the commits on one branch (the source) into
another (the target). Open one when your branch is ready for review, then
[review](https://git.cafe/docs/pull-requests/review.md) and [merge](https://git.cafe/docs/pull-requests/merge.md) it.

## Open a pull request in the browser

1. Push your branch (see [Git access](https://git.cafe/docs/getting-started/git-access.md)).
2. Open the repository's **Pull requests** tab and choose **New Pull Request**.
   From the **Branches** tab, **New pull request** on a branch row preselects
   that branch as the source.
3. Pick the **Source branch** and the **Target branch**. The target defaults to
   the repository's default branch.
4. Enter a **Title** (prefilled from the branch name: `fix-parser` becomes
   `Fix parser`) and an optional **Description**. The description supports
   Markdown and pasted or dropped images.
5. Choose **Create Pull Request**.

The diff between the two branches appears under the form so you can check what
you are proposing. If creation fails, the error appears above the button and
the form keeps your values.

## Open a pull request from a fork

On a fork, turn on **Create pull request on upstream `owner/repo`**. The target
picker then lists the parent repository's branches, and the pull request is
created in the parent with your fork's branch as its source. Cross-fork pull
requests have no diff preview on the form. The toggle is unavailable while the
description contains an attached image, because attachments belong to the
repository they were uploaded to.

## Open a pull request from the CLI

From a checkout of the repository, with your branch pushed:

```sh
cafe pr create --title "Fix the parser" --body-file description.md
```

The source is the current branch and the target is the default branch unless
you pass `--head` or `--base`. Add `--draft` to open it as a draft. See the
[CLI reference](https://git.cafe/docs/reference/cli.md#pull-requests) for the other commands.

## Change the target branch

To retarget an open pull request, run:

```sh
cafe pr edit 27 --base main
```

You need permission to edit the pull request, and the new target must already
exist. Members of a [stack](https://git.cafe/docs/pull-requests/stacks.md) follow their stack instead, and pull
requests in a repository synchronized with GitHub change their base on GitHub.

## Find a pull request

The **Pull requests** tab starts with **Status is Open**. Change that filter to
find merged or closed pull requests, or remove it to see every status.

- **Add filter** narrows by assignee, creator, label, milestone, source or
  target branch, cross-fork status, or whether the discussion is locked. Each
  filter supports **is** and **is not**.
- **Search** matches titles within your active filters.
- The address keeps your search and filters, so you can bookmark or share the
  view.

Results are newest first; **Load more** shows older ones. If a pull request is
missing, clear the search and check the status filter.


---

Source: https://git.cafe/docs/pull-requests/review

# Review a pull request

Open a pull request from the repository's **Pull requests** tab. Anyone with
read access can read it; reviewing, merging and moderating depend on your
repository role.

## Read the changes

A pull request has three tabs:

- **Conversation**: the description, checks, comments and reviews.
- **Commits**: the commits in the change, newest first. Select one to inspect
  it.
- **Files changed**: the diff, with inline review threads. Use the file tree
  to jump to a file; the toolbar shows the total lines added and removed. A
  moved file is shown once as `old → new`, with only the lines it changed, as
  `git diff -M` pairs it.

The header names the source and target branches. A pull request from a fork
shows its source as `owner:branch`, or **deleted fork** if the fork is gone.

When new commits arrive, the comparison refreshes. Review the latest changes
before approving or merging. Earlier reviews stay attached to the commits they
reviewed; whether an approval still counts depends on the repository's
[rulesets](https://git.cafe/docs/repositories/repository-rulesets.md).

To read a pull request from the terminal, run `cafe pr view <number>`, or
`cafe pr diff <number>` to list changed files. Add `--patch` for the patches,
or `--path <file>` for one file.

## Comment on lines

On **Files changed**, press **+** on a diff line to open a comment form. To
comment on several lines, select the range and press **+** at one end.

- **Add single comment** publishes the comment immediately.
- **Start a review** (then **Add review comment**) adds it to your pending
  review. Nobody else sees pending comments until you submit.

Replies to an existing thread are always published immediately. Pending
comments show a **Pending** mark and can be edited or deleted until you submit.

## Submit a review

While you have pending comments, a bar above the diff shows how many. Choose
**Review changes** (or **Review** in the pull request header), write an
optional summary and pick a verdict:

- **Comment**: general feedback.
- **Request changes**: the change should not merge as is.
- **Approve**: the change is ready.

Submitting publishes the review and all its pending comments together.
**Discard review** deletes the pending review and its comments.

A pending review belongs to the revision it was started on. If the pull request
is updated, you can no longer add to it: discard it and review the current
revision.

## Track viewed files

Tick **Viewed** on a file to collapse it and move to the next one. When you are
signed in, viewed files are remembered across browsers for the same revision;
they reset when the pull request is updated.

## Organize the review

Depending on your role, the sidebar lets you request reviewers, set assignees,
labels and a milestone, see checks for the latest commit, and lock or unlock
the conversation. The author or a maintainer can edit the title and description
with their pencil buttons. Reviewers and assignees can include
[app bots](https://git.cafe/docs/integrations/apps.md#bots-where-people-work), listed under
**Bots** after people; a requested bot's approval counts like a person's. Each
reviewer and assignee shows who requested or assigned them. Someone who can no
longer be asked, such as a person who left the organization or a bot whose app
is paused, stays with a warning until you remove them; other changes still
save.

Maintainers can hide or unhide a comment from its menu and dismiss a review
from its card. Hidden comments keep their place in the discussion; dismissed
reviews stay visible, marked as dismissed.

If someone else changes the pull request while you are editing, reload, review
their change and try again.


---

Source: https://git.cafe/docs/pull-requests/merge

# Merge a pull request

Merge an open pull request from the merge button at the bottom of its
**Conversation** tab. You need permission to merge, and the repository's
[rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) must be satisfied.

## Choose a merge method

Open the merge button's menu and choose one of:

- **Merge**: adds a merge commit that joins the two branches.
- **Squash and merge**: combines every commit into one commit on the target.
- **Rebase and merge**: replays each commit onto the target without a merge
  commit.

Methods the repository does not allow are marked unavailable. For a
repository synchronized with GitHub, GitHub performs the merge.
GitCafe uses its last observation of GitHub's repository settings and target-branch
rules to mark methods unavailable; GitHub still enforces its current rules.

From the terminal, run `cafe pr merge <number> --strategy squash` (`merge`,
`squash`, `rebase` or `fast-forward`; without the flag, the first method allowed
by pull status is used). Check readiness
first with `cafe pr ready <number>`. Members of a [stack](https://git.cafe/docs/pull-requests/stacks.md) merge through the stack
with `cafe stack merge`.

## Fix what blocks a merge

When something stands in the way, the merge area shows **N blockers** or
**Conflicts**. Open it to see each requirement: required reviews, required
checks, conflicts or missing permissions. Resolve them and try again.

If the latest commits from a fork have not arrived yet, choose **Retry**.

## Delete the branch after merging

When **Automatically delete head branches** is on in the repository's
[settings](https://git.cafe/docs/repositories/repository-settings.md), GitCafe deletes the source
branch after a merge. Default, target and protected branches, and branches used
by another open pull request, are kept. A deleted branch can be
[restored](https://git.cafe/docs/repositories/repository-settings.md#restore-a-deleted-branch).

## Close, reopen or mark as draft

The merge button's menu also offers **Close pull request** and **Convert to
draft**. A draft pull request shows **Ready for review** instead of merge
options. Closed pull requests can be reopened; merged ones stay **Merged**.
From the CLI, `cafe pr draft <number>` and `cafe pr undraft <number>` do the
same, and `cafe stack draft` and `cafe stack undraft` change a whole
[stack](https://git.cafe/docs/pull-requests/stacks.md).

## Merges made outside GitCafe

If the pull request's latest commit reaches the default branch another way,
such as a direct push, GitCafe marks the pull request **Merged**. Only that
exact commit counts: if you squashed or rebased the change before pushing,
close the pull request yourself.

To close issues when a change merges, see
[close issues from commits](https://git.cafe/docs/issues/issues.md#close-issues-from-commits-and-pull-requests).


---

Source: https://git.cafe/docs/pull-requests/stacks

# Stacked pull requests

A stack is an ordered chain of open pull requests in one repository. Each pull
request targets the branch of the one below it, and the bottom one targets the
**landing base**, such as `main`. Use a stack to split a large change into
small reviewable steps that depend on each other.

You need read access to view a stack and write access to change one. A pull
request belongs to at most one stack, and a stack holds 1 to 50 pull requests
waiting to land and at most 200 over its lifetime, landed ones included. Adding
past either limit fails with `STACK_MEMBER_LIMIT`: land part of the stack, or
start a new one.

## View stacks

Open the repository's stacks list at `/<owner>/<repo>/stacks`. Each row shows
the title of the lowest open pull request, who created the stack, every member
with its check state, and the first thing in the stack's way, such as failing
checks or requested changes.

The stack page, `/<owner>/<repo>/stacks/<number>`, is where you act on a stack.
It lists every member bottom up with its author, branches, checks, reviews and
readiness, and offers **Land**, **Restack**, **Unstack** and **Close stack**
with the live progress of the latest landing and restack.

A member is **Ready** when GitCafe would merge it now: it is open, not a draft,
targets the right branch, and its checks, reviews and the landing base's
[rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) allow it. A member above one
that is not ready is **Blocked downstack**. **Waiting on checks** means only
required checks are still running. A blocked member lists what blocks it.
Readiness is a preview: landing checks every pull request again.

A pull request in a stack shows the stack in its sidebar and links to the
previous and next layer under its title, on the same tab, so you can review a
stack layer by layer. The pull request list marks stacked pull requests with
their stack and layer, such as `Stack #7 · 2/3`.

```sh
cafe stack list
cafe stack status 14
```

## Publish a stack from local commits

`cafe stack submit` pushes each commit after the base as its own branch and
opens or reuses one pull request per commit. Run it from your checkout; it
works with Git, jj and Sapling.

```sh
cafe stack submit --name my-change --base main --dry-run
cafe stack submit --name my-change --base main
```

`--dry-run` shows the plan without pushing. To add commits to the stack later,
run the same command with the same `--name` and `--base`: the existing pull
requests must be the start of the new plan, and new ones are appended. Each
commit keeps the branch it was published on, so submitting again after
`cafe stack sync` dropped landed commits, or after you inserted one, updates
the same pull requests.

`--draft` opens the pull requests it creates as drafts; ones it reuses keep
their state. `cafe stack draft --stack <number>` and
`cafe stack undraft --stack <number>` convert every open member at once.

If submission stops part-way, the branches and pull requests it already
created remain. Fix the problem and run the same command again to continue.

To choose the version control tool once instead of passing `--vcs` each time:

```sh
cafe config set stack.vcs jj --global
```

## Build and edit a stack locally

The `cafe stack` local commands give you a day-to-day loop for the commits
before and between submissions. Each layer is one commit (in jj, one change)
and becomes one pull request when you submit.

```sh
cafe stack create -m "Add the parser" --all --branch parser
cafe stack create -m "Use the parser" --all
cafe stack log
cafe stack down
cafe stack modify --all
cafe stack submit --name parser
```

| Command               | What it does                                                                                       |
| --------------------- | -------------------------------------------------------------------------------------------------- |
| `create -m <message>` | Commit a new layer on the current one. Below other layers, add `--insert` to restack them above it |
| `modify`              | Amend the current layer (`-m` rewords it) and restack the layers above                             |
| `up`, `down`          | Move one layer, or as many as you name (`down 2`)                                                  |
| `top`, `bottom`       | Move to the top or bottom layer                                                                    |
| `checkout <layer>`    | Move to a layer by position (1 is the bottom), commit or change ID, or branch                      |
| `log`                 | Show the stack with each layer's pull request, and whether it needs submitting                     |
| `sync`                | Fetch the landing base, drop layers whose pull requests merged, and rebase the rest onto it        |

Only `log` and `sync` talk to GitCafe, and only to read pull requests. Your
version control tool's model is kept:

- **Git:** the stack is the commits between the remote's default branch and
  your branch. Start one on `main` with `--branch <name>`. Lower layers are
  checked out without a branch. `create` and `modify` use what you staged;
  `--all` stages everything. If a rebase conflicts, the command stops: resolve
  the files, run `git rebase --continue`, then `cafe stack log`.
- **jj:** the working-copy change is the current layer. `create` starts a new
  change, so make your edits after it; jj rebases the layers above for you.
- **Sapling:** the stack is your draft commits. `modify` runs `sl amend`; if
  a conflict stops it, resolve it and run `sl continue`.

Stacks are linear: there is one layer above each layer.

## Link existing pull requests

If the pull requests already exist and form a chain, link them bottom to top:

```sh
cafe stack link 12 13 14
```

Pull 13 must target pull 12's branch, and pull 14 must target pull 13's branch.
Linking does not push anything.

## Land a stack

Landing merges the stack from the bottom up through the pull request you
choose. Each landed pull request leaves the stack and the next one retargets
to the landing base. The stack is complete when its last pull request lands.

```sh
cafe stack merge 14 --dry-run
cafe stack merge 14 --strategy merge --yes
```

Replace `14` with the highest pull request you want to land, or pass
`--stack <number>` to land the whole stack. On the stack page, **Land** lands
every ready pull request from the bottom, and **Land through #N** on a member
lands up to that one; the dialog lets you pick the merge strategy. The preview
shows readiness; permissions and
[rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) are checked again for each
pull request as it lands, and landing stops if a branch changes after you
approved it.

Every strategy can land several pull requests at once:

- **merge** adds a merge commit for each pull request.
- **squash** adds one commit per pull request, in stack order.
- **rebase** replays each pull request's commits onto the landing base.

With squash or rebase, GitCafe rebases the next pull request's own commits
onto what just landed and pushes its branch before landing it, so its checks
and rulesets are evaluated again on the new commits. If a ruleset requires
checks that have not run on them yet, landing stops there; merge again once
they pass. The land dialog says so when you pick squash or rebase for more than
one pull request. If the rebase conflicts, landing stops with
`STACK_LAND_CONFLICT`: the pull requests below are merged and the conflicting
one is left as it was. Rebase it onto the landing base, push it, and run
`cafe stack merge` again for the rest of the stack.

A pull request in a stack can only merge through its stack: `cafe pr merge`
points you to `cafe stack merge`. Open pull requests outside the stack that
target a landed branch move to the landing base too.

### If landing does not finish

Landing continues in the background after the command returns. To check on it:

```sh
cafe stack merge --stack 7 --operation latest
```

If it is waiting for you, resume the same operation rather than starting a new
one:

```sh
cafe stack merge --stack 7 --operation <operation-id> --resume --yes
```

In the browser, the stack page and each member pull request show the
unfinished landing and offer **Resume landing** when it can continue.

### Stacks synchronized with GitHub

In a repository synchronized with GitHub through the GitHub App, with pull
request writes enabled, GitHub stacks are imported and stack changes made in
GitCafe are sent to GitHub. GitHub performs the merge and supports merge,
squash and rebase. A one-pull-request stack stays a plain pull request on
GitHub until you append a second one.

## Restack after the base changes

Restacking rebases each pull request in the stack onto the current version of
the one below it, so the chain stays up to date after a lower pull request
changes or the landing base moves. Use **Restack** on the stack page to restack
the whole stack, **Restack** in a member's sidebar to start at that member, or:

```sh
cafe stack restack --stack 7 --dry-run
cafe stack restack --stack 7
```

Each upper pull request replays only its own commits. If a pull request no
longer contains the previous tip of the one below it, restacking stops there
with a conflict: rebase the remaining pull requests locally and push them.
A failed restack may already have updated the lower branches, so check the
stack before trying again. `cafe stack restack --stack 7 --operation latest`
shows the latest result.

## Unstack or close a stack

**Unstacking** dissolves the stack and leaves its pull requests open, each
keeping its branch and base. Use **Unstack** on the stack page, or the
[REST API](https://git.cafe/docs/reference/api.md) (the stack `unstack` operation). Pull requests
cannot be reordered or removed from a stack individually.

**Closing** dissolves the stack and closes all its open pull requests without
merging them, for example when the change landed some other way. Use **Close
stack** on the stack page, or:

```sh
cafe stack close --stack 7 --dry-run
cafe stack close --stack 7 --yes
```

Closing needs write access and permission to close each open pull request.
Pull requests that already landed stay merged. Either everything closes or
nothing changes. A stack that is landing or restacking cannot be closed until
that finishes. Closed pull requests can be reopened one by one, but the stack
cannot be restored.


---

Source: https://git.cafe/docs/issues/issues

# Track work with issues

Issues record bugs, tasks and ideas for a repository. Open a repository's
**Issues** tab to find, create and discuss them. You need access to the
repository.

## Create an issue

1. Choose **New Issue**.
2. If the repository has [issue templates](https://git.cafe/docs/issues/issues.md#add-issue-templates), pick the one
   that fits.
3. Enter a title and a description. The description supports Markdown and
   pasted or dropped images.
4. Choose a priority. With triage access you can also set assignees, labels
   and a milestone.

From the terminal:

```sh
cafe issue create --title "Describe the problem" --body-file issue.md
```

## Find an issue

- **Search** matches words in titles and descriptions; every word must match.
- **Add filter** narrows by status, workflow state, priority, assignee,
  creator, label, milestone, or whether an issue is pinned or locked. Each
  filter supports **is** and **is not**.
- With no status or state filter, the list shows open issues.

The address keeps your search and filters, so you can bookmark or share the
view. Results are newest first, 25 per page. [Pinned issues](https://git.cafe/docs/issues/pin-and-lock.md)
appear above the list.

`cafe issue list` lists issues from the terminal; add `--state`, `--label` or
`--assignee` to filter.

## Update an issue

The issue sidebar has **State**, **Priority**, **Assignees**, **Labels**,
**Milestone** and **Pull requests**. Choosing a completed or canceled state
closes the issue; choosing an open state reopens it. Assignees can include
[app bots](https://git.cafe/docs/integrations/apps.md#bots-where-people-work), listed under
**Bots** after people. Each assignee shows who assigned them. Someone who can
no longer be assigned, such as a person who left the organization or a bot
whose app is paused, stays assigned with a warning until you remove them;
other changes still save.

Use [labels](https://git.cafe/docs/issues/labels.md) to categorize work and [milestones](https://git.cafe/docs/issues/milestones.md) to
group issues and pull requests toward a target. To connect an issue to a pull
request, use the sidebar's **Pull requests** picker or
`cafe issue link <issue> <pull>`.

## Close issues from commits and pull requests

Write `Fixes #12` in a commit message or pull request description, and issue
#12 closes when the change reaches the default branch. These keywords work, in
any letter case: `close`, `closes`, `closed`, `fix`, `fixes`, `fixed`,
`resolve`, `resolves`, `resolved`.

Only issues in the same repository close, and only when the person who pushed
or merged has at least the triage role.

## Add issue templates

Put templates in `.gitcafe/ISSUE_TEMPLATE/` or `.github/ISSUE_TEMPLATE/` as
`.md`, `.markdown` or `.txt` files. GitCafe uses `.gitcafe` when both exist.
A single `ISSUE_TEMPLATE.md` (or `ISSUE_TEMPLATE`, `issue_template.md`) in
either directory also works when there is no template directory.

Up to 20 templates and 1 MiB of template content are read.


---

Source: https://git.cafe/docs/issues/pin-and-lock

# Pin and lock issues

Users with triage access to a repository can pin important issues and lock
heated or finished conversations. Both controls are in the issue sidebar.

## Pin an issue

Choose **Pin to top** in the sidebar. Pinned issues show a **Pinned** chip and
appear in a block above the issue list, whatever filter is active. Choose
**Unpin** to remove it. A repository can pin up to three issues.

From the terminal: `cafe issue pin <number>` and `cafe issue unpin <number>`.

## Lock a conversation

In the sidebar's **Conversation** row, choose **Lock** and optionally a reason:
off-topic, too heated, resolved or spam. While an issue is locked, only users
with triage access can comment; everyone else sees a notice with the reason.
Choose **Unlock** in the notice or the sidebar to reopen the conversation.

From the terminal: `cafe issue lock <number> --reason resolved` and
`cafe issue unlock <number>`.

Pull request conversations can be locked the same way from their sidebar, or
with `cafe pr lock`.

## Hide or delete another person's comment

Users with triage access can moderate anyone's issue comment from the comment's
menu:

- **Hide** asks for a reason: spam, abuse, off-topic, outdated, duplicate or
  resolved. A hidden comment collapses to "This comment was hidden as …";
  only users with triage access can choose **Show comment** to read it, and
  **Unhide** restores it. Its author can no longer edit it.
- **Delete** removes it. Everyone else can delete only their own comments.

A token or app needs the comments **Moderate** permission to do either.
Hiding stays in GitCafe; it is not written to a synced GitHub issue.


---

Source: https://git.cafe/docs/issues/labels

# Labels

Labels categorize issues and pull requests. Each repository has its own set,
managed under **Settings → Labels**. The label picker on an issue or pull
request links there through **Manage labels**.

Anyone who can read the repository can browse its labels. Creating, editing
and deleting them requires permission to manage labels.

## Create a label

Choose a name (1 to 50 characters), an optional description and a colour:
pick a preset swatch or type a `#rrggbb` value. The chip above the form
previews the result.

```sh
cafe issue label create bug --color "#d73a4a" --description "Something is broken"
```

## Edit or delete a label

Edit a label's name, colour or description in place. Deleting a label asks
for confirmation and removes it from every issue and pull request that has it.

If someone else changed the label first, your change is rejected and the list
refreshes; apply it again.

## Apply labels

Use the **Labels** picker in an issue or pull request sidebar, or:

```sh
cafe issue label add 12 bug
cafe issue label remove 12 bug
```


---

Source: https://git.cafe/docs/issues/milestones

# Milestones

A milestone groups issues and pull requests toward a shared target, such as a
release, and tracks how many are done. Open **Milestones** from the issue list,
or **Manage milestones** from the milestone picker.

## Create a milestone

Choose **New milestone** and enter a title, an optional due date and an
optional description.

```sh
cafe milestone create "v1.0" --due 2026-12-01
```

## Track progress

Each milestone shows its due date (**Past due** in red while it is still open),
the start of its description, a progress bar, and open and closed counts for
its issues and pull requests. **View issues** opens the issue list filtered to
that milestone. Use the tabs to switch between open and closed milestones.

## Assign issues and pull requests

Use the **Milestone** picker in the sidebar, or
`cafe issue milestone <number> <milestone>` and
`cafe pr milestone <number> <milestone>`.

## Close, edit or delete a milestone

Edit the title, due date or description in place, and close or reopen a
milestone when its work is done. Deleting a milestone asks for confirmation;
its issues and pull requests stay, without a milestone.


---

Source: https://git.cafe/docs/account/account-settings

# Account settings

Open **Settings → Account** to manage your email address, password, passkeys
and signed-in sessions, or to close your account.

## Verify or change your email

After signing up, open the link in the verification email. Until your address
shows **Verified**, you cannot create or import repositories. **Send
verification message** sends a fresh link.

To change your address, choose **Change email**, confirm it's you, and open the
link sent to the new address. Your current address stays active until then.

## Change your password

Enter your current password and a new one of at least 12 characters, twice.
Changing your password signs out every other session. If you have forgotten it,
see [password recovery](https://git.cafe/docs/account/password-recovery.md).

## Use passkeys

Passkeys let you sign in without a password and confirm sensitive changes.

- **Add passkey** asks for a label, then runs your browser's passkey prompt.
  You can have up to 20.
- Each passkey shows whether it is **Synced** across devices or
  **Device-bound**, and when it was added and last used. Rename it with the
  pencil or delete it with the bin.
- A **Counter anomaly** badge means the passkey may have been copied; delete it.

**Require a passkey** turns off password sign-in and email password recovery
for your account. Before you turn it on, make sure you can always reach a
passkey, then generate recovery codes.

### Recovery codes

With **Require a passkey** on, **Generate recovery codes** creates eight
one-time codes for getting back in if you lose every passkey. They are shown
once: save them somewhere safe and offline. Generating a new set, or turning
the passkey requirement off and on, invalidates the old codes. See
[password recovery](https://git.cafe/docs/account/password-recovery.md#recover-a-passkey-only-account).

## Confirm it's you

Sensitive changes, such as adding a passkey, creating a token or changing your
email, need a recent confirmation. When needed, a **Confirm it's you** dialog
asks for a passkey or your password. The confirmation lasts 10 minutes with a
password and 15 with a passkey, and the action you started continues on its
own.

## Manage signed-in sessions

**Active sessions** lists every signed-in browser with when it was created,
last used (updated at most once a minute) and expires. **This device** marks
the current one.

- **Revoke** signs out one other session.
- **Sign out everywhere else** signs out all other sessions.

## Close your account

Closing your account signs you out everywhere, revokes your tokens, keys and
passkeys, and archives your account. Your past issues and comments keep your
name.

You can close an account only when it has nothing left in it:

- no personal repositories (transfer them first; deleting them is not enough),
- no organization memberships (ask an owner to remove you, and hand over
  ownership of organizations where you are the only owner),
- no billing history,
- no apps installed on your account (uninstall them in
  [**Settings → Apps**](https://git.cafe/docs/integrations/apps.md#install-an-app-on-your-account)).

Choose **Close account**, confirm it's you, and type your handle. The page
lists anything that still blocks closure.


---

Source: https://git.cafe/docs/account/password-recovery

# Recover your account

If you forget your password, reset it by email. If your account requires a
passkey and you lose every passkey, use a recovery code.

## Reset your password by email

1. Open `/auth/recover` (**Forgot password?** on the sign-in page).
2. Enter your account's email address and choose **Send recovery link**.
3. Open the link in the email within 15 minutes.
4. Enter a new password of at least 12 characters, twice.

For your security, the page always says a link is on its way, whether or not
the address has an account. Requesting again replaces the earlier link.

Resetting your password signs out every session and revokes all your passkeys
and tokens, including CLI logins and SSH key access grants. Sign in with the
new password, then add passkeys and create tokens again.

Email recovery is not available when **Require a passkey** is on.

## Recover a passkey-only account

You need one of the recovery codes you saved from
[account settings](https://git.cafe/docs/account/account-settings.md#recovery-codes).

1. Open `/auth/recover` and expand **Use a recovery code**.
2. Enter your email address, one code and a new password.

This uses up all your remaining codes, turns off **Require a passkey**, sets
the new password, and signs you out everywhere, revoking your passkeys and
tokens. Sign in, add new passkeys, and generate a new set of codes.


---

Source: https://git.cafe/docs/account/profile

# Your public profile

Your profile at `/<handle>` shows your avatar, display name, bio and, when set,
your company, location and website, followed by your pinned and public
repositories. Anyone can view it without signing in. Organizations have
profiles at `/<handle>` too.

## Edit your profile

Open **Settings → Profile** to edit your display name, bio (up to 500
characters), company, location and website (a full `http` or `https` URL).
Clearing a field removes it from your profile.

To change your avatar, upload a PNG, JPEG, GIF, WebP or AVIF image of up to
1 MiB and 4096 pixels per side. It is cropped to a square; animated images use
their first frame. Removing the avatar shows your initials instead.

## Pin repositories

Pinned repositories appear first on your profile, in the order you choose, up
to six. Only public repositories are shown to visitors.

Pins are managed with the CLI. Sign in with `cafe auth login` and, on the
consent page, select **Also allow editing my public profile and pinned
repositories**. Then:

```sh
cafe profile pin owner/first owner/second
cafe profile pins
```

`cafe profile pin` with a new list replaces the old one; with no repositories
it clears your pins.

## Edit your profile from the CLI

With the same consent:

```sh
cafe profile edit --bio "Building tools" --website https://example.com
```

Only the fields you pass change; an empty string clears a field.


---

Source: https://git.cafe/docs/account/appearance

# Appearance

Open **Settings → Appearance** and choose a **Theme**: **Light**, **Dark** or
**System**, which follows your operating system. The change applies
immediately.

The theme is saved in your browser, not your account, so each browser keeps
its own. The sidebar menu has the same theme toggle.


---

Source: https://git.cafe/docs/account/ssh-keys

# SSH keys

Add an SSH key to push and pull over SSH, to sign commits, or both. Open
**Settings → SSH Keys**.

## Add an SSH key

1. Choose **Add SSH key**.
2. Enter a **Title** to recognise the key.
3. Choose the **Key type**:
   - **Authentication**: use the key for Git over SSH.
   - **Signing**: use the key to sign commits and tags. Requires an Ed25519
     key.
   - **Authentication & Signing**: both.
4. Paste your public key, the contents of your `.pub` file. Supported types are
   `ssh-ed25519`, `ssh-rsa`, `ecdsa-sha2-nistp256/384/521` and their `sk-`
   security-key variants.
5. For authentication keys, choose under **Access** what the key may do, using
   the same permissions as [personal access tokens](https://git.cafe/docs/account/personal-access-tokens.md#custom-permissions).
6. Choose **Add key**.

A key can be registered to only one account. To change an existing key's type
or access, delete it and add it again.

From the terminal, `cafe auth ssh setup` finds your key, registers it and can
configure Git to sign with it. `cafe auth ssh add ~/.ssh/id_ed25519.pub` adds a
key directly.

## Use your key with Git

Copy the SSH address from the repository's **Code → SSH** tab, such as
`git.cafe:owner/repo`, and clone with it:

```sh
git clone git.cafe:owner/repo
```

See [Git access](https://git.cafe/docs/getting-started/git-access.md#use-ssh). SSH connections
only serve Git; there is no shell, port forwarding or SFTP.

## Sign commits with your key

Add the key with **Signing** usage, then configure Git:

```sh
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
```

`cafe auth ssh setup --configure-git-signing --sign-commits` does the same.

## Remove an SSH key

Choose the bin icon on the key and confirm with **Delete**. Its access is
revoked at the same time.


---

Source: https://git.cafe/docs/account/gpg-keys

# GPG keys

Add your GPG public key so commits and tags you sign show as **Verified**. Open
**Settings → GPG Keys**.

## Add a GPG key

1. Export your public key:

   ```sh
   gpg --armor --export <key-id>
   ```

2. Choose **Add GPG key** and paste the whole block, starting with
   `-----BEGIN PGP PUBLIC KEY BLOCK-----`.
3. Choose **Add key**.

The key ID, fingerprint, subkeys and email addresses are read from the key.
Private keys are refused, and a key can be registered to only one account.

From the terminal: `cafe signing gpg add key.asc`.

## Remove a GPG key

Choose the bin icon on the key and confirm with **Delete**.

## How verification shows

Signed commits and annotated tags show **Verified** or **Unverified** in
history and on the commit page. Unsigned ones show no badge. To sign with an
SSH key instead, add it with signing usage on the [SSH keys](https://git.cafe/docs/account/ssh-keys.md) page.


---

Source: https://git.cafe/docs/account/personal-access-tokens

# Personal access tokens

A personal access token lets scripts, CI jobs and tools use the REST API or Git
over HTTPS as you, with only the permissions you grant. Tokens start with
`gct_` and always expire. Manage them at **Settings → Tokens**.

For your own terminal, the [`cafe` CLI](https://git.cafe/docs/getting-started/cli.md) login is
usually simpler: it handles Git credentials for you.

## Create a token

1. Choose **Generate new token**.
2. Under **What is this token for?**, pick a template or **Custom**. Nothing
   is selected until you choose.
3. Check the **Name** (a template fills it in when the field is empty) and
   choose an **Expiration**: 30, 60 or 90 days, or 1 year.
4. Read the summary: **This token will be able to** lists what the token can
   do in plain words, and what it can't do among pushing code and opening,
   reviewing and merging pull requests. **Show permissions** lists every
   permission.
5. Create the token and copy it from the **Token created** dialog.

The token is shown only once. If you lose it, rotate the token or create a new
one.

### Templates

| Template            | Grants                                                                                                                             | Use it for                                                  |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| **Agent / CLI**     | Code (read & write), repository metadata (read), image attachments, pull-request contents, reviews and merges, issues and comments | Coding agents and `cafe` in automation, `cafe pr create`    |
| **CI read-only**    | Code, repository metadata, pull requests, reviews and comments (read)                                                              | CI jobs that clone and inspect pull requests                |
| **Push code**       | Code (read & write), repository metadata (read)                                                                                    | Deploy bots and mirrors that only push; no pull requests    |
| **Manage webhooks** | Repository and organization webhooks (read & write), their metadata (read)                                                         | Tools that register webhooks                                |
| **Org admin**       | Every organization and team permission at its highest level                                                                        | Managing organization settings, members, teams and webhooks |

**Agent / CLI** grants the same permissions as `cafe auth token create`
without `--scope`. Every template applies to all of your resources unless you
limit it under **Repository access**;
**Customize these permissions** opens it in the custom builder to narrow it.

### Custom permissions

The custom builder lists each resource once: Repositories, Branches, Pull
requests, Issues, Comments, Organizations, Teams, Tokens & keys and Personal
profile. Each resource has one level, **No access**, **Read**, **Write** or
**Admin** (whichever apply; Comments' highest is **Moderate**, Tokens & keys
has only **Manage**):

| Resource      | Read                            | Write                                         |
| ------------- | ------------------------------- | --------------------------------------------- |
| Repositories  | Code and repository metadata    | Push code, upload attachments, read metadata  |
| Branches      | Branch rules                    | Branch rules (pushing is Repositories)        |
| Pull requests | Pull requests and their reviews | Open and edit your own, leave comment reviews |
| Issues        | Issues                          | Open issues and edit your own                 |
| Comments      | Comments                        | Write comments and edit your own              |
| Organizations | Profile and members             | Profile, webhooks; members read               |
| Teams         | Teams and their members         | Teams, their members and repository grants    |

**Admin** is every permission of that resource at its highest level.
**Details** under each resource lists its exact permissions, with a line for
the ones people confuse: approving (counts toward required approvals, as a
person's does) is separate from leaving a review and from dismissing one;
**Edit others'** changes what other people wrote; **Code & contents** is the
repository's code, while a branch's **Contents** are branch rules. Changing a
permission there marks the resource **Custom**. Branches and pull requests can
also be limited there with **Applies to**: branches matching a pattern such as
`release/*`, or pull requests targeting a branch.

### Repository access

Under **Repository access**, choose **All repositories you can access** (the
default) or **Only select repositories…** and tick them. A limited token holds
each repository permission (repositories, branches, pull requests, issues and
comments) once per selected repository; organization, team and account
permissions are not limited by repositories. Repositories created later are
never added. A token holds at most 100 permissions, so the more permissions
you choose, the fewer repositories it can be limited to (at most 50); the form
shows the limit.

Permissions are separate per resource. Code access never covers pull requests,
and branch permissions never allow `git push`. The builder warns when:

- the token can push code but can't open pull requests;
- it can open pull requests but can't push code;
- it has branch write permissions but can't push code.
- it can write pull requests, issues or comments but can't attach images to
  them.

Each warning has a button that adds the missing permission. Templates and the
builder only fill in the form: the server checks every request against the
token's permissions.

## Use a token

Send it as a bearer token to the API:

```sh
curl -H "Authorization: Bearer $GITCAFE_TOKEN" https://git.cafe/api/auth/principal
```

That request returns the account the token acts for, which is a quick way to
check a token works.

For Git over HTTPS, enter it as the password when Git asks. With the CLI, set
`CAFE_TOKEN` to use it instead of your stored login.

## Review, rotate or revoke tokens

The list shows each token's name, last eight characters, expiry and a summary
of its permissions; **Show permissions** expands the details. Select a name to
see when it was created and who it acts for.

- **Rotate** replaces the secret, keeping the name, permissions and expiry, and
  shows the new secret once. The old secret stops working.
- **Revoke** ends the token immediately.

Creating, rotating and revoking ask you to [confirm it's
you](https://git.cafe/docs/account/account-settings.md#confirm-its-you).


---

Source: https://git.cafe/docs/account/device-authorization

# Approve a CLI sign-in

Command-line tools such as the [`cafe` CLI](https://git.cafe/docs/getting-started/cli.md) sign in
with a device code. The tool shows a code like `ABCD-EFGH` and opens `/device`
in your browser, usually with the code filled in.

## Approve the sign-in

1. Sign in to GitCafe if asked; you return to the same code afterwards.
2. Choose **Continue** to see the permissions the tool is asking for.
3. Optionally select extra permissions (below).
4. Choose **Approve**, or **Deny** to refuse.
5. Return to your terminal.

The approved login lasts 90 days. Codes are eight characters, and are not case
sensitive. A mistyped, used or expired code is refused; start again from the
tool. You may be asked to [confirm it's you](https://git.cafe/docs/account/account-settings.md#confirm-its-you)
before approving.

## Optional permissions

Both are off by default. Logins you already approved do not gain them; sign in
again to change your choice.

- **Also allow editing my public profile and pinned repositories** enables the
  [profile commands](https://git.cafe/docs/account/profile.md#edit-your-profile-from-the-cli).
- **Also allow managing my tokens and SSH/GPG keys** lets the CLI create,
  rotate and revoke your tokens and keys. Only allow this for tools you trust:
  credentials they create can outlive the login.

Without the second permission, the CLI can list your public keys and inspect or
revoke its own token. It can never rotate the token it is using: run
`cafe auth login` again, then revoke the old login from **Settings → Tokens**.

## Revoke a CLI login

CLI logins appear in **Settings → Tokens**. Revoke one there, or run
`cafe auth logout` on the machine to remove its stored credentials.


---

Source: https://git.cafe/docs/account/billing

# Billing and plans

Your personal account and each organization have their own plan. See the
personal plan at **Settings → Billing**, and an organization's at
**Billing** in its settings (`/orgs/<handle>/settings/billing`). Organization
owners and admins can view billing; only owners can change it.

## Plans

| Plan          | For                      | Price (USD)            | API rate limits |
| ------------- | ------------------------ | ---------------------- | --------------- |
| Free          | Personal or organization | $0                     | 1×              |
| Pro           | Personal                 | $20 per month          | 2×              |
| Team Standard | Organization             | $40 per seat per month | 5×              |
| Team Premium  | Organization             | $80 per seat per month | 10×             |
| Enterprise    | Organization             | Contact sales          | 20×             |

Private repositories are free on every plan during the public beta.
[Pricing](https://git.cafe/pricing) lists each plan's storage, repository and
transfer allowances. The rate-limit multiplier applies to every member of an
organization; see [API rate limits](https://git.cafe/docs/reference/rate-limits.md).

## Upgrade

1. Open the billing page. The **Current plan** card shows your plan, its status
   and its renewal date.
2. Under **Upgrade**, choose Pro (personal accounts) or a Team tier
   (organizations). Enterprise links to sales.
3. Complete payment in Stripe Checkout. An organization pays for one seat per
   member; outside collaborators do not use seats.

You return to the billing page when Checkout finishes. The plan changes once
payment is confirmed; refresh the page if it still shows the old plan, rather
than starting another upgrade.

## Manage payment and invoices

With an active subscription, **Manage payment & invoices** opens the Stripe
customer portal, where you can change your payment method, download invoices
or cancel.

A **Past due** status means a payment failed; update your payment method in the
portal.

## Control seats and overage

Organizations on a per-seat plan have:

- **Seats**: seats in use and an optional **Seat limit**. Adding a member
  beyond the limit is refused; leave it empty to let seats follow membership.
  Your subscription's seat count follows the member count automatically.
- **Overage**: allow usage beyond the plan's limits, with an optional monthly
  spend cap in dollars.


---

Source: https://git.cafe/docs/organizations/organizations

# Organizations

An organization owns repositories on behalf of a group of people, with shared
roles, [teams](https://git.cafe/docs/organizations/teams.md), [rulesets](https://git.cafe/docs/organizations/organization-settings.md#rulesets) and
[billing](https://git.cafe/docs/account/billing.md). Every account also has a personal namespace
with the same handle as the user.

## Create an organization

1. Open the account menu at the bottom of the sidebar and choose **New
   organization**, or go to `/orgs/new`.
2. Enter a display name. A handle is suggested from it; handles are 1 to 39
   lowercase letters, digits or single hyphens, with no hyphen at either end.
3. Create the organization. You become its owner.

```sh
cafe org create acme --name "Acme"
```

## Find your organizations

Organizations you belong to are listed in the account menu. The organization
page, `/orgs/<handle>`, has these tabs:

- **Repositories**: the organization's profile and every repository you can see.
- **Members** and **Teams**: see [members](https://git.cafe/docs/organizations/organization-members.md) and
  [teams](https://git.cafe/docs/organizations/teams.md).
- **Settings**: for owners and admins; see
  [organization settings](https://git.cafe/docs/organizations/organization-settings.md).

Only members can open the organization page. Everyone can see its public
profile and public repositories at `/<handle>`.


---

Source: https://git.cafe/docs/organizations/organization-members

# Organization members

Owners and admins manage who belongs to an organization on its **Members**
tab, `/orgs/<handle>/members`.

## Roles

| Role                 | Access                                                           |
| -------------------- | ---------------------------------------------------------------- |
| Owner                | Everything, including billing and closing the organization       |
| Admin                | Manage members, teams, settings and repositories                 |
| Member (write)       | Write access to the organization's repositories                  |
| Member (read)        | Read access to the organization's repositories                   |
| Member (no access)   | Only what teams and repositories grant directly                  |
| Outside collaborator | Not a member; access only to the repositories they were added to |

Members also get the organization's
[default repository permission](https://git.cafe/docs/organizations/organization-settings.md#general-settings),
and [teams](https://git.cafe/docs/organizations/teams.md) or individual repositories can grant more.

## Apps

Below the members, **Apps** lists the bot of each
[installed app](https://git.cafe/docs/integrations/apps.md#bots-where-people-work): its app and
publisher, who installed it, the repositories it can reach and when it was
last issued a token (**Last token issued**; a token is not proof the bot did
anything). Owners and admins see the repositories' names; other members see how
many. Bots take no seat and have no role: what they may do is the app's
permissions on the repositories you selected for it.

## Invite a member

1. Choose **Invite member**.
2. Enter a GitCafe handle or an email address, and choose a role.
3. Send the invitation.

The invitee receives an email. Pending invitations appear under the member list
with their role and expiry, and can be revoked there.

```sh
cafe org invitation create acme alice --role org-member-write
```

## Accept an invitation

Open the link in the invitation email and sign in with the invited account.
Choose **Accept invitation**, then **Go to organization**.

An invitation sent to an email address can be accepted only once that address
is verified on your account. If the page says the invitation expired, was
revoked or was already used, ask an owner or admin for a new one.

## Change a role or remove a member

Choose a new role from the member's **Role** menu, or remove them with the bin
icon and confirm. The last owner cannot be demoted or removed, and you cannot
remove yourself here. Removing a member does not delete repositories. Changing
a member to outside collaborator removes them from the organization's teams.

```sh
cafe org member set acme alice --role org-admin
cafe org member remove acme alice --yes
```

## Leave an organization

Ask an owner or admin to remove you. If you are the only owner, make someone
else an owner first.


---

Source: https://git.cafe/docs/organizations/teams

# Teams

Teams group organization members and give them a shared role on repositories.
Teams can be nested: a child team's path joins its parents' handles with dots,
such as `eng.web`. Open the **Teams** tab of an organization.

## Create a team

Owners and admins choose **New team**, then enter:

- a name and a slug (suggested from the name);
- the visibility: **visible** to all members or **secret**;
- an optional parent team. A visible team cannot sit under a secret one, and a
  team with visible teams under it cannot be made secret.

```sh
cafe team create acme web --name "Web" --parent eng
```

To delete a team, use the bin icon in the team list. Its child teams are
deleted with it.

## Add members

On the team's **Members** tab, choose **Add member**, pick an organization
member and a team role: **Reader**, **Triager**, **Writer** or **Maintainer**.
Organization owners can change a member's role in their row; the bin icon
removes a member.

```sh
cafe team member set acme web alice --role team-writer
```

## Grant repository access

On the **Repositories** tab, choose **Add repository**, pick one of the
organization's repositories and the role the team gets: **Read**, **Triage**,
**Write**, **Maintain** or **Admin**. Change the role in the row, or revoke it
with the bin icon. You can also grant team access from a repository's
[access settings](https://git.cafe/docs/repositories/repository-access.md#give-a-team-access).

## Team rulesets

On the **Rulesets** tab, organization owners, admins and team maintainers can
add rulesets with the same editor as
[repository rulesets](https://git.cafe/docs/repositories/repository-rulesets.md). A team ruleset
applies to every repository the team has access to, and appears in their
**Effective rules** with the `team` scope. Removing the team's access to a
repository removes its rules there.

## Rename or move a team

The **Settings** tab changes the team's name, visibility and parent. Moving a
team changes its path and its address. A team cannot move under one of its own
children.


---

Source: https://git.cafe/docs/organizations/organization-settings

# Organization settings

Owners and admins manage an organization at `/orgs/<handle>/settings`. The
settings pages are **General**, **Billing**, **Webhooks**, **Apps**,
**Integrations** and **Rulesets**.

## General settings

- **Avatar**: upload a PNG, JPEG, GIF, WebP or AVIF image of up to 1 MiB and
  4096 pixels per side, or remove it.
- **Organization name**: the display name shown instead of the handle.
- **Description**, **parent company**, **location** and **website**: shown on
  the organization's profile. The description holds up to 500 characters.
- **Default repository permission**: what every member gets on the
  organization's repositories: **No access**, **Read** or **Write**. Teams and
  individual repositories can grant more.
- **Allow forking**: whether members may fork the organization's private
  repositories.

Choose **Save** to apply your changes. If another admin saved first, reload and
apply your change again.

```sh
cafe org edit acme --base-permission read
```

## Rulesets

Organization rulesets apply to every repository the organization owns. Open
**Rulesets**, choose **New ruleset** or a ruleset's name, and use the same
editor as [repository rulesets](https://git.cafe/docs/repositories/repository-rulesets.md). They
appear in each repository's **Effective rules** with the `org` scope.

## Other settings

- **Billing**: see [billing and plans](https://git.cafe/docs/account/billing.md).
- **Webhooks**: see [webhooks](https://git.cafe/docs/integrations/webhooks.md).
- **Apps**: install apps and choose their repositories; see
  [apps](https://git.cafe/docs/integrations/apps.md).
- **Integrations**: connect Buildkite; see [Buildkite](https://git.cafe/docs/ci/buildkite.md).

## Close an organization

Owners can close an organization from **General** after confirming it's them
and typing the organization's handle. Closing archives the organization; past
issues and comments keep its name.

An organization can be closed only when it has no repositories (transfer or
delete them first; deleted repositories still count) and has never had a paid
subscription. The page lists anything that still blocks closure.


---

Source: https://git.cafe/docs/ci/actions

# Run your GitHub Actions workflows on GitCafe

GitCafe runs the workflows in your repository's `.github/workflows/`
directory with the same YAML you use on GitHub. Jobs run on the official
GitHub Actions runner: on `ubuntu-latest` they get a fresh GitCafe-hosted
Linux VM, and on your own Linux, macOS or Windows machines, including
hardware-in-the-loop rigs, they run on [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md).
Results show on the repository's **CI** tab and as checks on pull
requests.

GitCafe reads workflows with GitHub's own parser and expression engine. Most
workflows run unchanged; [what's supported](https://git.cafe/docs/ci/workflow-syntax.md) lists the
exceptions, and [the table below](https://git.cafe/docs/ci/actions.md#what-works-differently) the actions that
need GitHub itself.

## Turn on Actions

Coming from GitHub? Follow [Move CI from GitHub](https://git.cafe/docs/ci/migrate-from-github.md),
including how to avoid duplicate runs while syncing.

Jobs run on your [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md).
GitCafe-hosted runners are in beta for organizations GitCafe admits; their
setting appears in **Settings → CI** once they're available to yours.

Actions is off for every repository until a repository admin turns it on:

1. Open the repository's **Settings → CI**.
2. Check **Enable Actions**.
3. If **Use GitCafe-hosted runners** is shown, keep it checked for
   `ubuntu-latest` jobs, or uncheck it to run only on your own runners.
4. Choose **Save Actions settings**.

```sh
cafe ci enable            # --no-hosted to use only your own runners
cafe ci settings
```

Turning Actions off stops runs that started before: running jobs are
cancelled and jobs that had not started don't start, even if you turn Actions
on again. Rerun them afterwards. Turning Actions on does not run anything by
itself: the next push, pull request update or dispatch does.

## Your first workflow

Add `.github/workflows/ci.yml`, or keep the one you already have:

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test
```

Push it: the run appears on the **CI** tab within a minute. If the
workflow has a mistake, you get a failed run whose page lists each problem
and where it is, instead of silence.

## Where jobs run

`runs-on` picks a runner by its **labels**: a job runs on a runner that has
every label it lists (letter case doesn't matter).

- **GitCafe-hosted runners** serve `ubuntu-latest`, `ubuntu-24.04` and
  `ubuntu-22.04`. Each job gets a new Linux x64 VM with Docker and the
  official runner, which is deleted after the job. The image has Node,
  Python, Git and the usual build tools, but not everything GitHub's images
  have; `setup-*` actions download what's missing. A repository uses them
  while **Use GitCafe-hosted runners** is on, if the server offers them:
  **Settings → CI** lists the labels it serves.
- **Self-hosted runners** are your machines. Each has the labels
  `self-hosted`, its operating system (`Linux`, `macOS` or `Windows`), its
  architecture (`X64` or `ARM64`) and any you add when you configure it, so
  `runs-on: [self-hosted, macOS, ARM64]` or `runs-on: [self-hosted, hil]`
  work as on GitHub. A repository can use the runners registered on it and
  those of its organization's [runner groups](https://git.cafe/docs/ci/self-hosted-runners.md#runner-groups)
  that it has access to. Labels choose a runner; they never grant access.

**Settings → CI** shows every runner the repository can use, with its
labels and status. A job that no runner matches waits until one with the
right labels comes online. Its run page and its check say which labels no
runner has. A run waits at most
24 hours for runners, on top of its jobs' time limits, then ends as **Timed
out**.

## Triggers

GitCafe reads the workflow files at the commit being tested, so a change to a
workflow applies to the commit that makes it. Three events run workflows:

- **`push`** runs for each branch a push creates or updates, whether you push
  with Git or GitCafe updates the branch, such as when you merge a pull
  request. Deleting a branch and pushing tags run nothing. Branches that
  change because GitCafe imports them, from [GitHub sync](https://git.cafe/docs/integrations/github-sync.md)
  or a pull mirror, run nothing either ([moving CI while synced](https://git.cafe/docs/ci/migrate-from-github.md#moving-ci-while-synced)).
  `branches`, `branches-ignore`, `paths` and `paths-ignore` filters work as
  on GitHub.
- **`pull_request`** runs when a pull request is opened or reopened, and each
  time its head branch gets a new commit. It tests the **head commit** of the
  pull request, not a merge with the base branch: `GITHUB_SHA` is the head
  commit and `GITHUB_REF` is `refs/pull/<number>/head`. `branches` filters
  match the base branch and `paths` filters the changed files. GitCafe
  reports every pull request event as the `synchronize` activity type, so a
  `types` list must include `synchronize` (the default list does). Because
  the base branch is not merged in, a pull request can pass its checks and
  still break the base once merged. To prevent that, turn on strict checks in
  a ruleset's **Require status checks** rule (`strict`): the pull request
  must then be up to date with its base before it merges, so the commit
  its checks tested already contains the base ([rulesets](https://git.cafe/docs/repositories/repository-rulesets.md#how-rules-affect-pull-requests)).
- **`workflow_dispatch`** runs a workflow on demand, at the commit a branch or
  tag points to now. You need write access to the repository. Dispatch it
  with the Cafe CLI; the web UI has no **Run workflow** button yet:

  ```sh
  cafe ci dispatch .github/workflows/ci.yml --ref main
  ```

  Inputs take their `default` values; you can't type input values yet.

A workflow may list other events, such as `schedule` or `release`: GitCafe
accepts the file and runs it only for the three events above.

## Checks on pull requests

Every run reports checks on its commit, next to Buildkite checks and commit
statuses ([read CI results](https://git.cafe/docs/ci/checks-and-logs.md)):

- one check per job, named `<workflow name> / <job name>`, such as
  `CI / test` or, for a matrix, `CI / test (22)`;
- one check per run, named `<workflow name>`, which also reports when the
  workflow could not be read.

Each check links to its run. To require a check before merging, add its exact
name to a [ruleset](https://git.cafe/docs/repositories/repository-rulesets.md)'s required status
checks. When you rerun a workflow, the new run's checks replace that run's
earlier ones.

## Secrets and variables

`${{ secrets.NAME }}` and `${{ vars.NAME }}` work as on GitHub: add them in
**Settings → Secrets and variables** of a repository or an organization
([secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md)). Every job also gets a
`GITHUB_TOKEN` that can read its own repository, valid while the job runs;
`actions/checkout` uses it to clone from GitCafe.

## Follow runs and read logs

The repository's **CI** tab lists workflow runs and Buildkite builds
together, newest first, with filters for source, status and branch (and event
for workflow runs). A run's page shows its jobs by stage, with a
matrix's combinations together under their job, each job's steps with their
results, and its log, which updates while the job runs.

```sh
cafe ci list --branch main               # workflow runs and Buildkite builds
cafe ci list --source actions --pr 42    # workflow runs at a pull request's head
cafe ci view <run-id>
cafe ci logs <run-id>                    # the first failed job
cafe ci logs <run-id> --job test --tail 50
```

Anyone who can read the repository can see its runs and logs, including
anonymous visitors to a public repository. GitCafe keeps up to 4 MiB of each
job attempt's log; past that, the oldest output is dropped.

## Cancel or rerun a run

You need write access to the repository.

- **Cancel** stops a run that hasn't finished. Jobs that haven't started are
  cancelled at once; running jobs are told to stop.
- **Rerun** starts a new run of a finished run at the same commit, with the
  same workflow, and runs every job again. To run a changed workflow, push a
  new commit instead.

```sh
cafe ci cancel <run-id>
cafe ci rerun <run-id>
```

GitCafe never reruns a job by itself. If a runner disappears mid-job, the job
fails once its runner has been silent for a few minutes; rerun it when you're
ready.

## Coming from the GitHub CLI

`cafe ci` covers the repository's CI: GitCafe Actions workflow runs,
Buildkite builds and, on a repository synced with GitHub, the GitHub Actions
runs it syncs, like the **CI** tab (`cafe actions` is an alias). Runs are
addressed by id (`actwfrun_…` for a workflow run, `ghsuite_…` for a synced
GitHub run, from `cafe ci list`), not by run number. `cafe ci view`, `jobs`,
`watch` and `logs` read a synced GitHub run, its logs from GitHub; cancel and
rerun it on GitHub.

| `gh`                                     | `cafe`                                                                                 |
| ---------------------------------------- | -------------------------------------------------------------------------------------- |
| `gh run list`                            | `cafe ci list --source actions` (`--branch`, `--event`, `--status`, `--pr`, `--limit`) |
| `gh run view <run>`                      | `cafe ci view <run-id>`                                                                |
| `gh run view <run> --log`                | `cafe ci logs <run-id> --job <job> --full` (one job at a time)                         |
| `gh run view <run> --log-failed`         | `cafe ci logs <run-id>` (the first failed job, last 200 lines)                         |
| `gh run watch <run>`                     | `cafe ci watch <run-id>` (exits 8 unless the run succeeded)                            |
| `gh run cancel <run>`                    | `cafe ci cancel <run-id>`                                                              |
| `gh run rerun <run>`                     | `cafe ci rerun <run-id>` (every job)                                                   |
| `gh workflow run <workflow> --ref <ref>` | `cafe ci dispatch <workflow>.yml --ref <ref>`                                          |
| `gh secret set NAME [--org ORG]`         | `cafe ci secret set NAME [--org ORG]` (value from `--body`, `--env` or stdin)          |
| `gh variable set NAME [--org ORG]`       | `cafe ci variable set NAME [--org ORG]` (value from `--body`, `--env` or stdin)        |
| `gh pr checks <pr>`                      | `cafe pr status <pr>` (every check: workflow jobs, Buildkite and commit statuses)      |

What's missing:

- No rerun of only the failed jobs (`gh run rerun --failed`, `--job`): `rerun` runs
  every job again.
- No dispatch inputs (`gh workflow run -f`/`--json`): inputs take their defaults.
- `cafe ci list` has no `--workflow` or `--user` filter. `--branch` matches
  runs on that branch's ref; pull request runs are on
  `refs/pull/<number>/head`, so find them with `cafe ci list --pr <pr>` (the
  runs at the pull request's current head) or `cafe ci status --pr <pr>`.
- For organization secrets and variables, `--visibility` and `--repos` take
  repository names of that organization (`name` or `org/name`); without them,
  `set` keeps an existing one's access and creates a new one as `private`.

## Pull requests from forks

Workflows do not run for pull requests from forks, and `pull_request_target`
is not one of the events GitCafe runs. A fork's run is recorded with the
result **Action required** and its check says that workflows do not run for
pull requests from forks; it cannot be rerun. This applies whatever the
repository's fork setting is.

## What works differently

Some actions and features depend on github.com or on services GitCafe doesn't
offer yet:

| What                                                                                         | On GitCafe                                                                                                                                                                                  |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actions/upload-artifact@v4`, `actions/download-artifact@v4`                                 | Fail: their own code refuses servers other than github.com. Artifacts are not supported yet; publish files elsewhere, such as a release or object storage.                                  |
| `actions/cache`                                                                              | No cache service yet: the cache step finds nothing to restore.                                                                                                                              |
| OIDC (`permissions: id-token: write`)                                                        | Not supported yet: jobs get no OIDC token, so cloud login actions that use one fail. Use a secret instead.                                                                                  |
| Pull requests from forks, `pull_request_target`                                              | Don't run ([above](https://git.cafe/docs/ci/actions.md#pull-requests-from-forks)).                                                                                                                                             |
| Actions that call the GitHub REST API (`actions/github-script`, release and comment actions) | `GITHUB_TOKEN` and `github.api_url` point at GitCafe, which implements only a few endpoints (reading a repository), and the token can only read. Calls to anything else fail.               |
| Third-party runners (Depot, Namespace, Blacksmith, ...)                                      | Their `runs-on` labels never match: those services serve github.com only. See [runner providers](https://git.cafe/docs/ci/runner-providers.md).                                                                      |
| Deployment environments (`environment:`)                                                     | The environment's name is shown on the job, but there are no protection rules, environment secrets or variables: the job runs at once.                                                      |
| Actions from repositories                                                                    | `uses: owner/repo@ref` downloads the action from public github.com. An action whose `owner/repo` is also a GitCafe repository is refused for now. Private github.com actions can't be used. |

## Limits

- A job runs for at most its `timeout-minutes` (default and maximum 360),
  then ends as **Timed out**.
- A run waits at most 24 hours for runners, on top of its jobs' time limits.
- More in [what's supported](https://git.cafe/docs/ci/workflow-syntax.md#limits).


---

Source: https://git.cafe/docs/ci/migrate-from-github

# Move CI from GitHub to GitCafe

Keep workflows in `.github/workflows`; don't move them or rewrite their YAML.
GitCafe runs them with the official GitHub Actions runner and labels their
results **GitCafe Actions** in the CI tab.

## Prepare the repository

1. Open **Settings → CI → Migration readiness**. Check each workflow's blockers
   and warnings before enabling it.
2. Copy [secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md). Secret values cannot
   be read back from GitHub; retrieve them from their original source and use
   `cafe ci secret set NAME`. Copy variables with `cafe ci variable set NAME`.
3. Register [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md). A GitCafe registration
   can run beside a separate GitHub registration on the same machine. Or use
   GitCafe-hosted Linux labels: `ubuntu-latest`, `ubuntu-24.04`, `ubuntu-22.04`.
4. Enable Actions in **Settings → CI** and push a change on GitCafe or dispatch
   a workflow with `cafe ci dispatch ci.yml --ref main`.

`GITHUB_TOKEN` is a GitCafe token, not a GitHub token. Steps that call github.com
with `gh`, `actions/github-script`, or pushes to github.com need an explicit
GitHub credential secret; configure the step's token and endpoint explicitly.

Keep workflows for fork pull requests, `schedule`, `issue_comment`, `release`,
cross-repository reusable workflows, and workflows that require cache on GitHub
for now. See [unsupported features](https://git.cafe/docs/ci/actions.md#what-works-differently) and
[supported workflow syntax](https://git.cafe/docs/ci/workflow-syntax.md).

## Moving CI while synced

[GitHub sync](https://git.cafe/docs/integrations/github-sync.md) does not transfer CI execution or
results:

- **One-way (pull):** pushes and pull requests made on GitHub do not start
  workflows on GitCafe yet. Only changes made on GitCafe run workflows here.
- **Two-way:** a push made on GitCafe runs workflows here and again on GitHub
  after it syncs. Changes arriving from GitHub still do not start workflows here.

To move a whole workflow, disable it on GitHub with
`gh workflow disable <file>` or the repository's Actions settings, and let
GitCafe run it. Make changes on GitCafe: disabling GitHub's workflow does not
make inbound sync trigger GitCafe runs.

To move one job, guard it on GitCafe:

```yaml
if: github.server_url != 'https://github.com'
```

Keep GitHub-only jobs guarded with the opposite condition:

```yaml
if: github.server_url == 'https://github.com'
```

A skipped GitHub job counts as passing for GitHub required checks. Update GitHub
branch protection to stop requiring checks GitHub no longer runs. GitCafe
results are not posted to GitHub yet; until then, gate merges on the side that
runs the job, using [GitCafe rulesets](https://git.cafe/docs/repositories/repository-rulesets.md)
for jobs moved here.


---

Source: https://git.cafe/docs/ci/workflow-syntax

# What's supported

GitCafe reads workflow files with GitHub's own workflow parser and expression
engine, so [GitHub's workflow syntax reference](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax)
applies. Jobs run on GitHub's official runner, which evaluates steps itself.
This page lists where GitCafe differs: what is **not yet** supported, and
what **never** will be because it belongs to github.com. Anything not listed
works as on GitHub. To get started, see
[run your GitHub Actions workflows](https://git.cafe/docs/ci/actions.md).

## Workflow files

- Workflows are `.yml` or `.yaml` files directly in `.github/workflows/`.
  Subdirectories, symlinks and submodules there are skipped.
- GitCafe reads the files at the exact commit being tested, sorted by path,
  at most 32 of them, each at most 64 KiB.
- When a workflow is invalid, you get a failed run listing up to 20 problems,
  each with its location, as GitHub reports them.

## At a glance

| Feature                                                                                                       | Supported | Notes                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- |
| `name`, `env`, `defaults`, `permissions`                                                                      | Yes       | `permissions` is passed to the runner, but `GITHUB_TOKEN` can only read the repository ([tokens](https://git.cafe/docs/ci/workflow-syntax.md#the-github_token)).       |
| `run-name`                                                                                                    | Accepted  | Runs show the workflow's `name`.                                                                                            |
| `on: push`, `pull_request`, `workflow_dispatch`                                                               | Yes       | See [triggers](https://git.cafe/docs/ci/workflow-syntax.md#triggers).                                                                                                  |
| Other events (`schedule`, `release`, `issues`, `workflow_run`, ...)                                           | Not yet   | Accepted in the file, but never start a run.                                                                                |
| `on: pull_request_target`, runs for fork pull requests                                                        | Not yet   | [Pull requests from forks](https://git.cafe/docs/ci/actions.md#pull-requests-from-forks) don't run.                                                  |
| `on: workflow_call` (local reusable workflows)                                                                | Yes       | `uses: ./.github/workflows/<file>.yml` at the same commit, with `with`, `secrets` (or `inherit`) and outputs.               |
| Reusable workflows from other repositories (`owner/repo/...@ref`)                                             | Not yet   | Rejected with a message.                                                                                                    |
| `jobs.<id>.runs-on` labels, `runs-on.group`                                                                   | Yes       | [Where jobs run](https://git.cafe/docs/ci/actions.md#where-jobs-run). With `group`, the job runs on that runner group's runners.                     |
| `needs`, `if`, job `outputs`, `strategy` (`matrix`, `fail-fast`, `max-parallel`)                              | Yes       | `include`, `exclude` and `fromJSON` matrices too.                                                                           |
| `timeout-minutes`, `continue-on-error`, `cancel-timeout-minutes`                                              | Yes       | `timeout-minutes` at most 360.                                                                                              |
| `concurrency` (workflow and job), `cancel-in-progress`                                                        | Yes       | Groups apply within one repository.                                                                                         |
| `environment`                                                                                                 | Partly    | The name is shown on the job; no protection rules, environment secrets or variables.                                        |
| `container`, `services`                                                                                       | Yes       | Need Docker on the runner: GitCafe-hosted runners have it; your Linux runners need it installed.                            |
| `jobs.<id>.snapshot`                                                                                          | Never     | GitHub-hosted custom images; rejected.                                                                                      |
| Steps: `run`, `shell`, `working-directory`, `env`, `if`, `id`, `name`, `continue-on-error`, `timeout-minutes` | Yes       | Run by the runner as on GitHub.                                                                                             |
| Steps: `uses` JavaScript, composite and Docker actions, `docker://` images                                    | Yes       | See [actions](https://git.cafe/docs/ci/workflow-syntax.md#actions).                                                                                                    |
| `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, step outputs                                                    | Yes       |                                                                                                                             |
| `GITHUB_STEP_SUMMARY`                                                                                         | Not yet   | Accepted, not shown.                                                                                                        |
| Expressions, contexts and functions, `hashFiles()`                                                            | Yes       | GitHub's expression engine; `vars`, `secrets`, `inputs`, `matrix`, `needs`, `strategy`, `job`, `steps`, `runner`, `github`. |
| Artifacts (`actions/upload-artifact@v4`, `download-artifact@v4`)                                              | Not yet   | The actions refuse servers other than github.com.                                                                           |
| `actions/cache`                                                                                               | Not yet   | No cache service.                                                                                                           |
| OIDC tokens (`id-token: write`)                                                                               | Not yet   | Jobs get no OIDC token.                                                                                                     |

## Triggers

- **`push`** runs for branches only, never for tags or deletions.
  `branches`, `branches-ignore`, `paths` and `paths-ignore` work as on
  GitHub, including `!` negations. When GitCafe can't tell which files a push
  changed, `paths` filters let the workflow run.
- **`pull_request`** runs when a pull request is opened, reopened or its head
  branch moves. `branches` and `branches-ignore` match the base branch; path
  filters match the pull request's changes. Every event is the `synchronize`
  activity type, so a `types` list must include `synchronize` (the default
  does).
- **`workflow_dispatch`** runs with `cafe ci dispatch`; the web UI has
  no **Run workflow** button yet. `inputs` are declared as on GitHub and take
  their `default` values; you can't type values yet.

## Actions

`uses: owner/repo@ref` (and `owner/repo/path@ref`) downloads the action from
public github.com, as GitHub does: pin a tag, a branch or a full commit SHA.
JavaScript actions run on the runner's Node, composite actions run their
steps, Docker actions build or pull their image on the runner.

- `actions/checkout` clones from GitCafe with the job's `GITHUB_TOKEN`.
- `actions/setup-node`, `setup-python` and other `setup-*` actions work; on
  GitCafe-hosted runners, Node and Python are preinstalled and others are
  downloaded.
- Private actions on github.com can't be used: GitCafe downloads anonymously.
- An action whose `owner/repo` is also the name of a GitCafe repository is
  refused for now.
- Actions that call the GitHub API reach GitCafe's
  (`${{ github.api_url }}`), which implements few endpoints; see
  [what works differently](https://git.cafe/docs/ci/actions.md#what-works-differently).

## The `GITHUB_TOKEN`

Each job gets a `GITHUB_TOKEN` (`${{ secrets.GITHUB_TOKEN }}`,
`${{ github.token }}`) valid while the job runs. Whatever `permissions` say,
it can only read the job's own repository: clone it, fetch it, and read the
repository through GitHub's REST API as `actions/checkout` does. It can't
push, comment or create releases.

## Variables and contexts

The runner sets the usual `GITHUB_*` and `RUNNER_*` variables. On GitCafe:

| Variable            | Value                                                     |
| ------------------- | --------------------------------------------------------- |
| `GITHUB_SERVER_URL` | the GitCafe server, such as `https://git.cafe`            |
| `GITHUB_API_URL`    | the server's `/api/v3`, such as `https://git.cafe/api/v3` |
| `GITHUB_REF`        | `refs/pull/<number>/head` for pull requests               |
| `GITHUB_SHA`        | the commit being tested (a pull request's head commit)    |
| `GITHUB_RUN_ID`     | a number, as on GitHub                                    |

`secrets` and `vars` hold the repository's and organization's
[secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md).

## Limits

| Limit                                                     | Value                                       |
| --------------------------------------------------------- | ------------------------------------------- |
| Workflow files read per commit                            | 32                                          |
| Workflow file size                                        | 64 KiB                                      |
| Jobs per workflow                                         | 256                                         |
| Matrix combinations per job                               | 256                                         |
| Concrete jobs per run (all matrices and called workflows) | 1,000; exceeding this fails startup         |
| Reusable workflow nesting                                 | 4 levels, 20 called files per run           |
| `timeout-minutes`                                         | default and maximum 360                     |
| Run wait for runners                                      | 24 hours, on top of its jobs' time limits   |
| Job log kept                                              | 4 MiB per job attempt; older output dropped |
| Problems reported per invalid file                        | 20                                          |


---

Source: https://git.cafe/docs/ci/self-hosted-runners

# Self-hosted runners

Run [Actions](https://git.cafe/docs/ci/actions.md) jobs on machines you manage: Linux, macOS or
Windows computers, build servers, or hardware-in-the-loop rigs with boards
attached. A self-hosted runner is GitHub's official runner,
[`actions/runner`](https://github.com/actions/runner), unmodified: you
download it from GitHub, configure it against GitCafe instead of github.com,
and run it. GitCafe presents itself to the runner as a GitHub Enterprise
Server.

A runner belongs to an organization (personal accounts too) and serves the
repositories its [runner group](https://git.cafe/docs/ci/self-hosted-runners.md#runner-groups) allows, or it belongs to one
repository. Organization owners and admins manage organization runners;
repository admins manage a repository's runners.

## Add a runner

1. Open your organization's **Settings → Runners** and choose **New runner**
   (or, for a runner that serves one repository, the repository's
   **Settings → CI** and **Add runner**).
2. In the dialog, choose the machine's operating system and architecture.
3. Choose **Create registration token** and run the commands shown on the
   machine, as the user the runner should run as.

On Linux or macOS the commands are:

```sh
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.338.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.338.0/actions-runner-linux-x64-2.338.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.338.0.tar.gz
./config.sh --url https://git.cafe/acme --token GCR...
./run.sh
```

macOS packages are named `actions-runner-osx-<arch>-<version>.tar.gz`. On
Windows, in PowerShell, download `actions-runner-win-x64-2.338.0.zip`,
extract it and run `./config.cmd` and `./run.cmd` with the same arguments.

`--url` is your organization's address on GitCafe, such as
`https://git.cafe/acme`, or a repository's, such as
`https://git.cafe/acme/firmware`, for a runner that serves only that
repository. `config.sh` asks for the runner's name, group, labels and work
folder; press Enter to accept the defaults.

From the CLI:

```sh
cafe runner token create --org acme --name rig-1   # prints the config.sh command
cafe runner token create --repo acme/firmware      # a repository runner
```

A registration token starts with `GCR` and works for one hour; it can
register any number of runners until then. It is shown once. Scripts that
create tokens with GitHub's REST API ("create a registration token for an
organization" or "for a repository") can do the same against GitCafe: use
`https://git.cafe/api/v3` as the API URL and a GitCafe API token of an
organization or repository admin.

The runner needs HTTPS access to your GitCafe server and, for the actions
your workflows use, to `api.github.com` and `codeload.github.com`. It keeps
the release you installed: GitCafe doesn't update runners.

## Labels

Each runner has these labels, which it reports itself:

- `self-hosted`;
- its operating system: `Linux`, `macOS` or `Windows`;
- its architecture: `X64` or `ARM64`;
- any labels you add with `--labels` when you configure it, or at the
  `config.sh` prompt, such as `--labels gpu,cuda-12`.

A job runs on a runner that has every label in its `runs-on`; letter case
doesn't matter. To change a runner's labels, configure it again with
`./config.sh --url … --token … --replace` and the new `--labels`.

**Settings → Runners** lists each runner with its status (**Idle**, **Busy**
running a job, or **Offline** when it hasn't polled GitCafe for two
minutes), its labels (the ones it added itself, then yours), group,
architecture, version and when it was last seen.

## Runner groups

Groups decide which repositories may use an organization's runners. Every
organization has a **Default** group that all its repositories may use; new
runners join it unless you choose another group with `--runnergroup NAME`
(or at the prompt).

In **Settings → Runners → Runner groups**, choose **New group** to create one
for all repositories or only selected ones, and a group's edit (pencil)
button to rename it or change its repositories. You can delete a group that has no runners;
**Default** can be neither renamed nor deleted.

```sh
cafe runner groups create --org acme --name "HIL rigs" --repos firmware,drivers
cafe runner groups update "HIL rigs" --org acme --visibility all
cafe runner groups list --org acme
cafe runner groups delete "HIL rigs" --org acme --yes
```

To move a runner to another group, configure it again with `--replace` and
`--runnergroup`. A repository's **Settings → CI** lists the groups and
runners it can use.

## Hardware-in-the-loop rigs

Give each rig labels that say what is attached to it, and target them in
`runs-on`:

```sh
./config.sh --url https://git.cafe/acme --token GCR... --name rig-stm32 --labels hil,stm32 --runnergroup "HIL rigs"
```

```yaml
jobs:
  flash-and-test:
    runs-on: [self-hosted, hil, stm32]
    concurrency: hil-stm32
    steps:
      - uses: actions/checkout@v4
      - run: ./flash.sh && ./hil-test.sh
```

A runner takes one job at a time. Use a `concurrency` group per rig when
several workflows target it, so a queued run waits instead of interleaving
with another, and a runner group to keep the rig to the repositories that
should use it.

## Run the runner as a service

`./run.sh` runs in the foreground until you stop it. To start the runner at
boot, install it as a service after configuring it:

- **Linux** (systemd): `sudo ./svc.sh install`, then `sudo ./svc.sh start`.
  `sudo ./svc.sh status` and `sudo ./svc.sh stop` show and stop it.
- **macOS** (launchd): `./svc.sh install`, then `./svc.sh start`, as the user
  the runner runs as; the service starts when that user logs in.
- **Windows**: answer **Y** when `config.cmd` asks whether to run the runner
  as a service, or pass `--runasservice`.

## Ephemeral and just-in-time runners

`./config.sh … --ephemeral` registers a runner that takes one job and then
unregisters itself, for machines you reset between jobs.

A just-in-time (JIT) runner is created by an API call instead of
`config.sh`, for scripts that start a fresh machine per job:

```sh
curl -X POST https://git.cafe/api/v3/orgs/acme/actions/runners/generate-jitconfig \
  -H "Authorization: Bearer $GITCAFE_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "vm-42", "runner_group_id": 1, "labels": ["hil"]}'
```

The answer's `encoded_jit_config` starts the runner without configuring it:
`./run.sh --jitconfig <encoded_jit_config>`. It is a credential: pass it to
the machine privately. The runner is ephemeral. `runner_group_id` is the
group's number, which `cafe runner groups list` shows. For a repository
runner, use the repository's `generate-jitconfig` endpoint, as on GitHub. The
API token must belong to an admin of the organization or repository. For an
organization, a token with only the **Runners** permission (`org:runners`,
write) also works, as does an installed app's token with it; see
[runner providers](https://git.cafe/docs/ci/runner-providers.md#build-a-runner-service-as-an-app).

## Remove a runner

Choose the remove (trash) button next to the runner in **Settings → Runners**
(or the repository's **Settings → CI**), or:

```sh
cafe runner list --org acme
cafe runner remove <runner-id> --org acme --yes
```

Its credential stops working at once and a job it is running fails. Then
delete the runner folder on the machine, after `sudo ./svc.sh uninstall` (or
`./svc.sh uninstall` on macOS) if it runs as a service.

You can also remove a runner from the machine itself:
`./config.sh remove --token <token>` removes it from GitCafe and deletes its
configuration. Get the remove token from GitHub's REST API ("create a remove
token for an organization" or "for a repository") at
`https://git.cafe/api/v3`, with a GitCafe API token of an admin.

## Move a runner from GitHub

One runner installation is registered with one server: it can't serve
GitHub and GitCafe at the same time. To move a machine:

1. On the machine, stop the runner (`sudo ./svc.sh stop` and
   `sudo ./svc.sh uninstall` if it runs as a service).
2. Remove it from GitHub: `./config.sh remove --token <remove token>`, with a
   remove token from the runner's settings page on GitHub.
3. Configure the same installation against GitCafe with a GitCafe
   registration token, keeping its name and labels:
   `./config.sh --url https://git.cafe/acme --token GCR... --labels <same labels>`.
4. Start it again, or install the service again.

Your workflows' `runs-on` keeps working when the runner keeps its labels. To
try GitCafe without touching a GitHub runner, install a second copy of the
runner in another folder.

## Troubleshooting

- **The job waits for a runner.** Check the runner is **Idle** and has every
  label in the job's `runs-on`, and that its group lets the repository use it
  (the repository's **Settings → CI** lists what it can use).
- **`config.sh` fails with "Http response code: NotFound".** The token has
  expired, or `--url` names another organization or repository than the
  token: create a new token from the same page.
- **The runner shows Offline.** It hasn't polled GitCafe for two minutes:
  check it's running and can reach your GitCafe server.
- **Logs.** The runner writes its own logs to the `_diag` folder in its
  installation directory: `Runner_*.log` for registration and polling,
  `Worker_*.log` for each job.


---

Source: https://git.cafe/docs/ci/runner-providers

# Runner providers

A runner provider is a service or autoscaler that starts a fresh runner for
each queued job and throws it away afterwards. This page says which ones
work with GitCafe [Actions](https://git.cafe/docs/ci/actions.md) and how a runner service connects to
GitCafe as an app.

## GitCafe-hosted runners

GitCafe's own hosted runners (`ubuntu-latest`, `ubuntu-24.04`,
`ubuntu-22.04`) are started by GitCafe's fleet. The protocol the fleet uses
to learn about queued jobs is internal to GitCafe and closed to third
parties; you don't need to do anything to use hosted runners.

## Third-party runner services

Services that run GitHub Actions jobs on their own machines connect to
GitHub, not to GitCafe:

- **Depot, Namespace and hosted Ubicloud** serve github.com only.
- **Blacksmith, WarpBuild** and others that support GitHub Enterprise Server
  could serve GitCafe.

A runner service can serve GitCafe by publishing a GitCafe
[app](https://git.cafe/docs/integrations/apps.md) that your organization installs; see
[Build a runner service as an app](https://git.cafe/docs/ci/runner-providers.md#build-a-runner-service-as-an-app). None
is available today. Until then, a workflow whose `runs-on` names such a
service's labels waits for a runner until its run times out. Change `runs-on`
to `ubuntu-latest` or to your own
[self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md).

## Autoscalers you run yourself

**actions-runner-controller (ARC)** with runner scale sets,
**terraform-aws-github-runner** and **RunsOn** need GitHub's scale-set and
webhook APIs, which GitCafe doesn't offer yet; they don't work with GitCafe.

To start a fresh machine for each job with your own tooling, use
[ephemeral or just-in-time runners](https://git.cafe/docs/ci/self-hosted-runners.md#ephemeral-and-just-in-time-runners):
your script asks GitCafe for a JIT configuration, starts a machine with it,
and the runner unregisters itself after one job.

## Build a runner service as an app

A runner service publishes one GitCafe app; each organization that installs
it lets it start just-in-time runners in that organization's
[runner groups](https://git.cafe/docs/ci/self-hosted-runners.md). Its runners are GitHub's official
runner, as on GitHub Enterprise Server.

1. In the app's manifest, ask for the **Runners** permission
   (`{ "kind": "org", "family": "runners", "level": "write" }` in
   `permissions.autonomous.scopes`) and subscribe the app's webhook to
   `actions.workflow_job`. **Read** is enough for the events and the demand
   read below; **Write** also starts runners. Both levels are **Org-wide**
   and **Owner only**: the events and the demand name every repository the
   organization's runner groups serve, private ones and ones outside an
   installation's selected repositories included, and the runners take jobs,
   with their secrets, from all of them. An organization admin's API token
   with the same permission works the same way.
2. The organization installs the app, on all repositories so its webhook
   hears about every job (app webhooks send events only from the
   repositories an installation can reach).
3. Start runners for queued jobs: `actions.workflow_job` sends `queued` when
   a job one of the organization's runner groups serves is queued, with the
   job's `labels` and, when only one group serves it, its `runnerGroupId`.
   If you serve those labels, mint a runner with an
   [installation token](https://git.cafe/docs/integrations/apps.md#publish-an-app):

   ```sh
   curl -X POST https://git.cafe/api/v3/orgs/acme/actions/runners/generate-jitconfig \
     -H "Authorization: Bearer $INSTALLATION_TOKEN" -H "Content-Type: application/json" \
     -d '{"name": "depot-7f3a", "runner_group_id": 12, "labels": ["depot-ubuntu-24.04"]}'
   ```

   Start a fresh machine with `./run.sh --jitconfig <encoded_jit_config>`.
   The runner is ephemeral: it takes one job and unregisters itself. When
   `runnerGroupId` is `null`, several groups serve the job: use the group
   the organization chose while setting your app up
   (`cafe runner groups list` shows each group's number and pool ID).

4. Follow the job through its events: `in_progress` when a runner takes it,
   with the pool and runner, and `completed` when it ends, or when it is
   cancelled before any runner took it. See
   [Actions job events](https://git.cafe/docs/integrations/webhooks.md#actions-job-events).
5. Catch up after missed deliveries with the pool's demand: the **Read pool
   demand** operation of the [REST API](https://git.cafe/docs/reference/api.md), with the pool
   ID as `pool` and the same installation token. The answer lists the pool's
   oldest queued jobs with their repository and labels and carries an
   `ETag`; send it back as `If-None-Match` with `wait=<seconds>` (up to 50)
   to hold the request until the demand changes. A pool of another
   organization, or one the token cannot read, answers 404.

An app reaches only its installing organization's own pools: never the
GitCafe-hosted pool, another organization's pools or repository-level
runners. Registration tokens and listing or removing runners still need an
organization admin.


---

Source: https://git.cafe/docs/ci/secrets-and-variables

# Secrets and variables

Workflows read credentials from **secrets** and plain configuration from
**variables**, as on GitHub: `${{ secrets.NPM_TOKEN }}` and
`${{ vars.NODE_VERSION }}`. You can keep them on a repository or on an
organization, where they can reach many of its repositories.

Every job of a workflow receives them, on GitCafe-hosted runners and on
[self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md) alike.

## Secrets

A secret's value is encrypted when you save it and can't be read back, by you
or anyone else; GitCafe decrypts it only to hand it to a job. Lists show only
the names of secrets and when they last changed. The runner masks secret
values in job logs.

To add or replace a repository secret, open the repository's **Settings →
Secrets and variables**, choose **New secret**, and enter its name and value.
To change a value, choose **Update** and enter the new value. You need to be a
repository admin.

```sh
cafe ci secret set NPM_TOKEN --env NPM_TOKEN   # from an environment variable
cafe ci secret set DEPLOY_KEY < deploy_key      # from stdin
cafe ci secret list
cafe ci secret delete NPM_TOKEN --yes
```

`--body VALUE` also works, but leaves the value in your shell history. A
value read from stdin loses one trailing newline. The CLI never prints a
value.

## Variables

Variables are stored in plain text, and admins can read their values in
**Settings → Secrets and variables → Variables** or with the CLI. Don't use
them for anything you would put in a secret.

```sh
cafe ci variable set NODE_VERSION --body 22
cafe ci variable list
cafe ci variable delete NODE_VERSION --yes
```

## Names and values

- Names may contain letters, digits and underscores, and may not start with a
  digit or with `GITHUB_`.
- Names are not case-sensitive: GitCafe saves them in upper case, so
  `npm_token` and `NPM_TOKEN` are the same secret. Use any case in workflows.
- A value can't be empty and can be at most 48 KB.
- A repository can have up to 100 secrets and 500 variables; an organization
  up to 1,000 of each.

## Organization secrets and variables

Organization owners and admins manage them in the organization's **Settings
→ Secrets and variables**, and choose which repositories each one reaches:

- **All repositories** of the organization;
- **Private repositories**: every repository that isn't public;
- **Selected repositories**: only the repositories you pick. You can change the
  list later without entering a secret's value again.

```sh
cafe ci secret set NPM_TOKEN --org acme --env NPM_TOKEN                  # private repositories
cafe ci secret set NPM_TOKEN --org acme --visibility all --env NPM_TOKEN
cafe ci secret set NPM_TOKEN --org acme --repos app,site --env NPM_TOKEN  # selected
cafe ci variable list --org acme
```

## What a job receives

When a job starts, it receives:

- every secret and variable of its repository;
- every organization secret and variable that reaches the repository, except
  those with the same name as one of the repository's: **a repository secret
  or variable overrides the organization's**.

A change applies to jobs that start afterwards. A job receives at most 200
secrets and 200 variables: the repository's first, then the organization's,
each in name order.

Runs for pull requests from forks receive no secrets and no variables, as on
GitHub: `vars` is empty in their steps and in job-level fields such as
`runs-on` and `if`. They don't run in this version anyway
([pull requests from forks](https://git.cafe/docs/ci/actions.md#pull-requests-from-forks)).

## Not supported

GitHub's REST endpoints for secrets, which take values sealed with the
repository's public key, are not supported; use the CLI or the settings pages.
Environment secrets and variables are not supported either: a job with an
`environment` receives the repository's and organization's.


---

Source: https://git.cafe/docs/ci/buildkite

# Connect Buildkite

GitCafe shows Buildkite builds on a repository's **CI** tab and as checks on
pull requests. Builds run in Buildkite; GitCafe does not start or schedule
them. Connecting takes two steps: an integration for your account or
organization, then a pipeline for each repository.

## Add the integration

1. Open **Settings → Integrations → Buildkite** for your personal account, or
   **Integrations** in an organization's settings.
2. Enter a display name and your Buildkite organization slug.
3. In Buildkite, add a webhook notification service. Copy its **Token** into
   GitCafe's webhook secret field, and set the service's URL to the receiver
   URL GitCafe shows. Select the `X-Buildkite-Signature` header.
4. Save.

The webhook secret is never shown again. When you rotate the token in
Buildkite, enter the new one in GitCafe.

```sh
cafe integration create acme --type buildkite --name Buildkite \
  --provider-org acme --webhook-secret-env BUILDKITE_WEBHOOK_TOKEN
```

### Integrations an app set up

An app installed with the `org` `ci` `admin` permission can create an
organization's integrations. Once an app has set an integration's webhook
secret or API token, GitCafe accepts its Buildkite events and reads its logs
only for repositories the app's installation reaches at that moment. A
pipeline bound to any other repository, including one later removed from the
installation, gets no checks or statuses: GitCafe refuses the delivery, so
Buildkite shows it as failed. Its logs are unavailable too. Uninstalling the
app refuses every repository, until a person replaces the credentials.
Pausing the app refuses every repository until it resumes; events Buildkite
sends while it is paused are not applied, so rebuild after resuming.

An organization administrator lifts this by replacing the webhook secret, and
the API token if one is saved, in the same edit. That does not change who
controls the Buildkite organization the integration names: whoever runs it
there can still see and change its webhook token and builds. If you do not
trust the app with your repositories' checks, add a new integration for a
Buildkite organization you control and move the pipelines to it.

The app can change, disable or remove the integration only while its
installation reaches every repository with a pipeline bound to it
(`CI_INTEGRATION_UNSELECTED_BINDING`). Another app can replace the
credentials only all at once: the webhook secret, and the API token if one is
saved, in the same edit (`CI_INTEGRATION_OTHER_INSTALLATION`). An
organization administrator can always change them.

## Connect a repository's pipeline

1. Open the repository's **Settings → CI** and, under **Buildkite
   pipelines**, choose **Add pipeline**.
2. Choose the integration, enter the Buildkite pipeline slug and a check name
   (the context shown on pull requests).
3. Choose **Add pipeline**. Its builds then appear on the repository's **CI**
   tab beside workflow runs.

```sh
cafe ci pipeline create --integration <id> --pipeline my-pipeline --context buildkite
```

Transferring a repository to another organization removes its pipelines,
which belong to the old organization's integrations. Add them again from the
new organization; transferring it back does not restore them.

## Share job logs

To show jobs and logs in GitCafe, save a Buildkite API token with
`read_builds` and `read_build_logs` in the integration's log access form. Then
turn on **Repository readers can view Buildkite logs** for each pipeline.

Log sharing is off by default. When on, everyone who can read the repository
can read its logs, including anonymous visitors to a public repository.

With an API token saved, GitCafe also asks Buildkite for any unfinished build
it has not heard about for 15 minutes, less often the longer the build stays
quiet. That way a webhook event that never arrived does not leave the build's
check queued or running.

## Publish results to GitHub

For a repository synchronized with GitHub, you can publish each pipeline's
results to GitHub as checks. Turn on publication in the repository's CI
integration settings. It needs the GitHub App to have Checks read and write
permission on the linked repository.

A run's publication status is shown separately from its result: a run can be
**Successful** while **GitHub publication blocked** means GitHub has not
received it yet. Check the GitHub App's permissions if publication is blocked.


---

Source: https://git.cafe/docs/ci/checks-and-logs

# Read CI results and logs

Once [Buildkite is connected](https://git.cafe/docs/ci/buildkite.md), GitCafe shows build results on
the repository's **CI** tab and as checks on pull requests and commits.
Workflows that GitCafe runs itself with [Actions](https://git.cafe/docs/ci/actions.md) report checks
the same way; their runs and logs are on the repository's **CI** tab
([follow runs and read logs](https://git.cafe/docs/ci/actions.md#follow-runs-and-read-logs)).

## See checks on a pull request

The checks panel on a pull request's **Conversation** tab lists every check on
its latest commit: Buildkite and GitCafe checks (including one per Actions job
and one per Actions workflow), checks apps and scripts
[report through the API](https://git.cafe/docs/integrations/check-runs.md), commit statuses and
checks imported from GitHub. A GitHub check re-run on the same commit shows
only its newest attempt, as GitHub does. A commit's page shows the same panel.
The badge summarizes them all, and a check's name opens its log in GitCafe
(a commit status's name links to its own target). Checks are grouped into
Failing, In progress, Successful and Skipped sections; the first two stay
open, and the panel's footer shows or hides the other two. An app's suite of
checks stays together, in the section of its worst check. The header counts
every check. An Actions
job's check is named `<workflow name> / <job name>`, with each matrix
combination under its own name, such as `CI / test (22)`. GitHub names the
same check `test (22)`, so update required check names you carry over from
GitHub.

Required checks are set with [rulesets](https://git.cafe/docs/repositories/repository-rulesets.md);
a pull request cannot merge until they pass on the exact commit being merged.

```sh
cafe pr status 42
```

`cafe pr status` lists every check on the pull request's head commit, Actions
jobs and Buildkite builds alike, with whether it is required and, when it has
one, a link to its run. `cafe ci status --pr 42` shows the CI runs at that
commit: workflow runs and Buildkite builds.

## Browse runs

Open the repository's **CI** tab to list runs. Filter by status and page back
through older runs; active runs refresh while the page is open. Select a run to
see its jobs and logs.

```sh
cafe ci list --branch main                 # workflow runs and Buildkite builds
cafe ci list --source buildkite --pr 42    # Buildkite builds at a pull request's head
cafe ci watch <run-id>
```

`cafe ci` lists GitCafe Actions workflow runs and Buildkite builds together,
newest first, like the **CI** tab; `--source actions` or `--source buildkite`
shows one. `--branch` lists the runs on that branch, not only at its tip.
`cafe ci watch` waits until the run finishes and exits nonzero unless it
succeeded ([follow runs and read logs](https://git.cafe/docs/ci/actions.md#follow-runs-and-read-logs)).
It polls less often while a run reports no progress and gives up after two
hours (`--timeout <minutes>`).

## Read a job log

Open a run, or select a check's name on a pull request. Failed jobs are listed
first, and the first failed job's log opens automatically. The job is part of
the address, so you can share a link to it.

The viewer shows the log as it appeared in the terminal, with colours and
progress lines:

- It starts with the last 256 KB. **Load 256 KB more** loads earlier output;
  **Load the whole log** loads all of it.
- The failures button steps through lines that report a failure: failed tests,
  compiler errors, panics and non-zero exit codes.
- Search finds text in the loaded output (Enter for next, Shift+Enter for
  previous). **Sections** jumps to a Buildkite section header.
- **Download** saves the whole log; **Copy** copies the loaded part.
- While a job runs, new output appears every few seconds. **Latest output**
  jumps back to the end.

Logs are available only when the repository [shares them](https://git.cafe/docs/ci/buildkite.md#share-job-logs).
A job that has not started has no log yet; a step Buildkite never ran on this
build (for example one its branch filter excludes) is listed as **Skipped**.

## Download logs from the CLI

```sh
cafe ci jobs <run-id>
cafe ci logs <run-id> <job-id> --output job.log
cafe ci logs <run-id> <job-id> --tail 200
```

`--full` prints the whole log, `--tail N` the last N lines, and, for a
Buildkite job, `--bytes FIRST-LAST` one byte range.

## Read a GitHub check's log

On a repository [synced with GitHub](https://git.cafe/docs/integrations/github-sync.md), checks
imported from GitHub keep their results in GitCafe but not their logs. GitCafe
reads a log from GitHub when you ask for it, with the repository's GitHub App
installation, so it takes a little longer than a GitCafe log.

GitHub keeps job logs only for GitHub Actions. A check from another app
(codecov, a linter, another CI) has no log to read: open it on GitHub, where
its app's report is. `cafe ci logs` fails for one with an error that gives its
GitHub link. A job that has not finished and a log GitHub no longer keeps are
errors too, never an empty log. Reading logs needs the GitHub App's
**Actions: read** permission, and logs over 64 MiB are refused.

On a pull request's **Conversation** tab, select a GitHub Actions check's name
to open its log in the same viewer as a GitCafe job, with search, failure
stepping, **Copy** and **Download**; **GitHub** opens the check there. Another
app's check links to its own page, as do checks imported before GitCafe
recorded which app made them. GitHub serves a job's log only once the job
finishes, so a running job has none yet. In the CLI,
`cafe pr status` prints each GitHub check's link, which `cafe ci logs` takes in
place of a run id:

```sh
cafe pr status 42
cafe ci logs https://github.com/acme/project/runs/53845667812             # the last 200 lines
cafe ci logs https://github.com/acme/project/runs/53845667812 -o job.log  # the whole log
```

`--tail N` prints the last N lines and `--full` the whole log.

## See GitHub Actions runs synced from GitHub

The repository's **CI** tab lists the GitHub Actions runs of its GitHub
mirror next to GitCafe Actions runs and Buildkite builds, each marked
**Synced from GitHub**; choose **GitHub (synced)** as the source to see only
them. GitCafe groups the synced jobs of one GitHub workflow run (one check
suite) into a run and keeps the latest attempt of each job, so a list row is
named by its jobs, failing ones first. GitCafe keeps no ref for these runs, so
a branch filter leaves them out. Jobs synced before GitCafe recorded their
check suite are not grouped into runs; they still show on the pull request.

Open a run to see it laid out like a GitCafe Actions run: its jobs, then the
chosen job's steps and log. The workflow's name, run number, event and branch,
the steps and the log are read from GitHub when you open the run, so they take
a little longer; without the GitHub App's **Actions: read** permission the run
is named by its jobs and shows no steps or logs. **GitHub** opens the run
there.


---

Source: https://git.cafe/docs/integrations/github-sync

# Import and sync from GitHub

Bring repositories from GitHub into GitCafe with their issues, pull requests,
reviews and comments, and optionally keep both sides in sync. Imports use the
GitCafe GitHub App, including for public repositories. GitHub is optional:
repositories created in GitCafe need no GitHub account.

## What syncs

An imported repository keeps receiving changes from GitHub. Two-way sync also
writes GitCafe changes back to GitHub.

| What                                     | One-way (default) | Two-way     |
| ---------------------------------------- | ----------------- | ----------- |
| Branches and tags                        | From GitHub       | Both ways   |
| Issues and pull requests                 | From GitHub       | Both ways   |
| Reviews and comments                     | From GitHub       | Both ways   |
| Labels, milestones, assignees, reactions | From GitHub       | Both ways   |
| Checks and commit statuses               | From GitHub       | From GitHub |
| Releases                                 | Not synced        | Not synced  |

GitHub Actions results arrive as checks. Images in conversations are linked,
not copied; see [images and attachments](https://git.cafe/docs/integrations/github-sync.md#images-and-attachments).

## Create an account with GitHub

On the sign-up page, choose **Sync with GitHub** and approve GitCafe on GitHub.
If GitHub does not confirm your email address, verify it with the email
GitCafe sends before importing.

Your new account also gets the SSH and GPG keys on your GitHub profile, with
the default SSH access; keys GitCafe cannot use are skipped. Manage them
afterwards under [SSH keys](https://git.cafe/docs/account/ssh-keys.md) and
[GPG keys](https://git.cafe/docs/account/gpg-keys.md).

If your GitHub account is already linked to a GitCafe account, sign in to that
account instead; use **Reconnect GitHub** if the link needs refreshing.

## Import repositories

Open `/import/github`, or choose **Import from GitHub** on `/new`. The page
walks you through three steps.

1. **Connect GitHub**: authorize your GitHub account. You return to the import
   page afterwards.
2. **Choose repositories**: if the App is not installed yet, choose **Install
   GitCafe on GitHub**, pick the account or organization, and grant access to
   the repositories you want. An organization owner may need to approve. Back
   in GitCafe, the list refreshes; use **Check access** if it has not. Use
   **Modify repository access** to add repositories later, or **Use another
   account or organization** for another installation.
3. **Review and import**: choose the destination owner and name for each
   repository (up to ten at a time), check the settings and choose
   **Import N repositories**.

Imported repositories keep their GitHub visibility. Each repository shows its
own progress; **Sync status** opens the details. The code usually arrives
before the issues and pull requests.

Imports continue on the server if you close the page. Open the import page
again to see their progress, and use **Continue imports** if any stopped.

## Choose one-way or two-way sync

By default, an import copies from GitHub and never writes back. To keep both
sides in sync, select **Enable two-way sync, including writes to GitHub** for
the repository and choose which side wins when both change: **GitCafe wins** or
**GitHub wins**. Two-way sync needs write permission on GitHub.

After the import, changes made on GitHub arrive through the App's webhooks.
Pushes and merges made in GitCafe are sent to GitHub when two-way sync is on.

## Manage sync settings

Open the repository's **Settings → GitHub sync**. It shows the connected
GitHub repository, the sync direction, recent jobs and any changes still
waiting to reach GitHub. From there you can:

- **Renew authorization** when the connection asks for it.
- Change **When both sides change** for two-way sync.
- **Reconcile now** to check changes marked **Awaiting confirmation**, which may
  already be on GitHub.
- **Retry import** for an initial import that stopped. If it stopped because
  this repository has history GitHub does not (it started in GitCafe), choose
  **Two-way sync** instead to send that history to GitHub.

If your GitHub authorization expired or was revoked, open **Account
connection**, reconnect GitHub, and try again.

A repository stays synced with the one GitHub repository it was imported from;
connecting it to another GitHub repository is refused. To sync
another GitHub repository, import it as a new repository.

## Disconnect from GitHub

If you no longer use GitHub, a repository admin can disconnect the repository
under **Settings → GitHub sync** with **Disconnect from GitHub**, or with
`cafe repo github disconnect <owner>/<repo> --yes`. This works in any state,
including an import that stopped or needs attention. The repository becomes a
plain GitCafe repository: sync stops, unfinished sync work is cancelled, and
branches the mirror protected can be deleted again. Code, issues, pull requests
and comments already imported stay. Nothing on GitHub is changed or deleted;
uninstall the GitCafe App on GitHub separately if you want.

Disconnecting waits for any change that may already be on its way to GitHub
or into the repository: if one is still settling, it is refused. Use
**Reconcile now** or wait, then try again. To sync with GitHub again later,
connect the repository again under **Settings → GitHub sync**. After
disconnecting a repository with a very large sync backlog, the cancelled work
is cleared in the background, within about 12 hours; connecting again before
that is refused with a "previous disconnect is still finishing" message that
says how much is left; each attempt clears more, so try again later.

## Merge pull requests that come from GitHub

Pull requests opened on GitHub, including from contributors' forks, appear in
GitCafe without the contributor doing anything. Their commits are copied
separately, so a new or just-pushed pull request can say that GitCafe is still
copying its commits; **Files changed** and **Commits** load on their own once
they arrive. Through the API, its source reads (`/changes`, `/commits`,
`/diff-file`, `/diff-files`) answer `503` with code `pending` and `Retry-After` until then. With
two-way sync, choose
**Merge with GitHub** in the pull request's merge controls: GitHub performs the
merge with the method you pick, subject to its own checks and branch
protection, and GitCafe then picks up the result. If GitHub refuses the merge,
the web merge panel and `cafe pr merge` (including `--operation` polling) show
GitHub's reason when available, for example when repository rules disallow the
selected merge method. Choose an allowed method or resolve the reported rule
violation before retrying.
**Retry merge** sends a new attempt, from the web or with `cafe pr merge`.

GitCafe observes GitHub's repository merge settings and effective target-branch
rules when it scans the repository's pull requests: at least every six hours, and soon after a repository, ruleset or branch protection change when the GitHub App receives those events. The web controls offer only the
observed allowed methods, falling back to the first allowed method when your
saved choice is unavailable. Without `--strategy`, `cafe pr merge` merges with a
merge commit when allowed, otherwise the first allowed method. An explicitly disallowed method is refused
before queuing a GitHub merge. Repository settings and branch rules intersect;
required linear history excludes merge commits. Observations can lag changes
on GitHub until the next scan. Before the first scan, or when GitHub does not
expose settings or branch rules, unknown restrictions do not hide methods:
GitHub still makes the final decision.

## Issue and pull request numbers

An imported issue or pull request always keeps its GitHub number. If GitCafe
already uses that number for something else, the import waits for an
administrator instead of taking another number, so later GitHub numbers still
match.

With two-way sync, opening a pull request or issue in GitCafe creates it on
GitHub first, as you, within the same request; it then appears in GitCafe under
GitHub's number. If GitHub does not have the pull request's branch yet,
GitCafe pushes it there first. If GitHub fails, or the push and GitHub
together take more than about 8 seconds, nothing is created and you can retry:
the push keeps going, and a retry finds the pull request or issue if GitHub did
create it. If you already opened a pull request on GitHub from
the same branch into the same base, GitCafe uses that one. A pull request from
a fork in GitCafe cannot be created on GitHub, so it is refused.

A repository that only reads from GitHub, whose sync is paused, or that does
not write that kind of item to GitHub, refuses new pull requests and issues in
GitCafe; open them on GitHub. GitHub sync gives each one its GitHub number.

Earlier releases gave some pull requests a temporary number from 1000000000 up.
Links to those numbers redirect to the pull request's GitHub number.

When text is written to GitHub, references like `#5` become full GitCafe links, so `Fixes #5` cannot
close an unrelated GitHub issue. Use full URLs when you refer to an issue on
the other site.

Issues closed on GitHub as duplicate or not planned appear as canceled in
GitCafe.

GitHub bots appear with a **Bot** badge and their name without the `[bot]`
suffix. A suffixed bot login links to its GitHub App page; otherwise the name
is unlinked. Bots use their cached avatar when available, or an initial when
it is missing. They remain GitHub authors, not GitCafe accounts or native apps.

Apps installed in GitCafe can be assigned to synced issues and pull requests
and asked for reviews there. They have no GitHub account, so they stay in
GitCafe: two-way sync writes only the people to GitHub, and changes made on
GitHub leave the app assigned.

## Images and attachments

Images and videos from GitHub conversations display in GitCafe. When a comment
is written to GitHub, images attached in a public repository stay images;
images from a private repository become **View attachment in GitCafe** links,
so they stay private.

## When something is missing

- GitHub does not send events for reactions, so reactions made on GitHub
  update only when the related conversation changes again.
- If a change made on GitHub never arrives, open the App's webhook delivery
  history on GitHub and redeliver the failed event.
- A configured branch that has not synced for six hours is checked
  automatically.

## Comment length

A comment, review comment or review summary holds up to 261,120 bytes of text
on GitCafe, a little under GitHub's 262,144, so a comment written here still
fits on GitHub once sync adds its hidden marker. Comments from GitHub import up
to GitHub's own limit.

To mirror a single branch with a server other than GitHub, use
[Git mirrors](https://git.cafe/docs/repositories/git-mirrors.md).


---

Source: https://git.cafe/docs/integrations/webhooks

# Webhooks

Webhooks are in beta: event coverage and payload fields may still change.
A webhook sends a signed HTTPS request to your server when something happens
in a repository or organization, such as a push, a new issue or a merged pull
request.

## Add a webhook

Repository admins open **Settings → Webhooks** in the repository; organization
owners and admins open **Webhooks** in the organization's settings.

1. Choose **Add webhook**.
2. Enter your receiver's HTTPS URL and select events. **All events**
   subscribes to every supported event. To test a new receiver first, select
   only `webhook.ping`.
3. Save, then copy the signing secret into your receiver. It is shown only
   once.
4. Choose **Manage**, then **Send signed ping**, and check **Delivery history**
   to confirm your receiver accepted it.
5. Edit the webhook to add the events you need.

A repository or organization can have up to ten webhooks.

Through the API, use a personal access token. Apps and clients you connected
with OAuth, such as MCP clients and apps acting as you, can list and delete
webhooks but cannot create, edit or re-key one: a webhook keeps sending new
data after you disconnect them.

### Webhooks of installed apps

An app installed in your organization can receive events for the
repositories you selected for it, if its manifest asks for them and you
approved the installation. These webhooks belong to the installation: they do
not appear under **Webhooks**, do not count toward the limit of ten, and you
cannot edit, test or delete them there. Pausing the app stops its deliveries,
resuming starts them again for new events, removing a repository from the app
stops deliveries for that repository, and uninstalling the app removes its
webhook. Deliveries still waiting when you do any of this are not sent, even if
you resume the app or add the repository back. A delivery already being sent
finishes and is recorded with the receiver's answer, but is not retried. The
app is still told that it was paused, resumed, uninstalled or given different
repositories ([installation events](https://git.cafe/docs/integrations/webhooks.md#installation-events)), so it can stop
work and drop credentials; these events name no repository it can no longer
read.

If you publish an app, see [App webhooks](https://git.cafe/docs/integrations/webhooks.md#app-webhooks) for what your
receiver gets, and [Your app's deliveries](https://git.cafe/docs/integrations/webhooks.md#your-apps-deliveries) for its
delivery history.

From the terminal, pass an organization slug or `owner/repo` as the target:

```sh
cafe webhook create acme/project --url https://hooks.example.com/gitcafe \
  --event issue.created --event pull.merged
cafe webhook ping acme/project <webhook-id> --request-id first-ping --expected-version <version>
```

`cafe webhook capabilities` lists the supported events, and
`cafe webhook readiness` checks whether a webhook is ready to send.

## Events

| Events                                                                       | Sent when                                            |
| ---------------------------------------------------------------------------- | ---------------------------------------------------- |
| `git.push`                                                                   | Branches or tags change by push, merge or sync       |
| `git.ref_created`, `git.ref_deleted`                                         | Branches or tags are created or deleted              |
| `repo.default_branch_changed`                                                | The default branch changes                           |
| `issue.created`, `issue.updated`, `issue.closed`, `issue.reopened`           | An issue is created, edited, closed or reopened      |
| `issue.locked`, `issue.unlocked`, `issue.pinned`, `issue.unpinned`           | An issue is locked, unlocked, pinned or unpinned     |
| `issue.assignees_updated`, `issue.labels_updated`, `issue.milestone_updated` | An issue's assignees, labels or milestone change     |
| `issue.linked`, `issue.unlinked`                                             | A pull request is linked to or unlinked from it      |
| `issue.comment_created`, `issue.comment_updated`, `issue.comment_deleted`    | An issue comment changes                             |
| `issue.reaction_added`, `issue.reaction_removed`                             | A reaction is added or removed                       |
| `pull.created`, `pull.updated`, `pull.closed`, `pull.reopened`               | A pull request is opened, edited, closed or reopened |
| `pull.synchronized`                                                          | A pull request's source branch gets new commits      |
| `pull.merged`                                                                | A pull request merges in GitCafe                     |
| `pull.drafted`, `pull.ready`, `pull.locked`, `pull.unlocked`                 | Draft or lock state changes                          |
| `pull.reviewers_updated`, `pull.assignees_updated`                           | Reviewers or assignees change                        |
| `pull.labels_updated`, `pull.milestone_updated`                              | Labels or milestone change                           |
| `pull.linked`, `pull.unlinked`                                               | An issue is linked or unlinked                       |
| `pull.review_submitted`, `pull.review_dismissed`                             | A review is submitted or dismissed                   |
| `pull.comment_created`, `pull.comment_updated`, `pull.comment_deleted`       | A pull request comment changes                       |
| `pull.comment_hidden`, `pull.comment_unhidden`                               | A comment is hidden or shown                         |
| `pull.comment_resolved`, `pull.comment_unresolved`                           | A review thread is resolved or reopened              |
| `pull.reaction_added`, `pull.reaction_removed`                               | A reaction is added or removed                       |
| `label.created`, `label.updated`, `label.deleted`                            | A repository label changes                           |
| `milestone.created`, `milestone.updated`, `milestone.deleted`                | A milestone changes                                  |
| `ci.run_updated`                                                             | A [CI](https://git.cafe/docs/ci/checks-and-logs.md) run's state changes |
| `actions.workflow_job`                                                       | An Actions job on your organization's pool changes   |
| `webhook.ping`                                                               | You send a test ping                                 |

Events describe changes made in GitCafe. Issues and pull requests imported
from GitHub do not send `issue.*` or `pull.*` events, and past activity is not
sent when you add a webhook.

### Actions job events

`actions.workflow_job` tells an autoscaler when to start and stop runners, as
GitHub's `workflow_job` does. It is sent for a job that your organization's
runner groups serve, with an `action` of `queued`, `in_progress` (a runner
took the job) or `completed`:

- `queued` is sent when the job is queued and at least one enabled runner
  group of the repository's organization may run it. The job has no pool
  yet (`poolId` is `null`); `runnerGroupId` names the group when only one
  serves the job and is `null` when several do. Decide from the job's
  `labels` whether to start a runner.
- `in_progress` follows when a runner of one of your pools takes it, with
  that pool and runner, and `completed` when it ends. Every job announced
  `queued` gets a `completed`: a job cancelled or timed out before any runner
  takes it, or taken by GitCafe's hosted runners after it was announced,
  sends `completed` with its conclusion and no pool or runner (a hosted
  runner taking it sends no `in_progress`).
- A job no runner group of your organization serves sends nothing, and so
  does a job on, or served by, GitCafe's hosted runners.

Each action is captured once per job, in order, and a receiver is never
sent `in_progress` or `completed` for a job it was not sent `queued` for:
when a runner of your pool takes a job that was not announced to you
(GitCafe's hosted runners also served it, or your group was created, bound
or enabled after it was queued), `queued` is captured first. Deliveries may
still arrive out of order, because a retried delivery waits for its next
attempt while later events go out: a retried `queued` can arrive after its
`in_progress`. Order a job's events by `status` and the `queuedAt`,
`startedAt` and `completedAt` timestamps, as on GitHub. A job whose
repository is transferred to another organization while it runs sends no
`completed` to the old organization; reconcile it with the demand read.
`workflowJob` holds the job and
run IDs, the pool and runner IDs once a runner took the job, a runner group
number (`runnerGroupId`, the `runner_group_id` of `generate-jitconfig`), the
run attempt, the job name, its labels (as in `runs-on`, lower-cased), its
`status` (the same as `action`), its `conclusion` once completed, and when
it was queued, started and completed. It never contains scripts,
environment variables, secrets, tokens, clone URLs or logs, and has no
`actor`. The shape follows GitHub's `workflow_job` event but is not
compatible with it. A receiver that missed deliveries reconciles with the
pool's provider demand read.

The body changed when `queued` was added: `workflowJob` gained
`runnerGroupId`, and `poolId` is now `null` on `queued` and on a `completed`
no runner of your pools took. A decoder that rejects unknown fields or a
`null` `poolId` must accept both.

An [installed app](https://git.cafe/docs/integrations/webhooks.md#app-webhooks) receives these events when it has the
**Runners** permission (`org:runners`, read or write), only for jobs on its
installing organization's own pools.

## The request

Each delivery is an HTTPS `POST` with a JSON body. The body has an event `id`,
a `type`, a timestamp, the repository (its ID, its organization's ID, and its
`owner` and `name` when the event happened, as in `/api/repos/<owner>/<name>`),
the `actor` who made the change, and a snapshot of the resource after the
change. Edit events include the fields that changed. Text bodies longer than
65,536 characters are cut and flagged as truncated.

| Header                  | Contains                                      |
| ----------------------- | --------------------------------------------- |
| `X-GitCafe-Event`       | The event type, same as the body's `type`     |
| `X-GitCafe-Delivery`    | The delivery ID, the same across retries      |
| `X-GitCafe-Timestamp`   | When this attempt was signed, in Unix seconds |
| `X-GitCafe-Key-Version` | Which signing secret was used                 |
| `X-GitCafe-Signature`   | `sha256=` followed by the hex HMAC            |

The full payload schemas are in the `webhooks` section of the
[OpenAPI description](https://git.cafe/api/openapi).

### Git events

`git.push`, `git.ref_created` and `git.ref_deleted` carry a `git` object:

- `refUpdates`: each branch or tag that changed, with its `ref`,
  `beforeOid`, `afterOid` and whether it was `created`, `deleted` or
  `forced`. At most 100 are listed; `refUpdatesCount` is the total and
  `refUpdatesTruncated` is `true` when some were left out.
- `source`: what changed the refs. `push` is a Git push to GitCafe,
  `merge` and `merge_cleanup` a pull request merge and its branch cleanup,
  `restack` a stack update, `mirror` a pull mirror fetching from its
  upstream, and `github_sync` a repository [synced with GitHub](https://git.cafe/docs/integrations/github-sync.md)
  importing GitHub's refs.

A CI service should not start builds for `github_sync` (or `mirror`) pushes:
for a synced repository, GitHub remains where CI runs on those changes, and
GitCafe Actions does not run for them either.

## Verify the signature

Before you trust a request:

1. Read `X-GitCafe-Timestamp` and reject it if it is too old for your liking.
2. Compute HMAC-SHA256 with your signing secret over the timestamp, a literal
   `.`, and the raw request body bytes.
3. Compare `sha256=<lowercase hex digest>` with `X-GitCafe-Signature` using a
   constant-time comparison.

```js
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, timestamp, body, signature) {
  const expected = `sha256=${createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(body)
    .digest("hex")}`;
  return (
    expected.length === signature.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
  );
}
```

## Respond and handle retries

Return any `2xx` status once you have stored the event. GitCafe retries on
network errors, timeouts (10 seconds), `408`, `425`, `429` and `5xx`, honouring
`Retry-After` up to an hour, for up to eight attempts within 24 hours. Other
statuses and redirects end the delivery.

Deliveries can arrive more than once and in any order. Deduplicate by the
body's event `id`.

## Inspect and redeliver

Under **Manage**, **Delivery history** shows each delivery's state, attempts
and HTTP status; **View attempts** and **View payload** show the details.
For a delivery that finally failed, fix your receiver, then choose **Request
redelivery** to send the same event again. Each event can be redelivered twice,
within 30 days.

```sh
cafe webhook deliveries acme/project <webhook-id> --state failed
cafe webhook redeliver acme/project <webhook-id> <delivery-id> \
  --request-id retry-1 --expected-version <version> --event-created-at <timestamp>
```

Delivery history is kept for 30 days.

## Rotate the signing secret

Choose **Rotate signing secret** under **More actions**, or run
`cafe webhook rotate-secret <target> <webhook-id> --yes`. The new secret is
shown once. From then on every attempt, including retries of earlier events,
is signed with the new secret, so update your receiver straight away.
`X-GitCafe-Key-Version` tells you which secret signed a request.

## App webhooks

An app that lists `capabilities.webhooks` (an HTTPS `url` and the `events` it
needs) in its manifest receives those events for every repository each
installation selected. Each event needs read access to its payload in the
manifest's `permissions.autonomous`; registration refuses one that lacks it.
Label and milestone events need repository **Metadata** (`repo:metadata`),
and reaction events need comments too, since a reaction can be on a comment.
`actions.workflow_job` needs the organization **Runners** permission
(`org:runners`) and is sent only for jobs on the installing organization's own
pools; a runner service uses it with
[`generate-jitconfig`](https://git.cafe/docs/ci/runner-providers.md#build-a-runner-service-as-an-app).
Apps cannot subscribe to all events with `*` or to `webhook.ping`.

The signing secret belongs to the app registration: it is shown once when you
register the app, and
`POST /api/orgs/<publisher>/apps/<app>/webhook-secret/rotate` replaces it for
every installation at once and shows the new one once. Requests are signed and
verified as above, and `X-GitCafe-Key-Version` names the secret version. The
body also contains `installation: { "id": ... }`; deduplicate by the
installation and event `id` together.

An app receives what its bot could read: the `body` of a hidden or deleted pull
request comment, and of a deleted issue comment, is `null`. Events your app's
own bot causes are not sent to that installation.

Apps also have events about their own bot, sent only to the installation
whose bot is targeted and never to other webhooks. Each names the bot as
`bot: { "id": ... }`.

| Events                                       | Sent when                                         | Needs read access to     |
| -------------------------------------------- | ------------------------------------------------- | ------------------------ |
| `issue.bot_assigned`, `issue.bot_unassigned` | Someone assigns or unassigns the bot on an issue  | Issues                   |
| `pull.bot_assigned`, `pull.bot_unassigned`   | Someone assigns or unassigns it on a pull request | Pull requests            |
| `pull.bot_review_requested`                  | Someone requests the bot's review                 | Pull requests            |
| `issue.bot_mentioned`, `pull.bot_mentioned`  | A new comment `@mentions` the bot                 | The subject and comments |
| `check.rerequested`                          | Someone asks to re-run a check the app reported   | Checks                   |

`check.rerequested` names the app's bot and carries `checkRun`: the check's
`id`, `key`, `headOid`, `name`, `externalId`, `detailsUrl` and the
`conclusion` being re-run. See [Report checks from your CI](https://git.cafe/docs/integrations/check-runs.md#re-run-a-check).

Mentions count in new comments, inline comments and replies, outside code, up
to 20 handles a comment; edits, review summaries and descriptions send
nothing. The bot must be mentionable (`capabilities.bot.mentionable`).

### Installation events

An app with `capabilities.webhooks` also hears when an organization admin
installs or changes its installation, whether or not `events` lists these. They go only to
that installation's webhook, need no permission, and name the installation and
its organization as `installation: { "id": ... }` and
`organization: { "id": ... }`, plus the admin as `actor`.

| Event                               | Sent when                                        |
| ----------------------------------- | ------------------------------------------------ |
| `installation.created`              | The app is installed and the installation active |
| `installation.paused`               | The installation is paused                       |
| `installation.resumed`              | It is resumed                                    |
| `installation.uninstalled`          | The app is uninstalled                           |
| `installation.permissions_accepted` | A newer revision of the app is approved          |
| `installation.repositories_updated` | The repositories the installation reaches change |

`installation.created` is sent once per installation, when an admin completes
the install. It has `repositorySelection` and, for selected repositories,
`repositories`: each one's `id`, `owner` and `name`, up to 50 (the most an
installation selects; `repositoriesTruncated` would say more were selected).
For all repositories it names none. Like `repositoriesAdded`, it names only
the repositories the installation may still read when it is sent: one removed
before then, or every one if the installation was paused, is left out, and the
event is still sent. Read the installation
(`GET /api/apps/installations/<id>`) for its current repositories. Uninstalling
and installing the app again is a new installation, with a new ID, and sends
`installation.created` again.

`installation.permissions_accepted` is sent once when an admin approves a newer
revision of the app (GitHub's `new_permissions_accepted`); reviewing an update
without approving it, or approving the revision the installation already runs,
sends nothing. It has the adopted `revision` and the permissions the
installation now holds, as `GET /api/apps/installations/<id>` shows them:
`permissions` for the bot, and `user` with `permissions` and `audiences` for
people who connect the app. It names no repository. Approving stops every token
the app holds, so this is the app's cue to request new ones; it is sent to the
webhook the new revision names, and arrives even though the app's old tokens
no longer work.

Pausing and uninstalling still drop every other delivery that was waiting
(see [Webhooks of installed apps](https://git.cafe/docs/integrations/webhooks.md#webhooks-of-installed-apps)), but not these
events: `installation.paused` and `installation.uninstalled` are sent, and
retried, after the app has lost access. They carry no repository. After
`installation.uninstalled` the installation sends nothing more; drop its
credentials and stop its work. After `installation.resumed`, events from while
it was paused are not sent: read the installation
(`GET /api/apps/installations/<id>`) to reconcile.

`installation.repositories_updated` has `repositorySelection` (`all` or
`selected`) and `previousRepositorySelection`. Between two selections,
`repositoriesAdded` names each repository the installation gained (`id`,
`owner` and `name`) and `repositoriesRemoved` lists only the `id` of each one
it lost; `repositoriesRemovedTruncated` is `true` if more than 100 were removed.
A change to or from all repositories lists neither: read the installation for
its selection. `repositoriesAdded` names only the repositories the installation
may still read when the event is sent: one removed again before then, or every
one if the installation was paused, is left out, and the event is still sent,
unless that leaves it with nothing added, nothing removed and the same
selection. So `repositoriesRemoved` can list, by ID only, a repository the app
never received, such as one added and removed again while the installation was
paused. A repository your bot creates joins the selection without an event.

Only an organization admin's changes send these events. GitCafe suspending your
app sends none, and none is sent while it is suspended: the events waiting when
it was suspended, and the changes admins make during it, are not sent after
GitCafe lifts the suspension. Once it is lifted, read each installation
(`GET /api/apps/installations/<id>`) to reconcile. Nor does a revision you
publish send one: what it removes leaves every installation at once, and
`installation.permissions_accepted` follows only when an admin approves it.
Until then, installation events still go to the webhook the installation
approved, even if the new revision lists none of its `events` or has no
`capabilities.webhooks`. Once approved, a new webhook URL gets only events from
then on (installation events waiting for the old one are not sent), and a
revision without `capabilities.webhooks` leaves the installation no webhook, so
nothing more is sent to it, not even the `installation.permissions_accepted`
for that approval: the revision removed its URL, and the old one gets nothing.

Installation events are not ordered with each other or with other events: a
retried event can arrive after a later one. Use `createdAt`, or read the
installation, when the order matters.

### Your app's deliveries

Owners and admins of the organization that publishes an app see its deliveries
under **Settings → Apps**: edit the app and open **Webhook deliveries**. The
list covers every installation, newest first, with the event, the installation
and its organization, the state, the attempts, the last HTTP status and error,
and when the event happened. **View attempts** shows each attempt's status and
duration, and **View payload** the body your receiver was last sent, exactly as
it was signed. Signing headers and your receiver's response bodies are never
shown. Only deliveries GitCafe has tried to send are listed, and the
redeliveries you request, so an event dropped before its first attempt (when
the app was paused, or a repository removed) does not appear. History is kept
for 30 days, also after an organization uninstalls your app: its organization
is named by its handle while your app is installed there, and by its ID only
once it is uninstalled.

**Redeliver** sends an event again, under the same limits as other webhooks:
twice per event, one at a time, within 30 days. It goes only to the URL the
event was first sent to, and only while the installation may still receive
the event, so events from a repository the organization removed from your app
are not sent again. The installation's own events
([installation events](https://git.cafe/docs/integrations/webhooks.md#installation-events)) can be sent again while GitCafe
has not suspended your app. Once an organization uninstalls your app, nothing
of that installation can be redelivered, its own events included, though its
history stays listed.

```sh
GET  /api/orgs/<publisher>/apps/<app>/deliveries?state=failed&installationId=<id>
GET  /api/orgs/<publisher>/apps/<app>/deliveries/<delivery>/attempts?eventCreatedAt=<timestamp>
GET  /api/orgs/<publisher>/apps/<app>/deliveries/<delivery>/payload?eventCreatedAt=<timestamp>
POST /api/orgs/<publisher>/apps/<app>/deliveries/<delivery>/redeliver
     {"requestId": "retry-1", "eventCreatedAt": "<timestamp>"}
```

Organizations that install your app do not see these deliveries
([Webhooks of installed apps](https://git.cafe/docs/integrations/webhooks.md#webhooks-of-installed-apps)).

## Stop a webhook

Disable it to pause deliveries, or delete it under **More actions**. Both stop
pending deliveries. Changing a repository's visibility or removing the person
who created a webhook does not remove it; webhooks can carry private
repository data, so review them when access changes.


---

Source: https://git.cafe/docs/integrations/apps

# Apps

An installed app acts as its own bot on the repositories you give it, even
when nobody is online. Its bot is listed among the
organization's members but takes no seat, so installing an app adds no seats. Apps GitCafe lists in the directory can be installed by any
organization and on your own [personal account](https://git.cafe/docs/integrations/apps.md#install-an-app-on-your-account); an app that is not listed can be installed only by the
account that publishes it and by those GitCafe admitted to its pilot.

## Find an app

The directory at `/apps` shows every listed app by category (coding agents,
code review, CI and deploys, security and quality, workflow automation), with
a search. You also reach it from **Browse apps** in **Settings → Apps**, and
organization owners and admins from the organization's **Apps** settings.

An app's page shows its publisher, its description, what it does on its own as
its bot, what people may do through it as themselves, the events it receives
and links to its website, support and privacy policy. When you are signed in,
**Install on** lists **Your account** first, then each organization you belong
to, with what you can do there: **Install**, **Manage** an installation it
already has, or **Request** it from the organization's admins.

## Install an app

Organization owners and admins install apps in an organization; on your own
account, you do (see [below](https://git.cafe/docs/integrations/apps.md#install-an-app-on-your-account) for what
differs).

1. Choose **Install** on the app's page, or follow the install link the
   publisher gave you, and choose your account or the organization.
2. Review the consent page: the app's name and publisher, what it does, and
   its permissions in plain words grouped by repositories, pull requests,
   issues, comments, organization and teams, each marked **Read**, **Write**
   or **Admin**. Permissions that publish, move, delete or change who has
   access are marked **Destructive**; those marked **Owner only** need an
   organization owner to install. **Org-wide** permissions (organization
   settings, organization members, team members, runners) act on
   the whole organization, not just the repositories the app can reach: they
   can give people access to all current and future repositories, and their
   changes stay after you uninstall the app. Runners an app starts take jobs,
   with their secrets, from every repository your runner groups serve. Under **People**, the page also says
   whether members may **Assign issues and pull requests to** the app and
   **Mention** it. **Details** shows the exact app ID,
   revision and manifest you are approving.
3. Under **Repository access**, choose **All repositories**, which covers every
   current and future repository in the organization, private ones included,
   or **Only select repositories…** and pick up to 50. Nothing is chosen for
   you, and **Install** stays unavailable until you choose. Selected access
   never covers repositories created later, except ones the bot creates itself
   when it may create repositories.
4. Choose **Install** (the button names the app) and
   [confirm it's you](https://git.cafe/docs/account/account-settings.md#confirm-its-you) if asked.

If the app has a setup page, GitCafe then sends you there to finish setting it
up; otherwise you land on the app's page in your settings. If the publisher
released a new version while the consent page was open, nothing is installed.
Choose **Review changes**, check the permissions again, and install. An
organization, or your account, can have up to ten installed apps.

## Install an app on your account

You can install an app on your personal account, for example to get its
webhooks and bot for your own repositories. Open **Settings → Apps**, or choose
**Install** next to **Your account** on the app's page or install link. The
consent page then reads, for example, **Install Relay on your account**;
everything else works as in an organization.

- **Only you** install, manage, pause and uninstall it. Nobody can request an
  app on someone else's account.
- **It reaches only repositories you own**, the ones under your handle. **All
  repositories** covers every one of them, private ones included, and the ones
  you create later. It never reaches an organization's repositories, even ones
  you can write to: each organization installs the app itself.
- **Its bot** is `@<you>-<slug>`, such as `@alice-amp`, and shows the app's
  icon with a **Bot** badge, as in an organization.
- If the app lets people [act as themselves](https://git.cafe/docs/account/mcp-connections.md#connect-a-gitcafe-app-as-yourself),
  its consent screen lists **Your account** first; there it reaches only your
  own repositories.

**Settings → Apps** lists the apps on your account and opens each one to manage
it, as described in [Manage an installed app](https://git.cafe/docs/integrations/apps.md#manage-an-installed-app).
Links to `/orgs/<you>/settings/apps` open the same pages there. To
[close your account](https://git.cafe/docs/account/account-settings.md#close-your-account),
uninstall its apps first.

## Request an app

Members who cannot install apps in an organization choose **Request** next to
the organization on the app's page, optionally say why, and choose **Send request**. The
organization's owners and admins see it under **Requests** in **Apps**:
**Approve** opens the consent page, where you choose its repository access,
and installing approves every pending
request for that app; **Deny** takes an optional reason. The member sees the
answer, with the reason, on the app's page and can ask again. An organization
holds up to 25 pending requests.

## How an app shows up

Each installation gets a bot with its own handle, such as `@acme-amp`. Its
comments, reviews and mention suggestions show the app's icon and name with a
**Bot** badge, and its name links to the bot's profile. The bot can act only on
the repositories it can reach, within the listed permissions; it cannot use
other repositories, including public ones. Each permission covers exactly what
it names: labeling issues does not let the bot close them, and leaving review
comments does not let it approve or dismiss reviews. An app you allow to
**Approve or request changes** counts toward required reviews like a person
does; like a person, it cannot approve its own pull requests. Webhooks an app
creates send only events from repositories it can reach, stop while it is
paused and are deleted when you uninstall it. Apps cannot change organization
or team rulesets, which also cover repositories it cannot reach. No app can
install apps, manage billing or close the organization.

### Bots where people work

The organization's **Members** page lists each installed app's bot under
**Apps**, with who installed it, the repositories it can reach (their names
for owners and admins, a count for other members) and when it was last issued
a token (**Last token issued**: when the app last asked for access, not proof
that it did anything).
Its profile, at its handle (such as `/acme-amp`), shows the app, its
publisher, your organization and the issues, pull requests and review
requests assigned to it that you can see. Only members of the organization
that installed it can open the profile.

If the app asks for it:

- **Assign issues and pull requests to** the bot from the assignee picker,
  where bots are listed under **Bots** after people. The bot must be able to
  read the issue or pull request in a repository it can reach.
- **Request a review** from the bot when it may also **Write reviews** or
  **Approve or request changes**. Its approval counts like a person's.
- **Mention** the bot with `@` in a comment; the suggestions list only bots
  that may be mentioned in the repository.

A paused or suspended app, or one that lost the repository, is not offered
and cannot be assigned or asked for a review. A bot that was already assigned
or asked stays, with a warning in the sidebar and the picker, until someone
removes it; other changes to the assignees and reviewers still save, and
keeping it tells the app nothing. The sidebar shows who assigned each assignee
and who asked each reviewer, and the app is told through its webhook when it
is assigned, unassigned or asked (see
[App webhooks](https://git.cafe/docs/integrations/webhooks.md#app-webhooks)).

Apps that let members [act as themselves](https://git.cafe/docs/account/mcp-connections.md#connect-a-gitcafe-app-as-yourself)
attribute that work to the member: **Alice via Hoplite**, without a **Bot**
badge. Its avatar shows the app's icon with the member's own picture in the
corner.

## Manage an installed app

**Apps** lists each installed app with who installed it and when, when it was
last issued a token (**Last token issued**), the repositories it can reach and
whether it is active. Choose
an app to open its page: its bot, what it does on its own, what people may do
through it and its repository access.

- **Manage repositories**: switch between **All repositories** and **Only
  select repositories…**, or change the selection, and choose **Save
  repository access**. Giving the app more access may ask you to confirm it's
  you. If someone else changed the installation meanwhile, nothing is saved;
  choose **Start over**.
- **Pause** (in the **⋯** menu): stops the app at once until you choose
  **Resume**.
- **Suspended** means GitCafe suspended the app in every organization. It
  can't act until GitCafe lifts the suspension, and there is no **Resume**
  for it.
- **Uninstall** (in the **⋯** menu): stops the installation's access and
  tokens. It does not delete data the publisher already holds; ask the
  publisher to delete it. Comments the bot made stay attributed to it.
- **People connected** (for apps members can connect to act as themselves):
  the members who connected the app here, with the audiences each connection
  covers, its repositories and permissions, when they connected and when it
  last issued a token (**Last token issued**). **Remove access** works as in
  **Connections** below.

Changing repository access, pausing and uninstalling stop the access tokens
the app already holds; the app requests new ones when it may act again. An app
with a webhook is told when it is installed, about each of these, about
resuming and about approved updates
([installation events](https://git.cafe/docs/integrations/webhooks.md#installation-events)), but not about a
suspension by GitCafe or about what it does while suspended.

## See and remove members' connections

**Connections** in the organization's settings lists every app and client that
people connected to act as themselves on this organization's repositories:
installed apps and unverified clients such as MCP apps alike, each with
**Authorized by** its number of people, newest first, with **Load more** when
there are more. It includes everyone whose connection reaches the organization,
whether through membership, a repository role or a team's role. Owners and
admins can open one to see each person's connection: its audiences (**API**,
**MCP**, **Git**), its repositories here, its permissions, when they connected
and when it last issued a token (**Last token issued**; using a token does not
update it).

**Remove access** ends that person's connection for this organization's
repositories only: reads, writes, Git and MCP stop at once, lists leave the
organization's repositories out, and reconnecting does not bring it back. The
member's connection keeps working in other organizations. **Restore** lifts the
removal; it gives access back, so it asks you to
[confirm it's you](https://git.cafe/docs/account/account-settings.md#confirm-its-you).

### Review an update

When a publisher changes an app, the app keeps what you approved until you
review the change. Its row in **Apps** shows **Update available**; choose
**Review update** from its actions to see what the new revision changes:
permissions added, raised, lowered or removed, for the bot and for people who
connect it; how people can connect it (API, MCP, Git); where it sends events
and which ones; and whether people can assign or @mention its bot.

Choose **Approve update** to adopt it. Approving may ask you to confirm it's
you, and an app with owner-level permissions needs an organization owner for
every update. If the publisher changed the app again meanwhile, nothing
changes; choose **Review changes** to see the newest revision. The app keeps
its bot and repositories. Tokens it already holds stop, and it requests new
ones with the approved permissions; an app with a webhook is told
([`installation.permissions_accepted`](https://git.cafe/docs/integrations/webhooks.md#installation-events)).

Anything a publisher removes leaves every installation at once, without a
review: the app's tokens lose it at their next use, and the app is not told
through its webhook.

## Publish an app

Organization owners and admins publish apps in the organization's **Settings →
Apps**, under **Your apps**. Choose **New app** and fill in:

- **App**: its icon, name, slug (it never changes) and a one-line summary.
  Each organization that installs the app gets its own bot, usually named
  `@<installing-org>-<slug>`: `amp` installed in `acme` is `@acme-amp`. A
  slug that already starts with `<installing-org>-` is used as is. When that
  handle is taken or longer than 39 characters, the bot is the slug followed by
  eight random hexadecimal characters, such as `@amp-1f3a9c0e`.
- **Links**: its homepage, support and privacy policy, each an HTTPS URL.
  Every app needs all three, listed or not: install consent and the app's page
  link to them.
- **Listing** (optional): a category and Markdown description for the app
  directory. Filling it in doesn't list the app: GitCafe reviews every app
  in the directory (see below).
- **Acts on its own**: the bot's name, whether people can assign it work and
  request its review (**Assignable**) or @mention it (**Mentionable**), and what
  the bot may do. Set one level per resource, or open **Details** for exact
  permissions. Owner-only and destructive permissions are marked.
- **Acts for people**: whether members can connect the app to act as themselves,
  through which of API, MCP and Git, the most they can let it do, and its OAuth
  redirect URIs. An authorization request with no `scope` asks for all of it.
- **Setup and webhooks**: an optional setup URL admins go to after installing,
  and the webhook URL and events, by category. Each event needs the bot to read
  what it describes; the form says which read access is missing and adds it
  with **Add read access**.

**Create app** saves revision 1. If a field needs fixing, the form names it
next to the button and moves to the first one. When the app has webhooks, its
signing secret shows once; copy it then.

Actions that create or widen an app's authority, such as creating an app,
installing one and registering a signing key, ask the person to
[confirm it's you](https://git.cafe/docs/account/account-settings.md#confirm-its-you) unless they
did so recently, and then continue on their own. A confirmation lasts 10
minutes with a password and 15 with a passkey. An access token can't do these.

Choose **Edit** on an app to change it. **Save as revision N** seals the next
revision; if someone else saved a revision after you opened the form, nothing
is saved, and **Start over** opens the newest one. Installations keep what they
approved until their admins review the update; anything you remove leaves them
at once. The slug, signing keys and webhook secret don't change with a revision.

The edit page also shows the app's **Identifiers**, each with a copy button:
the **App ID**, which your backend's signed token requests name, and the
**OAuth client ID** people connect the app through. An app that acts for people
gets its OAuth client when you choose **Register OAuth client** there. Only
people who can manage the organization's apps see them.

### Register a signing key

Your backend proves it is the app with an ES256 JWT signed by a P-256 key pair
you generate. Register the public half; the private key never leaves your
backend, and GitCafe refuses one if you paste it.

In **Settings → Apps → Your apps**, choose **Register key** on the app, enter a
**Key ID** (up to 64 letters, digits, `.`, `_` or `-`; your backend puts it in
each JWT's `kid` header) and paste the **Public key**, either as a PEM block
that begins `-----BEGIN PUBLIC KEY-----` or as a JWK (a key set with just that
key works too). For example, with OpenSSL:

```sh
openssl ecparam -name prime256v1 -genkey -noout -out app-key.pem
openssl ec -in app-key.pem -pubout
```

Or call the **Register an app public key** operation of the
[REST API](https://git.cafe/docs/reference/api.md) from a recently confirmed browser session,
with the key ID and the public JWK:

```json
{
  "kid": "2026-10",
  "jwk": {
    "kty": "EC",
    "crv": "P-256",
    "x": "<43 base64url characters>",
    "y": "<43 base64url characters>"
  }
}
```

It returns `201` with the key's `id`, `kid`, `createdAt`, and `lastUsedAt` and
`revokedAt`, both `null` for a new key. An app has at most 5 live keys and
registers at most 100 over its life; past either limit the key is refused
(`APP_KEY_LIMIT`). A key ID can't be reused (`APP_KEY_EXISTS`), and a point
that isn't on P-256 is refused (`APP_KEY_INVALID`). To rotate, register the new key, move your backend to it,
then revoke the old one.

Apps and their keys can also be managed with the Apps operations of the
[REST API](https://git.cafe/docs/reference/api.md). Your backend acts as an installation's bot with an installation
token: the **Mint an installation token** operation, called with a client
assertion signed by one of the app's keys, returns a token that lasts 15
minutes and holds the installation's approved permissions.

To look up an app's identifiers, run `cafe app view <org> <app>` or call the
**Read one of the organization's apps** operation. It shows the app ID, which
your backend's client assertions use as `iss` and `sub`, and the app's OAuth
client ID once the **Register an app's OAuth client** operation has registered
it. Unlike the actions above, it works with an access token, so a coding agent
can read it for you; it never registers anything or shows a secret.

A runner service asks for the **Runners** permission and the
`actions.workflow_job` event to start runners for the organizations that
install it; see
[Build a runner service as an app](https://git.cafe/docs/ci/runner-providers.md#build-a-runner-service-as-an-app).

To appear in the directory, save a category and description under **Listing**,
then choose **Request listing** in the same section of the app's settings. Its
status reads **Not requested**, **Waiting for GitCafe review**, **Listed** (with
the date) or **Withdrawn**; a GitCafe instance admin lists the app or declines,
and you can request again after a withdrawal. Give it a
`capabilities.setup_url` to receive admins after they install: GitCafe adds
`installation_id`, `org` and the install link's `state` as query parameters.
Link people to `/apps/<slug>/install?state=<value>`, where the state is up to
256 letters, digits, `.`, `_`, `~` or `-`.

### Clone as the app

An app whose bot may **read repository contents** (`repo` `contents` `read`)
can clone and fetch the repositories its installation reaches, Git LFS files
included, with a short-lived installation token:

1. Your backend signs a single-use ES256 JWT with a registered key: header
   `kid` names the key; `iss` and `sub` are the app ID; `aud` is the exact
   token URL below; `jti` is unique; `exp` is at most 5 minutes after `iat`.
2. It posts the JWT to
   `https://git.cafe/api/apps/installations/<installation-id>/access_tokens` as
   `{"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_assertion": "<jwt>"}`,
   optionally with `permissions` to narrow the token (such as only
   `[{"kind": "repo", "family": "contents", "level": "read"}]`) and
   `repositoryIds` to narrow it to some of the installation's repositories
   (up to 100). Every listed repository must be one the installation reaches
   now, or no token is issued (`INSTALLATION_TOKEN_REPOSITORIES`). A narrowed
   token never reaches another repository, not even through organization
   permissions such as deleting repositories or reading webhook deliveries.
   It cannot create or change organization webhooks
   (`WEBHOOK_NARROWED_TOKEN`), which would receive every selected
   repository's events after it expires, or organization CI integrations
   (`CI_INTEGRATION_NARROWED_TOKEN`), which sign checks and statuses into
   every repository bound to them; it can still read both. Even a token
   for the whole installation changes, disables or removes an organization
   CI integration only when the installation reaches every repository bound
   to it (`CI_INTEGRATION_UNSELECTED_BINDING`), and an integration whose
   webhook secret or API token the app set signs checks and serves logs only
   for repositories the installation reaches at the time
   ([Buildkite](https://git.cafe/docs/ci/buildkite.md#integrations-an-app-set-up)). Other
   organization and team settings stay as approved. The response has the
   `token` (`gct_…`), its `expiresAt`, 15 minutes later, and the
   repositories it reaches.
3. Git uses the token as the HTTPS password; the username is not checked, so
   use `x-access-token`:

   ```sh
   git clone https://x-access-token:<token>@git.cafe/<org>/<repo>
   ```

   `Authorization: Bearer <token>` works too, for example through
   `git -c http.extraHeader=...`.

Each Git request checks the token again. It stops working when it expires and
as soon as the installation is paused, uninstalled or its repository access
changes, or the app is suspended; request a new one then. It reaches only the
installation's repositories (or the ones you listed), not other public
ones; any other repository looks as if it does not exist. An installation gets at
most 120 tokens an hour.

When a job finishes before its token expires, revoke the token with the token
itself: `DELETE https://git.cafe/api/apps/installation/token` with
`Authorization: Bearer <token>` returns `204`, and every later request with
that token is refused as unauthenticated (`401`). Only installation tokens can
revoke themselves this way.

The publishing organization's **Apps** page lists its apps under **Your
apps**, with each key's creation, last use and revocation. To retire a key,
choose **Revoke**. Select **This key is compromised** to also stop every token
already issued to the app's installations; installations work again once the
app requests tokens with another key.

Choose **Add icon** (or **Change icon**) to upload the app's icon: a PNG,
JPEG, GIF, WebP or AVIF image up to 1 MiB, as for avatars. It shows in the
directory, on the consent page, in each organization's **Apps** list and on
every installation's bot, including work the bot did before the change.


---

Source: https://git.cafe/docs/integrations/check-runs

# Report checks from your CI

An [app](https://git.cafe/docs/integrations/apps.md) or a script with an access token can report CI results on a
commit as checks. A reported check shows on the commit's page and in a pull
request's checks panel next to GitCafe's own checks, and a
[ruleset](https://git.cafe/docs/repositories/repository-rulesets.md) can require it by name.
Commit statuses still work; checks add a stable identity, a summary,
annotations and re-runs. The operations are under **CI** in the
[REST API](https://git.cafe/docs/reference/api.md) reference.

## Permissions

Writing checks needs `repo:checks:write`; reading one back needs
`repo:checks:read`. Neither reaches code: an app with only
`repo:checks:write` cannot push or post commit statuses, and an app must ask
for `repo:checks:write` explicitly, since `repo:contents:write` does not include
it. An app asks for the permission in its manifest's
`permissions.autonomous.scopes` for its bot, and in `permissions.user.scopes`
to report checks for the people who use it:

```json
{ "kind": "repo", "family": "checks", "level": "write" }
```

An app's bot needs no role: the permission and the repositories the
installation selected are enough. A person acting through an app gets only what
both allow: the app's permissions and the person's own access to the
repository, so someone who can only read cannot write checks through any app. A
personal access token that can write code can also write checks, and its
person still needs write access to the repository.

## Report a check

Use **Report a check run** with the commit's SHA and a key of your choice,
`build` here, and the check's whole state as the body:

```json
{
  "name": "depot/build",
  "status": "completed",
  "conclusion": "failure",
  "detailsUrl": "https://depot.example/builds/7",
  "externalId": "build-7",
  "reportedAt": "2026-10-23T12:05:00Z",
  "output": { "title": "1 test failed", "summary": "`test/a.ts` failed." },
  "annotations": [
    {
      "path": "test/a.ts",
      "startLine": 4,
      "endLine": 4,
      "level": "failure",
      "message": "expected 1, got 2"
    }
  ],
  "rerequestable": true
}
```

- **The commit** is the full 40-character SHA, and the repository must have
  it.
- **The key** (`build`) identifies the check for you:
  up to 100 letters, digits, `.`, `_`, `:` or `-`, starting with a letter or
  digit. Writing the same key on the same commit again updates that check;
  another app's checks never collide with yours.
- **`name`** is what people see and what a ruleset requires. Keep it stable
  across commits.
- **`status`** is `queued`, `in_progress` or `completed`. A completed check
  needs a `conclusion`: `success`, `failure`, `neutral`, `cancelled`,
  `skipped`, `timed_out` or `action_required`. `success`, `neutral` and
  `skipped` pass a required check; the rest fail it.
- **`suite`** (optional) groups checks: your checks on a commit with the same
  suite key, in the key's format, show together under it in the checks panel,
  with the suite's worst state. Another reporter's suite with the same key is a
  separate group. A suite only groups; rulesets still require checks by name.
- **`detailsUrl`** is an HTTPS link to your results; `externalId` is your own
  ID for the run, sent back with re-run requests.
- **`output`** has a `title` (255 characters) and a Markdown `summary` and
  `text` (65,535 characters each). Up to 50 **annotations** point at lines of
  files; columns are allowed on a single line.
- Each write replaces the whole check: leave out a field and it is cleared.
  `startedAt` and `completedAt` default to when GitCafe first saw the check
  running and completed. Only `rerequestable` keeps its value when you leave
  it out.

The response is `201` when it created the check and `200` otherwise, with the
stored check and an `outcome`:

| `outcome`       | Meaning                                                          |
| --------------- | ---------------------------------------------------------------- |
| `created`       | A new check.                                                     |
| `updated`       | The check now has what you sent.                                 |
| `unchanged`     | You sent what it already had; nothing changed.                   |
| `ignored_stale` | Your `reportedAt` is older than the stored one; nothing changed. |

Send `reportedAt` (when your system observed the state) so retries and
out-of-order deliveries cannot overwrite newer results. Without it, GitCafe
uses the time it receives the write. At the same `reportedAt`, a completed
check is not reopened. Times more than a minute in the future are refused.

## Deadlines

A check that stays `queued` or `in_progress` does not block a pull request
forever. Six hours after your last write that left it unfinished, GitCafe
concludes it `timed_out`, which fails a required check. Set
`timeoutMinutes` (1 to 4320, three days) on an unfinished write to choose
another bound. Each such write starts the bound again, so a long job stays
alive by reporting its state again before the deadline, even unchanged. The
stored check shows the current `deadlineAt`.

A timed-out check keeps its key. A later write from you, with a newer
`reportedAt`, replaces it as usual. A re-run request your app does not answer
within six hours also concludes `timed_out`.

GitCafe sends your app nothing when a check times out. Your app sees the
`timed_out` conclusion in the response to its next write for that key, or by
reading the check back.

From a shell or CI job, `cafe check report <key> --commit <sha> --name <name>
--status <status>` reports a check and `cafe check list` shows every check on a
commit ([CLI](https://git.cafe/docs/reference/cli.md#checks)).

## Report many checks at once

**Report check runs in a batch** writes up to 50 of your checks on one commit
in one request. Send each check's key and state:

```json
{
  "items": [
    { "key": "unit", "check": { "name": "depot/unit", "status": "queued" } },
    { "key": "lint", "check": { "name": "depot/lint", "status": "queued" } }
  ]
}
```

Each item is written as a single report would be, and the response lists one
`outcome` per item in the same order. An item GitCafe declines, such as one
with a time in the future, comes back as `refused` with an `error`, and the
others are still written. A malformed body, or a key that appears twice,
refuses the whole batch. So does a batch over 500 annotations, or over 1 MiB of
output and annotation text (paths included), in all: it fails with `check-batch-too-large` and
writes nothing, so split it.

**Get a reported check run** reads a check back with its annotations, and
**List checks for a commit** lists every check on a commit, from any source.

## Require a check

Add the check's `name` to a ruleset's **Require status checks** rule. A pull request
can merge only when every required name passes on its head commit. Commit
statuses and checks with the same name count together, and a failure from any
of them fails the requirement. Anyone who may write checks or statuses can
report under any name.

To accept a name only from your app, pin it: list it under the rule's `checks`
with your app's ID, as in `{"context": "ci/build", "appId": "int_…"}`. Then
only a check your app's installation reported through this API satisfies it;
a commit status, a person's check or another app's check with the same name
does not. On the ruleset page, under **Checks pinned to an app**, choose
**Add pinned check**, enter the name and choose the app from the apps installed
in the organization; organization admins see that list. The rulesets API and
`cafe repo ruleset create|edit --rule` take the same `checks` entries.

Only reports from the app's current installation count for a pin. After you
uninstall and reinstall the app, its new installation reports again and
replaces whatever the old one left on the commit.

## Re-run a check

When a check is `completed` and your app reported it with
`"rerequestable": true`, the commit and pull request show **Re-run** next to
it. People with write access to the repository can choose it, or use **Request a
re-run of a check**. Only people can: an app's bot, your own included, cannot
request a re-run. GitCafe then:

1. shows the check as queued, so a required check blocks merging, and
2. sends [`check.rerequested`](https://git.cafe/docs/integrations/webhooks.md#app-webhooks) to your app's
   installation, with the check's `id`, `key`, `headOid`, `name`,
   `externalId`, `detailsUrl` and earlier `conclusion`.

Answer by writing the same key on the same commit again, usually `queued`
then `completed`. Writes with a `reportedAt` from before the request do not
answer it. Asking again while a request is open sends nothing more. Both
happen or neither does: a re-run is refused, and the check left as it was,
when your app could not receive `check.rerequested` for this repository. That
is when the app is paused, suspended or uninstalled, the repository is no
longer selected, the permission is gone, or no webhook endpoint subscribes to
the event.

Only apps can offer re-runs: subscribe to `check.rerequested` in the manifest's
`capabilities.webhooks.events`, which needs a `repo:checks` permission. If your
app cannot re-run a check, leave `rerequestable` out or set it to `false`.


---

Source: https://git.cafe/docs/account/mcp-connections

# Connect an MCP app

GitCafe runs an MCP server at `https://git.cafe/mcp`, so AI tools such as
Claude and ChatGPT can read repositories and, with your approval, work on
issues and pull requests. Each connection is limited to the repositories and
permissions you approve.

## Read public repositories without signing in

Add `https://git.cafe/mcp` to your MCP client as a remote server. Without
connecting an account, its read tools work on public repositories: code,
branches and tags, issues, pull requests, reviews and CI results.

A write tool, or `get_me`, asks the client to sign in, which starts the
connection steps below. Connect first if you want the tools to read private
repositories.

## Connect a client

You need a verified GitCafe account.

1. In your MCP client, add `https://git.cafe/mcp` as a remote MCP server.
2. Follow the sign-in link the client opens.
3. Check the app name on the consent screen; **Details** shows its client ID
   and return address. GitCafe does not verify app names, so only continue if
   you started this.
4. Review the permissions. They are granted as the app requested; if they are
   more than you want to give, cancel.
5. Choose the repository access: **All repositories you can access** (the
   default, including repositories you get access to later) or **Only select
   repositories…**, which lists your repositories to pick from. A selection
   does not grow: a repository you add later needs a new connection. Some
   permission sets limit how many you can select, and the screen says how
   many.
6. Choose **Authorize** and confirm it's you if asked. You return to the
   client.

Choose **Cancel** to refuse. Never paste a token or password into a
connector's settings: the browser flow handles sign-in.

### Claude

Open **Customize → Connectors → Add → Add custom connector**. Enter a name and
`https://git.cafe/mcp`, keep the detected sign-in and automatic registration
options, then choose **Add → Connect** and approve the connection. Leave the
tools on **Needs approval** to review each call.

### ChatGPT

With developer mode enabled, open **Plugins → Create app → Create MCP App**.
Enter a name and `https://git.cafe/mcp`, keep **OAuth**, acknowledge the
custom-server warning and choose **Create**. Approve the connection, then open
the app and choose **Try in chat**.

Menus vary by client version and plan.

## What a connected app can do

With the permissions you approved, an app can:

- read your identity and its approved repositories (`get_me`);
- read code, branches, tags and search results;
- read and write issues, pull requests, comments and reviews;
- resolve review threads;
- merge pull requests, if you approved **pr:merge:write**;
- read CI runs, checks, jobs and shared logs (`ci_read`).

An app cannot create branches or commits, push, start or cancel CI, or
administer or delete repositories. Repository rules apply to everything it
does.

A merge request returns `accepted` before the merge finishes. Have the app
check the merge status until it reports `completed` before it assumes the pull
request merged.

## Review or revoke access

Open **Settings → Authorized Apps** to see each connection: the app, the
audiences it covers (**API**, **MCP**, **Git**), its repositories by name or
**All repositories you can access**, its permissions, when you connected it
and when it last issued a token (**Last token issued**). Choose **Revoke** and
confirm to end a connection, with all its tokens, immediately. Tokens issued to
apps are listed there, not under **Tokens**, which lists only your personal
access tokens.

You have one connection per app. Connecting the same app again, for example
from another device or for another audience, updates it in place: your other
devices and audiences keep working, and the consent screen shows what changes
when you choose different repositories. A connection lasts until you revoke
it, and ends after 90 days without a new token; each time the app refreshes its
token, the 90 days start again.

An organization's owners and admins can see which apps their members connected
(**Organization settings → Connections**) and remove one person's connection
from that organization's repositories. The app then stops reaching that
organization's repositories at once, through every audience; your connection
keeps working elsewhere, and reconnecting does not undo the removal. Authorized
Apps shows **Removed from** the organization until an admin restores it.

Connections made before October 2026 lasted 30 days from approval. They keep
that end; approving the app again moves the connection to the 90-day rule.

## Connect an API client

OAuth clients can also call the [REST API](https://git.cafe/docs/reference/api.md). Use
`https://git.cafe/api` as the OAuth resource; a token issued for the API does
not work for MCP, and the reverse is also true. Consent screens and
**Authorized Apps** show the audiences as **API** and **MCP** chips.

- Use the authorization-code flow with PKCE (`S256`), and send the resource
  ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707)) in the authorization,
  token and refresh requests.
- One connection covers every resource it was approved for. The token and
  refresh requests each name one resource and get a token for that resource
  only; naming a resource the connection does not cover returns
  `invalid_target`. A client that connected for MCP and then for the API holds
  one connection covering both.
- Access tokens last up to 15 minutes. Each refresh rotates the refresh token.
  The connection ends after 90 days without a token request, or when revoked.
  Each device's refresh tokens form their own family: reusing an old refresh
  token, or revoking one, ends that device's tokens only.
- API OAuth covers repository operations on the selected repositories. It does
  not grant organization, team or profile access, or Git credentials.
- To revoke a token, send it with the connection's `client_id` to the
  revocation endpoint.

To let people sign in to your service with their GitCafe account, see
[Sign in with GitCafe](https://git.cafe/docs/integrations/sign-in-with-gitcafe.md).

For the other ways to authenticate, see
[choose how to authenticate](https://git.cafe/docs/agent-workflows.md#choose-how-to-authenticate).

## Connect a GitCafe app as yourself

Some apps, such as coding agents in the apps pilot, are registered GitCafe apps
that can act as you, including cloning and pushing over Git. They work only in
organizations that installed the app, or on your own account if you installed
it there, and only on repositories that installation selected.

- You have one connection to the app across organizations. The consent screen
  lists **Your account** first if you installed the app there, then each
  organization you belong to that installed it (up to 100), and you
  choose, for each one you connect it in, **All repositories you can access in**
  that organization (the installation's repositories that you can reach) or
  **Only select repositories…**, which lists those of its repositories you can
  read. Nothing is chosen for you. Organizations you leave unchosen are not
  connected, and ones already connected keep their access.
- One consent covers every audience the installations approved for their
  members (for example **API**, **MCP** and **Git**), so you see one row in
  Authorized apps. The app asks for a token for one audience in one
  organization at a time: the token request may name an `installation_id`, and
  otherwise uses the organization you approved most recently; the token
  response says which. The app can request only
  the permissions that installation allows apps to use for its members. The
  app's own bot permissions never apply to you.
- The app acts with your own current access: if you lose a role on a
  repository, the app loses it too. It never gets more than you have. The
  consent screen and Authorized apps name only the repositories you can still
  read and the installation still selects; one you lost, or one moved out of
  the organization, is left out.
- Your actions through the app are recorded as yours, via the app's
  installation: your comments, reviews and pull requests show as, for example,
  **Alice via Hoplite**.
- The consent screen names the app and its publisher, and says whether it can
  clone, or clone and push, with Git.
- **Authorized apps** lists the connection under the app's name with its
  audience chips and publisher, and each organization it acts in with its
  repositories and permissions there. **Disconnect** an organization to stop
  the app acting as you there only, or **Revoke all** to end the connection.
- For Git, the app requests a token for the `https://git.cafe/git` resource.
  That token works only for Git, sent as the password of HTTPS Git credentials
  or as a bearer token; API and MCP tokens do not work for Git, and a Git token does not work for the
  API or MCP. Cloning needs **repo:contents:read**, pushing needs
  **repo:contents:write**, your own write access and the repository's rules.
  Git LFS and SSH do not accept these tokens.
- Access stops at once when an organization admin pauses or uninstalls the app,
  removes a repository from its installation or removes your connection from
  the organization, and when you disconnect that organization or revoke the
  connection. A paused installation cannot renew tokens until it resumes.

Ordinary OAuth clients, including ones registered dynamically, cannot use the
Git resource.

## Connect an agent using auth.md

An agent without a registered OAuth client can connect through the flow
described at `https://git.cafe/auth.md`. It grants REST API access for up to one
hour; it does not grant MCP or Git access.

1. Give the agent your GitCafe account's email address. The agent registers and
   shows you a verification link and a six-digit code.
2. Open the link, sign in, and enter the code. Only continue if you asked the
   agent to connect.
3. Choose the repositories and permissions, then choose **Authorize**. The
   agent's request names every permission, so here you choose which to grant;
   write and admin permissions start off.

The code expires after ten minutes and locks after five wrong attempts. Access
tokens last 15 minutes and the agent can renew them until the connection ends
after one hour. For longer access, use API OAuth, MCP or the CLI instead.

To stop the agent, revoke **Agent registration (auth.md)** in **Settings →
Authorized Apps**.


---

Source: https://git.cafe/docs/integrations/sign-in-with-gitcafe

# Sign in with GitCafe

GitCafe is an OpenID Connect provider. Your service can add a **Sign in with
GitCafe** button that tells you who the person is, and, in the same approval,
ask for access to their repositories. Any standard OpenID Connect client
library works; this guide shows the settings it needs.

## What you get

| You ask for                                    | The person approves                         | You receive                                                       |
| ---------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------- |
| `openid` (with `profile`, `email` as you need) | Who they are, nothing else                  | An ID token, and an access token that reads only userinfo         |
| `openid` plus repository permissions           | Who they are and the repositories they pick | An ID token, and an access token for the API, MCP or Git          |
| Repository permissions alone                   | The repositories they pick                  | An access token, as for [MCP apps](https://git.cafe/docs/account/mcp-connections.md) |

A sign-in alone never shows a repository picker: the consent screen lists only
what you learn about the person, and says you cannot read their repositories.

## Discovery

Point your library at the issuer `https://git.cafe`. It reads
`https://git.cafe/.well-known/openid-configuration`, which names:

| Field                    | Value                                     |
| ------------------------ | ----------------------------------------- |
| `authorization_endpoint` | `https://git.cafe/api/oauth/authorize`    |
| `token_endpoint`         | `https://git.cafe/api/oauth/token`        |
| `userinfo_endpoint`      | `https://git.cafe/api/oauth/userinfo`     |
| `jwks_uri`               | `https://git.cafe/api/oauth/jwks`         |
| `registration_endpoint`  | `https://git.cafe/api/oauth/register`     |
| ID token signature       | `RS256`                                   |
| Client authentication    | `none`: public clients with PKCE (`S256`) |

## Register your client

Clients are public: there is no client secret, and every authorization uses
PKCE with `S256`. Register once and keep the `client_id`:

```sh
curl -X POST https://git.cafe/api/oauth/register \
  -H 'content-type: application/json' \
  -d '{"client_name":"Tripwire","redirect_uris":["https://tripwire.sh/auth/callback/gitcafe"]}'
```

The redirect URI must match exactly on every request. A registered
[app](https://git.cafe/docs/integrations/apps.md) can instead use the client its publisher registers in the app's
settings, which names the app and its publisher on the consent screen; an
unregistered client is shown as unverified.

## Scopes

| Scope     | Claims in the ID token and userinfo                         |
| --------- | ----------------------------------------------------------- |
| `openid`  | `sub`: the person's GitCafe account ID, which never changes |
| `profile` | `preferred_username`, `name`, `picture`, `profile`          |
| `email`   | `email`, `email_verified`                                   |

Use `sub` as the account key. Usernames can change; the ID cannot, and is never
given to anyone else.

To ask for repository access in the same approval, add permission scopes
(`repo:contents:read`, `issue:contents:write` and the others in
`scopes_supported`) and a `resource` naming where the token is used:
`https://git.cafe/api`, `https://git.cafe/mcp` or, for an app, `https://git.cafe/git`.
A sign-in alone needs no `resource`.

## Tokens

The token response carries `id_token` whenever the approval included
`openid`. The ID token is valid for 10 minutes and carries `iss`, `aud` (your
client ID), `exp`, `iat`, your `nonce` and `at_hash`; verify it against the
JWKS. Access tokens last at most 15 minutes. A refresh returns a new ID token
for the same person, without a `nonce`.

`GET` or `POST` to userinfo with `Authorization: Bearer <access token>`
returns the claims the person approved. Any live access token from the
connection works there; a sign-in's token works nowhere else.

The connection lasts until the person removes it in
[Authorized apps](https://git.cafe/docs/account/mcp-connections.md#review-or-revoke-access) or leaves
it unused for 90 days.

## Library settings

Auth.js (NextAuth):

```ts
{
  id: "gitcafe",
  name: "GitCafe",
  type: "oidc",
  issuer: "https://git.cafe",
  clientId: process.env.GITCAFE_CLIENT_ID,
  client: { token_endpoint_auth_method: "none" },
  checks: ["pkce", "state", "nonce"],
  authorization: { params: { scope: "openid profile email" } },
}
```

`openid-client` (Node):

```ts
const config = await client.discovery(
  new URL("https://git.cafe"),
  process.env.GITCAFE_CLIENT_ID,
  undefined,
  client.None(),
);
```

Other libraries need the same four things: the issuer, the client ID, no
client secret (`token_endpoint_auth_method` `none`) and PKCE.

## Brand

Label the button **Sign in with GitCafe**, with the GitCafe name and its square
mark. In monochrome the mark is a plain filled square.


---

Source: https://git.cafe/docs/reference/cli

# CLI reference

Every `cafe` command, grouped by area. Run `cafe <command> --help` for all of a
command's options. To install the CLI and sign in, see
[install the CLI](https://git.cafe/docs/getting-started/cli.md).

## Global options

| Option                            | Effect                                               |
| --------------------------------- | ---------------------------------------------------- |
| `--repo <owner/name>`             | Repository to act on (default: the current clone)    |
| `--host <url>`                    | GitCafe API address (default `https://git.cafe/api`) |
| `--json`                          | Print structured JSON on stdout                      |
| `--format pretty\|markdown\|json` | Choose the output format                             |
| `--yes`                           | Confirm destructive actions without asking           |
| `--no-input`                      | Fail instead of prompting                            |
| `--no-browser`                    | Never open a browser                                 |
| `--no-update-check`               | Do not check for a newer release                     |

| Environment variable   | Effect                                     |
| ---------------------- | ------------------------------------------ |
| `CAFE_HOST`            | Same as `--host`                           |
| `CAFE_REPO`            | Same as `--repo`                           |
| `CAFE_TOKEN`           | A token to use instead of the stored login |
| `CAFE_NO_UPDATE_CHECK` | Same as `--no-update-check`                |

## Authentication

| Command                                              | Does                                                                               |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `cafe auth login`                                    | Sign in through the browser ([device sign-in](https://git.cafe/docs/account/device-authorization.md)) |
| `cafe auth status`                                   | Show who you are signed in as                                                      |
| `cafe auth logout`                                   | Remove the stored login (`--all` for every server)                                 |
| `cafe auth http setup`                               | Let Git use your login for HTTPS remotes                                           |
| `cafe auth http status`                              | Check the Git credential setup                                                     |
| `cafe auth ssh setup`                                | Find and register your SSH key, optionally set up signing                          |
| `cafe auth ssh list\|add\|remove`                    | Manage SSH authentication keys                                                     |
| `cafe auth setup`                                    | Configure HTTPS and SSH in one step, without prompts                               |
| `cafe auth token list\|create\|view\|rotate\|revoke` | Manage [personal access tokens](https://git.cafe/docs/account/personal-access-tokens.md)              |
| `cafe signing ssh list\|add\|remove`                 | Manage SSH signing keys                                                            |
| `cafe signing gpg list\|add\|remove`                 | Manage [GPG keys](https://git.cafe/docs/account/gpg-keys.md)                                          |

Managing tokens and keys needs the optional credential-management permission
when you sign in.

## Repositories

| Command                                              | Does                                                                                             |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `cafe repo list`                                     | List repositories you can access                                                                 |
| `cafe repo create <name>`                            | Create a repository (`--private`, `--org`, `--clone`)                                            |
| `cafe repo view [owner/name]`                        | Show a repository                                                                                |
| `cafe repo clone <owner/name> [dir]`                 | Clone a repository                                                                               |
| `cafe repo edit`                                     | Change description, topics, visibility, default branch                                           |
| `cafe repo fork [owner/name]`                        | Fork a repository                                                                                |
| `cafe repo sync`                                     | Update a fork's default branch from its parent                                                   |
| `cafe repo compare-upstream`                         | Show how far a fork is ahead or behind                                                           |
| `cafe repo forks`                                    | List a repository's forks                                                                        |
| `cafe repo transfer <owner/name> <new-owner>`        | Transfer a repository                                                                            |
| `cafe repo delete <owner/name>`                      | Delete a repository                                                                              |
| `cafe repo star\|unstar\|stargazers\|starred`        | Manage and list stars                                                                            |
| `cafe repo archive <branch\|tag\|commit> <ref>`      | Write a tar.gz snapshot to stdout                                                                |
| `cafe repo collaborator list\|add\|remove`           | Manage [collaborators](https://git.cafe/docs/repositories/repository-access.md)                                     |
| `cafe repo ruleset list\|view\|create\|edit\|delete` | Manage [rulesets](https://git.cafe/docs/repositories/repository-rulesets.md) (`--org`, `--team` for inherited ones) |
| `cafe repo ruleset effective <branch>`               | Show the rules that apply to a branch                                                            |
| `cafe repo branch-role list\|assign\|revoke`         | Manage per-branch roles                                                                          |
| `cafe repo github disconnect <owner/name>`           | [Disconnect from GitHub](https://git.cafe/docs/integrations/github-sync.md#disconnect-from-github) (`--yes`)        |

### Code

| Command                                 | Does                                          |
| --------------------------------------- | --------------------------------------------- |
| `cafe repo code tree [path]`            | List files                                    |
| `cafe repo code blob <path>`            | Print a file (`--raw` for the bytes)          |
| `cafe repo code commits`                | List commits                                  |
| `cafe repo code commit <sha>`           | Show a commit and its diff                    |
| `cafe repo code compare <base>..<head>` | Compare two revisions (`--patch`)             |
| `cafe repo code refs`                   | List branches and tags                        |
| `cafe repo code search <pattern>`       | [Search code](https://git.cafe/docs/repositories/code-search.md) |
| `cafe repo code status <sha>`           | Show a commit's combined check status         |

### Branches and tags

| Command                                      | Does                                         |
| -------------------------------------------- | -------------------------------------------- |
| `cafe branch list`                           | List branches (`--deleted` for deleted ones) |
| `cafe branch delete <name>`                  | Delete a branch                              |
| `cafe branch restore <name>`                 | Restore a deleted branch                     |
| `cafe tag list`                              | List tags                                    |
| `cafe tag view <name>`                       | Show a tag                                   |
| `cafe tag delete <name>`                     | Delete a tag                                 |
| `cafe repo ref-deletion list\|view\|inspect` | Check the result of branch and tag deletions |

## Pull requests

| Command                                  | Does                                                                                  |
| ---------------------------------------- | ------------------------------------------------------------------------------------- |
| `cafe pr list`                           | List pull requests (`--state`)                                                        |
| `cafe pr create`                         | [Open a pull request](https://git.cafe/docs/pull-requests/open.md) from the current branch (`--draft`)   |
| `cafe pr view <number>`                  | Show a pull request                                                                   |
| `cafe pr checkout <number>`              | Check out a pull request's branch                                                     |
| `cafe pr edit <number>`                  | Change the title, description or target (`--base`)                                    |
| `cafe pr diff <number>`                  | List changed files (`--patch`, `--path`)                                              |
| `cafe pr commits <number>`               | List the commits                                                                      |
| `cafe pr status [number]`                | Show reviews, checks and mergeability                                                 |
| `cafe pr draft\|undraft [number]`        | Convert to a draft, or mark ready for review                                          |
| `cafe pr ready <number>`                 | Check merge readiness against rulesets (not ready for review: see `pr undraft`)       |
| `cafe pr merge <number>`                 | [Merge](https://git.cafe/docs/pull-requests/merge.md) (`--strategy merge\|squash\|rebase\|fast-forward`) |
| `cafe pr close <number>`                 | Close without merging                                                                 |
| `cafe pr review <number>`                | Submit a review (`--approve`, `--request-changes`, `--comment`)                       |
| `cafe pr reviews <number>`               | List reviews                                                                          |
| `cafe pr review-view\|review-dismiss`    | Show or dismiss a review                                                              |
| `cafe pr reviewer list\|add\|remove`     | Manage requested reviewers                                                            |
| `cafe pr comments <number>`              | List comments                                                                         |
| `cafe pr comment <number>`               | Comment, or comment on a line with `--path` and `--line`                              |
| `cafe pr comment-edit\|comment-delete`   | Edit or delete a comment                                                              |
| `cafe pr resolve\|unresolve`             | Resolve or reopen a review thread                                                     |
| `cafe pr milestone <number> [milestone]` | Set or clear the milestone                                                            |
| `cafe pr lock\|unlock <number>`          | Lock or unlock the conversation                                                       |
| `cafe pr links\|link\|unlink`            | Manage linked issues                                                                  |

## Stacks

See [stacked pull requests](https://git.cafe/docs/pull-requests/stacks.md).

| Command                            | Does                                                                                        |
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
| `cafe stack submit`                | Push local commits as a stack of pull requests (`--name`, `--base`, `--draft`, `--dry-run`) |
| `cafe stack link <pulls…>`         | Link existing pull requests into a stack, bottom first                                      |
| `cafe stack list`                  | List stacks                                                                                 |
| `cafe stack status [pull]`         | Show the stack containing a pull request                                                    |
| `cafe stack merge [pull]`          | Land the stack up to a pull request (`--stack` for all)                                     |
| `cafe stack restack`               | Rebase the stack onto its updated base                                                      |
| `cafe stack close [pull]`          | Close the stack and its open pull requests                                                  |
| `cafe stack draft\|undraft [pull]` | Convert every open member to a draft, or mark them all ready for review                     |

## Issues and milestones

| Command                                                          | Does                                                  |
| ---------------------------------------------------------------- | ----------------------------------------------------- |
| `cafe issue list`                                                | List issues (`--state`, `--label`, `--assignee`)      |
| `cafe issue create`                                              | Create an issue                                       |
| `cafe issue view <number>`                                       | Show an issue                                         |
| `cafe issue edit <number>`                                       | Change title, body, state, priority or due date       |
| `cafe issue close <number>`                                      | Close an issue as completed                           |
| `cafe issue comments\|comment`                                   | List or add comments                                  |
| `cafe issue comment-edit\|comment-delete`                        | Edit or delete a comment                              |
| `cafe issue assign\|unassign`                                    | Manage assignees                                      |
| `cafe issue label list\|create\|edit\|delete`                    | Manage the repository's [labels](https://git.cafe/docs/issues/labels.md) |
| `cafe issue label add\|remove`                                   | Label or unlabel an issue                             |
| `cafe issue milestone <number> [milestone]`                      | Set or clear the milestone                            |
| `cafe issue pin\|unpin`                                          | [Pin](https://git.cafe/docs/issues/pin-and-lock.md) an issue             |
| `cafe issue lock\|unlock`                                        | Lock or unlock the conversation                       |
| `cafe issue links\|link\|unlink`                                 | Manage linked pull requests                           |
| `cafe milestone list\|view\|create\|edit\|close\|reopen\|delete` | Manage [milestones](https://git.cafe/docs/issues/milestones.md)          |

## CI

GitHub Actions workflow runs and Buildkite builds; `cafe actions` is an alias
of `cafe ci`.

| Command                                                   | Does                                                                     |
| --------------------------------------------------------- | ------------------------------------------------------------------------ |
| `cafe ci list`                                            | List runs newest first (`--source`, `--status`, `--branch`, `--pr`, ...) |
| `cafe ci status`                                          | Show the runs at a pull request's head, a branch's tip or a commit       |
| `cafe ci view <run>`                                      | Show a run                                                               |
| `cafe ci watch <run>`                                     | Wait until a run finishes; exits nonzero unless it succeeded             |
| `cafe ci jobs <run>`                                      | List a run's jobs                                                        |
| `cafe ci logs <run> [job]`                                | Print or save a job log (`--full`, `--tail`, `--output`)                 |
| `cafe ci cancel\|rerun <run>`                             | Cancel or rerun a workflow run                                           |
| `cafe ci dispatch <workflow>`                             | Run a [workflow](https://git.cafe/docs/ci/actions.md) on demand                             |
| `cafe ci settings\|enable\|disable`                       | Show or change the repository's Actions settings                         |
| `cafe ci secret\|variable list\|set\|delete`              | Manage [secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md)           |
| `cafe ci pipeline list\|view\|create\|edit\|delete`       | Connect Buildkite pipelines to a repository                              |
| `cafe integration list\|view\|create\|edit\|delete <org>` | Manage [Buildkite integrations](https://git.cafe/docs/ci/buildkite.md)                      |

## Checks

Every check on one commit, and the [check-run API](https://git.cafe/docs/integrations/check-runs.md)
for scripts and CI jobs.

| Command                                   | Does                                                                  |
| ----------------------------------------- | --------------------------------------------------------------------- |
| `cafe check list`                         | List every check at a pull request's head, a branch's tip or a commit |
| `cafe check report <key> --commit <oid>`  | Report a check under your key (`--name`, `--status`, `--suite`, ...)  |
| `cafe check report --commit <oid> --file` | Report a batch of checks from a JSON file                             |
| `cafe check rerequest <check run>`        | Ask the reporting app to run a check again                            |

## Webhooks

The target is an organization slug or `owner/repo`. See
[webhooks](https://git.cafe/docs/integrations/webhooks.md).

| Command                                           | Does                                 |
| ------------------------------------------------- | ------------------------------------ |
| `cafe webhook list\|view <target>`                | List or show webhooks                |
| `cafe webhook create\|edit\|delete <target>`      | Manage webhooks                      |
| `cafe webhook rotate-secret <target> <id>`        | Replace the signing secret           |
| `cafe webhook ping <target> <id>`                 | Send a signed test ping              |
| `cafe webhook capabilities\|readiness`            | Check supported events and readiness |
| `cafe webhook deliveries\|dead-letter\|attempts`  | Inspect delivery history             |
| `cafe webhook redeliver <target> <id> <delivery>` | Send a failed event again            |

## Organizations and teams

| Command                                                      | Does                                                       |
| ------------------------------------------------------------ | ---------------------------------------------------------- |
| `cafe org list\|view\|create\|edit\|delete`                  | Manage [organizations](https://git.cafe/docs/organizations/organizations.md)  |
| `cafe org member list\|set\|remove <org>`                    | Manage [members](https://git.cafe/docs/organizations/organization-members.md) |
| `cafe org invitation list\|create\|delete <org>`             | Manage invitations                                         |
| `cafe org invitation accept <org>`                           | Accept an invitation                                       |
| `cafe team list\|create\|view\|edit\|reparent\|delete <org>` | Manage [teams](https://git.cafe/docs/organizations/teams.md)                  |
| `cafe team member list\|set\|remove <org> <team>`            | Manage team members                                        |
| `cafe team repos <org> <team>`                               | List a team's repositories                                 |
| `cafe team repo create <org> <team> <name>`                  | Create a repository for a team                             |
| `cafe app view <org> <app>`                                  | Show a published [app](https://git.cafe/docs/integrations/apps.md)'s IDs      |

## Profile

| Command                      | Does                                       |
| ---------------------------- | ------------------------------------------ |
| `cafe profile view [handle]` | Show a public profile                      |
| `cafe profile edit`          | Edit your [profile](https://git.cafe/docs/account/profile.md) |
| `cafe profile pins`          | List your pinned repositories              |
| `cafe profile pin <repos…>`  | Replace your pinned repositories           |

## Other commands

| Command                             | Does                                        |
| ----------------------------------- | ------------------------------------------- |
| `cafe api <path>`                   | Send an authenticated [API](https://git.cafe/docs/reference/api.md) request |
| `cafe config get\|set\|unset\|list` | Read and write settings such as `stack.vcs` |
| `cafe skills list\|show\|install`   | Install `cafe` skills for coding agents     |
| `cafe completion <shell>`           | Print a shell completion script             |
| `cafe version`                      | Show the version (`--check` for the latest) |


---

Source: https://git.cafe/docs/reference/api

# REST API

The GitCafe REST API lives at `https://git.cafe/api`. Everything you can do in
the browser goes through it, and most of it is open to your own tools.

## Find the endpoints

The [API reference](https://git.cafe/api/openapi) lists every operation with its parameters and
response schemas. It is generated from the API itself, so it always matches
the server you are using. The same description is available as
[OpenAPI 3.1 JSON](https://git.cafe/api/openapi/json) for code generators, and as
[Markdown](https://git.cafe/api/openapi.md).

## Authenticate

Send a credential as a bearer token:

```sh
curl -H "Authorization: Bearer $GITCAFE_TOKEN" https://git.cafe/api/auth/principal
```

- **Personal access token**: for scripts and CI. Create one in
  [Settings → Tokens](https://git.cafe/docs/account/personal-access-tokens.md) with only the
  permissions you need.
- **OAuth**: for applications acting for other users; see
  [connect an API client](https://git.cafe/docs/account/mcp-connections.md#connect-an-api-client).
- **The CLI**: `cafe api <path>` sends an authenticated request using your
  `cafe` login, for example `cafe api "/repos/{owner}/{repo}"` inside a clone.

Requests without a credential can read public data, with a lower
[rate limit](https://git.cafe/docs/reference/rate-limits.md).

## Work with versions

Most resources carry a `version` number. When you change a resource, send the
version you read as `expectedVersion`. If someone changed it in the meantime,
the API answers `409 Conflict`: read the resource again, check the new values,
and retry with the new version. This stops you from overwriting changes you
have not seen.

## Retry safely

Operations that start work, such as merging, landing a stack, deleting a
branch or syncing a mirror, take a `requestId` that you choose (a UUID is a
good choice). If the response is lost, send the same request with the same
`requestId` again: you get the original result instead of a second operation.
Use a new `requestId` only when you mean to do something new.

Many such operations answer `202 Accepted` with an operation you can poll.
Accepted means the work is queued, not finished; read the operation until it
reports a final state.

## Page through lists

List responses return `items` and a `next` cursor. Pass `next` as the `after`
parameter to get the following page, and stop when `next` is `null`.
`cafe api --paginate` follows the cursors for you.

## Errors

Errors use `application/problem+json` with a `type`, `title`, `status` and
`detail`, and often a machine-readable `code`. A private repository you cannot
see answers `404`, the same as one that does not exist. A request that does not
match the contract answers `400` `validation-error`, whose `detail` names up to
eight failing fields by path, such as `query.state` or `body.labels.0`, but
never their values.


---

Source: https://git.cafe/docs/reference/rate-limits

# API rate limits

Every request to `/api` and `/mcp` counts against a budget. Signed-in requests
count against your account; anonymous requests count against your network
address. This page lists the limits, how to read them and what to do when you
hit one.

## Limits

| Policy      | Applies to                                                                                                    | Limit                                                 |
| ----------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `api`       | Every signed-in API request and every signed-in MCP request                                                   | 1,000 per 60 s, times your plan multiplier            |
| `api-heavy` | Expensive requests (compare, blame, code search, archives, pull request diffs), on top of `api`               | 120 per 60 s signed in, times your plan; 10 anonymous |
| `api-anon`  | Every anonymous API and MCP request                                                                           | 1,200 per hour per address                            |
| `feedback`  | Sending feedback, from the web, the API or MCP, on top of `api`                                               | 20 per day                                            |
| `web`       | Every web page load                                                                                           | 3,600 per hour per address                            |
| `web-git-*` | Anonymous web pages that read Git (overviews, `llms.txt`, files, trees, commits, history, pull request diffs) | 300 per hour per address; see below                   |
| `og`        | Every social preview image                                                                                    | 300 per hour per address                              |

- Every credential of one account (browser session, personal access token,
  OAuth token, CLI login) shares one count.
- Anonymous requests are counted per IPv4 address or per IPv6 /64 network.
- Password Basic authentication uses the anonymous address budgets, including
  the reduced heavy-request limit, before password verification. It does not
  receive a per-account or plan-multiplied budget. A grant token sent as a Basic
  password uses the token budget instead.
- Each social preview image also spends up to three `api-anon` requests.
- Anonymous web pages count their server-side API reads against `api-anon`
  (and heavy reads against the anonymous heavy limit) too.
- Anonymous pages that read Git have tighter limits on top of `web`: 300 per
  hour per address (`web-git`), 60 for crawlers and other non-browser clients
  (`web-git-automated`), 600 per hour for each declared crawler across all its
  addresses (`web-git-agent`), and 600 per hour per repository for pages
  pinned to a commit ID (`web-git-pinned`). Signed-in pages are not affected.
- Git clone, fetch and push are not rate limited. Git LFS batch requests are
  limited per address: 120 per 60 s without a credential, 1,200 with one.
  LFS object downloads and uploads are not limited.

The [API reference](https://git.cafe/api/openapi) marks each limited operation with its
policies in `x-gc2-ratelimit`.

## Plan multipliers

The `api` and `api-heavy` limits are multiplied by the highest
[plan](https://git.cafe/docs/account/billing.md) among your personal account and the
organizations you are a member of. Outside collaborators do not get an
organization's plan. Anonymous, feedback, web and og limits are never multiplied.

| Plan          | Multiplier |
| ------------- | ---------- |
| Free          | 1×         |
| Pro           | 2×         |
| Team Standard | 5×         |
| Team Premium  | 10×        |
| Enterprise    | 20×        |

A plan or membership change takes effect within about 11 minutes.

## Read your limit

Successful responses carry the policies that were checked, with the quota `q`
and the window `w` in seconds:

```
RateLimit-Policy: "api";q=2000;w=60
```

The API does not report how many requests remain.

## Handle a 429

A request over its budget is refused with `429 Too Many Requests`:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 17
RateLimit: "api-heavy";r=0;t=17
RateLimit-Policy: "api";q=1000;w=60, "api-heavy";q=120;w=60
Content-Type: application/problem+json

{
  "type": "https://cafe.sh/errors/rate-limit-exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "The api-heavy limit allows 120 requests per minute.",
  "policy": "api-heavy",
  "limit": 120,
  "window_seconds": 60,
  "retry_after_seconds": 17
}
```

1. Wait at least `Retry-After` seconds before retrying.
2. If the retry is limited too, back off further: limits use a sliding window,
   so a caller far over its budget can need more than one window to recover.
   An hourly or daily budget recovers gradually through the next hour or day.
3. Spread bulk work over time, sign in to get a per-account budget, or upgrade
   your plan for a higher multiplier.


---

Source: https://git.cafe/docs/reference/markdown

# Read GitCafe as Markdown

Every guide, and most repository pages, can be read as Markdown. This is handy
for scripts, coding agents and anything that prefers text to HTML.

## Get the Markdown version of a page

Add `?format=markdown` to the address (or `&format=markdown` if it already has
a query), or send `Accept: text/markdown`. Guides also have a `.md` address,
such as `/docs/getting-started/quickstart.md`.

```text
https://git.cafe/<owner>/<repo>?format=markdown
https://git.cafe/<owner>/<repo>/tree/main/src?format=markdown
https://git.cafe/<owner>/<repo>/blob/main/README.md?format=markdown
https://git.cafe/<owner>/<repo>/commits/main?format=markdown
https://git.cafe/<owner>/<repo>/issues/123?format=markdown
https://git.cafe/<owner>/<repo>/pull/123.md
https://git.cafe/<owner>/<repo>/pull/123/diffs.md
```

The same permissions apply as in the browser: private repositories need you to
be signed in and otherwise answer `404`. A page with no Markdown version
answers `406`.

A `.md` at the end of a file, directory or commit address is part of the name
when that file exists: `/blob/main/README.md` is the file `README.md`. Otherwise
it asks for the Markdown version, so `/blob/main/src/a.ts.md` reads `src/a.ts`
as Markdown. Use `?format=markdown` when both could apply.

## Start from llms.txt

- `/llms.txt` lists every guide and site page.
- `/llms-full.txt` contains all of them in one file.
- `/<owner>/<repo>/llms.txt` summarizes a repository: its description, default
  branch, root files, the start of its README, and links to its code, history,
  issues and pull requests.

## Repository addresses

- Directory: `/<owner>/<repo>/tree/<revision>/<path>` (omit `<path>` for the
  root).
- File: `/<owner>/<repo>/blob/<revision>/<path>`.
- History: `/<owner>/<repo>/commits/<revision>`.
- Commit: `/<owner>/<repo>/commit/<sha>`.

A revision can be a branch, a tag, a commit SHA or a full ref; write slashes
inside a ref as `%2F` (`refs%2Fheads%2Fmain`). Links in a document point at the
exact commit it was made from, so the files you follow match what you read.

## Limits

Markdown documents are summaries, not full exports:

- History shows 100 commits per page; issue, pull request and stack lists show
  30, open items by default; owner repository lists show 20; branch and tag
  lists show 100.
- A directory, including the root files in `llms.txt`, lists at most 10,000
  entries; a longer one ends with a truncation notice and the tree API cursor
  to continue from.
- A pull request's commits and diffs show 20 files or commits per page, with
  notices for binary or very large files.
- Discussions, reviews and checks link to the browser instead of being copied.

Follow a document's **Next page** link unchanged to continue.

