---
title: GitHub token
description: A least-privilege GitHub token for one project, stored in 1Password and attached to it.
---

A project that calls GitHub's API (reading repositories, stars, releases)
needs its own token: not the one `gh` uses ([GitHub](/docs/your-projects/accounts/github)),
which can write to your repositories. One token per project, with only what
that project needs, so revoking it breaks nothing else.

Creating it is manual on purpose: GitHub has no API that creates personal
access tokens, so no tool can do it for you.

## 1. Create the token (once per project)

Open [https://github.com/settings/personal-access-tokens/new](https://github.com/settings/personal-access-tokens/new):

- **Token name:** `<project>-github`, the same name it gets in 1Password.
- **Expiration:** your choice. A date means renewing it (see *Renewing*).
- **Repository access:**
  - *Public repositories* if the project only reads public data. It then
    gets read access to every public repository on GitHub and nothing else.
  - *Only select repositories* if it works with specific repositories of
    yours, and then only the permissions it needs, such as *Contents: Read*.
- **Permissions:** none beyond what the project's documentation asks for.

Generate it and copy it: it's shown only once. GitHub may ask for 2FA first.

## 2. Store it (once per project)

```sh
dot secret add <project>-github
```

Paste the token when asked; it isn't shown while you type. The command
stores it in your development vault and prints the next command to run.

## 3. Attach it to the project

From the project's folder, with the variable name the project reads
(usually `GITHUB_TOKEN`):

```sh
dot secret attach <project>-github GITHUB_TOKEN
```

The project needs a `.env.schema` that loads Varlock's 1Password plugin
first; if it doesn't have one, the command says what's missing
([README.md](/docs/your-projects/secrets#3-attach-it-to-the-project)).

## 4. Check it

From the project's folder:

```sh
varlock run -- sh -c 'curl -s -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/rate_limit' | grep -m1 '"limit"'
```

`"limit": 5000` means GitHub accepted the token; without one it's 60. The
token only exists inside that command.

## Friction

| Criterion        | GitHub token for a project                          |
|------------------|-----------------------------------------------------|
| Time             | not measured                                        |
| Browser          | once per token, to create it                        |
| 2FA              | when GitHub asks, while creating it                 |
| Per machine      | nothing: 1Password syncs it to every Mac            |
| Expires          | as set when creating it                             |
| Secret on disk   | no                                                  |

## Renewing

GitHub emails you before a token expires. Create a new one (step 1), then:

```sh
dot secret update <project>-github
```

The reference stays the same, so the project picks up the new value on its
next run.

## Alternative: a GitHub App

A job that runs unattended, on a server or a schedule, can use a GitHub App
instead: the app mints tokens through the API that expire in an hour, so
nothing long-lived needs renewing. It takes more setup: the app, its private
key in 1Password, and code that exchanges one for the other.
