seekrit
Docs/LangGraph Platform

Sync to LangGraph Platform

LangGraph Platform runs your Agent Server for you, which means there is no container to inject into: the deployment's secrets are its environment, and the control plane is the only way to set them. A binding owns one deployment's secrets.

At a glance
What seekrit writesA deployment's secrets, delivered to the agent container as environment variables
Addressed byThe deployment UUID
Connection carriesThe region (or a self-hosted control-plane URL), and a workspace ID for org-scoped keys
Token permissionA LangSmith API key that can update the deployment
Takes effectOn the new revision the write creates — a rebuild and rollout
Value visibilityReadable through the control plane by anyone with a key for the workspace

New to sync? Read Third-party sync first — the decryption grant, name mapping, deletions, and failure handling are the same on every destination.

caution

A push redeploys the agent. LangGraph Platform applies a secret change by creating a new revision, and its control plane offers no way to stage a value without shipping it. In-flight runs are interrupted.

seekrit keeps that to the minimum it can: it reads the deployment first and sends nothing when nothing would change, so the periodic reconcile never rolls your agent on its own. But a rotation does. If the agent runs somewhere you control the process instead, seekrit run injects the same values with no copy at the platform and no redeploy.

1. Create a LangSmith API key

In LangSmith, Settings → API keys → Create API key. Prefer a workspace-scoped key (lsv2_pt_…): it names its own workspace, so the connection needs nothing else. An organization-scoped key reaches every workspace in the org, and without a workspace ID the control plane refuses it with a bare 403.

printf '%s' "$LANGSMITH_API_KEY" \
  | seekrit sync connect --name acme-langgraph --provider langgraph-platform

That is the whole connection for a US account. Two things can change it:

# An account in another region — chosen at signup and fixed afterwards.
printf '%s' "$LANGSMITH_API_KEY" \
  | seekrit sync connect --name acme-langgraph --provider langgraph-platform \
      --langgraph-region eu

# An organization-scoped key, which has to say which workspace.
printf '%s' "$LANGSMITH_API_KEY" \
  | seekrit sync connect --name acme-langgraph --provider langgraph-platform \
      --langgraph-tenant 8f1c2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d

The region matters more than it looks. LangChain runs the control plane on four hosts, a key minted in one is not accepted by another, and the failure is a plain 401 — so the wrong region here looks exactly like a bad credential:

--langgraph-regionControl-plane host
us (default)https://api.host.langchain.com
euhttps://eu.api.host.langchain.com
apachttps://apac.api.host.langchain.com
aws-ushttps://aws.api.host.langchain.com

An account created at smith.langchain.com is us.

note

Self-hosted LangSmith. Pass --base-url instead of --langgraph-region, pointing at the control plane on your own host — it is served under /api-host, e.g. https://langsmith.acme.com/api-host. It must be https:, because that address carries the API key. Setting both a region and a base URL is refused rather than resolved in seekrit's favour.

2. Bind an environment

A deployment has one set of secrets, so the deployment is the whole destination — there is no per-revision or per-graph scope, and no equivalent of Vercel's production/preview split. A deployment that needs different values is a different binding.

seekrit sync verify acme-langgraph --provider langgraph-platform \
  --langgraph-deployment 3970e0fe-8564-4903-9a55-c5f8de49fb8b

seekrit sync enable --connection acme-langgraph --provider langgraph-platform \
  --langgraph-deployment 3970e0fe-8564-4903-9a55-c5f8de49fb8b \
  --app storefront-agent --env production --acknowledge-decryption

--langgraph-deployment is the deployment UUID from its dashboard URL, or the id from GET /v2/deployments; --app is the seekrit application the environment belongs to.

Verify checks the deployment through its revision list rather than by fetching the deployment. Both prove the same three things you can get wrong from the connection dialog — key valid, workspace reachable, deployment real — but a revision carries only ids, timestamps, status and source, never a value. A deployment whose first revision is still building answers with an empty list, which passes: the deployment is there, and that is the question.

How a push behaves

  • It reads before it writes. The array sent is the array the deployment ends up with, so seekrit reads the current secrets and writes back (existing − removed) ∪ seekrit. Sending only seekrit's names would delete every environment variable on the deployment that seekrit does not manage.
  • Values you set by hand survive. A LOG_LEVEL added in the dashboard, or another team's key, is carried through a push exactly as it was read.
  • A run that would change nothing sends nothing. This is the one that matters here: without it, the reconcile timer would build a new revision of your agent every few minutes forever.
  • Only secrets is ever sent. A PATCH carrying source_config, source_revision_config or revision_source would decide which code the new revision builds. Omitting them lets the control plane fall back to the deployment's own source, so a secrets push can never be the thing that ships new application code. secret_references — Kubernetes Secret references on self-hosted installs — is never sent either.
  • A rejected write is blamed on every name in it, which is honest here: the control plane validates the array before applying it, so a rejected PATCH leaves the deployment exactly as it was and nothing landed.
  • A value the control plane echoes back in an error is scrubbed before the reason is stored on the run row — including a foreign value the array carried through.

Name and value rules

note

LangGraph Platform will not accept every name seekrit will. It reserves the names it sets itself — LANGSMITH_API_KEY, LANGCHAIN_PROJECT, POSTGRES_URI, REDIS_URI, PATH, PORT and around thirty more. seekrit skips a reserved name rather than sending it, and reports it as a failure against that name alone, because the whole run is one request: one unacceptable name would otherwise take the entire environment down with it. Watch for it if you use a --prefix.

A secret with an empty value is failed the same way. The control plane drops one rather than storing it, so a sent empty does not come back on the next read — and seekrit's "nothing changed" check would never agree, which would rebuild your agent on every reconcile tick for as long as the binding existed. Failing the one name is the cheap end of that trade.

Troubleshooting

SymptomCauseFix
401 on every nameThe key was revoked — or the connection names the wrong region, which fails identicallyCheck the region against the table above, then re-create the connection
403An organization-scoped key with no workspace ID, or a key without update permissionAdd --langgraph-tenant, or use a workspace-scoped key that can update the deployment
404 on the deploymentThe deployment name in the ID slot, or the deployment was deletedUse the UUID from the dashboard URL
One name failed, rest landedThe mapped name is reserved by LangGraph, or its value is emptyRename it (or fix the prefix); seekrit refuses to send it rather than fail the run
Every name reported failed at onceThe single PATCH was rejectedNothing landed — the control plane validates before applying. Fix the cause and re-run
The agent redeploys on every syncA value really is changing every run — an interpolated reference, or a rotation scheduleCheck the run ledger for which name; a stable environment sends no request at all
A run fails with "refusing to write"The control plane returned masked secret values, so writing them back would overwrite real onesPlease report it — the connector needs updating, and it fails loudly rather than destroying your environment

See also