bus integration containers

Overview

bus integration containers is the provider-neutral container integration router. It handles the public bus.containers.* Events contract and forwards each request to one configured backend event prefix such as bus.docker or bus.podman.

Use this module when a deployment needs one stable container event contract for bus-api-provider-containers, bus-containers, and developer task automation, while keeping Docker, Podman, UpCloud, SSH, and future runtime logic in separate integration modules.

Usage

Run the router against a backend worker. Bus Events must already be running and reachable at the --events-url used by both processes. Start the backend worker and the router concurrently in separate terminals or services; both commands are long-running workers.

For a local memory-backed Events API, start:

BUS_EVENTS_JWT_SECRET=not-a-secret-local-development-hs256-key \
bus-api-provider-events --addr 127.0.0.1:8081 --events-backend memory

For local testing, mint a reusable service token file with:

mkdir -p ./local
BUS_AUTH_HS256_SECRET=not-a-secret-local-development-hs256-key \
bus operator token issue --local \
  --subject container-router-local \
  --audience ai.hg.fi/api \
  --scope 'container:read container:run container:delete container:admin events:send events:listen' \
  --ttl 12h \
  --format token > ./local/container-router.token

The container scopes authorize protected bus.containers.* and default bus.docker.* events. The broad events:send and events:listen scopes are needed only for unprotected backend prefixes such as the default bus.podman.* example below, or for custom backend prefixes without an Events API ACL rule. Production deployments should use the normal service-token flow, grant only the scopes required by the selected backend prefix, and expose the resulting BUS_API_TOKEN to every Events API client process: backend worker, router, containers API provider, and test command.

In the first terminal or service, run the matching Docker worker with the same backend prefix. bus-integration-docker must be installed or run from its module checkout, and the process must have permission to access the Docker daemon through DOCKER_HOST or /var/run/docker.sock:

export BUS_API_TOKEN="$(cat ./local/container-router.token)"
bus-integration-docker \
  --provider docker \
  --events-url http://127.0.0.1:8081 \
  --event-prefix bus.docker

For Podman, use the same pattern with a Podman backend prefix:

export BUS_API_TOKEN="$(cat ./local/container-router.token)"
bus-integration-podman \
  --provider podman \
  --events-url http://127.0.0.1:8081 \
  --event-prefix bus.podman

Then set the router --backend-event-prefix to bus.podman instead of bus.docker.

In the second terminal or service, run the router:

export BUS_API_TOKEN="$(cat ./local/container-router.token)"
bus-integration-containers \
  --provider events \
  --events-url http://127.0.0.1:8081 \
  --backend-event-prefix bus.docker \
  --request-timeout 30m

In a third terminal with the same token, start the containers API provider against the same Events API:

export BUS_API_TOKEN="$(cat ./local/container-router.token)"
bus-api-provider-containers \
  --addr 127.0.0.1:8080 \
  --backend events \
  --events-url http://127.0.0.1:8081

Then verify routing from a fourth terminal:

export BUS_AI_API_URL=http://127.0.0.1:8080
export BUS_API_TOKEN="$(cat ./local/container-router.token)"
bus-containers run --profile codex -- sh -lc 'printf OK'

The response should contain OK; that proves the public bus.containers.run.request reached the configured backend prefix and returned through the router.

--provider static returns deterministic responses for self-tests. --provider events forwards public requests through Bus Events and waits for the backend response with the original correlation ID.

Event Mapping

Public request event Backend request suffix
bus.containers.status.request status.request
bus.containers.run.request run.request
bus.containers.input.request input.request
bus.containers.delete.request delete.request
bus.containers.runner.status.request runner.status.request
bus.containers.runner.start.request runner.start.request
bus.containers.runner.delete.request runner.delete.request

With --backend-event-prefix bus.docker, a public bus.containers.run.request becomes bus.docker.run.request, and the router waits for bus.docker.run.response before publishing bus.containers.run.response.

Live task input follows the same pattern. A public bus.containers.input.request becomes bus.docker.input.request, and the router returns bus.containers.input.response after the backend publishes the correlated backend response.

When this router is active, backend workers consume backend-prefixed events such as bus.docker.* or bus.podman.*. The public bus.containers.* events stay on the provider-neutral side of the router.

Local Compose

The superproject compose.yaml uses this routing layout:

bus-api-provider-containers
  -> bus.containers.* public events
  -> bus-integration-containers
  -> bus.docker.* backend events
  -> bus-integration-docker

Start it from the superproject root:

docker compose -f compose.yaml --profile dev-task up --build -d
docker compose -f compose.yaml --profile dev-task ps
docker compose -f compose.yaml --profile dev-task exec testing-agent sh

Inside the testing shell, run a direct Codex-profile container check:

cd /workspace/bus-containers
go run ./cmd/bus-containers run --profile codex -- codex --version

The command should return a successful run response with exit code 0 and stdout from the Codex CLI. That proves the public bus.containers.run.request reached the configured Docker backend prefix and returned through the router.

The root compose.yaml local AI Platform stack uses the same router shape for bus containers run --profile codex, while nginx exposes the containers API at /api/v1/containers/* and the protected runner API at /api/internal/containers/*.

Using from .bus files

Inside a .bus file, write the module target without the bus prefix:

# same as: bus integration containers --events-url "$BUS_EVENTS_API_URL" --backend-event-prefix bus.docker
integration containers --events-url "$BUS_EVENTS_API_URL" --backend-event-prefix bus.docker

The shell or service that runs the .bus file still needs BUS_API_TOKEN with the same Events permissions as the equivalent CLI command.