Use promo code BETATEST1 for full access
seekrit
Docs/Devin

Devin

Devin works inside a Linux VM that boots from a snapshot: one frozen image per organization, with your repos cloned and your toolchain installed. Credentials do not live in that image — Devin injects its Secrets as environment variables before every build step and again at the start of every session.

That injection is the thing to think about. Every key you add to Devin's secret store becomes a long-lived credential sitting in the environment of an autonomous agent that writes and runs code, in every session your organization starts. Rotating one means editing it in Devin and hoping nothing cached it; narrowing one is not possible at all, because the store has no notion of which session may see which key.

The seekrit shape is one value in Devin's Secrets — a service token bound to one environment — and everything else resolved at the moment of use.

Keys in Devin's SecretsOne seekrit token
What the VM holdsEvery key, for the whole sessionA credential that resolves one environment
NarrowingNone — org-wideThe environment's contents, composed to taste
RevokeEdit each secret, per keyseekrit token revoke, once
Rotate an upstream keyDevin's store, by handRotation; Devin's env is unchanged
Record of useNoneAudit trail

1. Scope an environment for Devin

Do not point the token at production. A service token resolves everything its environment can see, so the environment is the blast radius. Make one that holds what a coding agent legitimately needs — a test database, a sandbox model key — and nothing else:

seekrit env create --app storefront --name "Devin" --slug devin
seekrit secrets set DATABASE_URL --env devin --app storefront   # value on stdin
seekrit token create --name devin --app storefront --env devin
# prints skt_… once

Composing a group is the durable version of the same narrowing: a devin environment that composes only the group holding sandbox keys cannot leak a production credential, because it never had one.

2. Add the token to Devin's Secrets

In Devin, go to Settings → Secrets, add a raw secret at organization scope, and name it SEEKRIT_TOKEN.

note

Devin mangles secret names into valid shell identifiers — anything that is not a letter, digit, or underscore becomes an underscore, and a leading digit gets an underscore prefix. SEEKRIT_TOKEN survives that untouched, which is why it is the name to use.

Secrets reach only sessions created after you add them. After adding or rotating the token, start a new session rather than wondering why the current one still fails.

Personal-scope secrets are scoped to you, and secrets supplied inside a conversation reach only that session. Org scope is what you want here: one token, every session, revocable in one place.

3. Install the CLI in the blueprint

Devin's environment is declared as a YAML blueprint. Put the install in initialize, which runs when the snapshot is built:

initialize:
  - name: "Install seekrit"
    run: npm install -g @seekrit/cli

No Node in the image? The seekrit-run launcher is a single static binary with no runtime at all — it is service-token only, which is exactly what Devin has, and the command below becomes seekrit-run --:

initialize:
  - name: "Install seekrit-run"
    run: curl -fsSL https://run.seekrit.dev/install.sh | sh
caution

Never resolve secrets during a build step. The snapshot is a frozen image that every future session boots from, so anything a build step writes is baked into it — seekrit export > .env, or the $ENVRC pattern (seekrit export >> $ENVRC), puts plaintext values into an image shared by your whole organization, where rotating the underlying key changes nothing.

Devin's own secrets are safe in the blueprint precisely because they are injected at each step rather than written down. Keep seekrit's the same way: resolve at the moment of use, inside seekrit run.

4. Teach Devin to run commands through it

Devin reads the blueprint's knowledge entries to learn how to run your project. That is the right place to make injection the default path, so Devin never has a reason to go looking for a key:

knowledge:
  - name: test
    contents: seekrit run -- pnpm test
  - name: dev
    contents: seekrit run -- pnpm dev
  - name: migrate
    contents: seekrit run -- pnpm db:migrate

seekrit run resolves the environment the token is bound to, injects it into the child process, and exits with the command's status. The values exist for the life of that process — not in a file, not in ~/.bashrc, not in Devin's shell history.

Worth adding to your repo's Devin instructions in as many words: never echo, printenv, or commit a secret value; run the command through seekrit run instead. Devin will otherwise print an environment variable to debug a failure, and a printed value is a value in the session transcript.

# In a session, when something looks misconfigured:
seekrit doctor          # checks the silent failures; never prints values
seekrit secrets list    # names and versions, no values

5. Give Devin the MCP servers

Devin manages MCP servers under Customize → MCPs, at personal, organization, or enterprise scope, over stdio, HTTP, or SSE — which maps onto seekrit's two servers exactly.

Local crypto plane (stdio) — everything that touches a secret value:

FieldValue
Commandnpx
Arguments-y @seekrit/mcp
Environment variablesSEEKRIT_TOKEN = the same token

Hosted metadata plane (HTTP) — structure, audit, and keyless management, which can never decrypt:

FieldValue
Server URLhttps://mcp.seekrit.dev/mcp
AuthenticationAuth Header — Authorization: Basic <base64(clientId:clientSecret)>

The hosted server refuses skt_ tokens outright, so it needs machine credentials rather than the service token. At organization scope, Devin shares one authenticated connection across your team — use a credential minted for that purpose, not your own.

The reason to bother is run_command: it resolves, injects, runs, and returns only output and an exit code, so Devin can use a secret without its value entering the conversation. get_secret reveals a plaintext only when explicitly asked to.

note

The local server runs on Devin's machine, so decryption still happens next to the credential, and the hosted server still sees only ciphertext. The zero-knowledge boundary is unchanged by any of this — what changes is who is holding the credential, and Devin's VM is a machine you should treat as one more principal with a scoped grant.

Keeping the values out of the VM entirely

A token in Devin's environment still means Devin can read everything that environment holds. When the credential matters more than the convenience — a payment key, anything with a spend — hand the workload a placeholder and let seekrit-proxy swap in the real value on the way out.

Run it as a sidecar inside Devin's machine, started by the same script that starts your app:

seekrit proxy run --preset openai --preset anthropic &

Each preset prints the environment the app needs — a placeholder where the key used to be, and a base URL pointing at the proxy:

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

Those two are safe to put in Devin's Secrets, which is the nice part: the value in Devin's store is now a string that unlocks nothing anywhere. The real credential exists only inside the proxy process, for the moment of the request, and the preset's methods/paths bound what it can be spent on even if the agent tries something else with it.

caution

Be clear-eyed about what a sidecar buys you. Same machine, same user: this bounds what the credential can do and keeps values out of logs, commits, and the model's context — it is not a wall against an agent that goes looking on that box. A proxy on infrastructure you control is the real boundary, and it is only available if Devin's VM can already reach your network. Its data plane has no client authentication, so never expose one to the internet: put it on a private network, exactly as the proxy guide describes.

Rotate and revoke

Neither of these touches the snapshot:

seekrit token revoke skt_…
seekrit token create --name devin --app storefront --env devin

Paste the new token over the old one in Devin's Secrets; sessions started after that pick it up. The upstream keys inside the environment rotate independently — see rotation — and Devin's configuration never mentions them, so nothing there needs editing when they change.

seekrit audit shows what the org's principals did, and seekrit audit actions lists the vocabulary to filter on.

The Devin CLI, locally

Devin also ships a CLI that runs on your own machine. There, seekrit is the ordinary local setup — seekrit login for yourself, and the same two MCP servers added by hand:

devin mcp add seekrit-local -- npx -y @seekrit/mcp
devin mcp add seekrit --url https://mcp.seekrit.dev/mcp

Those write .devin/mcp_config.json (project) or ~/.config/devin/mcp_config.json (user) in the familiar mcpServers shape, so a project-scoped entry can be committed. Keep credentials out of the committed one: mcp_config.local.json is the gitignored sibling, and the local server reads SEEKRIT_TOKEN from the environment it inherits.

See also