From mirrord
Injects artificial latency and connection failures into mirrord sessions for chaos testing. Simulate slow dependencies or network degradation in dev or CI.
How this skill is triggered — by the user, by Claude, or both
Slash command
/mirrord:mirrord-chaosThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Help users inject artificial failures and disruptions into a mirrord session's **outgoing traffic**, to test how their app behaves under unexpected conditions. A **chaos rule** pairs a **selector** (which traffic to match) with an **effect** (what to do to it). Rules are created and managed through the **mirrord UI server's HTTP API**, and each rule is attached to a single mirrord session: it n...
Help users inject artificial failures and disruptions into a mirrord session's outgoing traffic, to test how their app behaves under unexpected conditions. A chaos rule pairs a selector (which traffic to match) with an effect (what to do to it). Rules are created and managed through the mirrord UI server's HTTP API, and each rule is attached to a single mirrord session: it never affects other sessions or the cluster.
This skill covers two modes:
mirrord ui, starts a session, and manages rules with curl.Trigger on questions like:
~/.mirrord/token; never echo it into CI logs, commit it, or paste it into shared docs. In CI, keep it in an environment variable and rely on the runner's log masking where available._experimental_.latency configThe mirrord config schema contains an _experimental_.latency option for outgoing latency. It is marked deprecated with "Please use the mirrord chaos feature instead", and it will be removed. Never generate or recommend _experimental_.latency for latency injection. Chaos is not a mirrord.json config key: it is a runtime HTTP API served by mirrord ui, and rules are created against a live session as described below.
selector + effect, plus an optional name and priority.priority value. If not set, priority defaults to 0, the lowest.id (UUID) on creation. name is a free-form label with no uniqueness guarantee: always use the id to modify or delete.hit_count of how many times it was applied. Updating a rule via PUT keeps its id but resets hit_count to zero.| Requirement | Detail |
|---|---|
| CLI | mirrord CLI 3.232.0+. |
| UI server | mirrord ui must be running: it serves the chaos API. Default port 59281 (override with -p). |
| Feature stage | Chaos testing is an alpha feature. |
{
"name": "latency for database interactions",
"priority": 10,
"selector": {
"upstream": "sonic.database.svc.cluster.local",
"percentage": 35
},
"effect": {
"latency": {
"read_ms": 750
}
}
}
| Field | Meaning |
|---|---|
upstream | The destination to match: a host, or host:port to match a specific port. Uses the same syntax as mirrord's outgoing traffic filter. |
percentage | Roughly how often a matched connection gets the effect. Integer 0–100; values above 100 are rounded down to 100. |
Two effects are supported. A rule has exactly one.
latency: delays the connection's read and/or write operations:
"effect": {
"latency": {
"read_ms": 100,
"write_ms": 200,
"jitter_ms": 25
}
}
At least one of read_ms or write_ms must be non-zero, or the server rejects the rule with: either 'effect.latency.read_ms' or 'effect.latency.write_ms' must be non-zero.
connection_error: fails the connection:
"effect": {
"connection_error": {
"type": "reset",
"after_ms": 0
}
}
type is one of reset (can be applied to ongoing connections), timed_out, refused.
Responses return the full rule. Note two differences from the request shape: the effect is nested inside the selector, and upstream comes back with an explicit port, where 0 means any port:
{
"id": "6b8f1c4e-2a73-4d9b-8e56-c3f0a7d1b924",
"name": "latency for database interactions",
"priority": 10,
"selector": {
"type": "tcp",
"upstream": "sonic.database.svc.cluster.local:0",
"percentage": 35,
"effect": {
"latency": {
"read_ms": 750
}
}
},
"hit_count": 0
}
Base URL: http://127.0.0.1:59281/api/chaos/rules/{session_id}. Every request carries the x-auth-token header.
| Method & path | Action |
|---|---|
POST / | Create a rule (returns it, with its id). |
GET / | List the session's active rules. |
DELETE / | Delete all of the session's rules. |
GET /{rule_id} | Get one rule. |
PUT /{rule_id} | Replace a rule (same id, hit_count resets). |
DELETE /{rule_id} | Delete one rule. |
Start the UI server and a mirrord session, then export the three values every request needs:
mirrord ui
mirrord exec -f .mirrord/mirrord.json -- node app.js
export UI_ADDRESS='http://127.0.0.1:59281'
export CHAOS_TOKEN="$(cat ~/.mirrord/token)"
export SESSION_ID='c425f391-e9cc-4199-8de9-7bdbb3e7dfcc' # printed by mirrord exec
export CHAOS_URL="$UI_ADDRESS/api/chaos/rules/$SESSION_ID"
Write the rule to a JSON file (e.g. latency-rule.json, see Rule anatomy) and manage it:
# Create
curl --request POST \
--header 'Content-Type: application/json' \
--header "x-auth-token: $CHAOS_TOKEN" \
--data @latency-rule.json \
"$CHAOS_URL"
# List
curl --header "x-auth-token: $CHAOS_TOKEN" "$CHAOS_URL"
# Modify (edit the file, resend with the rule id)
export RULE_ID='6b8f1c4e-2a73-4d9b-8e56-c3f0a7d1b924'
curl --request PUT \
--header 'Content-Type: application/json' \
--header "x-auth-token: $CHAOS_TOKEN" \
--data @latency-rule.json \
"$CHAOS_URL/$RULE_ID"
# Delete one rule / all rules
curl --request DELETE --header "x-auth-token: $CHAOS_TOKEN" "$CHAOS_URL/$RULE_ID"
curl --request DELETE --header "x-auth-token: $CHAOS_TOKEN" "$CHAOS_URL"
Deleting a rule stops it from affecting the session immediately.
Rule files are declarative JSON: check them into the repo (e.g. .mirrord/chaos/) so chaos scenarios are versioned and PR-reviewed alongside the tests that use them. Everything a human reads from terminal output has a scriptable equivalent:
mirrord ui start runs the UI server as a background task (mirrord ui stop tears it down).~/.mirrord/token: read it, don't parse output.GET /api/sessions: each entry has a session_id and its target.curl and jq are preinstalled on GitHub Actions and GitLab runners; no extra HTTP client needed.
# GitHub Actions excerpt: assumes mirrord is installed and kubeconfig is set up
- name: Run integration tests under chaos
run: |
mirrord ui start
export CHAOS_TOKEN="$(cat ~/.mirrord/token)"
export UI_ADDRESS='http://127.0.0.1:59281'
# Start the app under mirrord in the background
mirrord exec -f .mirrord/mirrord.json -- node app.js &
# Find the session by its target
export SESSION_ID="$(curl -s --header "x-auth-token: $CHAOS_TOKEN" \
"$UI_ADDRESS/api/sessions" \
| jq -r '.[] | select(.target == "deployment/my-app") | .session_id')"
export CHAOS_URL="$UI_ADDRESS/api/chaos/rules/$SESSION_ID"
# Apply every chaos rule checked into the repo
for rule in .mirrord/chaos/*.json; do
curl --fail-with-body --request POST \
--header 'Content-Type: application/json' \
--header "x-auth-token: $CHAOS_TOKEN" \
--data @"$rule" \
"$CHAOS_URL"
done
npm test
# Teardown
curl --request DELETE --header "x-auth-token: $CHAOS_TOKEN" "$CHAOS_URL"
mirrord ui stop
Polling note: session registration is asynchronous. The UI server discovers new sessions via a filesystem watcher, then connects and fetches their info before listing them. If the /api/sessions query returns nothing right after mirrord exec starts, retry with a short sleep (a couple of seconds covers the macOS fallback rescan interval).
| Issue | Solution |
|---|---|
| Rule creation rejected for a latency effect | At least one of read_ms / write_ms must be non-zero. |
| Rule created but never fires | Only outgoing TCP selectors are implemented today; file operation and HTTP selectors are planned and won't match. Also check percentage and that the session actually makes outgoing connections to the upstream. |
| Two rules match, only one applies | By design: highest priority wins. Raise the priority of the rule you want. |
percentage above 100 | Rounded down to 100. |
| Can't find a rule by name | name is not unique: list the rules and use the id. |
hit_count dropped to zero after an update | PUT resets hit_count; the id stays the same. |
upstream in responses shows host:0 | 0 means any port; it's the serialized form of a filter with no port. |
| 401 / auth errors | Wrong or stale token. Re-read ~/.mirrord/token while the UI server is running. |
| Nothing on the UI port | mirrord ui isn't running, or runs on a non-default port (-p). Default is 59281. |
mirrord ui running, a live mirrord session to attach rules to.~/.mirrord/token), session ID (terminal output or GET /api/sessions).read_ms or write_ms non-zero, type is one of reset / timed_out / refused, TCP upstream selectors only.DELETE on the session's rules and mirrord ui stop.User: "Make 30% of my service's calls to the payments API time out, so I can test our retry logic."
Response:
mirrord ui and a mirrord session are running, and the payments API hostname as the session sees it.payments-timeout.json: connection_error effect with "type": "timed_out", selector with upstream set to the payments host and "percentage": 30.curl with the x-auth-token header, and the DELETE for cleanup once testing is done.id needed to modify or delete it later.upstream filter syntaxnpx claudepluginhub metalbear-co/skills --plugin mirrordConfigures mirrord in CI pipelines for GitHub Actions, GitLab CI to run end-to-end, integration tests against real Kubernetes clusters. Troubleshoots CI-specific issues.
Guides chaos engineering workflows: define steady state, map failure domains, design experiments for network failures, timeouts, DNS issues using tc and toxiproxy. For resilience tests and game days.
Guides chaos engineering experiments: planning failure modes, defining steady-state metrics, and scoping blast radius for resilience validation.