Contents
Getting started
Using Codepanion
How it works
The connector is outbound-only. It opens a long-poll control channel to Codepanion and waits. When the agent needs your database, the query travels down that channel, the connector runs it locally, with the same read-only enforcement Codepanion applies everywhere (a read-only connection plus a transaction that is always rolled back, a 5-second timeout, and a 1,000-row cap), and posts the result back. Your firewall stays closed.
Good to know
The database connection string is configured on your side, as an environment variable on the container. Codepanion never sees it, never stores it, and cannot connect to your database itself. Every query is executed by the connector, inside your network, under read-only enforcement that runs on your side of the channel.
Create a connector API key
The connector authenticates with an API key scoped connector. It is created through the same endpoint the Settings card uses, with "scope": "connector" added. The card itself mints ingest-scoped keys, so this one is created by calling the endpoint directly. It can't upload code, and an ingest-scoped key can't open the connector channel. The value is shown once. To rotate it, create a new API key before revoking the old one.
POST /settings/ingest-tokens
{ "label": "datacentre-east", "scope": "connector" }Run the connector
One container, three environment variables. Run it anywhere inside your network that can reach the database, on Docker, Kubernetes, or a VM with Docker installed:
Getting the image
The image is delivered on request during early access rather than pulled anonymously. Ask us and we send registry credentials for your organisation; run docker login ghcr.io with them on the host before the first pull, and the commands on this page work as written. A public, signed image is the plan once the update channel below ships.
docker run -d --restart unless-stopped \
--name codepanion-connector \
-e CODEPANION_URL="https://api.codepanion.app/api" \
-e CONNECTOR_TOKEN="<your connector-scoped key>" \
-e CONNECTOR_DB_CONNECTION_STRING="Server=db.internal;Database=prod;User Id=codepanion_reader;Password=...;" \
ghcr.io/codepanion-app/connector:latestUse a dedicated read-only database user, exactly as you would for a direct connection. Least privilege applies on your side too. SQL Server and PostgreSQL are both supported; the connector detects the provider from the connection string. The image is hardened: a chiseled, distroless-style runtime with no shell and no package manager, running as a non-root user.
Point the agent at it
Mark a database environment as connector-mode and the agent routes that environment's queries through the channel instead of a stored connection string:
POST /settings/databases/prod
{ "mode": "connector" }Marking the environment is an API call today; the form on the Settings card creates and edits direct connections. The list under it shows the result: a connector environment carries a Connector badge in place of a provider and a last test, because nothing on our side holds a connection string for it or can test one.
One connector environment per tenant. The channel addresses a connector by tenant, and a container serves the single database it is pointed at, so marking a second environment as connector-mode returns a 409 instead of quietly answering staging queries from production. Tell us if you need two and we will build the routing around what you actually run.
If the connector isn't running when the agent asks, the query fails fast with a clear "no connector connected" answer. Nothing hangs, and the agent tells the support engineer what's wrong.
Catalog resolver (multi-tenant databases)
If your real database is looked up per tenant from a catalog (a registry table that maps a tenant key to its database) rather than living at one fixed connection string, the connector can resolve it for you, entirely inside your network. Point the connector at your local catalog database, and a small resolver snippet turns a tenant key into the actual connection string. The connector then runs the agent's query against the resolved database and posts back only the rows. The resolved per-tenant connection string never crosses the channel. We hold nothing.
docker run -d --restart unless-stopped \
--name codepanion-connector \
-e CODEPANION_URL="https://api.codepanion.app/api" \
-e CONNECTOR_TOKEN="<your connector-scoped key>" \
-e CONNECTOR_CATALOG_CONNECTION_STRING="Server=catalog.internal;Database=registry;User Id=codepanion_reader;Password=...;" \
ghcr.io/codepanion-app/connector:latestMark the environment as both connector-mode and catalog, supplying the resolver snippet and the lookup key:
POST /settings/databases/prod
{
"mode": "connector",
"kind": "catalog",
"resolverJs": "return connection.query({ table: 'tenants', where: [{ column: 'key', op: '=', value: resolveKey }], limit: 1 })[0].cs;",
"resolveKey": "acme"
}What runs, and how it's contained
The resolver runs on your connector, in a hardened sandbox: a JavaScript interpreter with no access to the host, the filesystem, or .NET, offering only a read-only connection.query() against your catalog, under strict time, memory, and statement limits. It is the one place the connector executes a snippet that came from Codepanion. If you would rather the connector never run a snippet delivered over the channel, pin your own resolver locally with CONNECTOR_RESOLVER_JS (inline) or CONNECTOR_RESOLVER_JS_FILE (a file path). A pinned resolver always wins, and the connector logs which source is active at startup.
Logs through the connector
The channel carries more than SQL. An environment's log source can be marked connector-mode too, and the agent's search_logs and get_log_context calls then run inside your network against a log store we never reach. Marking it is Settings work, set out under on-prem logs. What changes on this side is the container.
A log store the connector reaches with its own key needs two more variables, and they must be set together or the connector refuses to start:
docker run -d --restart unless-stopped \
--name codepanion-connector \
-e CODEPANION_URL="https://api.codepanion.app/api" \
-e CONNECTOR_TOKEN="<your connector-scoped key>" \
-e CONNECTOR_SEQ_URL="https://seq.internal" \
-e CONNECTOR_SEQ_APIKEY="<a Seq API key that never leaves your network>" \
ghcr.io/codepanion-app/connector:latestThe two ambient identities need no variable at all. With Azure Managed Identity or an AWS IAM role the connector queries Log Analytics or CloudWatch Logs as the host it runs on, so the grant is made once, on your infrastructure, to a workload you control. We store nothing to revoke.
On those two the workspace ID or log group is named in Settings and travels with each call, because it is ordinary non-secret configuration rather than a credential. The connector reads only what its host identity is already permitted to read, so scope that identity to exactly the workspaces and log groups you want reachable. A Seq endpoint is different: its URL and key come from the container's own environment and are never sent to us.
A connector can serve logs and nothing else: leave CONNECTOR_DB_CONNECTION_STRING unset and a query or schema call is answered with a clear "not configured" result rather than an obscure failure. On an ambient identity that makes a logs-only node two variables in total, CODEPANION_URL and CONNECTOR_TOKEN.
Network requirements
Outbound only. No inbound rules, no port forwarding, no VPN. If your egress is allow-listed, permit HTTPS (443) to these three, plus your own database and log store:
api.codepanion.app # the control channel
ghcr.io # pulling connector images
pkg-containers.githubusercontent.com # GHCR's blob storage (image layers)The two registry hosts cover both your initial docker pull and the automatic updates once they ship.
An ambient-identity log source needs its cloud's own endpoints as well, since the connector calls them directly as the host: login.microsoftonline.com and api.loganalytics.io for Azure Monitor, or logs.<region>.amazonaws.com for CloudWatch Logs. A node-local Seq needs no egress rule at all.
What we can and can't reach
Through the connector, the agent can do two things: run read-only queries expressed in Codepanion's structured query language, and read your database schema (tables, columns, keys, indexes). That's it. It cannot write: the connector wraps every query in a read-only connection and a transaction that is always rolled back. It cannot run arbitrary SQL, because queries arrive as a structured definition that is compiled and parameterised locally, against your actual schema. And it cannot reach anything else on your network: the connector connects only to the database(s) you configured, and nothing in the protocol can change that from our side.
Logs are searching only, and a Seq store is pinned the same way a database is: its URL and key are read from the container's own environment, so nothing in the protocol can aim the connector at a different one. The two ambient identities work differently, and the section above says how. The workspace ID or log group is named in Settings and travels with the call, bounded by whatever the host identity you granted is allowed to read.
The exception on the database side is the optional catalog resolver. If you enable it, the connector runs a resolver snippet and then dials whichever connection string that snippet returns, so the snippet decides which database is reached rather than a fixed environment variable. The snippet runs in a hardened, locked-down sandbox (no host, filesystem, or .NET access; read-only catalog queries only; strict resource limits), and the resolved string never leaves your network. To fix the reachable set on your side as well as the code, pin your own resolver with CONNECTOR_RESOLVER_JS. See that section.
Updates
The connector stamps its version on every request it makes, so we can see who is running what. Today, updating is a normal docker pull + restart (or your orchestrator's rolling update).
Coming: automatic updates
The connector is designed to keep itself current so you don't have to: roughly every 24 hours it will check ghcr.io for a newer image, verify the publisher's cosign signature, pin the exact image digest it verified, and restart onto it, so the update channel can't be hijacked between check and run. Until that ships, releases are infrequent by design (the connector is deliberately tiny) and we email connector customers when one is out.