Deploy Viper to Europe β Peter MΓΌller π¨π + ETH Zurich (2016), the Permission-Based Intermediate Verification Language that Powers Prusti, Gobra, and Nagini, on EU Infrastructure in 2026
Building a program verifier from scratch is a multi-year research project. Each new language β Rust, Go, Python, Java, C β requires a heap model, a proof calculus, an SMT encoding, and a counterexample extraction strategy. Do this once per language and you duplicate work indefinitely. The ETH Zurich Programming Methodology group's answer was to build the infrastructure once and share it.
Viper β Verification Infrastructure for Permission-based Reasoning β is an intermediate verification language and toolchain developed at ETH Zurich π¨π by Peter MΓΌller, Malte Schwerhoff, and Alexander Summers, first presented at VMCAI 2016 (Verification, Model Checking, and Abstract Interpretation). Viper provides a common backend for permission-based program verification: front-end tools translate source languages into Viper's Silver intermediate language, and Viper discharges the resulting proof obligations using Z3 via two alternative backends.
The result is an ecosystem: Prusti (Rust), Gobra (Go), Nagini (Python), VeriFast (C and Java), and Voila (concurrent objects) all share Viper's verification infrastructure. A bug fixed in Silicon benefits every language simultaneously. A new SMT strategy improves all front-ends at once. The research investment compounds across a growing portfolio of verified language communities β all rooted in EU institutions.
The Silver Intermediate Language
Viper's input language is Silver β a simple, expressive intermediate language designed specifically for permission-based heap verification. Silver has no syntax for loop bodies, conditionals, or assignments in the conventional sense; instead, it provides exactly the constructs needed for verification:
Methods and Functions
Silver distinguishes methods (with side effects, verified by symbolic execution) from functions (pure, used in specifications):
// Method: imperative, verified against pre/postconditions
method swap(a: Ref, b: Ref)
requires acc(a.val) && acc(b.val)
ensures acc(a.val) && acc(b.val)
ensures a.val == old(b.val) && b.val == old(a.val)
{
var tmp: Int := a.val
a.val := b.val
b.val := tmp
}
// Function: pure expression, usable in specifications
function sum(n: Int): Int
requires n >= 0
ensures result >= 0
{
n == 0 ? 0 : n + sum(n - 1)
}
old(expr) refers to the value of expr in the pre-state β the state at method entry. result is the implicit return value in postconditions and function bodies.
Predicates: Named Permission Abstractions
Predicates encapsulate heap access permissions and structural invariants into named, foldable units:
field val: Int
field next: Ref
predicate list(this: Ref) {
acc(this.val) &&
acc(this.next) &&
(this.next != null ==> list(this.next))
}
method append(head: Ref, newVal: Int)
requires list(head)
ensures list(head)
{
unfold list(head)
if (head.next == null) {
var node: Ref
inhale acc(node.val) && acc(node.next)
node.val := newVal
node.next := null
head.next := node
fold list(node)
} else {
append(head.next, newVal)
}
fold list(head)
}
fold list(head) converts the unfolded permissions acc(head.val) && acc(head.next) && list(head.next) back into the abstract predicate list(head). unfold is the inverse. This permits the verifier to reason about recursive data structures without unrolling the heap representation at every access.
Domains: Background Theories
Domains define uninterpreted types with axioms β the mechanism for encoding custom background theories that Z3 should treat as axiomatic:
domain Array {
function array_get(a: Array, i: Int): Int
function array_set(a: Array, i: Int, v: Int): Array
function array_len(a: Array): Int
axiom array_get_set_same {
forall a: Array, i: Int, v: Int ::
array_get(array_set(a, i, v), i) == v
}
axiom array_get_set_diff {
forall a: Array, i: Int, j: Int, v: Int ::
i != j ==> array_get(array_set(a, i, v), j) == array_get(a, j)
}
axiom array_len_positive {
forall a: Array :: array_len(a) >= 0
}
}
Domains allow front-ends to encode their source language's abstract data types β Go slices, Rust vectors, Python lists β as axiomatic types without requiring heap cells.
inhale and exhale
Two low-level constructs provide direct control over the permission and assertion state:
// inhale: add assertions and permissions to the current state (without proof)
inhale acc(x.val) && x.val >= 0
// exhale: remove assertions and permissions from the current state (with proof)
exhale acc(x.val) && x.val >= 0 // fails if x.val >= 0 cannot be proven
inhale models assumptions β the entry to a method body inhales the precondition. exhale models proof obligations β the exit from a method body exhales the postcondition. This makes the verification semantics of Viper programs explicit and machine-readable. Front-end translators use inhale/exhale to encode calling conventions, loop invariants, and language-specific memory models.
The Permission Model: Implicit Dynamic Frames
Viper's heap model is Implicit Dynamic Frames (IDF) β a permission-based approach to framing developed at ETH Zurich that provides the reasoning power of separation logic without requiring explicit separating conjunction syntax in every assertion.
The core construct is acc(x.f, p) β access permission to field f of heap location x with permission amount p:
acc(node.val, 1/1) // exclusive write permission (full ownership)
acc(node.val, 1/2) // fractional read permission (shareable)
acc(node.val, 3/4) // three-quarter permission (partial ownership)
Permission amounts range in (0, 1]. A permission of 1 grants exclusive write access. Any amount > 0 and < 1 grants read access. Multiple threads may each hold fractional read permissions summing to less than 1; only one may hold write permission (1).
The frame rule is implicit: if a method's precondition does not mention acc(x.f), the method cannot access or modify x.f. No explicit modifies clause is needed. The permission set defines the footprint automatically.
Separating Conjunction Without the Notation
In separation logic, P * Q asserts that P and Q hold over disjoint heap regions. In IDF, the same effect is achieved by holding acc(x.f) and acc(y.g) in the same permission set β the set cannot contain acc(x.f) twice without the amounts summing to at most 1:
method copy_field(src: Ref, dst: Ref)
requires acc(src.val, 1/2) && acc(dst.val)
ensures acc(src.val, 1/2) && acc(dst.val)
ensures dst.val == src.val
{
dst.val := src.val
}
Here src.val and dst.val can be the same location only if one holds 1/2 and the other holds 1 β but that would sum to 3/2 > 1, which is impossible. The type system enforces non-aliasing implicitly: no explicit src != dst assumption is required.
The Two Backends
Viper provides two independent verification backends for Silver programs:
Silicon: Symbolic Execution
Silicon (Peter MΓΌller + Malte Schwerhoff + Alexander Summers) implements symbolic execution over permission heaps. Execution forks at conditionals, accumulates path conditions, and discharges proof obligations to Z3 at each branch endpoint.
Silicon maintains a permission heap β a partial map from (location, field) pairs to (value, permission) β and updates it symbolically as Silver programs execute. The key invariant: any heap read requires the current path to hold the corresponding acc(x.f, p) for some p > 0.
Silicon is the default backend for Prusti, Gobra, and Nagini. It is generally faster than Carbon for programs with moderate branching and produces concrete counterexamples with heap visualizations.
Carbon: Verification Condition Generation
Carbon (Malte Schwerhoff + ETH Zurich) translates Silver programs into Boogie β Microsoft Research's intermediate verification language β and discharges the resulting verification conditions via Boogie's Z3 backend.
Carbon's approach: translate each Silver method into a Boogie procedure, encode permissions as Boogie maps, and emit weakest-precondition verification conditions. Carbon handles some Silver programs more efficiently than Silicon (particularly those with complex quantified permissions) and produces different diagnostic output β useful when Silicon and Carbon disagree as a cross-check.
| Property | Silicon | Carbon |
|---|---|---|
| Strategy | Symbolic execution | VCGen β Boogie |
| SMT encoding | Z3 directly | Boogie β Z3 |
| Counterexamples | Concrete heap states | Boogie-level values |
| Performance | Faster for branching | Faster for quantifiers |
| Output | Viper-native errors | Boogie-translated errors |
| Status | Primary backend | Secondary / cross-check |
Both backends are open source under MPL 2.0 and available in the viperproject GitHub organization.
The Viper Ecosystem: EU-Rooted Front-Ends
Viper is not used directly by application developers β it is used by front-end tool builders who translate their source language to Silver. The resulting ecosystem covers the dominant languages of modern software:
| Front-end | Language | Paper | Institution | Key authors |
|---|---|---|---|---|
| Prusti | Rust | PLDI 2019 | ETH Zurich π¨π | Peter MΓΌller π¨π + Alexander Summers π¨π + Vytautas Astrauskas |
| Gobra | Go | CAV 2021 | ETH Zurich π¨π | Felix Wolf π©πͺ + Malte Schwerhoff π©πͺ + Peter MΓΌller π¨π |
| Nagini | Python 3 | VMCAI 2018 | ETH Zurich π¨π | Marco Eilers π©πͺ + Peter MΓΌller π¨π |
| VeriFast | C, Java | iFM 2008 | KU Leuven π§πͺ | Bart Jacobs π§πͺ + Jan Smans π§πͺ |
| Voila | Concurrent objects | PLDI 2021 | ETH Zurich π¨π | Felix Wolf π©πͺ + Peter MΓΌller π¨π |
| Vercors | Java, C, OpenCL | FM 2017 | TU Twente π³π± | Marieke Huisman π³π± + Stefan Blom π©πͺ |
Every major front-end is from a European institution: ETH Zurich π¨π, KU Leuven π§πͺ, TU Twente π³π±. The verification infrastructure beneath modern Rust, Go, and Python programs in Europe is built entirely by EU and Swiss researchers using EU research funding.
What Front-Ends Do
A Viper front-end has three responsibilities:
- Parse source language β Rust MIR, Go AST, Python AST
- Translate to Silver β encode the source language's type system, ownership model, and execution semantics as Silver methods, functions, and predicates
- Lift counterexamples β translate Silicon/Carbon's Silver-level counterexamples back to source-language error messages
The translation is the research contribution of each front-end. Prusti's insight: Rust ownership = IDF write permission. Gobra's insight: Go channel send/receive = permission transfer. Nagini's insight: Python acc(self.field, 1/1) = MyPy-annotated field ownership.
Quantifiers and Triggers
Silver supports first-order quantifiers over Viper types:
// Universal quantification over array indices
method verify_sorted(arr: Array)
requires array_len(arr) >= 0
requires forall i: Int, j: Int :: {array_get(arr, i), array_get(arr, j)}
0 <= i && i < j && j < array_len(arr) ==>
array_get(arr, i) <= array_get(arr, j)
ensures true
{
// No body needed β specification is the theorem
}
Triggers β the {array_get(arr, i), array_get(arr, j)} annotation β guide Z3's quantifier instantiation. When Z3 encounters a term matching the trigger pattern in the current proof state, it instantiates the quantifier. Well-chosen triggers are critical for verification performance: ill-formed triggers cause quantifier explosions; missing triggers cause Z3 to miss necessary instantiations.
Viper's trigger language is richer than most SMT-level trigger systems: triggers can involve multiple terms, and the verifier validates that trigger terms are semantic triggers β they must be instances of the quantifier body's antecedent.
Labeled Old Expressions
Silver supports labeled old expressions β a mechanism for referring to the heap state at arbitrary past points in execution:
method transfer(src: Ref, dst: Ref, amount: Int)
requires acc(src.balance) && acc(dst.balance)
requires src.balance >= amount && amount >= 0
ensures acc(src.balance) && acc(dst.balance)
ensures src.balance == old(src.balance) - amount
ensures dst.balance == old(dst.balance) + amount
{
label pre_transfer
src.balance := src.balance - amount
dst.balance := dst.balance + amount
// old[pre_transfer](src.balance) == value before transfer
assert dst.balance == old[pre_transfer](dst.balance) + amount
}
label pre_transfer captures the current heap state under the name pre_transfer. old[pre_transfer](expr) evaluates expr in the captured state. This allows postconditions and intermediate assertions to refer to states other than the method entry β essential for verifying protocols with multi-step state transitions.
Magic Wands: Separation Logic Views
For advanced verification patterns, Silver supports magic wands β a separation logic connective that enables modular reasoning about heap contexts:
// A magic wand: "if you give me acc(y.val), I'll give you list(x)"
wand w := list_prefix(x) --* list(x)
// Apply the wand: consume its left side, gain its right side
apply w
Magic wands are particularly useful for verifying iterator patterns where a data structure is partially consumed β the wand represents the "rest of the structure" that will be provided when the iterator is exhausted. Gobra uses magic wands to verify Go concurrent data structure operations where permission temporarily leaves a predicate.
EU Regulatory Context: A Shared Verification Infrastructure
Viper occupies an unusual position in the EU regulatory landscape: it is not directly used by industrial teams, but it is the verification engine that industrial tools rely on. When Prusti verifies an Airbus software component for DO-178C compliance, it is Viper's Silicon backend that discharges the proof obligations. When Gobra verifies a Kubernetes controller for GDPR processing-purpose isolation, Z3 running on behalf of Viper produces the result.
EU AI Act Article 9 β Transitive Coverage
The EU AI Act (Regulation 2024/1689) requires high-risk AI providers to maintain a risk management system throughout the AI lifecycle. Article 9's verification requirements extend to the entire software supply chain β including the verification tools themselves.
For EU teams using Prusti, Gobra, or Nagini as Article 9 compliance evidence, the trustworthiness of Viper matters. Viper is developed at ETH Zurich β an ERA-associated Swiss institution outside US Cloud Act jurisdiction, funded by SNSF and EU Horizon Europe, with all code released under MPL 2.0. The verification chain from source language to Z3 is entirely auditable: Silver translations are open source, Silicon's symbolic execution engine is open source, and the Z3 SMT solver is open source under MIT license.
Cyber Resilience Act (2027) β Supply-Chain Verification
The CRA's software supply chain provisions (Article 13: security of software components) require manufacturers of digital products to document security properties of third-party components. For systems verified with Prusti or Gobra, the CRA documentation includes not only the source language specification but also the Viper Silver translation β the machine-checkable artifact that bridges source language semantics and SMT-level proof obligations.
GDPR Article 25 β Privacy by Design (Infrastructure Layer)
For Python FastAPI services verified with Nagini or Go microservices verified with Gobra, the Viper Silver output provides the infrastructure-level privacy specification: Silver preconditions and postconditions that encode GDPR Article 25's data minimization requirements as permission constraints. A Silver method that requires acc(request.user_id, 1/2) but does not require acc(request.payment_data) structurally enforces that the method cannot access payment data β regardless of application-level logic.
The ETH Zurich Programming Methodology Group
Viper is the product of ETH Zurich's Programming Methodology group, led since 2007 by Professor Peter MΓΌller π¨π. The group's research program began with Chalice (2009) β an earlier permission-based concurrent programming language β and evolved through the Implicit Dynamic Frames theory into the current Viper architecture.
Key group members and their Viper contributions:
- Peter MΓΌller π¨π β group leader, IDF theory co-developer, Viper architecture, Prusti and Nagini co-author, Silicon co-author
- Malte Schwerhoff π©πͺ β Silicon and Carbon primary developer, Gobra co-author (now TU Delft π³π±, VerCors collaboration)
- Alexander Summers π¨π β Silver language design, Silicon co-author, Prusti co-author (now University of British Columbia β but Viper developed at ETH)
- Marco Eilers π©πͺ β Nagini author and maintainer
- Felix Wolf π©πͺ β Gobra co-author, Voila co-author
- Vytautas Astrauskas β Prusti co-author, unsafe Rust verification
The group's funding comes from:
- Swiss National Science Foundation (SNSF) β Swiss federal research funding
- EU Horizon Europe β Switzerland has been associated with Horizon since 2014 (Framework Programme association)
- ETH Zurich institutional budget β Swiss federal university, no private shareholders
All software releases (Viper, Silicon, Carbon, Prusti, Gobra, Nagini) are under MPL 2.0 or Apache 2.0 β open-source licenses that permit commercial use and do not require open-sourcing of derivative works.
Running Viper Directly
Viper can be used without a front-end for verification researchers and tool builders who want to write Silver directly:
# Install via Viper IDE (VS Code extension: viperproject.viper-ide)
# Or download binaries from viper.ethz.ch
# Verify a Silver file with Silicon
viper --backend silicon myprogram.vpr
# Verify with Carbon
viper --backend carbon myprogram.vpr
# Both backends (cross-check)
viper --backend silicon myprogram.vpr && viper --backend carbon myprogram.vpr
The Viper IDE VS Code extension provides inline verification feedback: green checkmarks for verified methods, red squiggles with counterexample heap visualizations for failures. It supports step-by-step execution of the symbolic execution trace β invaluable for debugging Silver translations.
For Docker-based deployment on EU infrastructure:
FROM openjdk:17-slim
# Download Viper binaries (Silicon + Carbon)
RUN apt-get update && apt-get install -y curl unzip && \
curl -L https://github.com/viperproject/silicon/releases/latest/download/silicon.jar \
-o /usr/local/bin/silicon.jar && \
curl -L https://github.com/viperproject/carbon/releases/latest/download/carbon.jar \
-o /usr/local/bin/carbon.jar
# Z3 SMT solver
RUN curl -L https://github.com/Z3Prover/z3/releases/download/z3-4.12.2/z3-4.12.2-x64-glibc-2.35.zip \
-o /tmp/z3.zip && \
unzip /tmp/z3.zip -d /usr/local/ && \
ln -s /usr/local/z3-*/bin/z3 /usr/local/bin/z3
WORKDIR /app
COPY specs/ specs/
# Verify Silver specifications as CI gate
RUN java -jar /usr/local/bin/silicon.jar specs/invariants.vpr && \
java -jar /usr/local/bin/carbon.jar specs/invariants.vpr
CMD ["java", "-jar", "/usr/local/bin/silicon.jar"]
Deploy Viper on sota.io β EU-Native PaaS
sota.io is the EU-native PaaS for verification infrastructure workloads β GDPR-compliant, managed PostgreSQL 17, zero DevOps. Whether you are running Viper as a CI verification gate, hosting Silicon as an API for a Prusti-based DevSecOps pipeline, or deploying a Gobra-verified Go service, sota.io provides EU-resident infrastructure:
# Deploy Viper verification service to EU
sota deploy --region eu-west
No US Cloud Act exposure. Full GDPR compliance. For teams building EU AI Act-compliant systems where verification artifacts must be EU-resident, sota.io is the natural deployment target.
Free tier available. No credit card required.
Conclusion
Viper is the shared foundation beneath the EU's formal verification ecosystem for modern languages. Prusti (Rust), Gobra (Go), and Nagini (Python) are each built on Viper's Silver IL, Silicon symbolic executor, and Z3 SMT backend. A team applying formal verification across a polyglot system β a Rust cryptographic library, a Go service mesh, a Python inference service β uses three front-end syntaxes but shares one verification engine.
The Silver language is compact but complete: methods and functions for imperative and pure reasoning, predicates for encapsulating heap structure, domains for background theories, quantifiers with SMT triggers for collection properties, magic wands for context reasoning, and labeled old expressions for multi-step protocol verification.
For EU teams building under the AI Act, GDPR, or the Cyber Resilience Act, Viper provides the infrastructure-level guarantee: all proof obligations discharged by Prusti, Gobra, and Nagini pass through an open-source, ETH Zurich-developed, EU Horizon-funded verification engine β a machine-checkable audit trail from source language specification to Z3 satisfiability result.
Peter MΓΌller, Malte Schwerhoff, and Alexander Summers developed Viper at ETH Zurich. The project is open source under MPL 2.0 at github.com/viperproject. Documentation and the VS Code IDE extension are at viper.ethz.ch.
See Also
- Deploy Prusti to Europe β β Viper-based Rust verifier (ETH Zurich π¨π): translates Rust MIR to Silver IL, IDF permissions for ownership
- Deploy Gobra to Europe β β Viper-based Go verifier (ETH Zurich π¨π): goroutine/channel permission transfer, cloud-native Go verification
- Deploy Nagini to Europe β β Viper-based Python verifier (ETH Zurich π¨π): Python AST to Viper, thread safety via Lock/Thread annotations
- Deploy VeriFast to Europe β β Separation logic verifier for C/Java (KU Leuven π§πͺ): the conceptual predecessor to Viper IDF, iFM 2008
- Deploy Creusot to Europe β β EU-native Rust verifier (INRIA Saclay π«π·): Pearlite/Why3/Alt-Ergo, alternative to Prusti