Canopy is a governed runtime for AI agents. It runs the model loop, connects models and tools from anywhere, enforces exactly what agents can see and do, and records every session as a sealed execution history.
const session = await Canopy.start({ key: process.env.CANOPY_KEY!, values: { params: { temperature: 0.2 } }, tools: { time: async () => new Date().toISOString(), }, }); session.onReply((turn) => console.log(turn.text)); await session.send("What time is it?"); await session.done();
a key, your tools, and words: the whole surface
Canopy is not watching someone else's agent loop from the outside. It is the thing driving the loop, which is exactly why it can make real guarantees about authority, execution, and what happened.
Dial a session with a key and start sending. Canopy owns the model loop, the context, the tool calls, and the retries. Any model, any provider, your keys.
Policy decides which tools exist, and with which arguments. Deny by default, denial wins, and nothing reaches an effect without a recorded verdict admitting it.
Every session is one ordered log of inputs, decisions, invocations and results, sealed at close. The record isn't a report about the run. It is the run.
Nothing gets implicit authority. Not the caller, not the tool, not Canopy's own internals. Authority is established by identity, compiled from your config, and carried as a capability all the way to the effect, so a denied action never happens rather than being asked nicely not to.
Policy governs the whole surface: which model, which tools, and which arguments those tools will accept. A saved policy lives with the key's configuration, and a session can also carry one inline.
// policy and run, in the same file const session = await Canopy.start({ key: process.env.CANOPY_KEY!, tools: { "refunds.issue": issueRefund }, values: { policy: { allows: [ { requestee: "refunds", op: "issue" }, ], tools: [{ name: "refunds.issue", requestee: "refunds", op: "issue", schema: refundSchema, }], }, }, }); // nothing outside this exists for the model
Capabilities reach an agent from wherever you already keep them. They all arrive at the same governance surface, and none of them get a shortcut around it.
Plain functions handed to the session at dial. The callable surface is pinned when the session starts.
Adapt an in-process server with mcpLocal() and its tools join the same surface.
Connect it once through OAuth. Canopy holds the connection and relays every admitted call through one executor.
Bring your own keys and pick the model per session, within what the configuration permits.
A Canopy session is not a transcript written alongside the work. It is one totally-ordered log that the work happens through: the inputs, the policy it ran under, every verdict, every invocation and its result. Services can only react to what is on that log.
The arguments land once, addressed to the executor that will run it. The request, the verdict and the completion reference that payload by digest rather than copying it.
Decisions replay off the record afterward, which makes "what could this agent see, and what did it do" a question with one answer instead of an investigation.
// the session's own tail: the records // the runtime itself ran on for await (const r of session.records()) { console.log(r.cursor, r.kind); } // 12 app.policy.configured session policy // 13 app.client.user_message "refund 4471" // 14 inference.responded wants a tool // 15 app.invoke.requested + payload digest // 16 app.invoke.payload args to executor // 17 app.invoke.forward admitted // 18 app.invoke.completed outcome: success // 19 inference.responded tries a second // 20 app.invoke.requested payments.send // 21 app.invoke.denied outside policy // 22 inference.responded the model adapts // 23 app.session.halted terminal boundary
An LLM gateway, an MCP gateway, a policy layer, a tool registry, an observability stack. Five products with five APIs, five dashboards, and seams nobody owns. Canopy is one runtime, and governance should be cheap enough to leave on.
One client, one start, no bundler and no build step.
Configs, policies, providers, connections, sessions, and a dashboard over the same event surface the runtime uses.
Session history, causality, tool execution, model activity, policy decisions and failures are all the same records, not five exports.
No verdicts, no argument caps, no identity, no audit trail. Canopy is the layer that adds them, and it embraces MCP rather than fighting it. A server you already run connects once and becomes a governed source of tools, on the same record as everything else.
The runtime runs our own production app today. It isn't packaged for self-serve yet, deliberately. The first partners integrate with us hands-on and shape what gets packaged.
aiden@canopyhq.dev →