Sign inSign up

kubblabs/kubb-agent

By kubblabs

Updated 13 days ago

Official Docker image for running Kubb Agents

Image
Networking
API management
Developer tools
0

10K+

kubblabs/kubb-agent repository overview


kubb.agent

HTTP agent server for Kubb Studio

Built on Nitro, it exposes REST endpoints for triggering code generation, a health check, and a bidirectional WebSocket connection to Kubb Studio. Machine tokens are derived from the machine's network identity so they survive restarts. Sessions are cached locally to speed up reconnects.

This is a private application that powers Kubb Studio. It ships as a Docker image (kubblabs/kubb-agent) and is not published to npm.

Usage

Manual Server Start

Build the agent and run the compiled server directly:

pnpm turbo run build --filter=kubb.agent
KUBB_AGENT_CONFIG=./kubb.config.ts node apps/agent/.output/server/index.mjs

The server will be available at http://localhost:3000.

Local development against Kubb Studio

To run the agent against a local Kubb Studio instance, start each app on its own port and share a token between them.

  1. Start Studio on port 3000 with a pinned sandbox token. In apps/kubb.studio/.env set KUBB_STUDIO_SECRET and KUBB_AGENT_TOKEN_PRESET=kubb-agent-dev-token, then run:

    pnpm turbo run dev --filter=kubb.studio
    
  2. Start the agent on a different port, pointing at the local Studio. In apps/agent/.env set:

    PORT=4000
    KUBB_STUDIO_URL=http://localhost:3000
    KUBB_AGENT_TOKEN=kubb-agent-dev-token
    KUBB_AGENT_SECRET=KUBB_SERVER_TEST
    
    pnpm turbo run dev --filter=kubb.agent
    

The agent registers with Studio, opens a WebSocket, and Studio routes generation commands to it. Studio derives the WebSocket URL from its own KUBB_STUDIO_URL, so both apps must agree on that host.

For the full stack (Studio, agent, and Postgres) in containers, use the Kubb Studio docker-compose, which wires the same token across both services out of the box.

Docker

Run the agent standalone:

docker run --env-file .env \
  -p 3000:3000 \
  kubblabs/kubb-agent

A default kubb.config.ts is baked into the image at /kubb/agent/data/kubb.config.ts. To use your own config, bind-mount it over the default:

docker run --env-file .env \
  -p 3000:3000 \
  -v ./kubb.config.ts:/kubb/agent/data/kubb.config.ts \
  kubblabs/kubb-agent
Docker Compose

Use the provided docker-compose.yaml in apps/agent:

services:
  agent:
    image: kubblabs/kubb-agent:latest
    container_name: kubb-agent
    environment:
      PORT: 80
      KUBB_AGENT_ROOT: /kubb/agent/data
      KUBB_AGENT_CONFIG: ./kubb.config.ts
      KUBB_STUDIO_URL: https://kubb.studio
    volumes:
      - agent_kv:/kubb/agent/.kubb/data
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'node', '-e', "fetch('http://localhost:3000/api/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"]
      interval: 15s
      timeout: 10s
      start_period: 60s
      retries: 5

volumes:
  agent_kv:
docker compose up

The agent_kv named volume persists the KV store (session cache, machine token) across container restarts and upgrades.

Deploying from source (e.g. Dokploy without the Docker image)

KUBB_AGENT_CONFIG defaults to data/kubb.config.ts and KUBB_AGENT_ROOT falls back to the working directory (typically /app/apps/agent in a monorepo build). That resolves to the sample config committed at apps/agent/data/kubb.config.ts, so the agent loads a config out of the box even when a platform builds and runs the app straight from this repo instead of pulling the kubblabs/kubb-agent image. Set KUBB_AGENT_CONFIG (and KUBB_AGENT_ROOT if the path is relative) to point at your own config when you have one.

The packages that config imports still need to be installed. The Docker image installs them via the KUBB_PACKAGES build ARG (see the Dockerfile), which is skipped outside that image, and the @kubb/plugin-* packages are dev-only dependencies of this app. Install whatever your config imports (npm install @kubb/plugin-ts ...) in the deployment, or the agent fails with Cannot find module '<package>' the first time it loads the config.

Environment Variables
VariableDefaultDescription
KUBB_AGENT_CONFIGdata/kubb.config.tsPath to your Kubb config file. Relative paths are resolved against KUBB_AGENT_ROOT.
KUBB_AGENT_ROOT/kubb/agent (Docker) / cwdRoot directory for resolving relative paths.
PORT3000Server port.
HOST0.0.0.0Server host.
KUBB_STUDIO_URLhttps://kubb.studioKubb Studio WebSocket URL.
KUBB_AGENT_TOKEN(empty)Authentication token for Studio. Required to connect.
KUBB_AGENT_ALLOW_WRITEfalseSet to true to allow writing generated files to disk.
KUBB_AGENT_ALLOW_INPUTfalseSet to true to accept and generate from the OpenAPI spec sent by Studio. A separate opt-in from writing files.
KUBB_AGENT_RETRY_TIMEOUT30000Milliseconds to wait before retrying a failed Studio connection.
KUBB_AGENT_HEARTBEAT_URL(empty)URL to call every 5 minutes to signal the agent is alive (e.g. a Healthchecks.io ping URL). Leave empty to disable.
Automatic .env Loading

The agent automatically loads a .env file from the current working directory into process.env on startup. Variables already set in the environment take precedence.

Quick Start

  1. Create .env file:
PORT=3000
KUBB_AGENT_ROOT=/path/to/your/project
KUBB_AGENT_CONFIG=/path/to/your/project/kubb.config.ts
KUBB_AGENT_TOKEN=your-token-here
KUBB_STUDIO_URL=https://kubb.studio
  1. Run the agent:
node apps/agent/.output/server/index.mjs
  1. Agent is now available at:
http://localhost:3000

WebSocket Studio Integration

The agent connects to Kubb Studio on startup when KUBB_AGENT_TOKEN is set.

Startup Sequence

On startup the agent performs these steps before opening a WebSocket:

  1. Register — calls POST /api/agent/register with a stable machineToken derived from the machine's network interfaces and hostname (SHA-256). This binds the token to the machine. Registration failure is non-fatal — a warning is logged and the agent continues.
  2. Create session — calls POST /api/agent/session/create (includes machineToken for verification) and receives a WebSocket URL.
  3. Connect — opens a WebSocket to the returned URL using the Authorization header for authentication.
Connection features
FeatureDescription
Automatic reconnectionCaches session tokens to speed up reconnects
Real-time eventsStreams generation progress and events
Command handlingReceives generate and connect commands from Studio
Graceful shutdownNotifies Studio when disconnecting
Session management24-hour session expiration with auto-refresh; Studio re-validates every incoming message and disconnects if the session is revoked or expired
Session Caching

Sessions are cached in ./.kubb/data (relative to the working directory, or agent_kv volume in Docker) for faster reconnects:

  • Tokens are hashed (non-reversible) for security
  • Sessions auto-expire after 24 hours
  • Invalid sessions are automatically cleaned up

WebSocket API

Messages Sent by Agent

Connected — sent in response to a connect command

{
  "type": "connected",
  "payload": {
    "version": "x.x.x",
    "configPath": "/app/kubb.config.ts",
    "permissions": {
      "allowWrite": false,
      "allowInput": false
    },
    "config": {
      "plugins": [{ "name": "@kubb/plugin-ts", "options": {} }]
    },
    "studioConfig": {
      "plugins": [{ "name": "@kubb/plugin-ts", "options": { "enum": { "type": "asConst" } } }],
      "input": "openapi: 3.0.0\n..."
    }
  }
}

config is the agent's on-disk config. studioConfig is the last config a user picked in Studio, replayed so Studio prefills its UI with the previous plugin options, adapter options, and (when KUBB_AGENT_ALLOW_INPUT=true) OpenAPI spec. It is absent when nothing has been saved yet.

Data Events — streamed during code generation

{
  "type": "data",
  "payload": {
    "type": "plugin:start",
    "data": [{ "name": "plugin-ts" }],
    "timestamp": 1708000000000
  }
}

Available payload.type values: plugin:start, plugin:end, files:processing:start, file:processing:update, files:processing:end, generation:start, generation:end, info, success, warn, error.

Ping — sent every 30 seconds to keep the connection alive

{ "type": "ping" }
Messages Received from Studio

Generate Command — triggers code generation

{
  "type": "command",
  "command": "generate",
  "payload": { "plugins": [] }
}

payload is optional. When omitted, the agent falls back to kubb.config.studio.json (a temporal config file next to kubb.config.ts), and then to the config loaded from disk.

The payload may also include an input field containing a raw OpenAPI / Swagger spec (YAML or JSON string). A sandbox agent always honors it. A local agent honors it only when it opts in with KUBB_AGENT_ALLOW_INPUT=true, otherwise the spec is read from disk and the field is ignored. The agent advertises whether it accepts a Studio-supplied spec through permissions.allowInput in its connected payload. See Sandbox Mode below.

Connect Command — requests agent info

{
  "type": "command",
  "command": "connect",
  "permissions": {
    "allowWrite": false
  }
}

Pong — sent by Studio in response to an agent ping

{ "type": "pong" }

Status — sent by Studio with information about connected agents

{
  "type": "status",
  "message": "...",
  "connectedAgents": 1,
  "agents": [{ "name": "...", "connectedAt": "..." }]
}

Sandbox Mode

When Kubb Studio provisions a session for the Sandbox Agent (the shared agent hosted by Studio itself), it sets isSandbox: true in the session response. In sandbox mode the agent behaves differently from a user-owned agent:

behaviorNormal agentSandbox agent
Write generated files to disk✅ (when KUBB_AGENT_ALLOW_WRITE=true)❌ Never
Read a file input from disk✅ (falls back when no inline input supplied)
Accept inline input in generate payload✅ (when KUBB_AGENT_ALLOW_INPUT=true)
Why no filesystem writes?

The sandbox agent runs in a shared, docker environment inside Kubb Studio. Allowing arbitrary disk writes would create security and isolation problems. Instead, output.write is always set to false, and the generated files are returned to Studio via the WebSocket generation:end event where the UI renders them.

Inline input in sandbox mode

Because the sandbox agent cannot read arbitrary files from disk, callers must supply the OpenAPI / Swagger spec content inline via the input field in the generate command payload:

{
  "type": "command",
  "command": "generate",
  "payload": {
    "input": "openapi: 3.0.0\ninfo:\n  title: Pet Store\n  version: 1.0.0\n...",
    "plugins": [{ "name": "@kubb/plugin-ts", "options": {} }]
  }
}

The input value is passed straight through as the config's input, so the inline content overrides the input from the loaded config for that generation cycle. A local (non-sandbox) agent applies the same override once it opts in with KUBB_AGENT_ALLOW_INPUT=true; without the opt-in the field is ignored and the spec is read from disk.

Persisting the latest options and spec

Each generate from Studio saves the agent's latest options (and, when KUBB_AGENT_ALLOW_INPUT=true, the OpenAPI spec) to the local KV store under a single key, so it survives reconnects and restarts. A later generate that arrives without a payload restores this saved config, and the agent replays it as studioConfig in its connected payload so Studio prefills its UI with the previous options and spec. The spec is only kept when the input opt-in is enabled.

The agent keeps one saved config per process, shared by every session in the pool. To avoid one user's options and spec reaching another, persistence and replay are disabled when KUBB_AGENT_POOL_SIZE is greater than 1; a multi-user pool always reads its config from disk.

Configuration Example

1. Create a Kubb configuration file (kubb.config.ts):
import { defineConfig } from 'kubb'
import { pluginOas } from '@kubb/plugin-oas'
import { pluginTs } from '@kubb/plugin-ts'

export default defineConfig({
  input: './openapi.json',
  output: {
    path: './src/generated',
  },
  plugins: [pluginOas(), pluginTs()],
})
2. Start the agent server:
node apps/agent/.output/server/index.mjs

You'll receive a stream of events as the code generation progresses.

Supporting Kubb

Kubb is an open source project, and its development is funded entirely by sponsors. If you would like to become a sponsor, please consider:

My sponsors

License

MIT

Tag summary

Content type

Image

Digest

sha256:f124496e0

Size

62.2 MB

Last updated

13 days ago

docker pull kubblabs/kubb-agent