Skip to content

Container Contract

Every Meridian plugin runs as a container alongside a sidecar container. This page defines the contract between them.

Environment Variables

Required

Variable Description Example
INSTANCE_ID Unique instance name "oms-1", "dgm-yahoo-eod"
POOL_TYPE Plugin classification "OMSPool", "DGMPool", "SignalPool"

Optional (Sidecar)

Variable Description Default
IMAGE Container image reference
REQUESTED_GRANTS JSON array of {topic, action} ACL grants
TAGS Comma-separated capability tags
SIDECAR_CONFIG Path to YAML config file
SIDECAR_BUS_API_URL Bus API gRPC address "bus-api:8443"

Optional (Plugin)

Variable Description Default
SIDECAR_ADDR Sidecar gRPC address "127.0.0.1:9191"

Network Model

┌──────────────────────────────────────┐
│  Shared network namespace            │
│                                      │
│  ┌─────────┐    ┌─────────────────┐  │
│  │ Plugin   │───▶│ Sidecar         │  │
│  │          │9191│                 │──────▶ Bus API (8443)
│  └─────────┘    └─────────────────┘  │
│                                      │
└──────────────────────────────────────┘
  • Plugin connects to sidecar on 127.0.0.1:9191 only
  • Plugin never connects to Bus API directly
  • Sidecar connects outbound to Bus API on bus-api:8443
  • Plugin and sidecar share a network namespace (network_mode: "container:<sidecar>")

Startup Sequence

The sidecar performs 8 steps before accepting plugin traffic:

  1. Connect to Bus API
  2. Register instance (instance_id, pool_type, requested_grants)
  3. Recover ACL grants (blocks until complete)
  4. Recover state from durable store
  5. Start health check server
  6. Open gRPC listener on :9191
  7. Accept plugin connections
  8. Begin message routing

Steps 1-3 and 7 are fatal — sidecar exits if they fail. Steps 4-6 are best-effort.

Health Checks

The sidecar exposes /healthz for Kubernetes liveness/readiness probes. The plugin must respond to on_health_check() within 5 seconds.

Graceful Shutdown

On SIGTERM:

  1. Sidecar stops accepting new messages
  2. Plugin receives on_stop() callback
  3. Plugin drains in-flight work
  4. Sidecar deregisters from Bus API
  5. Both containers exit with code 0

Docker Compose Example

services:
  sidecar-my-plugin:
    image: gitea:3000/Societal-Lab-Inc/sidecar:0.1.0
    environment:
      INSTANCE_ID: my-plugin
      POOL_TYPE: SignalPool
      SIDECAR_BUS_API_URL: bus-api:8443
    networks:
      - meridian

  my-plugin:
    build: .
    network_mode: "container:sidecar-my-plugin"
    environment:
      SIDECAR_ADDR: "127.0.0.1:9191"
    depends_on:
      - sidecar-my-plugin

Dockerfile Best Practices

FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY . .
CMD ["python", "plugin.py"]
  • Use slim/alpine base images
  • Install dependencies before copying source (layer caching)
  • Don't run as root in production
  • Don't include dev dependencies in the production image

Next Steps