Composition
YAML and code share the path
YAML decides which service a proxy calls; the application decides what happens after the request arrives.
See how we compose it →Interactive artifact
Intent-Based Verification of Service Mesh ConfigurationDOI
Service-mesh routes are written in YAML; application behavior lives in code. Bugs hide where the two meet. This artifact compiles both layers and your intent into one model, asks Z3 whether the intent holds, and shows the request that breaks it.
Guided walkthrough
Full explorer
Open any case to inspect the complete YAML, generated routing DSL, app models, and solver output.
#assert holds for every modeled request.#exist found a witness.#exist is unsat; no witness exists.#assertUniversal property over modeled requests.#existReachability / witness query.NSelectNondeterministic choice for unknown or unspecified behavior (weighted traffic split, app internal state, Istio unspecified behavior).Results are precomputed at build time; Compile and Check reveal saved output.
Each numbered item is a property checked by Z3; the formal assertions follow.
With over-approximating app summaries, #exist may pass with an unrealizable witness (false positive), and #assert may fail with an unrealizable counterexample (false alarm).
reqSimple is one fixed empty request: modeled headers are absent (nostr) and modeled HTTP fields are empty strings. It is not an arbitrary request. #exist with reqSimple checks whether a model choice can satisfy the query for that request; forall (req : Req) ranges over all modeled requests.
Concepts
A short primer from mesh YAML to a solver query.
Step 1
Every service gets a sidecar proxy. Service-to-service traffic goes through sidecars, not directly.
The control plane pushes YAML routing rules to every sidecar; the sidecar decides the next hop. The application only handles the request.
Routing, canaries, and access control live in YAML outside the app. A YAML mistake changes the path without touching application code.
See the full model in the walkthrough →Step 2
Three things make the mesh hard to reason about: configuration and code compose, apps branch at runtime, and some behavior is deliberately unspecified.
Composition
YAML decides which service a proxy calls; the application decides what happens after the request arrives.
See how we compose it →App behavior
The call graph is not fixed: an application can branch on request data or on state it keeps elsewhere.
See how we summarize apps →Unspecified behavior
gw[x] → VS1, VS2 → server[a]
gw[x] → VS2, VS1 → server[b]
When several VirtualServices bind the same host, Istio does not guarantee their merge order.
See the merge-order case →for every modeled request
Step 3
YAML becomes a proxy program. App summaries become functions. Intent becomes a query. Z3 either proves it for every request or returns one request that breaks it.
No sampling, no test suite: the solver reasons about every modeled request.
See the full pipeline in the walkthrough →uri != "/login"→frontendelse if session in DB→redirect-serviceelse→login-serviceuri=/home can be sent to login-service.Step 4
Exact needs DB state. Mixed keeps the URI guard and turns only the DB choice into NSelect; coarse also drops the URI guard, so it can create false alarms.
Try the login walkthrough → Open the full login case →Step 5
Istio deliberately leaves some behavior unspecified. The paper's main example is VirtualService merge order: when multiple VirtualServices bind the same gateway host, their match order depends on the order in which the VirtualServices start and is not formally specified.
Two allowed orders reach different servers. The verifier considers every allowed order, so one counterexample is enough to make an assertion fail.
Open the merge-order example →The walkthrough and examples turn these ideas into executable models.