Reference

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-stopped

TLS 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:/data

Self-signed upstream certs

If your upstream uses a self-signed certificate (common in staging environments), set 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 CI

What'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
  }'