1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# Integration overlay for cf-controlplane + cf-dataplane.
#
# Usage from this repo:
# export CF_INTEGRATION_ROOT="$PWD"
# export CF_DATAPLANE_IMAGE="ghcr.io/contextforge-org/contextforge-data-plane:latest"
# export CF_DATAPLANE_PLATFORM="linux/amd64"
# # Or let `cf-integration stack up --lane external` resolve `auto`.
# docker compose \
# -f /path/to/cf-controlplane/docker-compose.yml \
# -f "$CF_INTEGRATION_ROOT/docker/docker-compose.cf-dataplane.yaml" \
# up
services:
# cf-controlplane's upstream compose service key is gateway; expose it as
# cf-controlplane on mcpnet. DATAPLANE_PUBLISHER is the only behavioral
# change to the upstream gateway: it publishes virtual server configs to
# Redis for cf-dataplane.
gateway:
networks:
mcpnet:
aliases:
- cf-controlplane
environment:
DATAPLANE_PUBLISHER: "true"
# Short snapshot interval so runtime-created users/servers reach the
# dataplane quickly in functional runs; raise for load benchmarks.
# Requires a control-plane image with configurable publisher interval;
# older images ignore the variable (60s behavior).
DATAPLANE_PUBLISHER_INTERVAL_SECONDS: ${CF_DATAPLANE_PUBLISHER_INTERVAL_SECONDS:-2}
volumes:
- ${CF_INTEGRATION_ROOT:?Set CF_INTEGRATION_ROOT to the integration harness root}/scripts/prepare_standalone_config.py:/opt/contextforge-integration/prepare_standalone_config.py:ro
dataplane:
image: ${CF_DATAPLANE_IMAGE:?Set CF_DATAPLANE_IMAGE to the cf-dataplane image tag}
pull_policy: ${CF_DATAPLANE_PULL_POLICY:-always}
platform: ${CF_DATAPLANE_PLATFORM:?Set CF_DATAPLANE_PLATFORM to the cf-dataplane image platform}
labels:
name: cf-dataplane
restart: unless-stopped
networks:
mcpnet:
aliases:
- cf-dataplane
extra_hosts:
- host.docker.internal:host-gateway
expose:
- "4445"
environment:
CONTEXTFORGE_DATA_PLANE_ADDRESS: 0.0.0.0:4445
CONTEXTFORGE_DATA_PLANE_REDIS_HOSTNAME: redis
CONTEXTFORGE_DATA_PLANE_REDIS_PORT: "6379"
CONTEXTFORGE_DATA_PLANE_REDIS_CONNECTION_MODE: plain-text
CONTEXTFORGE_DATA_PLANE_TOKEN_SECRET: ${JWT_SECRET_KEY:-my-test-key-but-now-longer-than-32-bytes}
# The published image currently includes its non-production `with_tools`
# bootstrap routes, whose clap model requires an RSA signing-key path.
# The standalone load helper calls only the internal user-config route;
# token creation stays disabled, so satisfy the unused path without a key.
CONTEXTFORGE_DATA_PLANE_TOKEN_VERIFICATION_PRIVATE_KEY: /dev/null
CONTEXTFORGE_DATA_PLANE_UPSTREAM_CONNECTION_MODE: plain-text-or-tls
# These two MCP transport settings intentionally retain the historical
# prefix in the current dataplane configuration contract.
CONTEXTFORGE_GATEWAY_RS_MCP_ALLOWED_HOSTS: ${CF_DATAPLANE_MCP_ALLOWED_HOSTS:-127.0.0.1:${NGINX_PORT:-8080},localhost:${NGINX_PORT:-8080},nginx}
CONTEXTFORGE_GATEWAY_RS_MCP_ALLOWED_ORIGINS: ${CF_DATAPLANE_MCP_ALLOWED_ORIGINS:-http://127.0.0.1:${NGINX_PORT:-8080},http://localhost:${NGINX_PORT:-8080}}
# Disable the per-subject config cache for functional runs: its sliding
# TTL freezes stale configs under steady traffic (retry loops renew it
# forever), hiding servers created after first contact. Set to 60 to
# restore the image default for load benchmarks.
CONTEXTFORGE_DATA_PLANE_USER_CONFIG_CACHE_EXPIRY_SECONDS: ${CF_DATAPLANE_USER_CONFIG_CACHE_EXPIRY_SECONDS:-0}
RUST_LOG: ${CF_DATAPLANE_LOG:-info}
depends_on:
redis:
condition: service_started
deploy:
replicas: 1
nginx:
volumes:
- ${CF_INTEGRATION_ROOT:?Set CF_INTEGRATION_ROOT to the integration harness root}/docker/nginx.cf-dataplane.conf:/etc/nginx/nginx.conf:ro
depends_on:
gateway:
condition: service_healthy
dataplane:
condition: service_started