Bouncer performance tuning

Audience: DevOps / Platform / Security engineers
Time: ~25 min to configure and verify
Prerequisites: At least one Bouncer deployed (Bouncer deployment), controls syncing via Policy Bridge (Deployment overview)

This guide covers the settings that keep inline authorization (PEP → policy engine) in the sub-5 ms product latency envelope: local Unix-socket evaluation, Policy Bridge–preloaded attribute data, optional agentic batch checks, and memory ceilings. Use it when you need predictable P50/P99 decision latency under load.

What “fast” means in Control Core

TargetValue
P50 decision latency≤ 0.8 ms at 20k requests/second
P99 decision latency≤ 4.8 ms at 100k requests/second
P99.9 during Policy Bridge sync bursts≤ 8.0 ms
Policy engine + Policy Bridge client memory≤ 256 MB per Bouncer pod (guidance)

These apply to the allow/deny decision path (authorization filter). Response-body mutation (masking/redaction) has a separate, longer timeout and is not part of the sub-5 ms contract.

Troubleshooting: If latency spikes only when controls change, check Policy Bridge sync health in the Control Plane audit feed (OPAL_SYNC_* / sync history) before changing Bouncer knobs. See Troubleshooting.


1. Local evaluation transport (Unix socket) — ~5 min

By default the Bouncer evaluates controls against a co-located policy engine over a Unix domain socket inside the pod. That avoids TCP loopback overhead on every decision.

SettingDefaultPurpose
OPA_UDS_PATH/var/run/opa/opa.sockSocket path for decision evaluation
(fallback)set to off or tcpForce TCP to the policy engine (compatibility / debugging only)

The policy engine still listens on TCP port 8181 for Policy Bridge data writes and health probes. Decision traffic should use the socket.

Verification

# Inside the Bouncer container — socket should exist and be writable by the bouncer user
ls -la /var/run/opa/opa.sock

# Confirm UDS is configured (example for Kubernetes)
kubectl exec -n <ns> deploy/<bouncer> -- printenv OPA_UDS_PATH

Expect OPA_UDS_PATH=/var/run/opa/opa.sock (or your custom path), not off.

Troubleshooting: If decisions fail open/closed unexpectedly after a restart, confirm the socket directory is mounted and owned correctly, then check Bouncer logs for transport fallback messages. Full reference: Troubleshooting.


2. Keep attribute data off the hot path — ~10 min

Do not call external attribute APIs (IdP, HR, threat intel) on every request. Attribute data should arrive through Policy Bridge into the local policy engine memory (data.pip.*). Per-request “enrichment” HTTP to the Control Plane is off by default and must stay off for production latency.

SettingDefaultPurpose
CC_CONTEXT_ENRICHMENT_ENABLEDfalseAllows background context HTTP only when true — never use as a per-request dependency

PIP freshness tiers (banking / defence posture)

After each decision, the Bouncer can check freshness metadata (_cc_pip_meta) for preloaded PIP documents. Stale realtime data can fail closed in strict mode.

SettingDefaultNotes
BOUNCER_PIP_STALENESS_MODEwarnstrict / warn / ignore
BOUNCER_PIP_REALTIME_TTL_SEC30Max age for realtime attributes
BOUNCER_PIP_REFERENCE_TTL_SEC900Max age for reference attributes (15 min)
BOUNCER_PIP_SENTINEL_TTL_SEC3600Max age for sentinel / threat intel (60 min)
CC_PIP_HMAC_KEYunsetTier-0 secret: verify integrity of PIP metadata when set
BOUNCER_PIP_CLASSIFIED_ISOLATIONfalseDefence posture: classified data namespace + hardened isolation

Helm posture overlays (Custom / Kubernetes installs):

  • Banking / OSFI-FINTRAC style: values-pip-banking.yamlstrict, HMAC expected, enrichment off, UDS on
  • Defence / classified: values-pip-defence.yaml — stricter realtime TTL, classified isolation, memory limit via GOMEMLIMIT
helm upgrade --install controlcore ./controlcore \
  -f values.yaml \
  -f values-pip-banking.yaml

Store CC_PIP_HMAC_KEY in your secret manager (secretKeyRef / ExternalSecrets) — never plaintext in values files.

Verification

  1. In Control Plane → Audit, confirm Policy Bridge sync completed for the environment.
  2. Confirm CC_CONTEXT_ENRICHMENT_ENABLED is unset or false on Bouncer pods.
  3. For banking/defence: confirm BOUNCER_PIP_STALENESS_MODE=strict and HMAC secret mounted.

Troubleshooting: Unexpected denies with reason containing PIP_STALE_* mean freshness failed closed — refresh the PIP source via Policy Bridge or temporarily set mode to warn while investigating. Attribute mapping help: PIP admin guide.


3. Agentic batch authorization (optional) — ~5 min

AI agents that predict several tool calls can request N decisions in one gRPC round-trip instead of N separate authorization calls. This reduces IPC overhead for agentic sequences. The API accepts generic subject / action / resource / environment tuples only — no application-specific branches in the Bouncer.

SettingDefaultPurpose
BOUNCER_BATCH_AUTHZ_ENABLEDfalseStart the batch authorization gRPC service
BATCH_AUTHZ_PORT9194Listen port inside the pod
BOUNCER_BATCH_AUTHZ_MAX_TUPLES64Max decisions per request (hard cap 256)
BOUNCER_BATCH_AUTHZ_WORKERS8Concurrent evaluators per batch

Verification

# With the flag enabled, the Bouncer should listen on the batch port
kubectl exec -n <ns> deploy/<bouncer> -- printenv BOUNCER_BATCH_AUTHZ_ENABLED BATCH_AUTHZ_PORT

Wire your agent runtime to controlcore.batch.v1.BatchAuthz/Evaluate on that port (same network namespace as the Bouncer). Leave the flag off unless an agent platform needs it.

Troubleshooting: If batch calls time out, check network policies allow pod-local TCP to BATCH_AUTHZ_PORT, and that tuple count ≤ BOUNCER_BATCH_AUTHZ_MAX_TUPLES. Standard API traffic continues to use the Envoy authorization filter regardless of this flag.


4. Memory ceiling for the policy engine — ~5 min

There is no opa --max-memory-alloc flag. Cap memory with the Go soft limit plus Kubernetes limits:

SettingExamplePurpose
GOMEMLIMIT256MiBSoft memory limit for the policy engine process
Pod resources.limits.memoryGOMEMLIMITHard cgroup limit (set in Helm values)

Defence overlay sets GOMEMLIMIT=256MiB as a starting point. Raise only with measured heap profiles — keep the pod budget near 256 MB for policy engine + Policy Bridge client unless you have an approved exception.

Verification

kubectl exec -n <ns> deploy/<bouncer> -- printenv GOMEMLIMIT
kubectl top pod -n <ns> -l app.kubernetes.io/component=bouncer

Troubleshooting: OOMKills with a low GOMEMLIMIT usually mean the controls bundle or PIP data is larger than planned — trim unused controls, reduce PIP payload size, or raise the limit with a documented capacity review. See Custom (Kubernetes / Helm).


5. Control authoring tips that preserve latency

Controls are Rego under the hood. High-frequency rules should use exact equality on hot fields (method, path prefix via data allowlists) so the policy engine can index them. Prefer data-driven allowlists from PIP / Control Plane over heavy regex.match / contains in every request.

See Rego guidelines and PBAC best practices.

Troubleshooting: If only certain routes are slow, profile which controls match those paths — a single expensive regex on a hot path can dominate P99. Prefer allowlists. Help: Troubleshooting — controls.


6. Load validation (lab)

Use a sized lab cluster (not a laptop) to prove SLA gates:

  1. Point traffic at the Bouncer / Envoy listener (path that does run authorization — not a health bypass).
  2. Run constant-arrival-rate tests at 20k RPS (P50) and 100k RPS (P99).
  3. Record P50/P99 and attach them to your change record.

Operator playbooks for slow evaluation: PBAC operator playbooks.

Troubleshooting: If you cannot reach target RPS, scale Bouncer replicas horizontally inside the environment (Fleet scaling caps) before changing evaluation settings.


Settings quick reference

AreaKey settingsDefault posture
Eval transportOPA_UDS_PATHSocket on
Hot-path I/OCC_CONTEXT_ENRICHMENT_ENABLEDOff
PIP freshnessBOUNCER_PIP_STALENESS_MODE, TTLs, CC_PIP_HMAC_KEYwarn / banking→strict
Batch authzBOUNCER_BATCH_AUTHZ_ENABLED, BATCH_AUTHZ_PORTOff
MemoryGOMEMLIMIT + K8s limits~256 MiB guidance

Next steps