# Syncs

A sync copies one environment of a project to a Vercel project or to GitHub Actions secrets, and keeps it up to date. envpass pushes after every change, so the destination never needs a token or a build step that pulls values.

A typical setup for one app:

| envpass | Destination |
|---|---|
| `shop` / `prod` | Vercel `shop-web`, Production |
| `shop` / `staging` | Vercel `shop-web`, Preview |
| `shop` / `dev` | Vercel `shop-web`, Development |
| `shop` / `prod` | GitHub `acme/shop`, environment `production` |

## Before you start

Setting up a sync takes two steps: a workspace **connection** to Vercel or GitHub, then a **sync** in each project.

- Adding a connection needs **Manage service tokens** in your workspace role.
- Adding a sync needs **Manage service tokens** in the project, through your workspace or project role, plus at least `read` on the environment.

Both need a user login. Service tokens can't manage connections or syncs. See [Access control](/docs/access-control).

The Vercel or GitHub token you paste is encrypted at rest and never shown again, not even to admins. Removing a connection also deletes the syncs that use it.

## Connect Vercel

1. In Vercel, open [vercel.com/account/tokens](https://vercel.com/account/tokens) and click **Create Token**.
2. Name it (for example `envpass`), set **Scope** to the team that owns your projects, and choose an expiration. Copy the token.
3. In envpass, open **Integrations** and click **Connect Vercel**.
4. Paste the token and click **Check token**.
5. Under **Vercel scope**, pick the team, or your personal account.
6. Optionally rename the connection, then click **Save connection**.

A connection writes to one Vercel scope. Connect again with another token for each other team.

## Connect GitHub

1. In GitHub, open [github.com/settings/personal-access-tokens](https://github.com/settings/personal-access-tokens) and click **Generate new token** (fine-grained).
2. Set **Resource owner** to the user or organization that owns the repositories.
3. Under **Repository access**, pick **Only select repositories** and choose the ones you'll sync to.
4. Under **Repository permissions**, set **Secrets** to **Read and write**. To sync to GitHub environments, also set **Environments** to **Read and write**.
5. Generate the token and copy it. If the organization requires approval for fine-grained tokens, have an owner approve it.
6. In envpass, open **Integrations**, click **Connect GitHub**, paste the token, click **Check token**, then **Save connection**.

## Add a sync

1. Open the project's **Syncs** tab and click **Add sync**.
2. Pick the **Connection** and the envpass **Environment** to copy.
3. Pick the destination:
   - **Vercel:** the **Vercel project** and **Vercel environment** (Production, Preview or Development). For Preview, you can enter a **Git branch**. Leave it empty to set values for every preview branch.
   - **GitHub:** the **Repository** and the **Secrets scope**: repository secrets, or one of the repository's GitHub environments. Create the environment in GitHub first (**Settings → Environments**).
4. Click **Add sync**. The first push starts right away.

One connection can't have two syncs writing to the same destination.

## What gets pushed

A sync pushes the environment's team values, the same set a service token gets. Personal overrides are never pushed.

It runs:

- when you add or resume it, or click **Sync now**
- a couple of seconds after any change to team values in that project environment: edits, deletes, restores, renames, rollbacks, imports and promotes. Changes made close together go out in one run.

Each run only sends keys that are new, changed in envpass since the last push, or missing at the destination. Every run is recorded in the audit log as a read by the person who set up the sync.

### Vercel

- New variables are created for the chosen environment (and branch) with the type **Sensitive**, or **Encrypted** for Development, which doesn't support Sensitive. Their comment is "Managed by envpass".
- If a variable with the same name already exists for exactly that environment, envpass updates its value and keeps its type.
- Vercel applies new values on the **next deployment**. Redeploy to use them right away.

### GitHub

- Values are encrypted with the repository's (or GitHub environment's) public key before they leave envpass, as GitHub requires.
- Workflows get the new values on their next run, as `${{ secrets.NAME }}`. Jobs that use environment secrets need `environment: <name>`.

## What gets deleted

- When you delete or rename a key in envpass, the sync removes it from the destination, but only if envpass wrote it there.
- Variables at the destination that don't exist in envpass are never touched.
- If a key already existed at the destination when the sync started, envpass overwrites it and manages it from then on: deleting it in envpass deletes it there too.
- Pausing or deleting a sync, or removing its connection, leaves every value at the destination as it is.

## Skipped keys

Some keys are skipped on purpose. The rest of the sync still runs, and the sync lists each skipped key with the reason:

| Reason | What to do |
|---|---|
| `Vercel reserves names starting with VERCEL_` | Rename the key in envpass if your app needs it. |
| `GitHub reserves names starting with GITHUB_` | Same as above. |
| `Shared with other Vercel environments, so envpass leaves it alone` | The Vercel variable applies to more than one environment, for example Production and Preview. Limit it to one environment in Vercel, or delete it and let envpass create it. |
| Any other message from Vercel | Vercel refused that one variable. Fix its name or value in envpass. |

## Status and controls

The **Syncs** tab shows each sync's destination, environment and status: **Queued**, **Syncing**, **In sync**, **Failed** or **Paused**, with the last sync time, how many keys were created, updated and removed, and any skipped keys or error.

From a sync's menu:

- **Sync now** queues a run. It only pushes what changed or is missing.
- **Pause** stops runs on changes. **Resume** turns it back on and runs it once.
- **Delete** stops updating the destination. Values already there stay.

The same is available from the CLI with [`envpass syncs`](/docs/cli#webhooks-and-syncs) and from the API under `/api/v1/workspaces/{workspaceId}/projects/{projectId}/syncs`.

To get told when a sync fails, add a [webhook](/docs/webhooks) for `sync.failed`. It can post to Slack.

## Who a sync runs as

A sync reads values with the permissions of the person who set it up. If that person loses `read` on the environment or leaves the workspace, the sync fails with:

```
The person who set up this sync can no longer read this environment. Delete the sync and set it up again.
```

Delete it and add it again as someone who has access.

## Retries

When Vercel or GitHub is down, rate-limits envpass, or can't be reached, the sync retries with growing gaps, up to 5 attempts over about 7 minutes. Keys pushed before a failure are remembered, so the next run picks up where it stopped.

Errors a retry can't fix, like a rejected token or missing permission, fail straight away. Either way, the status turns to **Failed** and a `sync.failed` event is sent. The next change or **Sync now** tries again.

## Troubleshooting

| Error or symptom | Cause and fix |
|---|---|
| `Vercel rejected the token` or `GitHub rejected the token` | The token expired or was revoked. A connection's token can't be replaced: connect again with a new token, add the syncs again on the new connection, then remove the old connection. |
| `Vercel denied access` | The token's scope doesn't include the team that owns the project. Create a token for that team. |
| `GitHub denied access` | The token lacks **Secrets: Read and write** (or **Environments** for environment secrets), doesn't include the repository, or is waiting for organization approval. |
| `... could not find it` | The Vercel project, repository or GitHub environment was deleted or renamed, or the token can no longer see it. Delete the sync and add it again. |
| `... rate-limited the request`, `returned HTTP 5xx` or `could not be reached` | A problem on the provider's side. envpass retries on its own. If it still fails, click **Sync now** later. |
| `... returned HTTP 4xx: <message>` | The provider refused a request. The message after the colon comes from Vercel or GitHub. Fix what it names, then click **Sync now**. |
| `The person who set up this sync can no longer read this environment` | See [Who a sync runs as](#who-a-sync-runs-as). |
| `Another sync already writes to that destination` | A sync on the same connection already targets it, possibly in another project. Delete that one first, or pick another destination. |
| `Turn the sync on first` | The sync is paused. Resume it, then run it. |
| The Vercel app still has old values | Vercel applies values on the next deployment. Redeploy. |
| A value was edited by hand in Vercel or GitHub | envpass only checks that synced keys exist, not their values, so it won't notice. Change the value in envpass, or delete the variable at the destination and click **Sync now** to push it again. |
| A repository is missing from the list | envpass lists the 100 repositories the token can reach that were updated most recently. Limit the token to the repositories you sync. |
| A GitHub environment is missing from the list | Create it in the repository under **Settings → Environments**, and make sure the token has the **Environments** permission. |

## Limits

| Resource | Limit |
|---|---|
| Connections per workspace | 10 |
| Syncs per project | 20 |
| Attempts per run | 5 |
