Skip to main content
A policy layer is only worth using if you know exactly where its guarantees stop. This page is deliberately specific about both sides.
Shugo has not been audited by a third party, and it is deployed on devnet only. The Subscriptions & Allowances program it builds on was audited by Cantina; Shugo’s own program has not been. Treat it as hackathon-stage software, not something to put real funds behind.

What was proven: the decision logic

The core of Shugo is one pure function, evaluate(), that takes a snapshot of a policy plus a requested pull and returns Allow or Deny(reason). It has no accounts, no CPI, and no Solana runtime in it, which is what makes it possible to check exhaustively with Kani, a bit-precise model checker for Rust. The property proven is a biconditional: evaluate() returns Allow if and only if every check passes.
Both directions matter. “Allow implies every check passed” means nothing slips through. “Every check passed implies Allow” means the function can’t wrongly deny a valid pull either. The proof runs over every possible value of every input — all u64 caps and amounts, all i64 timestamps, all possible destination bytes — not a sampled set of test cases.
Result
The harness restates the specification independently of the implementation and asserts the two agree. Abridged:
To reproduce it yourself:

What the proof does not cover

These limits are as important as the result.
That property is false for Shugo and is not claimed. The window is fixed, not sliding, so an agent can pull up to the cap at the end of one window and again at the start of the next — up to 2× the cap across a boundary. See Velocity Caps.
The proof covers the decision function. It does not cover account validation in execute_pull, the CPI into S&A, the write-back of the updated window total, PDA derivation, or signer checks. Those are exercised by the integration tests below, which are behavioral checks of specific scenarios, not exhaustive proofs.
If the specification itself encodes a wrong idea of what should be allowed, the proof will faithfully confirm the code matches that wrong idea. It rules out implementation bugs relative to the spec, not mistakes in the spec. Review the five conditions above as the actual policy you’re agreeing to.
Both are assumed correct. S&A has its own audit; Shugo inherits its guarantees and its risks.

Integration tests

Seven tests run both compiled programs side by side in LiteSVM — Shugo’s program and the real, unmodified Subscriptions & Allowances binary — so the CPI between them executes under real runtime semantics rather than being mocked.
The suite also needs the S&A program built as a sibling checkout (or SUBSCRIPTIONS_SO_PATH pointing at its .so).

A bug the tests missed once

The first devnet create-policy run failed with S&A error 136, StaleSubscriptionAuthority. The SDK had passed a hardcoded 0 as the authority’s generation ID. A real authority’s ID is the slot it was created in, so on a live cluster that value is huge; every test had passed anyway because LiteSVM’s clock starts at slot 0, which made the wrong value accidentally correct. The SDK now uses S&A’s sentinel for same-transaction creation, and the test suite sets a realistic slot so this class of bug can’t hide again. It’s listed here because a security page that only reports successes isn’t worth much.

What you’re trusting

Reporting an issue

Open an issue on GitHub. For anything you believe is exploitable, please contact the maintainers privately before posting details publicly.