Addon Stack API
The Addon Stack API lets hosting providers generate Docker Compose overlay files that add services to an existing OpenClaw infrastructure. Unlike the full-stack generate() pipeline, these functions produce only the addon layer — no gateway, no Redis, no PostgreSQL, no reverse proxy.
This is the recommended integration point for platforms like Clawexa that already provision core infrastructure via cloud-init or similar tooling.
Installation
npm install @better-openclaw/core
# or
pnpm add @better-openclaw/coreQuick Example
import { generateAddonStack } from "@better-openclaw/core";
const result = generateAddonStack({
instanceId: "user-abc-123",
services: ["n8n", "qdrant", "meilisearch"],
generateSecrets: true,
reservedPorts: [3000, 5432, 6379], // ports used by your infra
});
// result.composeOverride → Docker Compose YAML string
// result.envFile → .env file contents
// result.proxyRoutes → routes to register in your reverse proxy
// result.openclawConfigPatch → openclaw.json skill entries
// result.metadata → resolved services, port assignments, etc.generateAddonStack(input)
Generates a complete addon stack from a list of service IDs. Returns Docker Compose YAML, environment variables, proxy routes, and skill configuration — ready to write to disk and docker compose up.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
instanceId | string | Yes | Unique identifier for this deployment (used as Docker project name) |
services | string[] | Yes | Service IDs to install (e.g. ["n8n", "qdrant"]). Infrastructure IDs like redis or postgresql are automatically filtered out with a warning. |
skillPacks | string[] | No | Optional skill pack IDs to include |
reservedPorts | number[] | No | Host ports already in use by your infrastructure. Conflicts are auto-reassigned to port+1000. |
generateSecrets | boolean | No | Generate random secrets for all service passwords/keys (default: true) |
credentials | Record<string, Record<string, string>> | No | User-provided credentials keyed by service ID, then env var name. Example: { "openai": { "OPENAI_API_KEY": "sk-..." } } |
prebuiltImages | Record<string, string> | No | Override Docker images for services that normally require docker build |
platform | string | No | Target platform (default: "linux/amd64") |
gpu | boolean | No | Whether the host has GPU support |
Return Value: AddonStackResult
| Field | Type | Description |
|---|---|---|
composeOverride | string | Complete docker-compose.override.yml YAML. Includes only addon services plus openclaw-network as external. |
envFile | string | Environment variable file contents. Excludes infrastructure-managed keys (REDIS_*, POSTGRES_*, OPENCLAW_*). |
envVars | EnvVarGroup[] | Structured env vars grouped by service, suitable for UI rendering |
proxyRoutes | ProxyRoute[] | Reverse proxy routes to register (path, port, protocol, stripPrefix flag) |
skillFiles | Record<string, string> | Skill definition files to write to the config directory |
openclawConfigPatch | object | JSON patch for openclaw.json — contains skill entries to merge |
warnings | string[] | Non-fatal warnings (filtered infra services, dependency notes, etc.) |
metadata | object | Includes serviceCount, resolvedServices, skippedServices, portAssignments, and addedDependencies |
Skipped Services
Services may be skipped (excluded from output) without throwing an error. Each skipped service includes a reason:
| Reason | Description |
|---|---|
missing_credentials | Service requires API keys that weren't provided and can't be auto-generated |
no_image | Service requires docker build and no prebuiltImage is available |
gpu_required | Service needs GPU but host doesn't have GPU support |
unknown_service | Service ID doesn't match any known service definition |
updateAddonStack(input)
Incrementally adds or removes services from an existing addon stack. Preserves user-modified environment values and computes diffs for orchestration.
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
instanceId | string | Yes | Same instance ID used in the original generation |
currentCompose | string | Yes | Current compose override YAML (as returned by generateAddonStack) |
currentEnv | string | Yes | Current .env file contents |
addServices | string[] | No | Service IDs to add |
removeServices | string[] | No | Service IDs to remove |
generateSecrets | boolean | No | Generate secrets for newly added services |
Return Value: AddonStackUpdateResult
| Field | Type | Description |
|---|---|---|
composeOverride | string | Updated Docker Compose YAML |
envFile | string | Merged environment file — existing values are preserved, new vars added |
proxyRoutes | ProxyRoute[] | Full set of proxy routes for the updated stack |
imagesToPull | string[] | Docker images that need to be pulled for added services |
metadata | object | Includes added, removed, and unchanged service arrays |
Infrastructure Filtering
The addon stack API automatically filters out infrastructure services that are expected to be managed by the hosting platform:
// These service IDs are always excluded from addon output:
openclaw-gateway, openclaw-cli, redis, postgresql,
open-webui, caddy, traefik, postgres-setup,
convex, convex-dashboard, mission-controlIf a user requests an infrastructure service, it's silently removed and a warning is added to the result. Services that depend on infrastructure (like n8n needing PostgreSQL) automatically get a postgres-setup init container that connects to the existing database.
Port Conflict Resolution
Pass your infrastructure's occupied ports via reservedPorts. When an addon service's default port conflicts, it's automatically reassigned to port + 1000. The actual assignments are reported in metadata.portAssignments.
const result = generateAddonStack({
instanceId: "prod-01",
services: ["n8n"],
reservedPorts: [5678], // n8n's default port is taken
});
// n8n will be assigned port 6678 instead
console.log(result.metadata.portAssignments);
// { "n8n:5678": 6678 }Proxy Routes
Each service with a proxyPath definition generates a proxy route. Use these to configure your reverse proxy (Caddy, Traefik, nginx):
for (const route of result.proxyRoutes) {
console.log(route);
// { serviceId: "n8n", path: "/n8n", port: 5678, protocol: "http", stripPrefix: true }
}
// Caddy example:
// handle_path /n8n/* {
// reverse_proxy n8n:5678
// }Environment Quirks
Some services have known environment variable issues. The addon stack API handles these automatically via the envQuirks system:
- empty_string_crashes — setting an env var to an empty string causes the service to crash. The API sets a safe default value.
- min_length — the value must be at least N bytes. The API generates appropriately-sized secrets.
- must_sync — two env vars must have the same value. The API syncs them automatically.
Zod Schemas
All input/output types are defined as Zod schemas and exported from @better-openclaw/core:
import {
AddonStackInputSchema,
AddonStackResultSchema,
AddonStackUpdateInputSchema,
AddonStackUpdateResultSchema,
ProxyRouteSchema,
SkippedServiceSchema,
} from "@better-openclaw/core";
// Validate external input before passing to generateAddonStack:
const parsed = AddonStackInputSchema.parse(untrustedInput);
const result = generateAddonStack(parsed);See also: REST API Endpoints | Hosting Provider Integration Guide