seekrit
Docs/Third-party sync (Vercel)

Third-party sync

Most ways of getting secrets into a running app keep decryption on your side: seekrit run decrypts in your process, the egress proxy decrypts in your proxy, the SDKs decrypt in your code, and ESO decrypts in your cluster.

Some platforms don't let you run anything before your app starts. A Vercel build reads environment variables that Vercel already holds; there is no earlier point to inject them. Third-party sync is for exactly that case: seekrit stays the source of truth, and pushes the environment out whenever it changes.

Choose the right tool first

If you…UseWho decrypts
control the process (CI job, container, server)seekrit runyour machine
have untrusted or agent workloadsegress proxyyour proxy
run KubernetesESO chartyour cluster
are writing the appa language SDKyour process
don't control the runtime (Vercel build env, a managed platform's env vars)syncseekrit's sync engine

Sync is the last row, and it is the only feature in seekrit where our servers decrypt anything. Reach for it when the rows above don't apply.

caution

Enabling sync lets seekrit decrypt that environment. To push a value to Vercel, something has to hold it in the clear, and sync runs when nobody is logged in — so the sync engine must be able to decrypt on its own.

This applies only to environments you explicitly enable it for, and only to the destination you named. Everything else stays zero-knowledge: seekrit cannot decrypt an environment that has no sync grant, and enabling one requires a key holder, so it can't be switched on server-side.

How it works

Enabling sync creates two things:

  1. A connection — the destination account (a Vercel token). The token is encrypted in your browser to a public key held by the sync engine, so the control plane stores ciphertext it cannot open.
  2. A binding — one environment → one destination. Creating it also creates a key grant: the environment's data key, wrapped in your browser to that same public key. This grant is what authorizes decryption, and it is an ordinary key grant — visible alongside your users and service tokens, and revocable the same way.
secret write ─▶ seekrit API ─▶ sync engine ──▶ Vercel project env
                               (decrypts here, only with a grant)

The private half of that keypair lives inside the sync engine and is never written to the database. Deleting the connection destroys it, which turns every grant made to it into ciphertext nobody can open — including us.

Set up a Vercel connection

1. Create a Vercel token

In Vercel, go to Settings → Tokens and create a token with access to the project you want to sync into. If the project belongs to a Vercel Team, note the team id (team_…) — a personal-scope token can't write to a team project, and Vercel reports that as a bare 403.

2. Add the connection

In the dashboard, open Sync → Add connection, choose Vercel, paste the token, and set the team id if you have one. The token is encrypted in your browser before it is sent.

Or from the CLI:

seekrit sync connections add vercel --name "acme-production" --team-id team_abc

3. Bind an environment

Pick the application environment to sync and the Vercel project and targets (production, preview, development). This is the step that shows the decryption disclosure and creates the key grant, so it has to be done by someone who can already read the environment.

seekrit sync enable acme/storefront/production \
  --to acme-production \
  --project prj_abc \
  --target production

seekrit pushes once immediately, then on every change.

Naming and filtering

By default, secret names are pushed verbatim. A binding can adjust that:

  • Prefix / suffix / case — e.g. prefix NEXT_PUBLIC_ or force upper case.
  • Rename — map individual names exactly. An explicit rename is used verbatim; prefix and case are not applied on top of it.
  • Include / exclude — glob allow/deny lists (DB_*, *_PASSWORD). Exclusion always wins over inclusion.

If two secrets would end up with the same destination name, the run fails and tells you which two. seekrit will not silently let one value shadow another.

Secret references

${OTHER_SECRET} references are expanded before the push, the same way seekrit run and the SDKs expand them. The destination receives the final value, not the reference — pushing the raw stored text would put a literal ${OTHER_SECRET} into your Vercel project.

A reference to a name that doesn't exist is left as written (so CI templating like ${GITHUB_SHA} passes through untouched). A reference cycle fails the run, because no value would be correct to push.

Composed environments

Sync pushes the effective environment — what your app actually resolves, including every composed group. That means the connection needs a grant on each composed group environment too, not only the application environment. The dashboard walks you through granting all of them; if one is missing, the run fails with no key grant for group "…" rather than pushing a partial environment.

Sync targets application environments. To sync a shared group, bind the application environments that compose it.

Branch environments

A branch environment can be synced like any other, and the pairing is a natural one: point a branch at a Vercel preview target with its git branch set, and each PR gets its own overlay downstream.

Two things follow from how branches inherit:

  • A write to the parent re-syncs its branches too, since a branch supplies only the values that differ.
  • When a branch expires, its binding goes with it and seekrit stops pushing. Values already at the destination are left alone — expiry removes the branch, not the copy Vercel is holding. Delete those in Vercel if you need them gone.

Deletions

A binding's onDelete policy decides what happens when a secret disappears from seekrit:

  • delete (default) — remove it at the destination too, so the destination is a true mirror.
  • retain — leave it. Use this when something else also writes to that project and seekrit is not the only source.

Only variables whose Vercel targets overlap the binding's are ever removed, so two bindings can point at one project with different targets without stepping on each other.

When a sync fails

Runs are recorded with per-name outcomes. A partial run is normal — the Vercel API is not transactional, so some names can land while others fail — and only the failures are retried.

Retries back off exponentially. After five consecutive failures the binding stops and org admins get an email, because the destination is now serving stale values and nothing downstream will say so on its own. Fix the cause, then re-enable the binding or use Sync now.

Two failures worth recognizing:

  • no key grant for … — the grant was revoked, or the environment now composes a group the connection can't read. Re-enable sync for that environment.
  • A Vercel 403 — the token lost access, or the project is team-owned and the connection has no team id.

Turning it off

Deleting a binding revokes that environment's grant to the connection (unless another binding still needs it). Deleting the connection revokes all of its grants and destroys its keypair.

Either way the values already at the destination stay there — seekrit stops updating them, it does not reach in and clean up. Remove them in Vercel if you want them gone.