seekrit
Docs/AI frameworks

AI frameworks

Every agent framework draws the same line: orchestration in, credential management out. LangGraph, Mastra, CrewAI, Pydantic AI, both Agents SDKs — none of them wants to be where your provider keys live, and all of them hand the question back to you.

This page answers it twice: the short version, which is three commands and no code, and then what to add as the agent grows teeth. Start at the top. Nothing below the first section is required to have a working agent, and plenty of projects never need any of it.

Get running in five minutes

You need the CLI and an account:

npm install -g @seekrit/cli
seekrit login

None of the three steps below is framework-specific. If you already keep an app's config in seekrit, skip to step 2.

1. Put the keys in an environment

An environment is the thing that holds secrets — one per app, per stage, like storefront/development. Everything in it travels together, so put the agent's provider keys in the environment its process should read. If they're in a .env file today, that's one command:

seekrit secrets import .env --app storefront --env development

Or set them one at a time, or paste them into the dashboard:

seekrit secrets set OPENAI_API_KEY sk-… --app storefront --env development
seekrit secrets set TAVILY_API_KEY tvly-… --app storefront --env development

No application yet? seekrit app create --name Storefront --slug storefront, or create one in the dashboard and its environments come with it. The quickstart walks through a first-time account (keys, passphrase, first secret) in more detail.

2. Mint a token for the agent

A service token is the credential a machine uses to read that environment. It is bound to one environment when you create it:

seekrit token create --name agent-dev --app storefront --env development
# prints once:  skt_XXXXXXXX_…

Copy it now — the value is shown once and cannot be retrieved later. In the dashboard the same thing is Organization → Service tokens → Mint token, or the no token badge on an environment column, which pre-fills the binding.

The token carries its own private key, so it decrypts on its own: no passphrase, no config file, nothing else to set up. See Service tokens.

3. Run the agent through the CLI

export SEEKRIT_TOKEN=skt_XXXXXXXX_…

seekrit run -- python agent.py       # or: mastra dev, langgraph dev, pnpm dev…

seekrit run resolves the token's environment, decrypts locally, and injects every value as an environment variable in the child process only — nothing on disk, nothing in shell history, nothing for a coding agent to find in a file later. Your framework picks the keys up exactly where it already looks: OPENAI_API_KEY, ANTHROPIC_API_KEY, TAVILY_API_KEY, your database URL.

To see what the agent will get, without printing any values:

seekrit export --format dotenv | cut -d= -f1
note

That is the whole integration. No SDK, no import, no code change — you are not integrating with seekrit so much as declining to write a .env file. Delete the old one once the import worked: seekrit run still overlays a .env if one is there, and a file that exists is a file an agent can still read.

For containers and CI, seekrit-run is the same behaviour as a static binary with no Node dependency.

If it didn't work

SymptomCause
The framework says no API key was foundThe key isn't in the environment the token is bound to. seekrit export --format dotenv | cut -d= -f1 lists what the token actually resolves; seekrit whoami prints the org/app/env it is bound to
The command runs but reads an old valueSomething above the managed layer is winning. Precedence is group < app-env < .env file < process env, so a stale .env or an exported variable overrides seekrit. seekrit run --explain names the source layer of every variable, without printing values
seekrit run warns and the process starts anywayResolving is best-effort by design — a missing token, an unreachable API, or a decryption failure logs to stderr and still launches the command with whatever .env and process.env provide. seekrit export is the strict version: it errors instead
A value is right in the dashboard, empty in the processThe name has a typo, or the secret exists in a different environment of the same app. seekrit secrets list --app <app> --env <env> shows names per environment

Which secrets does the agent get?

All of them, from the one environment its token is bound to — plus the slices of any group composed into that environment. There is no per-key selection step on the seekrit side, and nothing to attach per framework or per tool. The token's binding is the selection.

Two consequences worth knowing up front:

  • A secret you add later just shows up. The next seekrit run resolves the environment as it stands now. You don't re-mint or re-grant anything to give an agent a new key — put it in the environment and restart the process.
  • A token is a decryption credential for that whole environment. If it shouldn't hold everything in storefront/development, the fix is a narrower binding, not a narrower list of keys.

So the coarse control — the one the server enforces — is which environment:

You wantDo this
The agent to read only some of an app's keysGive the agent its own environment (seekrit env create --app storefront --name "Agent dev" --slug agent-dev) and bind its token there
Two agents with different keysA token each, bound to different environments
Shared keys plus per-agent onesKeep the shared ones in a group and compose it into each agent's environment
A key that works for one API and nowhere elseThat's not a visibility question — see hold a placeholder
One tool to spend a key the others can'tSame — see scope a key to one tool

The last two rows are the important distinction. Everything in this section is about what a token can read. The levels further down are about where a value is allowed to go once something can read it, which is a separate gate, lives in your own config, and is optional.

Where the seekrit token lives

One credential in, all the others out — so the remaining question is where it goes. That depends only on where the agent runs, not on the framework:

The agent runs…Authenticate with
On your own machineseekrit login. The session lives in ~/.config/seekrit/config.json, so there's no token in your environment at all. A session isn't bound to an environment, so pass --app and --env to run/export, and expect a passphrase prompt (SEEKRIT_PASSPHRASE to skip it)
On a deployed server or containerSEEKRIT_TOKEN as a platform environment variable — the one variable your platform holds. Or hold nothing: third-party sync pushes the values into the platform's own environment instead
In CISEEKRIT_TOKEN as a repository or organization secret — see CI/CD & containers
In an agent sandboxUsually neither: resolve outside the sandbox and inject at create, or give the sandbox placeholders and a proxy endpoint. See Agent sandboxes
Unattended, provisioning its own structureSEEKRIT_CLIENT_ID / SEEKRIT_CLIENT_SECRET — machine credentials that mint and cache an admin token automatically. See the MCP server
caution

seekrit run passes its own environment through to the child. A SEEKRIT_TOKEN you exported in your shell is therefore visible to the agent too — and a token decrypts everything its environment holds, not just what you injected. Fine while you're trying this out; on any machine where the agent can run shell commands, use seekrit login instead so there's nothing in the environment for it to find.

Then tighten it, when you need to

Four levels, in order of how little your agent's process ends up holding. Each one costs a bit more setup than the last, so take them when a specific thing worries you — not up front.

Wrap the process — where you already are

seekrit run -- <your dev command>. Zero code, every framework, every language. The values are real environment variables inside the child, which means anything the process can run can also read them.

Good enough when the process runs only code you wrote.

Resolve in code

import seekrit
seekrit.Client().into_env()      # or: secrets = seekrit.Client().resolve()

Three lines at startup with the SDKsPython, JS/TS, Go, Ruby.

Reach for it when there's no wrapper to hang seekrit run off (a serverless handler, a Worker, a notebook), or when different requests need different credentials — a multi-tenant agent resolving per tenant rather than per process.

Hold a placeholder, not a key

ChatOpenAI(base_url="http://127.0.0.1:8080/openai/v1", api_key="{{seekrit:OPENAI_API_KEY}}")

The egress proxy holds the credential; your process holds {{seekrit:OPENAI_API_KEY}}. On the way out the proxy swaps in the real value — and only toward the hosts, methods, and paths you allowlisted, default-deny. Anything in an environment variable can be printed, logged, or POSTed somewhere else; a placeholder cannot.

Reach for it when the code holding the credential is not code you trust: a sandbox, model-generated code, an agent whose next action you cannot predict.

Often still zero-code. OPENAI_BASE_URL is a de facto standard — Pydantic AI, CrewAI, and the OpenAI Agents SDK all read it, and Claude Code and the Claude Agent SDK take ANTHROPIC_BASE_URL. Where a framework honours it, this level is two environment variables and no source change:

export OPENAI_BASE_URL=http://127.0.0.1:8080/openai/v1
export OPENAI_API_KEY='{{seekrit:OPENAI_API_KEY}}'

Neither of those is a secret, so they belong in your compose file or Procfile, not in seekrit.

And often no proxy either. The SDKs ship the same substitution engine as a fetch wrapper and an httpx transport, so your code can hold the placeholder without a sidecar — see in-process injection. Weaker than the proxy, because it runs in the same process as the code holding the placeholder; much stronger than an environment variable.

Scope a key to one tool

A provider key buys tokens. A tool's Stripe or GitHub key does something irreversible. The framework adapters narrow an allowlist to a single tool call, so a prompt-injected search cannot reach a payment credential:

Reach for it when one tool in an agent can spend money or change state and the others have no business touching that credential.

What to add, and when

If…Add
You just don't want a .env fileNothing — wrapping the process is the answer
It's a Worker, Lambda, or notebook with no wrapperResolve in code
Each request serves a different tenantResolve per run, not per process
The process runs code you didn't writeA placeholder, via the proxy
A key must work for one operation but not othersA placeholder, with methods and paths on the route
You want a placeholder but not a sidecarIn-process injection
One tool may spend a key and the others may notPer-tool scoping
The agent must not hold the seekrit token eitherRun the proxy beside it, or resolve outside its process entirely
You want several of theseNothing conflicts; the proxy just sees a placeholder where a key used to be

Per framework

Each page shows the same levels in that framework's own idiom — the actual constructor arguments, the actual dev command.

FrameworkStart withWorth knowing
LangGraph / LangChainPythonWrapPer-tool scoping via LangChain 1.x middleware
MastraTypeScriptWrapmodel takes a function of the request — per-tenant keys are first-class
Pydantic AIPythonResolve in codedeps is a real per-run seam; a toolset scopes one tool
AI SDKTypeScriptWrap locally, resolve on serverlessfetch is a first-class provider option
OpenAI Agents SDKPythonWrapTracing is a second credential and a second egress host
Claude Agent SDKPython / TSWrapIt deliberately ignores .env, so wrapping is the only zero-code path
CrewAIPythonWrapThe crew's tool keys are the ones to move to a placeholder
LlamaIndexPythonWrapIngestion pipelines hold their credential for a long run

Not listed doesn't mean not supported: wrapping the process needs nothing from the framework at all. If a framework starts a process, seekrit run goes in front of it.

Agents that manage their own secrets

The pages above are for you wiring up an agent. If the agent itself should store and inject credentials — a coding agent setting up a project, say — install the agent plugin instead. It carries both MCP servers and the skills that keep values out of files.