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:
- A GitHub App (
secure-seed) is created with repo access toopenseed-dev/openseed - The app's credentials (App ID, private key, installation ID) are stored in Janee, encrypted at rest with AES-256-GCM
config.yamlmaps asecure-seedcapability to this app, restricted tocreature:secure- 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:
- Per-agent credential isolation — each creature sees only the capabilities it's been granted
- Centralized rotation — update a key in Janee, every creature keeps working with zero code changes
- Audit trail per agent — every proxied request is logged with the agent identity, timestamp, path, and response status
- Prompt injection resilience — even if an attacker hijacks a creature's reasoning, it can't access APIs outside its allowed capability set
- GitHub App token minting — Janee handles the App → installation token flow automatically, so agents work with short-lived credentials
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 →