# Devin

[Devin](https://docs.devin.ai) 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 Secrets | One seekrit token |
| --- | --- | --- |
| **What the VM holds** | Every key, for the whole session | A credential that resolves one environment |
| **Narrowing** | None — org-wide | The environment's contents, [composed](/docs/guides/environments) to taste |
| **Revoke** | Edit each secret, per key | `seekrit token revoke`, once |
| **Rotate an upstream key** | Devin's store, by hand | [Rotation](/docs/guides/rotation); Devin's env is unchanged |
| **Record of use** | None | [Audit trail](/docs/reference/cli#seekrit-audit) |

## 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:

```bash
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](/docs/guides/environments) 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:

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

No Node in the image? The [`seekrit-run` launcher](/docs/guides/run) 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 --`:

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

> **Warning:** **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:

```yaml
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.

```bash
# 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](/docs/guides/ai-agents) exactly.

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

| Field | Value |
| --- | --- |
| Command | `npx` |
| Arguments | `-y @seekrit/mcp` |
| Environment variables | `SEEKRIT_TOKEN` = the same token |

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

| Field | Value |
| --- | --- |
| Server URL | `https://mcp.seekrit.dev/mcp` |
| Authentication | Auth Header — `Authorization: Basic <base64(clientId:clientSecret)>` |

The hosted server refuses `skt_` tokens outright, so it needs
[machine credentials](/docs/guides/ai-agents#3-reconnect-with-the-credential)
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`](/docs/guides/agent-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:

```bash
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:

```bash
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.

> **Warning:** 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](/docs/guides/agent-proxy) describes.

## Rotate and revoke

Neither of these touches the snapshot:

```bash
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](/docs/guides/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:

```bash
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

- [AI agents](/docs/guides/ai-agents) — the two MCP servers, and why they are split
- [Agent sandboxes](/docs/guides/sandboxes) — the same problem when *your* code starts the VM
- [Credential broker](/docs/guides/agent-proxy) — placeholders instead of keys
- [Service tokens](/docs/guides/service-tokens) — scoping, granting, and revoking machine credentials
