How OpenSeed Secures Its Agents with Janee

February 2026 · 7 min read

OpenSeed is an autonomous agent platform where AI "creatures" run independently — auditing code for security issues, committing patches, and interacting with external services. Each creature needs API access to do real work. None of them should hold the keys.

Here's how OpenSeed uses Janee to give each agent exactly the API access it needs, with full isolation, audit logging, and zero credential exposure.

The problem: multiple agents, shared secrets

OpenSeed runs multiple creatures concurrently. One files GitHub issues. Another commits code. Future creatures will need Stripe, analytics, and other APIs.

The obvious approach — inject API keys as environment variables — breaks down fast at this scale. Every creature would see every key. There's no audit trail per agent. And a single prompt injection could exfiltrate credentials in one tool call.

They needed per-agent isolation with centralized credential management. That's what Janee provides.

Architecture: Janee as a shared credential proxy

OpenSeed's orchestrator spawns Janee once as a child process in HTTP mode. Each creature connects over MCP and gets its own isolated session:

┌──────────┐     MCP/HTTP    ┌────────┐    real creds   ┌──────────┐
│ Creature │ ──────────────> │ Janee  │ ──────────────> │ External │
│          │                 │        │   proxied req   │   API    │
└──────────┘                 └────────┘                 └──────────┘
   no keys              encrypted at rest               GitHub, etc.

Janee creates a fresh Server and Transport instance per MCP initialize handshake, following the official MCP SDK pattern. Creature A's session state, identity, and access decisions are completely isolated from Creature B's. No shared state, no cross-talk.

Agent identity via MCP — no custom plumbing

With multiple agents hitting the same Janee instance, identity matters. Which creature is making this request?

Early prototypes used custom HTTP headers (X-Agent-ID), but that's security theater — any client can set any header. OpenSeed landed on something simpler: the MCP protocol's built-in initialize handshake, where clients send clientInfo.name.

Each creature sets this to creature:{name} when it opens a session. Janee captures it from the transport layer — not from tool arguments the client controls:

const transport = new StreamableHTTPClientTransport(url);
await client.connect(transport);
// clientInfo.name = "creature:secure" sent during initialize

This works identically across stdio, HTTP, and in-memory transports. No extra headers, no extra arguments. Just MCP.

Least privilege by default

With identity resolved, access control is a config file away. In OpenSeed's ~/.janee/config.yaml:

server:
  defaultAccess: restricted

capabilities:
  secure-seed:
    service: secure-seed
    allowedAgents: ["creature:secure"]
    autoApprove: true

defaultAccess: restricted means capabilities without an explicit allowedAgents list are hidden from all agents. The secure-seed capability — backed by a GitHub App with repo access to openseed-dev/openseed — is only visible to creature:secure. Other creatures calling list_services won't even know it exists.

If a creature creates a credential at runtime via Janee's manage_credential tool, it defaults to agent-only — only the creating agent can use it. Isolation is the default, not an opt-in.

The real flow: filing a GitHub issue

OpenSeed's "Secure" creature audits the codebase for security issues. When it finds something, it needs to create a GitHub issue — which requires authenticating as a GitHub App installation. Here's the end-to-end flow:

  1. A GitHub App (secure-seed) is created with repo access to openseed-dev/openseed
  2. The app's credentials (App ID, private key, installation ID) are stored in Janee, encrypted at rest with AES-256-GCM
  3. config.yaml maps a secure-seed capability to this app, restricted to creature:secure
  4. When Secure finds an issue, it calls execute:
await janee({
  action: 'execute',
  capability: 'secure-seed',
  method: 'POST',
  path: '/repos/openseed-dev/openseed/issues',
  body: JSON.stringify({
    title: 'Security finding',
    body: '...'
  })
});

Janee looks up the capability, decrypts the GitHub App private key, mints a short-lived installation token (1 hour TTL), injects it into the request, and proxies to GitHub. The creature never touches the key. Janee logs the request. If something goes wrong, you revoke access in one place.

What this pattern enables

By putting Janee between agents and APIs, OpenSeed gets properties that would be painful to build from scratch:

Getting started with the same pattern

If you're building a multi-agent system and want the same setup:

npm install -g @true-and-useful/janee
janee init
janee add my-service         # store credentials
janee serve --http 3456      # start in HTTP mode for multi-agent

Set defaultAccess: restricted in your config, define capabilities with allowedAgents, and have each agent identify itself via clientInfo.name during the MCP handshake. That's it — per-agent isolation with no custom auth code.

Try Janee

Give your AI agents secure, scoped API access — without exposing your secrets.

View on GitHub →