ZerkerGateway

Zerker Agent Catalog

Every agent is registered before it is routed.

The catalog is the tenant-scoped record of every agent behind Gateway. Routing, observability, and the x402 gate take a catalog ID, not a URL, and resolve it at request time.

shipped Apache-2.0 /v1/agents

The record

One required field.

Registering an agent needs a name. Upstream, credential, protocol, price, and rate limit are optional and can be added later. An entry with no upstream is still useful: inventory what exists before deciding what routes.

Register

$ curl localhost:8080/v1/agents \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "support-bot",
      "upstream_url": "https://api.example.com"
    }'

What comes back

201 Created
{
  "id": "agt_019842c1-7f3e-7a91-b8d0-3c6e2f19ab44",
  "name": "support-bot",
  "status": "active",
  "protocol": "http",
  "created_at": "2026-07-14T09:22:41Z"
}

The agt_ ID is server-assigned, a UUIDv7, and never changes. Every other surface uses it as the handle, even when the name, upstream, or credential changes.

The mechanic

status is derived, never written.

Zerker computes status from the configuration. An agent is active because it has an upstream to send traffic to, not because someone set a flag.

State What produces it Invocable
pending No upstream_url set — catalogued, not yet wired No
active upstream_url is set Yes
inactive DELETE — soft, the record is kept for audit No

Suspension is not a status. It is a separate flag. Pausing an agent blocks calls without touching its configuration, and unpausing restores it exactly.

Credentials

Credentials never live on the agent.

An agent record holds a credential_ref, not a credential. The secret goes in once through /v1/credentials, is envelope-encrypted under a per-tenant key, and is injected at call time. No endpoint returns it.

$ curl /v1/credentials/cred_019842c4-1a08-7bd2-9f41-77c0e5b3d612
{
  "id": "cred_019842c4-1a08-7bd2-9f41-77c0e5b3d612",
  "name": "upstream-api-key",
  "last4": "9f2a",          ← metadata only; the secret is not a field
  "created_at": "2026-07-14T09:22:41Z"
}

The secret is never returned to a caller or written to a record. Rotate it once, and every agent that references it picks up the new value.

When you get it wrong

What the catalog refuses.

How the catalog answers on the bad path.

What you did What you get Why
Registered a name already used in your tenant 409 Names are unique per tenant, so a name is safe to use in your own tooling
Asked for an ID belonging to another tenant 404 — never 403 A 403 would confirm the record exists. The catalog will not tell you that.
Invoked an agent with no upstream Refused — it is pending A catalogued agent is not a routable one until you say where traffic goes
Deleted a credential an agent still references 409 Deleting it would break invocation silently, at request time, in production
Sent PATCH with a field omitted Unchanged Updates are tri-state — absent, null, and a value are three different instructions, so "clear this" is never a guess
Sent PATCH with a field set to null Cleared

Where the line falls

The catalog is open source.

Registering, listing, updating, soft-deleting, credential storage, and per-tenant isolation are Apache-2.0. What we sell sits above the catalog.

Capability Tier
The full /v1/agents and /v1/credentials API OSS
Envelope-encrypted, per-tenant credential isolation OSS
Governance and a control plane across tenants Commercial

The full boundary →

Next

The catalog says what exists. Routing says where it goes.

Once an agent has an upstream, the proxy takes over: transactional and streaming calls, SSRF checks, and credential injection on the way out.