Proxy Setup
Everything you need to integrate Gostly into a real development or CI environment: TLS termination, multiple services, per-service modes, and connecting CI pipelines.
Basic single-service setup
The simplest configuration — one upstream, one proxy. The compose file below matches what your dashboard generates for the free tier:
services:
ghost-proxy:
image: <your-registry-host>/gostlyai/proxy:latest
container_name: ghost-proxy
ports:
- "8080:8080"
environment:
GOSTLY_LICENSE_KEY: "YOUR_LICENSE_KEY"
BACKEND_URL: "http://your-service:3000" # any HTTP upstream
MOCK_DIR: /data/mocks
MODE_FILE_PATH: /data/mode.txt
volumes:
- ./data:/data
restart: unless-stoppedTLS termination with Caddy
For environments where your application expects HTTPS, front the proxy with Caddy. Caddy handles TLS termination and forwards to Gostly over plain HTTP:
# Caddyfile
mock.internal.example.com {
reverse_proxy ghost-proxy:8080
}services:
caddy:
image: caddy:2-alpine
ports:
- "443:443"
- "80:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy-data:/data
depends_on:
- ghost-proxy
ghost-proxy:
image: <your-registry-host>/gostlyai/proxy:latest
container_name: ghost-proxy
# Do NOT expose port directly — traffic comes through Caddy
environment:
GOSTLY_LICENSE_KEY: "YOUR_LICENSE_KEY"
BACKEND_URL: "https://api.example.com"
MOCK_DIR: /data/mocks
MODE_FILE_PATH: /data/mode.txt
volumes:
- ./data:/dataSelf-signed upstream certs
ACCEPT_INVALID_CERTS=true on the proxy. This emits a startup warning — do not use in production against real upstreams.Multiple upstream services
A single ghost-proxy instance handles multiple upstream services simultaneously. You register each service via the API with a routing rule — either host-based (matched on the Host request header) or path-based (matched on a URL prefix). Each service gets its own mock library, mode, chaos config, and redaction rules.
Register services after the stack is running:
# Host-based routing — route by the Host header your app sends
curl -X POST http://localhost:8000/services \
-H 'Content-Type: application/json' \
-d '{
"name": "payments",
"upstream_url": "https://payments.internal",
"routing_type": "host",
"routing_value": "payments.internal"
}'
# Path-based routing — route by URL prefix
curl -X POST http://localhost:8000/services \
-H 'Content-Type: application/json' \
-d '{
"name": "users",
"upstream_url": "https://users.internal",
"routing_type": "path",
"routing_value": "/api/users"
}'The proxy resolves routing at request time — no restart needed. Requests that don't match any registered service fall through to BACKEND_URL (the default upstream configured at startup).
Each service can be switched to a different mode independently:
# Switch one service to MOCK while others stay in LEARN
curl -X PUT http://localhost:8000/services/{service_id} \
-H 'Content-Type: application/json' \
-d '{"mode": "MOCK"}'
# Or switch the global mode for all services at once
curl -X POST http://localhost:8000/v1/mode \
-H 'Content-Type: application/json' \
-d '{"mode": "MOCK"}'CI integration
In CI you start the stack in MOCK mode against a committed mock library, run your test suite, and tear down. No upstream connectivity required — the full run is offline. Here's a GitHub Actions example:
# .github/workflows/test.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Authenticate with Gostly registry
run: |
TOKEN=$(curl -s -H "Authorization: Bearer ${{ secrets.GOSTLY_LICENSE_KEY }}" \
https://gostly.ai/v1/registry-token | jq -r .token)
echo "$TOKEN" | docker login -u AWS --password-stdin \
<your-registry-host>
- name: Start Gostly mock stack
run: |
docker compose -f docker-compose.ci.yml up -d
until curl -sf http://localhost:8080/health; do sleep 1; done
- name: Run tests
env:
API_BASE_URL: http://localhost:8080
run: npm test
- name: Tear down
if: always()
run: docker compose -f docker-compose.ci.yml down# docker-compose.ci.yml — MOCK mode only, committed ./data mounted read-only
services:
ghost-proxy:
image: <your-registry-host>/gostlyai/proxy:latest
container_name: ghost-proxy
ports:
- "8080:8080"
environment:
GOSTLY_LICENSE_KEY: ${{ secrets.GOSTLY_LICENSE_KEY }}
BACKEND_URL: "http://api.example.com"
MOCK_DIR: /data/mocks
MODE_FILE_PATH: /data/mode.txt
volumes:
- ./data:/data:ro # committed mock library, read-only in CIWhat's safe to commit from ./data/
Never commit data/traffic/ — it contains the full unscubbed append-only traffic log with raw HTTP bodies. data/mocks/ contains deduplicated response bodies and may contain PII depending on what the upstream returns — review before committing.
Safe to commit: data/mode.txt, data/machine_id. Exclude data/license_cache.json and data/traffic/ in your .gitignore.
The proxy runs standalone — ghost-web is not required to serve MOCK mode. In CI the proxy reads mocks from the mounted ./data/mocks/ directory and mode from INITIAL_MODE or data/mode.txt. No dashboard, no database, no upstream connectivity needed.
Chaos injection
Chaos config is applied per service and can be updated at runtime without restarting the proxy. Useful for resilience testing:
# Enable chaos for a specific service
curl -X PUT http://localhost:8000/services/payments/chaos \
-H 'Content-Type: application/json' \
-d '{
"enabled": true,
"error_rate": 0.05, // 5% of requests return a 500
"latency_ms_min": 50,
"latency_ms_max": 300,
"inject_status": null // or e.g. 429 to always return rate-limit
}'