# Docker Execution Hub — LLM Integration Guide > Canonical machine-readable guide for planning templates, launching disposable test machines, using SSH, and configuring public HTTP routing. Last verified against the repository contract: 2026-08-02. Runtime responses from `/api/v1/capabilities`, `/api/v1/images`, and `/api/v1/templates` take precedence over static examples in this document. ## Product purpose Docker Execution Hub creates short-lived Ubuntu test machines on an isolated worker. A machine can run a versioned template, an optional application ZIP, multiple Docker Compose projects, follow-up commands, file operations, a public HTTPS endpoint, and optional SSH access. Machines are removed automatically after their TTL or explicitly through the terminate endpoint. This is an internal trusted test toolkit, not a general-purpose public cloud and not an unrestricted container hosting platform. ## API and authentication - API base: use the API origin configured by the operator. Browser deployments expose it through `window.__API__`. - All `/api/v1/*` requests require `Authorization: Bearer `. - Creation requests require an `Idempotency-Key` header. Reusing a key with different content returns a conflict. - Never put the API token into a template ZIP, application ZIP, Docker image, Compose environment, logs, or source control. - Public discovery document: `GET /llms.txt` on the frontend origin. This document contains no credential. Important endpoints: - `GET /api/v1/capabilities` — supported execution modes, isolation kinds, features and limits. - `GET /api/v1/images` — allowed and currently available base images and measured tools. - `GET /api/v1/templates` — template library. - `GET /api/v1/templates/starter.zip` — starter template package. - `POST /api/v1/templates/import` — import a template ZIP; content type is application/zip. - `GET /api/v1/app-package-presets` — example application packages. - `POST /api/v1/payloads` — upload an application ZIP. - `POST /api/v1/machines` — start a template version with optional app payload, public route and SSH. - `POST /api/v1/executions` — low-level direct execution contract. - `GET /api/v1/executions/:id` — status, machine details, public URL and active SSH details. - `GET /api/v1/executions/:id/events` — lifecycle events. - `GET /api/v1/executions/:id/logs` — execution logs. - `POST /api/v1/executions/:id/commands` — run a follow-up command in an active machine. - `POST /api/v1/executions/:id/files` — list, read or write below `/workspace`. - `POST /api/v1/executions/:id/terminate` — remove the machine and its routes. ## Supported base images Always query `GET /api/v1/images`; availability is measured per active worker. Known standard aliases: - `ubuntu-24.04-docker` — Ubuntu 24.04 with Docker, Compose, Python 3, unzip and OpenSSH server. - `ubuntu-24.04-plain` — Ubuntu 24.04 without Docker, with curl, unzip and OpenSSH server. The image does not define public routing. Public routing is defined by the selected template version and its endpoint contract. ## Template package rules A template ZIP has `hub-template.yaml` at its root. Compose files inside uploaded packages must be named exactly `docker-compose.yml`. Guest-side examples named `compose.template.yml` in the repository are renamed only while building the ZIP. Do not add any additional repository file named `docker-compose.yml`; the host deployment starts every such file it finds. Minimal manifest: ```yaml apiVersion: execution-hub/v1 kind: MachineTemplate metadata: slug: agent-testserver version: 1.0.0 name: Agent Testserver description: Disposable agent test machine visibility: private spec: baseImage: ubuntu-24.04-plain settingsProfile: none allowAppPackage: false compose: autoDiscover: false projects: [] profile: label: Ubuntu + Agent Gateway components: [Ubuntu 24.04, Agent, Traefik] ingress: guest-traefik defaults: resources: { cpu: 2, memoryMb: 2048, diskGb: 20 } ttlSeconds: 3600 endpoints: - key: agent label: Agent API targetPort: 80 protocol: http publicByDefault: true health: { path: /healthz, timeoutSeconds: 180 } ``` Use a new semantic version for changed immutable package content. The slug remains stable. UI edits create a derived immutable version; they do not modify an existing version. ## Public HTTP and Traefik model The only supported public TLS boundary is the central edge Traefik: 1. The worker reserves one reusable hostname such as `deh-m02.example.test`. 2. Central Traefik owns ACME, the certificate private key and HTTPS termination. 3. Central Traefik forwards plain HTTP to the machine IPv4 and the manifest endpoint `targetPort`. 4. `passHostHeader` is enabled, so the guest receives the original public host. 5. Before the execution plan starts, the worker writes `EXECUTION_HUB_PUBLIC_HOST` and `EXECUTION_HUB_PUBLIC_URL` to the `.env` beside each Compose file that already exists below the execution work directory. The environment injection has an important limit: an OS-only template without a Compose file receives no automatic public-host environment file, and a bootstrap that creates Compose later does not receive a retroactive update. Such a guest should route all traffic arriving on its dedicated port with a catch-all rule such as `PathPrefix(`/`)`, or obtain the public URL through an explicit operator-controlled mechanism. The central edge has already selected the correct machine, so a second Host rule is optional. Never configure ACME, TLS, a certificate resolver, `acme.json`, private keys, or a self-signed public fallback inside a guest package. The guest should serve HTTP only. Clients still use the valid public HTTPS URL supplied by the Execution Hub. `profile.ingress: guest-traefik` describes that a router lives inside the guest. It does not create a route by itself. A manifest endpoint and public enablement are still required. For the common guest-gateway layout, use `targetPort: 80` and make the guest Traefik entrypoint listen on port 80. Docker labels are interpreted only by a guest Traefik Docker provider, never by the Execution Hub. Example for a runtime-installed stack: ```yaml services: hub: image: example/hub:latest networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.hub.rule=Host(`${EXECUTION_HUB_PUBLIC_HOST}`)" - "traefik.http.routers.hub.entrypoints=web" - "traefik.http.services.hub.loadbalancer.server.port=8080" - "traefik.docker.network=web" networks: web: name: web ``` Requirements for those labels: - Guest Traefik has an entrypoint named `web` listening on guest port 80. - Guest Traefik and service `hub` share the actual Docker network named `web`. - The service really listens on the labeled internal port, 8080 in this example. - Guest Traefik has its Docker provider enabled. Security boundary: imported template and app packages may not bind-mount `/var/run/docker.sock`; the package validator rejects Docker sockets and all absolute host bind mounts. Therefore a label-driven Docker provider is not supported inside an imported package. Use a Traefik file provider, route directly to a service listening on the endpoint port, or install the label-driven stack later inside the disposable guest under an explicitly trusted workflow. Safe file-provider example that also works for OS-only and later-created stacks: ```yaml http: routers: agent: rule: "PathPrefix(`/`)" entryPoints: [web] service: agent services: agent: loadBalancer: servers: - url: "http://127.0.0.1:8080" ``` ## Direct ingress versus guest-traefik - `direct`: central Traefik forwards to one ordinary HTTP service on `targetPort`. No guest router is required. - `guest-traefik`: central Traefik forwards to a guest router on `targetPort`; the guest router performs another HTTP routing step using the preserved Host header. Choose `direct` unless the guest genuinely needs to route several internal HTTP services. Do not choose `guest-traefik` merely to obtain HTTPS; HTTPS is always central. The standard template `ubuntu-traefik-monitor` uses `baseImage: ubuntu-24.04-docker`, `ingress: guest-traefik`, and endpoint port 8080. It is a monitor example and does not enable Traefik's Docker provider. It is not a drop-in port-80 agent gateway. ## SSH SSH is independent of HTTP exposure. Both standard base images contain OpenSSH server, but SSH remains disabled unless requested while starting the machine. Supported access: - public key only; - user and caller-supplied password; - user and generated 24-character password; - key and password together. SSH requires a persistent machine (`keepAlive: true`). The public SSH port is fixed by the allocated slot: slot 1 uses 2201 through slot 20 using 2220. The detail response returns host, port, user, command, methods and host-key fingerprint. An active generated password appears only in the protected detail response, not execution lists, requests or event logs. Cleanup removes stored credentials; daily retention removes old log chunks and legacy credentials after the configured period. ## Execution and Compose behavior Supported execution modes are reported by `/api/v1/capabilities` and normally include `empty`, `auto-compose`, `manifest`, `python`, and `steps`. - `empty`: provision a machine without starting an application. - `auto-compose`: discover and start Compose projects recursively. - `manifest`: follow a declared execution manifest. - `python`: run a Python script inside `/workspace`. - `steps`: explicit shell and Compose steps. Uploaded application content is extracted under `/workspace/app`. File and command working directories must remain under `/workspace`. Compose project startup order is the alphabetical relative path order unless represented by template project metadata. Shared networks must be declared in the template and used as external networks with the exact same fixed name in each Compose file. ## What the Hub can do - Build immutable machine template versions. - Clone disposable LXD container or VM machines from allowed images. - Run Docker and Compose inside a nested guest when the base image supports it. - Start several Compose projects and report their status separately. - Allocate CPU, memory, disk and TTL within operator limits. - Publish one selected HTTP endpoint through central HTTPS. - Preserve the public Host header for a guest router. - Provide optional SSH through a fixed slot port. - Run follow-up commands and scoped file operations. - Stream events and logs. - Terminate and automatically clean up machines, routes, leases and credentials. ## What the Hub cannot or will not do - It does not infer an Execution Hub public route from Docker labels. - It does not manage guest ACME, guest public TLS or guest certificates. - It does not publish a port absent from the selected immutable template endpoint. - It does not inject public-host variables into an OS-only template without Compose or into Compose files created after the execution plan starts. - It stores endpoint `health.path` but does not currently perform an active HTTP request to that path. Compose health waits rely on Docker health state; define a real container healthcheck. - It does not allow `privileged: true`, host networking, host PID/IPC, device mappings, Docker socket mounts, absolute host bind mounts or escaping build contexts in imported packages. - It does not accept `.env`, `acme.json`, private keys, certificates or common secret-file formats inside template packages. - It does not provide a general secret-reference provider or artifact export when capabilities report those features as false. - It does not make environment-variable edits in this repository become runtime settings automatically; the operator must update deployment environment through the supported manager. - It does not guarantee an image exists merely because its name is allowed; query `/api/v1/images`. - It does not repair a service that fails its endpoint health check. - It does not expose SSH unless explicitly requested. - It does not make a machine permanent; every machine has a TTL and can be terminated. ## Correct machine launch workflow 1. Read `/api/v1/capabilities`, `/api/v1/images`, and `/api/v1/templates`. 2. Select an immutable template version compatible with the desired routing and tools. 3. Upload an app ZIP only if the template permits one. 4. Call `POST /api/v1/machines` with a fresh idempotency key, template version, TTL, resources, `public: true` when needed, and optional SSH. 5. Poll `GET /api/v1/executions/:id` or consume events until `ready`, `failed`, or `deleted`. 6. Use `machine.details.publicUrl` as the only public URL. Do not invent a hostname. 7. Use `machine.details.ssh` only while the machine is active. 8. Terminate when finished; otherwise TTL cleanup removes the machine. Example body: ```json { "templateVersionId": "", "ttlSeconds": 3600, "public": true, "resources": { "cpu": 2, "memoryMb": 2048, "diskGb": 20 }, "ssh": { "enabled": true, "user": "root", "generatePassword": true } } ``` ## Diagnosing certificate failures If an agent reports `CERTIFICATE_VERIFY_FAILED` with a self-signed certificate: 1. Confirm the client uses `machine.details.publicUrl`, not the guest IPv4 or an internal guest URL. 2. Confirm the selected template version contains an endpoint and the machine was started with public routing enabled. 3. Confirm central HTTPS is reachable and the execution reached `ready`. 4. Confirm the endpoint `targetPort` equals the actual guest listener port. 5. Remove guest TLS, ACME and self-signed fallback from the public path. 6. For `guest-traefik`, confirm the guest router either matches `EXECUTION_HUB_PUBLIC_HOST` or uses a deliberate catch-all rule, and listens on the endpoint port. 7. Confirm the application itself answers through the public URL. Do not assume the stored manifest `health.path` was actively probed by the worker. The correct public flow is always: client HTTPS -> central Traefik TLS -> guest HTTP -> optional guest Traefik -> application.