---
title: "Configure from the environment"
description: "Create providers from FORGES_* environment variables."
canonical_url: "https://forges.link/guides/environment"
---
# Configure from the environment

> Create providers from FORGES_* environment variables.

If you are writing a CLI or a server that should work with whichever forges a user has credentials for, it would be tedious to write configuration code for each one. `forges/env` solves this by creating providers from environment variables. Every variable follows the form `FORGES_<KIND>_<FIELD>`, such as `FORGES_GITHUB_TOKEN` or `FORGES_GITLAB_BASE_URL`, and the forges that have variables set are the ones you get.

## Pick the variables for a task

Which variables you need depends on what you want to do. Choose a forge and a task in the picker below, and it shows you the variables to set, the scopes or permissions the credential needs and how to create such a credential. The table lists the capabilities that each task covers:

<credentials-wizard>
<table>
<thead>
  <tr>
    <th>
      Task
    </th>
    
    <th>
      Capabilities
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Read: Repositories and people
    </td>
    
    <td>
      <code>
        repos.get
      </code>
      
      , <code>
        repos.list
      </code>
      
      , <code>
        repos.byOwner
      </code>
      
      , <code>
        repos.labels
      </code>
      
      , <code>
        repos.milestones
      </code>
      
      , <code>
        repos.collaborators
      </code>
      
      , <code>
        repos.contributors
      </code>
      
      , <code>
        repos.contributorStats
      </code>
      
      , <code>
        repos.templates
      </code>
      
      , <code>
        repos.permissionFor
      </code>
      
      , <code>
        repos.assignableUsers
      </code>
      
      , <code>
        repos.reviewerCandidates
      </code>
      
      , <code>
        users.get
      </code>
      
      , <code>
        users.me
      </code>
      
      , <code>
        users.sponsorable
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Organisation teams
    </td>
    
    <td>
      <code>
        orgs.teams
      </code>
      
      , <code>
        orgs.teamMembers
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Issues and PRs
    </td>
    
    <td>
      <code>
        threads.get
      </code>
      
      , <code>
        threads.list
      </code>
      
      , <code>
        threads.getMany
      </code>
      
      , <code>
        threads.events
      </code>
      
      , <code>
        comments.list
      </code>
      
      , <code>
        comments.get
      </code>
      
      , <code>
        reactions.list
      </code>
      
      , <code>
        reviews.list
      </code>
      
      , <code>
        reviews.get
      </code>
      
      , <code>
        contents.threadFiles
      </code>
      
      , <code>
        contents.threadCommits
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Code and history
    </td>
    
    <td>
      <code>
        contents.file
      </code>
      
      , <code>
        contents.tree
      </code>
      
      , <code>
        contents.branches
      </code>
      
      , <code>
        contents.tags
      </code>
      
      , <code>
        contents.resolveRef
      </code>
      
      , <code>
        contents.commits
      </code>
      
      , <code>
        contents.commit
      </code>
      
      , <code>
        contents.compare
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Releases
    </td>
    
    <td>
      <code>
        releases.list
      </code>
      
      , <code>
        releases.get
      </code>
      
      , <code>
        releases.latest
      </code>
      
      , <code>
        releases.getByTag
      </code>
      
      , <code>
        releases.downloadAsset
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Checks and CI
    </td>
    
    <td>
      <code>
        checks.thread
      </code>
      
      , <code>
        checks.list
      </code>
      
      , <code>
        checks.get
      </code>
      
      , <code>
        ci.runs
      </code>
      
      , <code>
        ci.run
      </code>
      
      , <code>
        ci.jobs
      </code>
      
      , <code>
        ci.job
      </code>
      
      , <code>
        ci.log
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Search
    </td>
    
    <td>
      <code>
        search.threads
      </code>
      
      , <code>
        search.repos
      </code>
      
      , <code>
        search.commits
      </code>
      
      , <code>
        search.code
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Security alerts
    </td>
    
    <td>
      <code>
        securityAlerts
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Read: Notifications
    </td>
    
    <td>
      <code>
        notifications.list
      </code>
      
      , <code>
        notifications.unreadCount
      </code>
      
      , <code>
        subscriptions.get
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Comment and react
    </td>
    
    <td>
      <code>
        writes.comment
      </code>
      
      , <code>
        writes.upsertComment
      </code>
      
      , <code>
        comments.edit
      </code>
      
      , <code>
        comments.delete
      </code>
      
      , <code>
        writes.react
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Open, edit and close
    </td>
    
    <td>
      <code>
        writes.create
      </code>
      
      , <code>
        writes.update
      </code>
      
      , <code>
        writes.close
      </code>
      
      , <code>
        writes.reopen
      </code>
      
      , <code>
        writes.transfer
      </code>
      
      , <code>
        writes.markDuplicate
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Labels, assignees and milestones
    </td>
    
    <td>
      <code>
        writes.setLabels
      </code>
      
      , <code>
        writes.addLabels
      </code>
      
      , <code>
        writes.removeLabels
      </code>
      
      , <code>
        writes.setMilestone
      </code>
      
      , <code>
        writes.setAssignees
      </code>
      
      , <code>
        repos.createLabel
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Review PRs
    </td>
    
    <td>
      <code>
        writes.requestReview
      </code>
      
      , <code>
        reviews.create
      </code>
      
      , <code>
        reviews.submit
      </code>
      
      , <code>
        reviews.approve
      </code>
      
      , <code>
        reviews.reply
      </code>
      
      , <code>
        reviews.resolveThread
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Merge PRs
    </td>
    
    <td>
      <code>
        writes.merge
      </code>
      
      , <code>
        writes.approveAndMerge
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Report checks
    </td>
    
    <td>
      <code>
        checks.report
      </code>
      
      , <code>
        checks.rerun
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Commit files
    </td>
    
    <td>
      <code>
        contents.commitFiles
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Publish releases
    </td>
    
    <td>
      <code>
        releases.create
      </code>
      
      , <code>
        releases.update
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Write: Triage notifications
    </td>
    
    <td>
      <code>
        notifications.markRead
      </code>
      
      , <code>
        notifications.markDone
      </code>
      
      , <code>
        notifications.unsubscribe
      </code>
      
      , <code>
        notifications.markAllRead
      </code>
      
      , <code>
        notifications.markAllDone
      </code>
      
      , <code>
        subscriptions.set
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Events: Receive webhooks
    </td>
    
    <td>
      <code>
        sources.webhook
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Events: Poll or stream events
    </td>
    
    <td>
      <code>
        sources.poll
      </code>
      
      , <code>
        sources.subscribe
      </code>
      
      , <code>
        repos.events
      </code>
      
      , <code>
        users.events
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Administer: Manage webhooks
    </td>
    
    <td>
      <code>
        webhooks.list
      </code>
      
      , <code>
        webhooks.create
      </code>
      
      , <code>
        webhooks.update
      </code>
      
      , <code>
        webhooks.delete
      </code>
      
      , <code>
        webhooks.rotateSecret
      </code>
      
      , <code>
        webhooks.deliveries
      </code>
      
      , <code>
        webhooks.redeliver
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Administer: Add collaborators
    </td>
    
    <td>
      <code>
        repos.addCollaborator
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Administer: App installations
    </td>
    
    <td>
      <code>
        installations
      </code>
    </td>
  </tr>
</tbody>
</table>
</credentials-wizard>

If you need the same information in your code, [`provider.scopesFor(verb)`](https://forges.link/guides/authentication#show-the-scopes-an-operation-needs) returns the scopes for a single verb.

## Create the providers

To turn the variables into providers, pass them to `forgesFromEnv()`. It reads every set of variables, builds a provider for each forge that is configured and hands them all to `createForges()`:

```ts
import { forgesFromEnv } from 'forges/env'

const forges = forgesFromEnv(process.env)
```

## Report skipped forges

`forgesFromEnv()` silently leaves out any set of variables that can't configure a provider, for example because the token is missing. If you would rather tell the user, use `providersFromEnv()` instead. It returns one entry for each set of variables. An entry has a `factory` when the variables are enough to configure a provider, and a `skipped` reason when they aren't. The reason never contains a credential, so it is safe to log:

```ts
import { createForges } from 'forges'
import { providersFromEnv } from 'forges/env'

const entries = providersFromEnv(process.env)

for (const entry of entries) {
  if (entry.skipped) {
    console.warn(`Skipping ${entry.forge}: ${entry.skipped}`)
  }
}

const forges = createForges(
  entries.flatMap(entry => entry.factory ? [entry.factory] : []),
)
```

## Variables

In the variable names, `<KIND>` is the name of the forge in upper case, with an underscore in place of a hyphen. That gives `GITHUB`, `GITLAB`, `BITBUCKET`, `FORGEJO`, `GITEA`, `GITEE`, `AZURE_DEVOPS`, `CURSOR_ORIGIN`, `TANGLED`, `PUSHIN` and `CLOUDFLARE_ARTIFACTS`.

The following fields work for every forge. Most of them set the provider option of the same name, and the table describes what each one does:

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Effect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        TOKEN
      </code>
    </td>
    
    <td>
      Sets <code>
        auth
      </code>
      
       to a token
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        BASE_URL
      </code>
    </td>
    
    <td>
      Sets <code>
        baseUrl
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        INSTANCE
      </code>
    </td>
    
    <td>
      Sets <code>
        instance
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        INSTANCE_VERSION
      </code>
    </td>
    
    <td>
      Sets <code>
        instanceVersion
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        WEBHOOK_SECRET
      </code>
    </td>
    
    <td>
      Sets <code>
        webhookSecret
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        READ_ONLY
      </code>
    </td>
    
    <td>
      Sets <code>
        readOnly: true
      </code>
      
       when the value is <code>
        1
      </code>
      
       or <code>
        true
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ENABLED
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
      
       or <code>
        true
      </code>
      
       creates an anonymous provider without a token. <code>
        0
      </code>
      
       or <code>
        false
      </code>
      
       skips the forge
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        DEMO_REPO
      </code>
    </td>
    
    <td>
      Sets <code>
        entry.demoRepo
      </code>
      
       to an <code>
        owner/name
      </code>
      
       repository for your code to use, and enables anonymous reads
    </td>
  </tr>
</tbody>
</table>

A few forges need more than that, so they read additional fields:

<table>
<thead>
  <tr>
    <th>
      Forge
    </th>
    
    <th>
      Fields
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      GitHub, Cursor Origin
    </td>
    
    <td>
      <code>
        APP_ID
      </code>
      
      , <code>
        PRIVATE_KEY
      </code>
      
       and <code>
        INSTALLATION_ID
      </code>
      
       for app auth. Write the newlines in <code>
        PRIVATE_KEY
      </code>
      
       as <code>
        \n
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Bitbucket, Azure DevOps
    </td>
    
    <td>
      <code>
        USERNAME
      </code>
      
       and <code>
        PASSWORD
      </code>
      
       for Basic auth
    </td>
  </tr>
  
  <tr>
    <td>
      Azure DevOps
    </td>
    
    <td>
      <code>
        ORGANIZATION
      </code>
      
      , which is required
    </td>
  </tr>
  
  <tr>
    <td>
      Tangled
    </td>
    
    <td>
      <code>
        IDENTIFIER
      </code>
      
      , <code>
        PASSWORD
      </code>
      
       and an optional <code>
        PDS
      </code>
      
       for an app password. <code>
        API_URL
      </code>
      
       and <code>
        NOTIFICATIONS_URL
      </code>
      
       set the service URLs
    </td>
  </tr>
</tbody>
</table>

To learn what these options do on a particular forge, see its [provider page](https://forges.link/providers).

## Configure a second instance

You might have credentials for two instances of one forge, such as github.com and a company's GitHub Enterprise Server. To configure the second one, add a suffix after the field name. This starts a new set of variables for the same forge. Every variable in a set carries the same suffix, and you can read it from `entry.suffix`:

```sh
FORGES_GITHUB_TOKEN=ghp_...
FORGES_GITHUB_TOKEN_WORK=ghp_...
FORGES_GITHUB_BASE_URL_WORK=https://ghe.example.com/api/v3
```

As with any other registration, `createForges()` throws when two providers share a forge and an instance. If two sets of variables point at the same host, give one of them a different `INSTANCE`.


## Sitemap

See the full [sitemap](https://forges.link/sitemap.md) for all pages.
