Files
headquarter/docs/tool-manifest-spec.md
T
Fusion f33a563003 feat(FN-003): add tool manifest registry with FastAPI CRUD and built-in manifests
- Add ToolManifest Pydantic models with validators for ports, mounts, health checks, and traefik config

- Implement in-memory ToolRegistry with YAML loading and built-in manifest scanning

- Add FastAPI CRUD routes for listing, retrieving, and creating tool manifests

- Include built-in manifests for runfusion and code-server

- Harden web Dockerfile with unprivileged nginx and port 8080

- Add tool manifest specification documentation and architecture updates

Fusion-Task-Id: FN-003
2026-05-14 09:15:22 +02:00

8.9 KiB
Raw Blame History

Tool Manifest Specification

Canonical schema reference for Headquarter's manifest-driven tool registry. Version: 1.0.0 — aligned with FN-003.

Overview

Headquarter is a manifest-driven platform: every containerized tool (RunFusion, code-server, and future tools) is declared by a YAML manifest. The orchestration backend reads these manifests to generate Docker Compose services, Traefik routing labels, volume mounts, and resource constraints.

Design goal: Adding a new standard container tool requires only a YAML manifest—no backend code changes.

Manifest File Format

Manifests are YAML files with a single top-level mapping. They are validated on load by Pydantic v2 models.

Built-in location

Built-in manifests live in apps/api/app/tools/manifests/*.yml and are loaded automatically on API startup.

Minimal valid manifest

id: my-tool
name: My Tool
image: my-org/my-tool:latest
ports:
  - container_port: 8080
    primary: true
traefik:
  enabled: false

Field Reference

ToolManifest (top level)

Field Type Required Default Description
id string yes Lowercase slug with hyphens only (^[a-z0-9\-]+$). Used as the registry key.
name string yes Human-readable tool name.
description string no "" Short description of the tool.
version string no "1.0.0" Manifest version (semver-ish).
image string yes Docker image reference.
runtime_command string[] | null no null Override the container default command.
runtime_entrypoint string[] | null no null Override the container entrypoint.
runtime_user string | null no null User to run as inside the container.
runtime_working_dir string | null no null Working directory inside the container.
ports PortConfig[] no [] Exposed ports.
workspace_mounts MountConfig[] no [] Workspace volume mounts (project-scoped).
config_mounts MountConfig[] no [] Config volume mounts (user or tool-scoped).
env dict<string, string> no {} Static environment variables.
secrets SecretRef[] no [] Secrets injected as environment variables.
health_check HealthCheckConfig | null no null Health check definition.
resource_limits ResourceLimits | null no null CPU and memory constraints.
executable ExecutableConfig | null no null Node.js runtime metadata for executable environments.
traefik TraefikConfig | null no null Traefik routing configuration.

PortConfig

Field Type Required Default Description
container_port int yes Port inside the container. Range: 165535.
protocol "tcp" | "udp" no "tcp" Transport protocol.
name string | null no null Logical name, e.g. "http", "websocket".
primary bool no false The port used for default routing and health checks.

MountConfig

Field Type Required Default Description
type "volume" | "bind" no "volume" Mount type.
source_pattern string yes Template pattern resolved at spawn time, e.g. "{project_repo}".
target string yes Absolute path inside the container. Must start with /.
read_only bool no false Mount read-only.

SecretRef

Field Type Required Default Description
name string yes Secret identifier in the secret store.
env_var string yes Name of the environment variable injected into the container.
required bool no true Whether the tool fails to start if the secret is missing.

HealthCheckConfig

Field Type Required Default Description
type "http" | "tcp" | "command" no "http" Health check mechanism.
path string | null no null HTTP path. Required when type == "http".
command string[] | null no null Command to execute. Required when type == "command".
port int | null no null Override port; defaults to the primary port if unset.
interval_seconds int no 10 Check interval. ≥ 1.
timeout_seconds int no 5 Check timeout. ≥ 1.
retries int no 3 Retries before marking unhealthy. ≥ 1.
start_period_seconds int no 5 Grace period before checks count. ≥ 0.

ResourceLimits

Field Type Required Default Description
cpus float | null no null CPU limit. If set, ≥ 0.01.
memory_mb int | null no null Memory limit in MiB. If set, ≥ 16.
memory_swap_mb int | null no null Swap limit in MiB. -1 disables swap limit.

ExecutableConfig

Field Type Required Default Description
node_version string | null no null Expected Node.js version, e.g. "22", "lts".
npm_version string | null no null Expected npm version.
package_manager "npm" | "pnpm" | "yarn" | "bun" no "npm" Preferred package manager.
bootstrap_commands string[] no [] One-time setup commands run on first start.
install_commands string[] no [] Commands run before the main command.

TraefikConfig

Field Type Required Default Description
enabled bool no true Whether Traefik routing is generated for this tool.
subdomain_prefix string | null no null Subdomain prefix. Defaults to the tool id.
port int | null no null Container port to route traffic to.
middlewares string[] no [] Traefik middleware names to apply.
strip_prefix bool no false Strip path prefix before forwarding.
entrypoint string | null no null Override the environment default Traefik entrypoint.
cert_resolver string | null no null Override the environment default cert resolver.

Validation Rules

  1. id must match ^[a-z0-9\-]+$ (lowercase, digits, hyphens only).
  2. MountConfig.target must be an absolute path (starts with "/").
  3. When health_check.type == "http", path must be set and non-empty.
  4. When health_check.type == "command", command must be set and non-empty.
  5. container_port must be between 1 and 65535.
  6. cpus, if set, must be ≥ 0.01.
  7. memory_mb, if set, must be ≥ 16.
  8. If traefik.enabled is true, at least one port must have primary: true.

Example: RunFusion Manifest

id: runfusion
name: RunFusion
description: Executable Node.js environment for running and developing applications.
version: "1.0.0"
image: node:22-slim
runtime_working_dir: /workspace
ports:
  - container_port: 8080
    protocol: tcp
    name: http
    primary: true
workspace_mounts:
  - type: volume
    source_pattern: "{project_repo}"
    target: /workspace
    read_only: false
config_mounts:
  - type: volume
    source_pattern: "{user_config}/runfusion"
    target: /home/node/.config
    read_only: false
env:
  NODE_ENV: development
health_check:
  type: http
  path: /
  port: 8080
  interval_seconds: 10
  timeout_seconds: 5
  retries: 3
  start_period_seconds: 10
resource_limits:
  cpus: 2.0
  memory_mb: 2048
  memory_swap_mb: -1
executable:
  node_version: "22"
  package_manager: npm
  bootstrap_commands: []
  install_commands: []
traefik:
  enabled: true
  subdomain_prefix: runfusion
  port: 8080
  middlewares: []
  strip_prefix: false

Extension Guide: Adding a New Tool

To add a new standard container tool:

  1. Create a new YAML file in apps/api/app/tools/manifests/{tool-id}.yml.
  2. Populate all required fields (id, name, image, ports).
  3. Set traefik.enabled: true and mark one port as primary: true if the tool needs HTTP routing.
  4. Declare workspace_mounts and config_mounts as needed.
  5. Restart the API (or call registry.load_builtin_manifests()).

No backend code changes are required for standard containers that expose an HTTP port and need volume mounts.

Registry API

The in-memory registry exposes FastAPI routes under /api/v1/tools:

  • GET /api/v1/tools — list all registered manifests.
  • GET /api/v1/tools/{id} — retrieve a single manifest.
  • POST /api/v1/tools — register a new manifest (returns 409 if id already exists).

Built-in manifests are loaded automatically on application startup via the FastAPI lifespan context manager.