Signadot: Validate Microservice Changes
Use Signadot to validate code changes against real cluster dependencies without
building an image unless the service cannot run locally. Prefer a local-mapped
sandbox: run the changed service locally, route cluster traffic to it with a
routing key, and iterate until the full validation path passes.
Reference Map
Load these one-hop references only when the workflow reaches that topic:
- references/sandbox-setup.md: cluster and
workload resolution, sandbox reuse/create/update, devbox detection, env var
reconstruction, process startup, and port rules.
- references/validation-types.md: how to run
integration tests, existing e2e suites, ad-hoc browser automation, and
existing tagged Signadot plans with routing-key injection.
- references/troubleshooting.md: fast
diagnostics for 503s, baseline-looking responses, browser blanks, propagation
failures, async hops, process exits, and gRPC DNS delays.
- references/worked-example.md: an end-to-end
example of validating a UI change through a local sandbox.
Core Workflow
The workflow has four phases: A (before coding), B (set up the
sandbox), C (run validation), D (iterate). Steps are numbered for
sequence; each links to the reference that owns its detail.
- Define what "validated" means before coding (Phase A). If the user
did not specify the validation type, ask one question and offer these
choices: integration tests, existing e2e suite, ad-hoc browser
automation, or existing tagged Signadot plan. The validation type affects
implementation shape, sandbox ports, and routing-key plumbing. If they name
another tool, such as Locust, Postman, or a custom Cypress script, use the
same principle: find where its HTTP/gRPC client lives and how it will send
the routing key.
See references/validation-types.md.
- Resolve cluster and workload (Phase B). Use the Signadot MCP server
when available; otherwise use the CLI. Resolve names through tools or
repo-owned Signadot specs, not guesses. If a tool response asks for
confirmation because there are multiple clusters, workloads, or devboxes,
ask the user to choose.
See references/sandbox-setup.md.
- Reuse before creating (Phase B). Look for a live sandbox for the same
user and workload before making a new one. Reuse keeps the routing key
stable across local test env vars, curls, and browser automation.
See references/sandbox-setup.md.
- Create or update the sandbox (Phase B). Use the existing repo spec
under
.signadot/ when one matches. Otherwise create the smallest
local-mapped sandbox that contains only the service(s) changed or needed to
follow the changed user-visible path.
See references/sandbox-setup.md
for required fields, port rules, and the preview-endpoints policy.
- Pull env and start the service (Phase B). Read the repo's normal run
commands first. Export every required env var, especially service-to-service
addresses, secrets, and feature flags. Build before backgrounding, then
verify the process is alive and its port is listening.
See references/sandbox-setup.md
and the "Starting The Service" section.
- Validate through the cluster URL (Phase C). Send traffic to
http://<svc>.<namespace>.svc:<service-port>/... with routing-key headers.
Do not validate by calling localhost:<port> directly.
See references/validation-types.md for
the subsection matching the type chosen in Phase A.
- Iterate on failures (Phase D). Read the exact failure, fix the
smallest cause, restart affected services, and re-run the same validation
against the same sandbox. Do not report success until the agreed validation
path passes.
See references/troubleshooting.md.
- Close out cleanly. Report the sandbox name, cluster, routing key,
target URL, validation command or browser path, and local processes
stopped. Leave the sandbox up by default; surface the delete command as an
option only.
MCP And CLI Use
If a Signadot MCP server is available, use it for control-plane work: list and
inspect clusters, sandboxes, routegroups, workloads, endpoints, and devboxes;
create or update sandboxes when the side-effect policy below permits it. Search
by intent, load tool schemas before calling tools, and prefer tool output over
memory.
If MCP is unavailable, use CLI fallbacks:
signadot cluster list -o json
signadot sandbox list -o json
signadot sandbox get <name> -o json
signadot sandbox apply -f <spec.yaml>
signadot sandbox get-env <name>
signadot sandbox get-files <name>
Use kubectl get <kind> <name> -n <ns> -o yaml only when MCP cannot fetch the
same workload, ConfigMap, Secret, or Service data and the local environment has
cluster access.
Signal Inventory
| Signal |
Use |
| Local process receives real cluster traffic |
Default first choice for validating changed services without an image build |
| Cluster DNS/IP is resolvable locally |
signadot local connect or a devbox makes .svc names reachable |
| Local stdout/stderr streams during routed requests |
Watch requests land in changed code, see panics, attach a debugger |
| Sandbox readiness, forks, routing state, and tunnel connection |
Confirm the sandbox can route before testing code |
| Preview endpoints, when explicitly requested |
Deterministically hit a fork without manual routing headers, but may expose a public *.preview.signadot.com URL |
| Routing-key isolation |
Prove keyed traffic hits the sandbox while unkeyed traffic stays on baseline |
signadot local proxy |
Diagnose dependency reachability; not a validation result |
Side-Effect Policy
| Action |
Default behavior |
| Read clusters, workloads, endpoints, sandboxes, routegroups |
Do autonomously |
| Create an isolated sandbox for this task |
Do autonomously when cluster/workload are unambiguous |
| Update a sandbox clearly owned by this task |
Do autonomously |
| Update a shared sandbox or routegroup |
Ask first |
Add public preview endpoints such as defaultRouteGroup.endpoints |
Ask first |
| Delete a sandbox |
Ask first; default is leave it up |
Run sudo signadot local connect --cluster <cluster> or signadot local disconnect |
Ask the user to run it; it modifies the local network stack and may require sudo |
Run signadot local status or signadot local proxy |
Do autonomously when useful; proxy is diagnostic, not proof of validation |
Routing Rules
Two Ports Matter
Keep the two port concepts separate:
| Context |
Port to use |
How to resolve |
| Sandbox local mapping |
Workload/container port |
MCP workload-port resolver such as resolve_workload_port, or workload spec |
Validation traffic to .svc URL |
Kubernetes Service port |
Endpoint resolver such as resolve_endpoints, Service object, or repo Signadot spec |
The sandbox can be ready: true with a connected tunnel even when its mapping
uses the wrong container port; requests then fall through to baseline. If routing
looks broken, check for a virtual host entry such as
<sandbox>-<mapping>-*.<namespace>.svc in /etc/hosts.
Always Hit The Cluster URL
Validation traffic must target the cluster service URL, not the local process:
curl -sS "http://<svc>.<namespace>.svc:<service-port>/<path>" \
-H "baggage: sd-routing-key=<routing-key>" \
-H "tracestate: sd-routing-key=<routing-key>"
For gRPC:
grpcurl -plaintext \
-H "baggage: sd-routing-key=<routing-key>" \
-H "tracestate: sd-routing-key=<routing-key>" \
-d '{"field":"value"}' \
<svc>.<namespace>.svc:<service-port> package.Service/Method
baggage and tracestate with key name sd-routing-key are always accepted.
If clusterConfig.routing.customHeaders lists additional headers, inject every
listed header with the routing key as the value. Use query-param routing only
when the surface cannot set headers.
signadot local proxy can help debug whether a cluster dependency is reachable
from the machine, but a localhost proxy URL bypasses the same end-to-end routing
path and is not a validation result.
Forward The Key Across Hops
The routing key reaches a sandboxed downstream service only if every hop
propagates it. Raw HTTP/gRPC clients, custom proxy handlers, and async producers
often drop the key unless the code explicitly copies it. When adding a new
forwarder, copy the incoming baggage header onto outbound requests or use the
service's existing instrumented client.
Secrets And Env Vars
When reconstructing env vars from workload specs, ConfigMaps, or Secrets:
- Resolve every required value before blaming the code. Missing DB addresses,
credentials, and feature flags often appear as first-request 500s.
- Do not print secret values in chat, logs, or summaries. Redact them as
<redacted> and avoid pasting decoded Secret contents.
- Prefer exporting secrets directly into the process environment. If an env file
is necessary, write it with restrictive permissions and remove it during
cleanup.
- For service addresses, grep the code for config lookups such as
*_ADDR,
*_HOST, *_URL, and language-specific helpers. Defaults that work inside a
pod may not work from a local process.
Validation Type Picker
- Integration tests: run the language-native test command against the
cluster
.svc URL, injecting routing headers in the test client or a shared
transport.
- Existing e2e suite: use the repo's existing command and config. Point its
base URL at the
.svc URL and attach routing headers at the framework HTTP
layer.
- Ad-hoc browser automation: drive the UI from a browser to exercise the
change end-to-end. Use whatever browser-automation tooling is available
(Playwright is the common one); inject routing headers on every request,
clear previous routes first when browser state persists, and drive the full
UI path.
- Existing tagged Signadot plan: pick a tag by
selectionHint and run it
against the sandbox. For everything else about plans — params, secrets,
logs/outputs, authoring, tagging — defer to the signadot-plan skill.
Read references/validation-types.md before
running the chosen type.
Failure Loop
When validation fails, do not stop at "validation failed." Continue the loop:
- Quote the exact failure: status code, error message, stack trace, missing UI
element, bad field, or empty browser state.
- Identify whether the likely cause is app code, sandbox shape, missing env,
stale process, wrong port, routing-key propagation, or a changed downstream
consumer that must also run locally.
- Apply the smallest fix, rebuild and restart affected processes, and re-run
the same validation with the same routing key and target URL.
- Stop to ask only when the fix requires a judgment call that cannot be made
without the user — for example, choosing between fixing forward, making a
change backward-compatible, or accepting an intentional break. Do not ask
permission for mechanical fixes such as typos, missing env vars, or
restarting a stopped process; just apply them and continue the loop.
Before declaring done, consider whether the verified behavior should be codified
as a Signadot plan. Do this when the bug is deterministic, important, and not
covered elsewhere. Skip it for typos, infra flakes, exploratory checks, and
non-deterministic failure modes. The sandbox is still up — running the new
plan against it once confirms it catches the bug (or passes for the fixed
code) before tagging.
Quick Diagnostics
| Symptom |
Likely cause |
First check |
| Envoy 503 from devbox but pod is healthy |
Hit container port instead of Service port |
Service object / endpoint resolver |
| Response looks like old baseline behavior |
Routing key dropped or localhost target used |
Target URL and all routing headers |
| Sandbox ready and tunnel connected but local process gets no traffic |
Wrong sandbox mapping port |
/etc/hosts virtual sandbox entry |
| Local process starts, then first real request returns 500 |
Missing env var or unresolved dependency address |
Config lookups and exported env |
| Browser body or accessibility tree empties after interaction |
SPA runtime error |
Browser console errors and page.content().length |
| Background process exits soon after start |
Shell job received SIGHUP or port conflict |
Process status, listener, log file |
| gRPC dial stalls around DNS timeout |
Go gRPC SRV lookup delay |
passthrough:/// target and connection reuse |
Read references/troubleshooting.md for detailed
signals and fixes.
Final Report
Include:
- Validation type and command/tool path used
- Cluster, sandbox name, routing key, and service URL
- What passed, including browser/user path when relevant
- Any local process PIDs or ports stopped
- Sandbox teardown command as an option, not an action:
signadot sandbox delete <sandbox-name>
If the user ran local network connect, remind them that they can later run:
signadot local disconnect
1---2name: signadot-validate3description: Use this skill to validate code changes against real Kubernetes microservice dependencies with Signadot signals such as local sandboxes, cluster reachability, logs, endpoints, and routing-key isolation.4---56# Signadot: Validate Microservice Changes78Use Signadot to validate code changes against real cluster dependencies without9building an image unless the service cannot run locally. Prefer a local-mapped10sandbox: run the changed service locally, route cluster traffic to it with a11routing key, and iterate until the full validation path passes.1213## Reference Map1415Load these one-hop references only when the workflow reaches that topic:1617- [references/sandbox-setup.md](references/sandbox-setup.md): cluster and18 workload resolution, sandbox reuse/create/update, devbox detection, env var19 reconstruction, process startup, and port rules.20- [references/validation-types.md](references/validation-types.md): how to run21 integration tests, existing e2e suites, ad-hoc browser automation, and22 existing tagged Signadot plans with routing-key injection.23- [references/troubleshooting.md](references/troubleshooting.md): fast24 diagnostics for 503s, baseline-looking responses, browser blanks, propagation25 failures, async hops, process exits, and gRPC DNS delays.26- [references/worked-example.md](references/worked-example.md): an end-to-end27 example of validating a UI change through a local sandbox.2829## Core Workflow3031The workflow has four phases: **A** (before coding), **B** (set up the32sandbox), **C** (run validation), **D** (iterate). Steps are numbered for33sequence; each links to the reference that owns its detail.34351. **Define what "validated" means before coding** (*Phase A*). If the user36 did not specify the validation type, ask one question and offer these37 choices: integration tests, existing e2e suite, ad-hoc browser38 automation, or existing tagged Signadot plan. The validation type affects39 implementation shape, sandbox ports, and routing-key plumbing. If they name40 another tool, such as Locust, Postman, or a custom Cypress script, use the41 same principle: find where its HTTP/gRPC client lives and how it will send42 the routing key.43 See [references/validation-types.md](references/validation-types.md).442. **Resolve cluster and workload** (*Phase B*). Use the Signadot MCP server45 when available; otherwise use the CLI. Resolve names through tools or46 repo-owned Signadot specs, not guesses. If a tool response asks for47 confirmation because there are multiple clusters, workloads, or devboxes,48 ask the user to choose.49 See [references/sandbox-setup.md](references/sandbox-setup.md#discovery-and-confirmation).503. **Reuse before creating** (*Phase B*). Look for a live sandbox for the same51 user and workload before making a new one. Reuse keeps the routing key52 stable across local test env vars, curls, and browser automation.53 See [references/sandbox-setup.md](references/sandbox-setup.md#reuse-before-create).544. **Create or update the sandbox** (*Phase B*). Use the existing repo spec55 under `.signadot/` when one matches. Otherwise create the smallest56 local-mapped sandbox that contains only the service(s) changed or needed to57 follow the changed user-visible path.58 See [references/sandbox-setup.md](references/sandbox-setup.md#creating-or-updating-a-sandbox)59 for required fields, port rules, and the preview-endpoints policy.605. **Pull env and start the service** (*Phase B*). Read the repo's normal run61 commands first. Export every required env var, especially service-to-service62 addresses, secrets, and feature flags. Build before backgrounding, then63 verify the process is alive and its port is listening.64 See [references/sandbox-setup.md](references/sandbox-setup.md#env-and-config-reconstruction)65 and the "Starting The Service" section.666. **Validate through the cluster URL** (*Phase C*). Send traffic to67 `http://<svc>.<namespace>.svc:<service-port>/...` with routing-key headers.68 Do not validate by calling `localhost:<port>` directly.69 See [references/validation-types.md](references/validation-types.md) for70 the subsection matching the type chosen in Phase A.717. **Iterate on failures** (*Phase D*). Read the exact failure, fix the72 smallest cause, restart affected services, and re-run the same validation73 against the same sandbox. Do not report success until the agreed validation74 path passes.75 See [references/troubleshooting.md](references/troubleshooting.md).768. **Close out cleanly.** Report the sandbox name, cluster, routing key,77 target URL, validation command or browser path, and local processes78 stopped. Leave the sandbox up by default; surface the delete command as an79 option only.8081## MCP And CLI Use8283If a Signadot MCP server is available, use it for control-plane work: list and84inspect clusters, sandboxes, routegroups, workloads, endpoints, and devboxes;85create or update sandboxes when the side-effect policy below permits it. Search86by intent, load tool schemas before calling tools, and prefer tool output over87memory.8889If MCP is unavailable, use CLI fallbacks:9091```bash92signadot cluster list -o json93signadot sandbox list -o json94signadot sandbox get <name> -o json95signadot sandbox apply -f <spec.yaml>96signadot sandbox get-env <name>97signadot sandbox get-files <name>98```99100Use `kubectl get <kind> <name> -n <ns> -o yaml` only when MCP cannot fetch the101same workload, ConfigMap, Secret, or Service data and the local environment has102cluster access.103104## Signal Inventory105106| Signal | Use |107|---|---|108| Local process receives real cluster traffic | Default first choice for validating changed services without an image build |109| Cluster DNS/IP is resolvable locally | `signadot local connect` or a devbox makes `.svc` names reachable |110| Local stdout/stderr streams during routed requests | Watch requests land in changed code, see panics, attach a debugger |111| Sandbox readiness, forks, routing state, and tunnel connection | Confirm the sandbox can route before testing code |112| Preview endpoints, when explicitly requested | Deterministically hit a fork without manual routing headers, but may expose a public `*.preview.signadot.com` URL |113| Routing-key isolation | Prove keyed traffic hits the sandbox while unkeyed traffic stays on baseline |114| `signadot local proxy` | Diagnose dependency reachability; not a validation result |115116## Side-Effect Policy117118| Action | Default behavior |119|---|---|120| Read clusters, workloads, endpoints, sandboxes, routegroups | Do autonomously |121| Create an isolated sandbox for this task | Do autonomously when cluster/workload are unambiguous |122| Update a sandbox clearly owned by this task | Do autonomously |123| Update a shared sandbox or routegroup | Ask first |124| Add public preview endpoints such as `defaultRouteGroup.endpoints` | Ask first |125| Delete a sandbox | Ask first; default is leave it up |126| Run `sudo signadot local connect --cluster <cluster>` or `signadot local disconnect` | Ask the user to run it; it modifies the local network stack and may require sudo |127| Run `signadot local status` or `signadot local proxy` | Do autonomously when useful; proxy is diagnostic, not proof of validation |128129## Routing Rules130131### Two Ports Matter132133Keep the two port concepts separate:134135| Context | Port to use | How to resolve |136|---|---|---|137| Sandbox local mapping | Workload/container port | MCP workload-port resolver such as `resolve_workload_port`, or workload spec |138| Validation traffic to `.svc` URL | Kubernetes Service port | Endpoint resolver such as `resolve_endpoints`, Service object, or repo Signadot spec |139140The sandbox can be `ready: true` with a connected tunnel even when its mapping141uses the wrong container port; requests then fall through to baseline. If routing142looks broken, check for a virtual host entry such as143`<sandbox>-<mapping>-*.<namespace>.svc` in `/etc/hosts`.144145### Always Hit The Cluster URL146147Validation traffic must target the cluster service URL, not the local process:148149```bash150curl -sS "http://<svc>.<namespace>.svc:<service-port>/<path>" \151 -H "baggage: sd-routing-key=<routing-key>" \152 -H "tracestate: sd-routing-key=<routing-key>"153```154155For gRPC:156157```bash158grpcurl -plaintext \159 -H "baggage: sd-routing-key=<routing-key>" \160 -H "tracestate: sd-routing-key=<routing-key>" \161 -d '{"field":"value"}' \162 <svc>.<namespace>.svc:<service-port> package.Service/Method163```164165`baggage` and `tracestate` with key name `sd-routing-key` are always accepted.166If `clusterConfig.routing.customHeaders` lists additional headers, inject every167listed header with the routing key as the value. Use query-param routing only168when the surface cannot set headers.169170`signadot local proxy` can help debug whether a cluster dependency is reachable171from the machine, but a localhost proxy URL bypasses the same end-to-end routing172path and is not a validation result.173174### Forward The Key Across Hops175176The routing key reaches a sandboxed downstream service only if every hop177propagates it. Raw HTTP/gRPC clients, custom proxy handlers, and async producers178often drop the key unless the code explicitly copies it. When adding a new179forwarder, copy the incoming `baggage` header onto outbound requests or use the180service's existing instrumented client.181182## Secrets And Env Vars183184When reconstructing env vars from workload specs, ConfigMaps, or Secrets:185186- Resolve every required value before blaming the code. Missing DB addresses,187 credentials, and feature flags often appear as first-request 500s.188- Do not print secret values in chat, logs, or summaries. Redact them as189 `<redacted>` and avoid pasting decoded Secret contents.190- Prefer exporting secrets directly into the process environment. If an env file191 is necessary, write it with restrictive permissions and remove it during192 cleanup.193- For service addresses, grep the code for config lookups such as `*_ADDR`,194 `*_HOST`, `*_URL`, and language-specific helpers. Defaults that work inside a195 pod may not work from a local process.196197## Validation Type Picker198199- **Integration tests**: run the language-native test command against the200 cluster `.svc` URL, injecting routing headers in the test client or a shared201 transport.202- **Existing e2e suite**: use the repo's existing command and config. Point its203 base URL at the `.svc` URL and attach routing headers at the framework HTTP204 layer.205- **Ad-hoc browser automation**: drive the UI from a browser to exercise the206 change end-to-end. Use whatever browser-automation tooling is available207 (Playwright is the common one); inject routing headers on every request,208 clear previous routes first when browser state persists, and drive the full209 UI path.210- **Existing tagged Signadot plan**: pick a tag by `selectionHint` and run it211 against the sandbox. For everything else about plans — params, secrets,212 logs/outputs, authoring, tagging — defer to the `signadot-plan` skill.213214Read [references/validation-types.md](references/validation-types.md) before215running the chosen type.216217## Failure Loop218219When validation fails, do not stop at "validation failed." Continue the loop:2202211. Quote the exact failure: status code, error message, stack trace, missing UI222 element, bad field, or empty browser state.2232. Identify whether the likely cause is app code, sandbox shape, missing env,224 stale process, wrong port, routing-key propagation, or a changed downstream225 consumer that must also run locally.2263. Apply the smallest fix, rebuild and restart affected processes, and re-run227 the same validation with the same routing key and target URL.2284. Stop to ask only when the fix requires a judgment call that cannot be made229 without the user — for example, choosing between fixing forward, making a230 change backward-compatible, or accepting an intentional break. Do not ask231 permission for mechanical fixes such as typos, missing env vars, or232 restarting a stopped process; just apply them and continue the loop.233234Before declaring done, consider whether the verified behavior should be codified235as a Signadot plan. Do this when the bug is deterministic, important, and not236covered elsewhere. Skip it for typos, infra flakes, exploratory checks, and237non-deterministic failure modes. The sandbox is still up — running the new238plan against it once confirms it catches the bug (or passes for the fixed239code) before tagging.240241## Quick Diagnostics242243| Symptom | Likely cause | First check |244|---|---|---|245| Envoy 503 from devbox but pod is healthy | Hit container port instead of Service port | Service object / endpoint resolver |246| Response looks like old baseline behavior | Routing key dropped or localhost target used | Target URL and all routing headers |247| Sandbox ready and tunnel connected but local process gets no traffic | Wrong sandbox mapping port | `/etc/hosts` virtual sandbox entry |248| Local process starts, then first real request returns 500 | Missing env var or unresolved dependency address | Config lookups and exported env |249| Browser body or accessibility tree empties after interaction | SPA runtime error | Browser console errors and `page.content().length` |250| Background process exits soon after start | Shell job received SIGHUP or port conflict | Process status, listener, log file |251| gRPC dial stalls around DNS timeout | Go gRPC SRV lookup delay | `passthrough:///` target and connection reuse |252253Read [references/troubleshooting.md](references/troubleshooting.md) for detailed254signals and fixes.255256## Final Report257258Include:259260- Validation type and command/tool path used261- Cluster, sandbox name, routing key, and service URL262- What passed, including browser/user path when relevant263- Any local process PIDs or ports stopped264- Sandbox teardown command as an option, not an action:265266```bash267signadot sandbox delete <sandbox-name>268```269270If the user ran local network connect, remind them that they can later run:271272```bash273signadot local disconnect274```