docker-compose.oss.yml
Rama main · réplica de lectura del repositorio original.
# OSS / community edition overlay — build the moat-free images from source.
#
# ── THIS FILE IS NOT STANDALONE ──────────────────────────────────────────────
# It is an overlay. Rendering it alone fails, by design:
#
# $ docker compose -f docker-compose.oss.yml config --services
# service "connector-seed" refers to undefined volume mcp_connectors: invalid compose project
#
# Two objects are missing, not one, and Compose reports whichever it reaches
# first — the same command also prints
# `service "connector-deployer" refers to undefined network rsync-ai-mcp`
# depending on service iteration order, so do not treat either message as the
# whole diagnosis. Do NOT "fix" either by declaring them here. Both must be the SAME objects the
# base file creates — declaring a second `mcp_connectors` in this file would give
# the overlay its own empty volume, and `connector-seed` would seed a volume the
# orchestrator never reads. They are inherited from the base layered underneath.
#
# ── THE REQUIRED BASE IS docker-compose.quickstart.yml ───────────────────────
# Not `docker-compose.yml`. The cloud base has no `mcp_connectors` volume (its
# top-level `volumes:` block carries postgres/kafka/redis/minio/connect_secrets/
# tool_generator_oauth_tokens and nothing else), so
# `-f docker-compose.yml -f docker-compose.oss.yml` fails with
# `service "connector-deployer" refers to undefined volume mcp_connectors`.
# Cloud uses a bind mount for connectors; only the quickstart stack uses the
# named volume this overlay seeds.
#
# docker compose -f docker-compose.quickstart.yml -f docker-compose.oss.yml up -d
#
# Validate the merge before starting anything (rc=0 and 17 services today):
#
# docker compose -f docker-compose.quickstart.yml -f docker-compose.oss.yml \
# config --services
#
# ── WHAT THIS OVERLAY ACTUALLY CHANGES ───────────────────────────────────────
# The quickstart ALREADY runs the moat-free lifecycle service: it pulls
# `ghcr.io/rsync-ai/connector-lifecycle` for `tool-generator` and runs
# `src.lifecycle.main`. This overlay does NOT introduce that — it switches three
# services from PULLING the published images to BUILDING them from this working
# tree. Diff of the rendered project, quickstart alone -> quickstart + this file:
#
# tool-generator ghcr.io/rsync-ai/connector-lifecycle:latest
# -> rsync-ai/connector-lifecycle:local (llm-service/Dockerfile.oss)
# connector-seed ghcr.io/rsync-ai/connector-seed:latest
# -> rsync-ai/connector-seed:local (shared/mcp-connectors/Dockerfile.seed)
# connector-deployer ghcr.io/rsync-ai/connector-deployer:latest (tag kept)
# + build: ./connector-deployer
# tool-generator container_name rsync-ai-tool-generator -> rsync-ai-connector-lifecycle
#
# Nothing else differs. `depends_on`, networks, volumes and every env var are
# identical to the quickstart's — so use this file when you want to run YOUR
# source, and plain `docker compose -f docker-compose.quickstart.yml up -d` when
# you want the published release.
#
# Two consequences of the local tags: `docker compose pull` cannot resolve
# `rsync-ai/*:local`, so pull with `--ignore-buildable` (or just `up -d`, which
# builds what is missing); and `up -d` reuses an already-built local image, so
# pass `--build` after editing source.
#
# ── DO NOT scale context7-mcp ────────────────────────────────────────────────
# An earlier version of this header advised scaling llm-service, planner AND
# context7-mcp to zero. The third one is wrong and the retired flag is
# deliberately not spelled out here, because
# llm-service/tests/test_oss_overlay_documents_its_invocation.py scans this whole
# header for `--scale <name>=` and would match the cautionary quote as if it
# were live advice.
# `context7-mcp` carries `profiles: ["generate"]` (docker-compose.quickstart.yml)
# and is therefore ABSENT from the default project — `config --services` lists it
# only under `--profile generate`. Naming it in `--scale` scales a service the
# rendered project does not contain.
#
# `llm-service` and `planner` ARE in the default project and are pullable: both
# run the stripped `ghcr.io/rsync-ai/llm-service-oss` image, not the moat build,
# so their presence is deliberate rather than a leak. Scaling them to 0 is a
# resource choice (the Go data plane's REST create+run path is Python-free and
# every LLM feature degrades gracefully when absent), not a requirement:
#
# docker compose -f docker-compose.quickstart.yml -f docker-compose.oss.yml \
# up -d --scale llm-service=0 --scale planner=0
#
# ── OSS/cloud split ──────────────────────────────────────────────────────────
# No runtime edition gate anywhere in this file. `RSYNC_EDITION: community`
# below is cosmetic — its only reads are the `edition` field of the
# lifecycle service's /health and /version bodies (llm-service/src/lifecycle/
# main.py:52,60), and nothing branches on it. `RSYNC_BILLING_ENFORCED` is
# deliberately NOT set here: it defaults to enforced (the cloud behavior) and
# only docker-compose.quickstart.yml, the base underneath, sets it false.
# See docs/internal/oss-cloud-runtime-split.md.
# ─────────────────────────────────────────────────────────────────────────────
services:
# One-shot init: seed the mcp_connectors volume so a COLD self-host boots WITH connectors
# (fixes the empty-volume "dead on arrival"). Runs the image's seed-connectors.sh: a no-op on
# the same release, a refresh of the shipped connectors after an upgrade, generated
# connectors kept. No `command:` override -- see docker-compose.quickstart.yml.
# NOTE: orchestrator + temporal-adapter also mount mcp_connectors (:ro); they read connectors
# lazily at pipeline-run time, after this init has completed on boot.
connector-seed:
build:
context: ./shared/mcp-connectors
dockerfile: Dockerfile.seed
image: rsync-ai/connector-seed:local
container_name: rsync-ai-connector-seed
restart: "no"
volumes:
- mcp_connectors:/target
# Connector Deployer (SEC-H-02): THE trusted socket-holder for the OSS stack.
# tool-generator (the connector-lifecycle image below) drops root + the docker
# socket and delegates JIT build/run to this small no-LLM Go service over an
# authenticated internal RPC. Same Go image the cloud stack uses — the OSS/cloud
# split stays at the compose layer, no runtime edition gate. Identical in shape
# to the connector-deployer in docker-compose.quickstart.yml (the layer this file
# sits on), so layering merges cleanly. See
# docs/internal/jit-connector-sandboxing.md + connector-deployer/CONTRACT.md.
connector-deployer:
build:
context: ./connector-deployer
container_name: rsync-ai-connector-deployer
restart: unless-stopped
environment:
# ENVIRONMENT production => app-level S2S fail-closed (503 if secret unset),
# same posture the tool-generator carried. Compose-level :? guard is prod.yml only.
ENVIRONMENT: production
LOG_FORMAT: json
LOG_LEVEL: info
# Rotation for the connector containers THIS service creates -- compose's
# `logging:` blocks only reach the services compose itself starts.
RSYNC_LOG_MAX_SIZE: ${RSYNC_LOG_MAX_SIZE:-10m}
RSYNC_LOG_MAX_FILE: ${RSYNC_LOG_MAX_FILE:-3}
INTERNAL_SERVICE_SECRET: ${INTERNAL_SERVICE_SECRET:-}
DEPLOYER_DOCKER_NETWORK: rsync-ai-mcp
MCP_SHARED_NETWORK: rsync-ai-mcp
OAUTH_TOKENS_VOLUME_NAME: rsync-ai-oauth-tokens
# Root + RW docker.sock live HERE now, not on tool-generator.
user: "0:0"
networks:
- default
- rsync-ai-mcp
volumes:
- mcp_connectors:/app/shared/mcp-connectors:ro
- /var/run/docker.sock:/var/run/docker.sock
tool-generator:
# Build the moat-free lifecycle image instead of pulling the mono generation image.
build:
context: ./llm-service
dockerfile: Dockerfile.oss
image: rsync-ai/connector-lifecycle:local
container_name: rsync-ai-connector-lifecycle
restart: unless-stopped
# Replace the base's llm-service dependency entirely: the OSS stack has no llm-service,
# and the lifecycle service waits for the connector volume to be seeded so /v1/deploy can
# resolve connectors on disk. Also gate on the connector-deployer (SEC-H-02): the
# lifecycle service delegates JIT build/run to it and never touches the socket itself.
depends_on: !override
connector-seed:
condition: service_completed_successfully
connector-deployer:
condition: service_started
environment:
PORT: 5010
TOOLS_DIR: /app/shared/mcp-connectors
# Managed mode: the deployer builds user MCP images + starts containers when the
# orchestrator's preflight calls /v1/deploy. Set false to manage MCP containers via
# a separate stack.
RSYNC_MANAGED_CONNECTORS: ${RSYNC_MANAGED_CONNECTORS:-true}
DOCKER_NETWORK: rsync-ai-mcp
# SEC-H-02: delegate JIT build/run to the connector-deployer over its internal RPC.
DEPLOYER_URL: http://connector-deployer:5011
INTERNAL_SERVICE_SECRET: ${INTERNAL_SERVICE_SECRET:-}
RSYNC_EDITION: community
LOG_FORMAT: json
LOG_LEVEL: info
ENVIRONMENT: production
command: ["python", "-m", "src.lifecycle.main"]
# SEC-H-02: runs the image's default unprivileged appuser — no root, no
# docker.sock. The connector-deployer is the sole trusted socket holder.
networks:
- default
- rsync-ai-mcp
volumes:
- mcp_connectors:/app/shared/mcp-connectors