title: Bouncer placement description: Decide where to place the Bouncer (sidecar vs reverse-proxy, ingress vs egress) with a decision matrix, copy-paste manifests, and real-world scenarios. audience: Platform engineers and solution architects timeEstimate: 30 minutes prerequisites:

  • Control Plane installed (Kickstart or Helm)
  • Familiarity with your traffic path (ingress, egress, or both)
  • Day-1 posture uses BOUNCER_ENFORCEMENT_MODE=shadow

Bouncer placement

Choose where Control Core inspects traffic before you write controls. Placement is a topology decision — sidecar vs reverse-proxy, and ingress vs egress vs bidirectional — not an application-specific fork of the Bouncer binary.

Time estimate: ~30 min to pick a topology, apply a Day-1 shadow overlay, and verify no bypass path remains.

Day-1 default: set BOUNCER_ENFORCEMENT_MODE=shadow so traffic always reaches upstream while you collect decisions and fingerprints. Graduate to enforce after shadow discovery.

Core question

Where does traffic need to be inspected?

Answer that before choosing Helm values. Every viable design places the universal Bouncer on the path that carries the requests (and optionally responses) your controls must see:

You need to evaluate…Place the Bouncer…
Client → API / service (north–south)On the ingress path
App / agent → external LLM, SaaS, partner API (east–west egress)On the egress path
Both directions for the same workloadBidirectional (two placements or one Bouncer with both routes)

Controls (Rego) decide allow, deny, mask, and obligations. Placement only decides which packets the Bouncer can see.

Decision matrix

ScenarioTopologyRecommended placementWhy
Single REST API (VM or small Compose stack)Reverse-proxyOne Bouncer in front of the APISimplest DNS / LB swap; one pair (sandbox + production)
Kubernetes microservices fleetSidecar (per critical pod) or reverse-proxy at mesh edgeSidecar for high-value services; gateway reverse-proxy for shared entryPer-pod isolation vs shared edge — pick by blast radius
Existing API gateway / Ingress already in pathReverse-proxy (or sidecar behind gateway)Insert Bouncer as next hop after gateway, or replace upstream targetReuse TLS / WAF at the edge; Bouncer owns PBAC
AI / LLM provider trafficEgress reverse-proxy or egress sidecarBouncer between app and provider base URLControls see prompts, model IDs, and egress PII before they leave
Legacy monolith (DNS / VIP swap)Reverse-proxyPoint VIP / DNS at Bouncer :8080; upstream = monolith private hostZero app code change; classic modernization pattern
Multi-tenant SaaS (shared edge, many backends)Reverse-proxy (1:n)One or few edge Bouncers; TARGET_HOST / routes per backend groupOne Control Plane; scale Bouncers by tenant or product line
Financial low-latency / UDS co-locationSidecarCo-locate in pod; TARGET_HOST=127.0.0.1 (or loopback UDS when configured)Keeps hop local; still Day-1 shadow
Defense / air-gappedSidecar or reverse-proxy (offline Policy Bridge)Same patterns inside the enclave; no public egress required for enforcementOffline OPAL / last-good bundle; SPIFFE optional per site policy

Troubleshooting: If traffic never appears in Control Plane audit after deploy, the Bouncer is usually off the path (clients still hit the app port). Confirm Service / LB targetPort is 8080, not the app container port. Full reference: Troubleshooting.

Ingress vs egress vs bidirectional

Ingress (north–south)

Evaluates inbound requests (and often responses) before they reach your protected resource.

Click to enlarge

What it evaluates: subject, action, resource path/method, headers, body obligations (masking), environment attributes from PIPs.

Example — healthcare consent (ingress): A clinic API receives chart reads. Ingress Bouncer evaluates consent and role attributes from PIPs before the request hits the records service. Day-1: BOUNCER_ENFORCEMENT_MODE=shadow to prove would-deny without blocking care workflows.

Egress (outbound)

Evaluates traffic leaving your workload toward an external or shared upstream (LLM, partner API, shared tool).

Click to enlarge

What it evaluates: outbound URL/host metadata, model or tool identifiers, prompt/body fields for PII obligations, caller identity.

Example — financial LLM egress PII: Trading research agents call an LLM API. An egress Bouncer sits between the agent and the provider so controls can redact account numbers from prompts before they leave the VPC.

Bidirectional

Same workload needs both inbound governance and outbound tool/LLM governance.

Click to enlarge

Example — AI agent runner: Ingress sidecar (or edge reverse-proxy) governs who may invoke the agent; egress sidecar governs which tools/models the agent may call and what data may leave. Use two Bouncer registrations (or clearly named pairs) sharing one Control Plane.

Sidecar deep guide

When to use

  • Pod- or task-local enforcement with minimal cross-service coupling
  • Different controls per microservice without a shared gateway choke point
  • Latency-sensitive financial services that keep the hop on loopback
  • AI runners that need a dedicated egress sidecar next to the agent process

What BOUNCER_TYPE=sidecar means

Technically, sidecar means the Bouncer shares the workload network namespace and forwards to a localhost (or pod-local) upstream:

SettingTypical sidecar value
BOUNCER_TYPEsidecar
TARGET_HOST127.0.0.1 (Helm: targetHost)
TARGET_PORTApp listen port inside the pod (Helm: targetPort)
Client entryService / Ingress → Bouncer 8080

The app continues to bind its own port; clients must never reach that port from outside the pod.

Copy-paste Kubernetes Pod + Service

apiVersion: v1
kind: Pod
metadata:
  name: payments-api
  labels:
    app: payments-api
spec:
  containers:
    - name: app
      image: your-registry/payments-api:stable
      ports:
        - containerPort: 8000
          name: app
      # App listens on 8000; only the Bouncer should be Service-exposed.
    - name: cc-bouncer
      image: controlcoreio/cc-bouncer-envoy:<tag>
      ports:
        - containerPort: 8080
          name: bouncer
      env:
        - name: BOUNCER_TYPE
          value: sidecar
        - name: BOUNCER_ENFORCEMENT_MODE
          value: shadow
        - name: TARGET_HOST
          value: "127.0.0.1"
        - name: TARGET_PORT
          value: "8000"
        - name: ENVIRONMENT
          value: sandbox
        - name: PAP_API_URL
          value: "https://control-plane.example.internal"
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: cc-bouncer-sandbox
              key: api-key
        - name: BOUNCER_ID
          value: bouncer-payments-sandbox
        - name: RESOURCE_NAME
          value: Payments API
---
apiVersion: v1
kind: Service
metadata:
  name: payments-api
spec:
  selector:
    app: payments-api
  ports:
    - name: http
      port: 80
      # CRITICAL: target the Bouncer listener, not the app port
      targetPort: 8080

Common mistake: Setting Service targetPort to the app port (e.g. 8000). That bypasses the Bouncer entirely. Always use targetPort: 8080 (Bouncer). Keep the app port unexposed on the Service.

Troubleshooting: Pod Running but clients get unfiltered responses — kubectl get svc payments-api -o yaml and confirm targetPort: 8080. Check Bouncer logs for registration: kubectl logs <pod> -c cc-bouncer. Full reference: Troubleshooting.

Scenario: financial Kubernetes microservices fleet

Deploy a sidecar Bouncer on each payment / ledger service that must be independently controlled. Share one Control Plane; use consistent RESOURCE_NAME pairs (sandbox + production). Keep BOUNCER_ENFORCEMENT_MODE=shadow until gap reports and simulator review are green. For ultra-low latency, keep TARGET_HOST=127.0.0.1 and co-locate; tune separately in Bouncer performance tuning.

Scenario: AI agent runner (ingress + egress sidecars)

RoleBOUNCER_TYPETARGET_HOSTTraffic
Ingress sidecarsidecar127.0.0.1 → agent listen portClients → agent
Egress sidecarsidecarProvider or internal gateway hostAgent → LLM / MCP

Register both with distinct BOUNCER_ID values. Author controls against each resource independently — do not hardcode agent paths in deployment manifests; let Rego match on generic request attributes.

Reverse-proxy deep guide

When to use

  • Legacy hosts where you cannot inject a sidecar
  • Shared edge for many backends (SaaS, internal tools)
  • ALB / NLB / Ingress already terminates TLS and needs a single PBAC hop
  • DNS / VIP cutover modernization

Compose example

services:
  cc-bouncer:
    image: controlcoreio/cc-bouncer-envoy:<tag>
    ports:
      - "8080:8080"
    environment:
      BOUNCER_TYPE: reverse-proxy
      BOUNCER_ENFORCEMENT_MODE: shadow
      TARGET_HOST: app
      TARGET_PORT: "8000"
      ENVIRONMENT: sandbox
      PAP_API_URL: https://control-plane.example.internal
      API_KEY: ${CC_SANDBOX_API_KEY}
      BOUNCER_ID: bouncer-edge-sandbox
      RESOURCE_NAME: Edge API
    depends_on:
      - app
  app:
    image: your-registry/customer-api:stable
    # No host port publish — only reachable on the Compose network
    expose:
      - "8000"

VM example

  1. Run the Bouncer package (systemd or Docker) listening on :8080.
  2. Set BOUNCER_TYPE=reverse-proxy, TARGET_HOST=<private monolith IP>, TARGET_PORT=<app port>, BOUNCER_ENFORCEMENT_MODE=shadow.
  3. Point the VIP / DNS A/AAAA record at the Bouncer host.
  4. Restrict security groups so the monolith port is private-only.

Troubleshooting: After DNS cutover, if some clients still bypass controls, search for hardcoded IPs or alternate CNAMEs still aimed at the monolith. Validate with dig / nslookup from client networks. Full reference: Troubleshooting.

Scenario: insurance legacy DNS swap

Point api.carrier.example at the reverse-proxy Bouncer. Upstream remains the AS/400 or JVM monolith on a private VLAN. No application rewrite. Shadow mode first; promote controls after discovery.

Scenario: AWS ALB → ECS Fargate

LayerPoints to
ALB target groupBouncer task :8080
Bouncer TARGET_HOSTPrivate Cloud Map / Service Connect name of the app task
App task security groupAllow only Bouncer SG on the app port

Use BOUNCER_TYPE=reverse-proxy and Day-1 BOUNCER_ENFORCEMENT_MODE=shadow. Do not register the app task directly on the ALB.

Scenario: shared internal tools

One reverse-proxy Bouncer fronts a group of internal admin tools (ticketing, wiki, CI metadata APIs). Route by host or path at the edge load balancer into the Bouncer; configure upstreams via deployment values — keep path semantics in controls, not in Bouncer code.

Bypass prevention checklist

Sidecar

  • Service / Ingress targetPort = 8080 (Bouncer), never the app port
  • App container port not published on a second Service or NodePort
  • Frontend and server-side base URLs resolve to the Bouncer Service
  • NetworkPolicy (or equivalent) denies pod-external traffic to the app port
  • Synthetic check: Bouncer URL healthy; direct app URL unreachable from outside the pod network

Reverse-proxy

  • Public DNS / LB / ALB targets only the Bouncer
  • Upstream app has no public listener / public target group
  • Security groups / firewalls allow clients → Bouncer only; Bouncer → app private
  • No alternate CNAME or IP shortcut to the app
  • Synthetic check: bouncer path succeeds; direct app path fails from client plane

Helm values quick reference

Map each scenario to Helm / env. Upstream host is Helm targetHost → env TARGET_HOST. Always start with shadow; then follow Shadow discovery quickstart.

ScenarioBOUNCER_TYPEUpstream (TARGET_HOST)Shadow next step
Single REST APIreverse-proxyApp Service DNS / hostQuickstart
K8s microservices (per pod)sidecar127.0.0.1Quickstart
Existing gatewayreverse-proxyGateway’s private upstream or app behind gatewayQuickstart
AI / LLM egressreverse-proxy or sidecarProvider or internal AI gateway hostQuickstart
Legacy DNS swapreverse-proxyPrivate monolith hostQuickstart
Multi-tenant SaaS edgereverse-proxyPer-backend group host / VIPQuickstart
Financial low-latencysidecar127.0.0.1Quickstart
Defense air-gappedsidecar or reverse-proxyEnclave-local upstreamQuickstart

Example Helm overlay fragment:

bouncerSandbox:
  env:
    - name: BOUNCER_TYPE
      value: sidecar
    - name: BOUNCER_ENFORCEMENT_MODE
      value: shadow
  targetHost: "127.0.0.1"
  targetPort: 8000

Next steps: Pick a row from the decision matrix → apply the Helm/Compose fragment with BOUNCER_ENFORCEMENT_MODE=shadow → run the shadow discovery quickstart.