Debug Mode: Troubleshooting Policy Decisions with Verbose Logging
The useful version of “debug mode” in Keeptrusts is not a separate product surface. It is a disciplined combination of runtime logging, configuration inspection, dry-run validation, and event evidence. When a request is blocked, redacted, or routed in a way you did not expect, the goal is not to collect more output than necessary. The goal is to collect the right output from the right layer in the right order.
Use this page when
- A request is being blocked or modified and the policy reason is not obvious.
- You need to separate config problems from routing problems or provider behavior.
- You want a repeatable debugging workflow instead of ad hoc log chasing.
Primary audience
- Primary: Technical Engineers and platform operators
- Secondary: Security reviewers and technical leaders
The problem
Most “gateway debugging” is not really about the gateway first. It is about ambiguity.
An operator sees a 409 response, a redacted result, or a missing event and has to answer several questions quickly. Did the active config contain the expected chain? Did the gateway resolve provider credentials correctly? Was the request sent to the provider target the team thought it was using? Did the control plane receive the resulting event? Was the result caused by enforcement logic, config precedence, or upstream behavior?
When those questions are answered out of order, debugging gets noisy fast. Teams start changing YAML before proving which file is active. They restart the gateway before confirming whether the problem is a missing token. They inspect provider behavior before checking whether the request was pinned to the intended target.
That is why verbose logging alone is not enough. Extra output is only useful when it is anchored to the correct slice of the system.
The solution
The most reliable Keeptrusts debugging flow combines four surfaces:
kt config show --jsonto confirm what the CLI resolved.kt gateway check --verboseto validate config, provider readiness, and chain composition before runtime.RUST_LOG=debug kt gateway run ...to expose runtime behavior at the gateway process level.kt events tail --jsonto prove what decision the platform actually recorded.
That sequence keeps each layer honest.
Config inspection answers “what inputs are active.” Gateway check answers “is this configuration ready to run.” Debug logging answers “what happened in process.” Event tailing answers “what decision evidence was emitted.” Together, they are enough to diagnose most policy-decision surprises without guessing.
Implementation
Start with resolved configuration, not with the running process:
kt config show --json --file policy-config.yaml
This command is especially important when multiple environment variables or profiles might be in play. If the effective API URL or token presence is not what you expected, the rest of the debugging session will drift quickly.
Next, check the policy file and provider state without starting the server blindly:
kt gateway check --config policy-config.yaml --verbose
Because kt gateway check prints provider readiness, routing strategy, and policy-chain composition, it is a strong way to catch unresolved credentials or unexpected chain contents before you involve live traffic.
If the configuration looks correct, run the gateway with debug logging enabled:
RUST_LOG=debug kt gateway run \
--listen 0.0.0.0:41002 \
--policy-config policy-config.yaml
At this point you have a runtime you can interrogate with controlled requests. Keep the test request small and reproducible:
curl http://localhost:41002/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Keeptrusts-Provider: openai-primary" \
-d '{
"model": "gpt-5.4-mini",
"messages": [
{"role": "user", "content": "Summarize this policy incident in two sentences."}
]
}'
Pinning the provider is valuable during debugging because it removes routing uncertainty while still preserving policy evaluation. If the request behaves differently when pinned to a target, you have learned something concrete about routing or fallback rather than policy content.
After the request runs, switch from process logs to recorded evidence:
kt events tail --since 5m --verdict blocked --json | \
jq '.[] | {id, timestamp, verdict, reason_code, provider, model, config_version}'
That final step matters because logs and decisions are not the same thing. The debug log may tell you what the runtime attempted. The event tells you what outcome the platform recorded. During incident review, the event is usually the evidence you will want to preserve.
One practical workflow for a blocked request looks like this:
- Check resolved inputs with
kt config show --json. - Validate the config and provider credentials with
kt gateway check --verbose. - Re-run the request under
RUST_LOG=debug. - Query recent blocked decisions with
kt events tail --since 5m --verdict blocked --json. - Compare the reason code and config version against the change you just made.
One practical workflow for an unexpected route selection is similar, but use provider pinning to isolate the path. If the pinned request behaves correctly while the unpinned request does not, the next investigation surface is routing configuration instead of enforcement logic.
The important discipline is to avoid changing more than one variable at a time. A debugging session becomes misleading very quickly if you edit the policy chain, change environment variables, and test a different model all at once. Keeptrusts gives you enough observability to narrow the variable set instead of expanding it.
Results and impact
Teams that use this layered debugging pattern usually resolve policy surprises faster because each step answers a different question cleanly. They stop overusing deep logs for problems that are really config-precedence mistakes, and they stop changing YAML before they have event evidence.
The other benefit is better handoff quality. A debug session that ends with config output, gateway-check output, and a recent event slice is much easier for another engineer to continue than one that only ends with “it looked wrong in the logs.”
Key takeaways
- Keeptrusts debugging works best when you move from resolved config to runtime logs to recorded event evidence.
- Use
kt config show --jsonfirst whenever environment or profile ambiguity is possible. - Use
kt gateway check --verboseto catch readiness problems before you chase live traffic. - Use
RUST_LOG=debugfor the gateway process, not as a substitute for decision evidence. - Use
kt events tail --jsonto confirm what the platform actually recorded after the request completed.