SR9: A Motoko-Derived Language for Writing and Verifying Canisters

First public release of SR9: https://sr9n.com/

So far, it has taken 80 artificial R&D years over 8 months. We’re now looking to ramp that up to 8,000 artificial R&D years over the next year!

It runs inside a Docker container with all dependencies included, so nothing else gets installed on your PC. Docker is required.

There’s a skill you can add to your agent to get started - Codex with GPT-5.5 xhigh works best. It has access to ~500 pages of local documentation, so you can ask it to craft a personalized tutorial or answer any questions you may have.

Tagging people who might be interested in checking it out: @claudio @ggreif @nomeata @skilesare @timo @dominicwilliams @EdSalazar @senior.joinu @Snassy-icp @rvanasa @marc0olo

Infu, this may be amazing, but I’m afraid we’re all going to need at least an “explain like I’m a vibe coder” to really understand the value of what you have built.

Let’s start from Motoko’s type system. When you typecheck your code, the compiler does static analysis. It checks that the types across your program line up, and it puts boundaries around values so they cannot be used in ways that do not fit those types. That eliminates whole classes of errors.

You notice this even without AI. You add a new field to a record, run the compiler, and the typechecker goes through your code and tells you exactly where something no longer fits. It can make programs a bit harder to write at first, but once the types check, a lot of basic mistakes are gone. What remains are usually logical errors.

SR9’s verification syntax feels like the next layer of that idea.

Types let you say:

balance : Nat

SR9 lets you say things like:

balance < 20

or:

amount <= balance

or:

result == old(balance) - amount

It gives you a way to place more boundaries around your program. Not just type boundaries, but logical boundaries.

Take this simple example:

persistent actor {
  var balance : Nat = 10;

  public func withdraw(amount : Nat) : async Nat
    modifies balance
  {
    balance -= amount;
    balance
  };
}

SR9 rejects it:

verification failed (1 issue)
Assert might fail. Assertion 0 <= $Self.fld$balance - amount might not hold.
Counterexample: balance=38, amount=39

The verifier found a real problem. If the balance is 38 and the withdrawal amount is 39, then mathematically the new balance would be -1. But balance is a Nat, and a Nat cannot represent negative numbers. In Motoko, that subtraction can trap.

So the verifier gives your agent fast feedback: this function is not safe for every possible input.

The agent can fix it by adding a boundary:

persistent actor {
  var balance : Nat = 10;

  public func withdraw(amount : Nat) : async Nat
    modifies balance
    entry_requires amount <= balance;
    requires amount <= balance;
  {
    balance -= amount;
    balance
  };
}

Now the function says: this method is only valid when the requested withdrawal is less than or equal to the current balance. That is the missing logical boundary.

This example is trivial, but the idea scales.

Imagine there is more business logic inside. There are loops, switches, branches, intermediate values, and noisy refactors. It becomes much harder to tell by looking at the code whether balance always stays safe.

Here is a deliberately noisy example that still verifies:

persistent actor {
  type Mode = { #same; #double; #weird };
  var balance : Nat = 10;

  public func withdraw(amount : Nat) : async Nat
    modifies balance
    ensures balance < 20;
  {
    let normalized : Nat = if (balance < 20) { balance } else { 19 };
    assert normalized < 20;
    balance := normalized;
    let mode : Mode = if (amount == 0) { #same } else { #weird };
    var i : Nat = 0;
    var raw : Nat = 0;
    var capped : Nat = 0;
    var debit : Nat = 0;
    while (i < amount) { loop:invariant i <= amount; loop:invariant balance < 20; loop:invariant raw == 0; loop:invariant capped == 0; loop:invariant debit == 0; i += 1; };
    raw := switch (mode) { case (#same) amount; case (#double) amount + amount; case (#weird) if (i == amount) { i } else { amount } };
    capped := if (raw <= balance) { raw } else { switch (mode) { case (#double) balance; case (_) balance } };
    debit := if (capped == raw) { raw } else { capped };
    assert balance < 20;
    assert debit <= balance;
    assert balance - debit < 20;
    balance -= debit;
    balance
  };
}

This verifies for all possible amount values and all modeled starting states of balance.

A test can only try a few examples. The verifier builds a logical model of the program and asks an SMT solver to prove the property for every case covered by that model.

The important line is:

ensures balance < 20;

That is the contract. The function is obligated to return only after leaving balance below 20.

The assert statements in this example are not runtime traps. In SR9, plain assert is a proof step for the verifier. If you want a runtime check, you use runtime:assert or trap.

That is one of the few breaking changes from Motoko syntax, but it makes sense for verification. AI was getting confused when the same word meant both “prove this statically” and “check this at runtime.”

There are limitations today. You cannot just write arbitrary code and expect the verifier to understand everything. You often need to structure code in a narrow way, add loop invariants, add intermediate assertions, or use library models that the verifier understands.

But for AI coding, that is exactly the point.

Your agent can try an implementation, run verification, get a counterexample, and adjust the code. The verification layer becomes another feedback loop. It keeps the runtime code inside the boundaries you wrote down.

Actor invariants make this even more interesting.

For example:

persistent actor {
  var apples : Nat = 0;
  var oranges : Nat = 0;

  invariant apples >= oranges;

  // ...
}

Every verified method that can change apples or oranges has to prove that this invariant still holds.

Your canister might have 200 modules involved in calculations around apples and oranges. Someone reading the contract does not need to manually inspect every path to know the intended rule. If it verifies, then every verified path that touches this state has preserved the invariant.

That is the executive value of SR9: it turns important business rules into machine-checked obligations.

The typechecker keeps your values shaped correctly. The verifier keeps your logic inside the rules you declared.

For human developers, this is hard to write today. Even with AI, it takes tinkering and experimentation.

But if the future is AI writing more of the code, then this kind of boundary becomes much more practical. You do not want to rely only on the agent sounding confident. You want the agent to prove that the code it wrote still satisfies the rules. SR9 becomes a guardrail system for canister logic.

It lets you turn “I hope this is true” into “the verifier checked that every modeled path keeps this true.”

Does it actually modify the wasm that is written, or is it just a set of compile-time checks?

The verification layer is removed during compilation, it doesn’t make it into the wasm.
If you make something verify with SR9, you should be able to manually remove all the verification code and compile it with Motoko. That’s what it does under the hood pretty much.

Sounds promising. I may have a place to use it. As far as “Governed by Neutrinite”, what does that entail? The DAO approves upgrades to the code and what not? Or is it more than that?

Neutrinite will govern a verification service through which you can deploy canisters that receive a certificate after successful verification. This certificate is used when your canister makes calls to other SR9-verified canisters. These canisters have stricter rules, such as the ability to mark functions as immutable, so once deployed, they cannot be changed, and other canisters can rely on that behavior. SR9 can be used without that, you can deploy your canisters yourself without passing thru the service.

Also, Ntn will govern the automated development system that produces SR9 releases.

Is the verification service an on-chain LLM that reviews the code against those specific logical rules?

It’s not LLM. It can be cryptographically controlled remote machine running the verifier or be consensus based.

It seems that this would be the most challenging bit that the certificates hinge on. I’m not clear on how this would work, but I’d like to. I love the idea and it definitely tackles a real problem.

So far, it has taken 80 artificial R&D years over 8 months. We’re now looking to ramp that up to 8,000 artificial R&D years over the next year!

@infu , can you give a bit more light into this?

I am developing my own agentic systems, would love to learn more on your current experience, what worked for you vs not, especially as you start to scale way beyond a simple github copilot / claude code / cursor assistant.

  • You created a hierarchical mix of agents? Where one is the manager / product owner, another the dev and another the reviewer?
  • How have you dealt with product / features strategy and avoided drifting into useless features or bad architecture decisions?
  • You are using any particular stack to run them? Like Cloudflare, LangGraph, etc.? Custom parts on the IC? :slight_smile:

Think you have pulled something quite sophisticated (congrats :clap:), look forward to learn more on your experience building it this far :+1:

Still trying to figure out the system that can do 8,000 artificial R&D years. It will need more automation than what we are using now. Automating something that unpredictable is hard.

There isn’t one thing that makes it all work. It’s more like a digital brain - understanding how it works, where and why it makes mistakes, and what it’s good at helps you correct and steer it. I am not asking it to invent features on its own. What it does is try to find a solution that satisfies all the requirements set by us, so it tries hundreds of things until it finds one.

If its solution results in bad architecture, then it means we didn’t load all the constraints and goals into its brain. A panel of experts can help carry the requirements from different perspectives so they don’t get lost, but a text file also gets a lot of it done.

Infu,

Thank you so much for this contribution. For less experienced developers, the hardest part is catching logic mistakes.

Framing SR9 as a “logical boundary” makes the concept much more simple. It turns complex rules into a safety net, preventing mistakes before they ever happen. Thank you for making formal verification feel like a practical tool rather than just theory.

I would also like to thank you for the years of open source repositories you have published.

I agree, this seems like a really great project infu, looking forward to your progress.

SR9 new version is out. A lot of limitations around opaque were lifted.
To upgrade: Make sure you have the latest docker image. Refetch new docs and new skill.md or just ask your agent to do it.

Formally verified DEX demo: GitHub - Neutrinomic/demo_sr9_dex · GitHub
It’s part of the new docs and skill. All the patterns in the dex will be available to your agent.
These are pretty hard to find, so it’s best to see them in examples.

Whoever tried the previous version likely could only verify an actor with few functions and modules, but nothing too big, this version will allow a lot more.

Once we lift more limitations, the DEX code will likely be 50% reduced.
Note: It’s not only for DeFi, we will put more example apps.

daNimic, If I were leading Dfinity, I wouldn’t be rushing to endorse and announce it over the megaphone either - not yet. The claim is too big, and there are many other reasons.

Fyi airdrop was 2years ago, to SNS1 holders. You can send them to the DAO minter address and they will be burned eqsml-lyaaa-aaaaq-aacdq-cai