SOVR Protocol Specification
Stage 0 — Protocol Specification Status: Draft for review · Date: 2026-10-05 · Domain: sovr.pro
Every token is its own bank.
SOVR provides autonomous monetary systems for onchain assets. A SOVR-enabled token is given its own reserve, a transparent balance sheet, an economic constitution, a monetary mandate, an AI policy governor, policy regimes, a policy history, a constrained set of actions, and a public ledger of everything the system has done.
The governing design rule of the protocol is:
The AI does not control the money. The constitution controls the AI.
0. Document conventions
0.1 Normative language
The words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are used as in RFC 2119.
0.2 Source of truth
This specification is written against the reference implementation in this repository. Where the specification and the code disagree, the disagreement is a defect and MUST be resolved explicitly; neither silently wins.
| Concern | Reference file |
|---|---|
| Domain model, action vocabulary, prohibited actions, check IDs, rejection codes | src/core/types.ts |
Deterministic policy engine, PROTOCOL_LIMITS, validateIntent, verifyExecution, validateConstitution |
src/core/engine.ts |
Strict intent schema (parseIntent) |
src/core/schema.ts |
| Canonical JSON and SHA-256 hashing | src/core/hash.ts |
Reference constitution XYZ-001 and execution context |
src/core/fixtures.ts |
| Conformance tests | src/core/engine.test.ts |
Items marked [Specified, not implemented] describe interfaces or components that are normative for later stages but do not yet exist in code.
0.3 Units
| Type | Meaning |
|---|---|
Bps |
Basis points, integer, 0..10_000. 100 bps = 1%. |
Usd |
US dollars, number. Display and simulation only; onchain amounts are integer base units handled by chain adapters. |
UnixSeconds |
Integer seconds since epoch, UTC. |
Hex |
32-byte lowercase hex string (64 characters) where used as a hash. |
AssetId |
Uppercase symbol, ^[A-Z0-9]{1,12}$ (e.g. USDC, SOL, XYZ). |
VenueId |
Lowercase identifier, ^[a-z0-9-]{1,48}$ (e.g. orca-whirlpool). |
1. What SOVR is, and what it is not
1.1 The metaphor
"Bank" is a product metaphor. A SOVR instance is closer to a central bank, treasury, reserve manager and monetary-policy engine for assets owned by a token project than to a commercial bank.
A SOVR instance:
- holds a reserve of project-owned assets;
- allocates incoming project revenue across a fixed set of uses;
- executes a small set of bounded monetary operations (scheduled buybacks, protocol-owned liquidity, reserve rebalancing);
- publishes a balance sheet, its policy decisions, and the reasoning record behind them.
1.2 What SOVR V1 is not
SOVR V1 is not a deposit-taking bank and MUST NOT be described as one. In V1 there are:
| Excluded | Consequence |
|---|---|
| No user deposits | Users never place funds with a SOVR instance. Nothing is owed to token holders. |
| No lending or credit | The reserve never lends and never borrows (BORROW is a prohibited action). |
| No fractional reserve | No liability is created against the reserve by the protocol. |
| No insurance | Nothing is insured. No product surface, copy, or API may imply that holdings, deposits or token values are insured or guaranteed. |
| No leverage | LEVERAGE is a prohibited action; prohibitions.leverage is literal false. |
| No yield farming | DEPOSIT_YIELD is a prohibited action; prohibitions.externalYield is literal false. |
| No rehypothecation | Reserve assets are held in program-owned accounts and are never pledged or re-lent. |
| No regulated-bank status | SOVR is not a regulated bank, does not hold a banking licence, and MUST NOT claim or imply otherwise. |
All public copy MUST follow this rule: SOVR manages project-owned assets under a published constitution. It does not take custody of user funds, does not promise any price, and does not insure anything.
2. The six primitives
| # | Primitive | Role | Trust class |
|---|---|---|---|
| 1 | Constitution | The economic law of the instance: what may be done, by how much, how often, where, and how the law itself may change. | Deterministic, onchain, hash-committed. |
| 2 | Reserve | Program-owned vault of allowlisted assets plus protocol-owned liquidity positions. | Onchain, PDA-owned. |
| 3 | Mandate | Weighted, measurable objectives the governor optimises for. Price targeting excluded. | Part of the constitution. |
| 4 | Governor | AI/model pipeline that observes, classifies, simulates and proposes. Emits PolicyIntents. Holds no signing key. |
Untrusted. Its output is input. |
| 5 | Executor | Deterministic engine that validates an intent against the constitution and current state, enforces the timelock, and executes only allowlisted action types. | Deterministic. Onchain program + reference TS implementation. |
| 6 | Ledger | Append-only public record of every intent, verdict, execution, and measured outcome. | Onchain (canonical) + indexed offchain. |
The relationship is strictly one-directional: the Governor proposes, the Constitution judges (through the Executor), the Executor acts on the Reserve, and the Ledger records. Nothing flows from the Governor to the Reserve except through the Executor.
market / chain / oracle state (structured only)
│
▼
┌─────────────┐ PolicyIntent (untrusted JSON) ┌───────────────────────┐
│ GOVERNOR │ ────────────────────────────────▶ │ EXECUTOR │
│ (AI layers) │ │ parseIntent (schema) │
└─────────────┘ │ validateIntent │
▲ │ timelock │
│ measured outcomes │ verifyExecution │
│ └──────────┬────────────┘
┌─────────────┐ │ only allowlisted
│ LEDGER │ ◀──────── receipts, verdicts ────────────────┤ action types
│ append-only │ ▼
└─────────────┘ ┌───────────────────────┐
│ RESERVE (PDA vault) │
CONSTITUTION ── bounds every check ─────────────▶ │ USDC · SOL · governed │
└───────────────────────┘
3. The SovrInstance object
A SOVR instance is the unit of deployment: one governed token, one constitution, one reserve, one governor, one ledger. [Specified, not implemented] — the interface below is normative for Stage 3; the constituent types (Constitution, Mandate, Regime, Allocation) already exist in types.ts.
interface SovrInstance {
id: string; // e.g. "sovr:sol:xyz" — equals constitution.instanceId
chain: "solana" | "evm"; // V1: "solana" only
governedToken: { symbol: string; name: string; address: string; decimals: number };
constitution: { id: string; version: number; hash: Hex }; // hash = constitutionHash(c)
reserveVault: string; // address of the PDA-owned vault
policyGovernor: {
proposerAddress: string; // key that may ONLY submit intents (see §13.3)
modelVersion: string; // e.g. "sovr-governor/0.4.2"
status: "ONLINE" | "DEGRADED" | "OFFLINE";
};
policyExecutor: string; // program / account that validates and executes
oracleConfiguration: { sources: string[]; maxAgeSeconds: number; maxDeviationBps: Bps };
mandate: Mandate; // weights sum to 10_000; priceTargeting: false
currentRegime: Regime;
currentParameters: {
allocation: Allocation; // current revenue split, sums to 10_000
lastExecutedAt: UnixSeconds | null;
policyWindow: number; // current window index
};
policyHistory: string; // address of the append-only PolicyLedger
emergencyControls: {
paused: boolean;
pauseAuthority: string;
resumeRequestedAt: UnixSeconds | null;
resumeDelaySeconds: number;
};
metadata: { createdAt: UnixSeconds; template: ConstitutionTemplate; uri?: string };
}
id MUST equal constitution.instanceId. Every intent carries an instance field; an intent whose instance differs from the constitution's instanceId is rejected with INSTANCE_MISMATCH (check INSTANCE).
4. Constitution
4.1 Structure
The constitution is the full Constitution type in types.ts. Its fields fall into three classes.
| Class | Fields | Change process |
|---|---|---|
| Core immutable | prohibitions, emergency.pauseCanMoveFunds, amendment (the amendment process itself), instanceId, governedToken |
Never. Listed in PROTOCOL_LIMITS.unamendable. A constitution that lists any of these (or any sub-path of them) in amendment.amendableFields fails validateConstitution. |
| Amendable | Exactly those fields listed in amendment.amendableFields (for XYZ-001: allocationBounds, reserveAssets, venues, mandate.weights, policyWindowSeconds) |
Constitutional amendment only (§4.4). |
| Fixed at genesis | Every other field (e.g. maxStepBps, perPolicyMaxOutflowBps, rollingOutflow, oracle, maxSlippageBps, buyback, permittedActions, emergency.pauseAuthority, emergency.resumeDelaySeconds) |
Not amendable unless the creator listed them at genesis. |
Every amended constitution MUST itself pass validateConstitution. Amendment cannot be used to escape protocol limits.
4.2 Hard prohibitions
HardProhibitions fields are typed as literal false. A constitution in which any is not false does not type-check and is rejected at runtime.
| Field | Meaning |
|---|---|
mint |
The instance can never mint the governed token. |
arbitraryTransfer |
No transfer to an arbitrary address. |
borrowing |
The reserve never borrows. |
leverage |
No leveraged positions. |
governorMayAmendConstitution |
The governor can never amend its own authority. |
arbitraryCalldata |
No arbitrary program invocation. |
externalYield |
No deposit into external yield protocols. |
Additionally: emergency.pauseCanMoveFunds: false and mandate.priceTargeting: false are enforced.
4.3 Protocol limits
PROTOCOL_LIMITS in engine.ts are ceilings and floors that apply to every constitution. They protect against a creator constructing a malicious or reckless constitution. validateConstitution enforces them; a constitution that fails cannot be deployed.
| Limit | Value | Human form | Enforced on |
|---|---|---|---|
minExecutionDelaySeconds |
3_600 | ≥ 1 h public notice | minExecutionDelaySeconds |
minPolicyWindowSeconds |
21_600 | ≥ 6 h between executions | policyWindowSeconds |
maxStepBps |
2_500 | ≤ 25% bucket move per policy | maxStepBps (also must be > 0) |
maxPerPolicyOutflowBps |
500 | ≤ 5% of reserve per policy | perPolicyMaxOutflowBps |
maxRollingOutflowBps |
1_500 | ≤ 15% of reserve per rolling window | rollingOutflow.maxBpsOfReserve |
minRollingWindowSeconds |
604_800 | rolling window ≥ 7 d | rollingOutflow.windowSeconds |
maxSlippageBps |
300 | ≤ 3% slippage bound | maxSlippageBps |
maxOracleAgeSeconds |
600 | ≤ 10 min oracle age | oracle.maxAgeSeconds |
maxBuybackParticipationBps |
1_500 | ≤ 15% of 24 h volume | buyback.maxParticipationBps |
maxGovernedTokenReserveBps |
5_000 | governed token ≤ 50% of reserve | governed reserveAssets[].target.max |
maxVenueExposureBps |
5_000 | ≤ 50% exposure per venue | venues[].maxExposureBps |
minAmendmentTimelockSeconds |
604_800 | amendment timelock ≥ 7 d | amendment.timelockSeconds |
minResumeDelaySeconds |
3_600 | ≥ 1 h between unpause request and resumption | emergency.resumeDelaySeconds |
unamendable |
prohibitions, emergency.pauseCanMoveFunds, amendment, instanceId, governedToken |
permanently unamendable | amendment.amendableFields |
4.4 validateConstitution — full rule set
A constitution is valid iff it produces no issues under all of the following:
- Every
prohibitions.*isfalse. emergency.pauseCanMoveFunds === false.mandate.priceTargeting === false.- Mandate weights over the five objectives sum to exactly 10_000.
- Every
permittedActionsentry is inACTION_TYPES. - Each
allocationBounds[bucket]exists with0 ≤ min ≤ max ≤ 10_000. - Allocation feasibility:
Σ min ≤ 10_000 ≤ Σ max. 0 < maxStepBps ≤ 2_500.policyWindowSeconds ≥ 21_600.minExecutionDelaySeconds ≥ 3_600.perPolicyMaxOutflowBps ≤ 500.rollingOutflow.maxBpsOfReserve ≤ 1_500.rollingOutflow.windowSeconds ≥ 604_800.perPolicyMaxOutflowBps ≤ rollingOutflow.maxBpsOfReserve.maxSlippageBps ≤ 300.oracle.maxAgeSeconds ≤ 600.oracle.sources.length ≥ 2(at least two independent sources).buyback.maxParticipationBps ≤ 1_500.- No asset is listed twice in
reserveAssets. - The governed asset's
target.max ≤ 5_000. - Reserve feasibility:
Σ target.min ≤ 10_000 ≤ Σ target.max. - At least one reserve asset (the governed token) is declared with
governed: true; it may have a 0% maximum, but it must be declared. - Every
venues[].maxExposureBps ≤ 5_000. amendment.timelockSeconds ≥ 604_800.amendment.authoritydoes not name the governor (reference check:/governor/i).- No
amendment.amendableFieldsentry equals, or is a sub-path of, anunamendableentry. emergency.resumeDelaySeconds ≥ 3_600.
4.5 Policy versus constitutional amendment
These are two different processes with different actors, and the protocol MUST keep them distinct in data model, UI and copy.
| Policy | Constitutional amendment | |
|---|---|---|
| What changes | Parameters within constitutional bounds (allocation, a buyback, a POL addition, a rebalance) | The bounds themselves |
| Proposer | Governor (AI pipeline) | Amendment authority (token governance / multisig named in amendment.authority) |
| Can the governor initiate | Yes | Never. AMEND_CONSTITUTION is a prohibited action. |
| Validation | validateIntent against current constitution |
validateConstitution against PROTOCOL_LIMITS |
| Notice | minExecutionDelaySeconds (≥ 1 h; XYZ-001: 2 h) |
amendment.noticeSeconds (XYZ-001: 7 d) |
| Timelock | Same as notice | amendment.timelockSeconds (≥ 7 d; XYZ-001: 14 d) |
| Frequency | ≤ 1 executed policy per policyWindowSeconds |
No protocol frequency; each amendment runs the full process |
| Result | Policy receipt in ledger | New constitution version (version + 1), new constitutionHash |
Amendment flow:
PROPOSAL ─▶ PUBLIC NOTICE ─▶ TIMELOCK ─▶ GOVERNANCE APPROVAL ─▶ EXECUTION
(authority) (noticeSeconds) (timelockSeconds) (authority vote) (new version,
validateConstitution
must pass)
Because the policy hash commits to the constitution hash (§11.3), any policy approved under constitution version n fails verifyExecution once version n+1 is active. Amendments therefore implicitly cancel in-flight policies; the governor must re-propose under the new law.
4.6 Reference constitution XYZ-001
XYZ-001 (template BALANCED, instance sovr:sol:xyz, governed token XYZ, 6 decimals) is the conformance fixture. Its values are reproduced in the BALANCED column of §17.
5. Reserve and balance sheet
5.1 Reserve assets (V1)
V1 reserves hold exactly three asset classes: SOL, USDC, and the governed token. Each is declared in reserveAssets with a target range (share of total reserve value). The governed token MUST be declared with governed: true; its maximum share MUST NOT exceed 50%.
XYZ-001 targets: USDC 40–70%, SOL 15–45%, XYZ 0–25%.
5.2 Balance sheet
The balance sheet is published for every instance and recomputed every policy window.
Assets
| Line | Definition |
|---|---|
| Reserve — USDC | USDC held in the reserve vault, at oracle price. |
| Reserve — SOL | SOL held in the reserve vault, at oracle price. |
| Reserve — governed token | Governed token held in the reserve vault, at oracle price. Reported separately and haircut (§5.4). |
| Protocol-owned liquidity (POL) | Value of LP positions owned by the instance, per venue, at oracle prices of the underlying assets. |
| Total assets | Sum of the above (gross, governed token unhaircut — disclosed alongside the haircut figure). |
Liabilities
Liabilities MUST include only real, contractual, enforceable obligations of the instance: for example, a committed and unpaid operations disbursement already approved by policy, or a contractual vendor obligation registered by the project's governance. The protocol MUST NOT manufacture liabilities.
In particular:
- Market capitalisation is not a liability. Token holders have no claim on the reserve.
- Circulating supply is not a liability.
- Future buyback budgets are not liabilities; they are allocations that can be changed by policy.
In V1, with no deposits and no borrowing, liabilities are expected to be zero or small. The balance sheet MUST show the line even when zero.
Net reserve
The term used is net reserve, never "equity", "capital" or "backing".
| Metric | Formula |
|---|---|
| Net reserve | Reserve assets + POL − Liabilities |
| Net reserve (ex-governed) | (USDC + SOL reserve + non-governed share of POL) − Liabilities |
| Governed holdings (haircut) | Governed token in reserve × (1 − haircut) — see §5.4 |
5.3 Ratios — explicit definitions
Every ratio is published with its numerator, denominator and observation window. No ratio may be displayed without those three disclosures one click away.
| Ratio | Numerator | Denominator | Notes |
|---|---|---|---|
| Runway (months) | USDC + SOL reserve value | Trailing monthly operating outflow, assuming zero revenue | Trailing monthly operating outflow = operations disbursements over the trailing 90 days ÷ 3. Governed token excluded from numerator. SOL valued at current oracle price (see §26 on reflexivity). |
| Liquid reserve coverage | USDC + SOL reserve value | Circulating market capitalisation of the governed token | A coverage indicator, not a redemption promise. No holder can redeem against it. |
| Stable share | USDC reserve value | Total reserve value | Measures exposure to non-stable assets. |
| Governed concentration | Governed token reserve value | Total reserve value | Bounded by the constitution (target.max, ≤ 50%). |
| POL depth ratio | Total POL value | Circulating market capitalisation | Liquidity owned by the instance per unit of market value. |
| Venue exposure (per venue) | POL value at venue | Total reserve value + total POL value | Same denominator as engine check VENUE_EXPOSURE. |
| 30D net flow | 30D inflows − 30D outflows | — (USD) | Definitions below. |
30D inflows = revenue received by the instance in the trailing 30 days (fees and other project revenue routed to the instance), before allocation.
30D outflows = buyback spend + POL additions + operations disbursements, all in the trailing 30 days.
Rebalances are internal. A REBALANCE_RESERVE moves value between reserve assets and is not an outflow; only its execution cost (realised slippage + fees) is recorded, as a separate "execution cost" line.
Two notions of outflow. Accounting outflows (above) differ from the engine's cap-relevant outflow:
| Accounting 30D outflows | Engine outflow (actionOutflowUsd) |
|
|---|---|---|
| Buyback spend | Included | Included (full amount_usd) |
| POL additions | Included | Included (full amount_usd) |
| Operations disbursements | Included | Not included — these are paid from the operations allocation of revenue, not from the reserve vault |
| Rebalances | Excluded (internal) | Excluded (returns 0) |
| Used for | Reporting, runway | PER_POLICY_OUTFLOW and ROLLING_OUTFLOW checks |
5.4 Governed-token holdings
Governed-token holdings in the reserve are reflexive: their value falls exactly when the reserve is most needed, and buybacks convert stable assets into governed tokens. The protocol therefore:
- Reports governed-token holdings on a separate line, never merged into a headline "reserve" number without disclosure.
- Publishes a haircut value. Default haircut: 50%, adjustable by the creator at genesis, never below 25%. [Specified, not implemented]
- Excludes governed tokens from runway and liquid reserve coverage numerators.
See §26 for the open question of whether caps should use haircut values.
5.5 Worked example (fixture state)
From baseContext() in fixtures.ts:
| Line | USD | Share of reserve |
|---|---|---|
| USDC | 2,510,102 | 52.1% |
| SOL | 1,620,882 | 33.6% |
| XYZ (governed) | 690,307 | 14.3% |
| Reserve total | 4,821,291 | 100% |
| POL — Orca Whirlpools | 1,101,288 | |
| POL — Raydium CLMM | 740,000 | |
| Total assets | 6,662,579 |
Derived engine limits at this state:
| Limit | Computation | Value |
|---|---|---|
| Per-policy outflow cap | 4,821,291 × 2.5% | $120,532 |
| Rolling 30 d outflow cap | 4,821,291 × 6% | $289,277 |
| Buyback participation cap | 24 h volume 1,240,000 × 10% | $124,000 |
| Orca venue exposure | 1,101,288 / 6,662,579 | 16.5% (cap 35%) |
6. Mandate
The mandate is a set of weighted, measurable objectives. Weights are in bps and MUST sum to 10_000.
| Objective | Measured by (examples) |
|---|---|
liquidity_health |
POL depth ratio; depth within ±2% of mid on allowlisted venues; realised slippage on reference trade sizes. |
reserve_durability |
Runway; stable share; net reserve (ex-governed) trend. |
revenue_sustainability |
30D net flow; operations coverage from revenue alone. |
market_stability |
Realised volatility of the governed token relative to a sector benchmark; depth asymmetry. Not a price level. |
capital_efficiency |
Idle reserve above target; POL fee yield per unit of POL; execution cost per dollar deployed. |
XYZ-001 weights: liquidity 30%, reserve 30%, revenue 20%, market stability 10%, capital efficiency 10%.
Price targeting is excluded. priceTargeting is typed literal false and is enforced by validateConstitution. No objective may be expressed as a price level or price floor. The governor MUST NOT state or imply that any action supports, defends or targets a price.
7. Governor
7.1 Principle
The governor is an advisory, proposal-generating pipeline. Its output is a PolicyIntent, which is untrusted input. The governor:
- holds no signing key with authority over funds;
- cannot call any program other than submitting an intent (§13.3);
- cannot express an action outside the five-action vocabulary;
- cannot amend the constitution, its own authority, or the pause.
7.2 Layers
| # | Layer | Kind | Input | Output |
|---|---|---|---|---|
| 1 | Economic Observer | Deterministic + model | Structured chain, reserve, market and oracle state from the indexer | Normalised state vector; evidenceHash = hash of that state |
| 2 | Regime Classifier | Model | State vector | One of seven regimes + classification uncertainty |
| 3 | Policy Planner | Model | State, regime, mandate, constitution bounds | Candidate PolicyIntent(s) |
| 4 | Simulator | Deterministic / stochastic model | Candidate intents, state, scenarios | Scenario ranges; simulationHash |
| 5 | Policy Critic | Model (independent of Planner) | Candidate, simulation, mandate | Accept / revise / withdraw recommendation, structured objections |
| 6 | Constitution Engine | Deterministic. Not AI. | Constitution, ExecutionContext, raw intent |
PASS / REJECT / NO_ACTION verdict with full check report |
| 7 | Executor | Deterministic. Not AI. | Approved policy hash, payload, state at execution time | Transaction or rejection |
Rules:
- No model is both proposer and judge. The Planner and Critic MUST be separate invocations with separate prompts; SHOULD be separate model families or versions. Neither can override layer 6.
- Layers 6 and 7 contain no natural-language interpretation.
engine.tsis pure, total, fail-closed, and performs no I/O. - The Critic can only withdraw or ask for revision; it cannot approve. Only layer 6 approves.
7.3 Structured rationale (no chain-of-thought)
The governor MUST NOT publish raw chain-of-thought. It publishes a structured rationale attached to (but not part of) the executable intent:
| Field | Content |
|---|---|
assessment |
Observed state, as metrics with values and sources. |
recommendation |
The proposed actions, in the same terms as the intent. |
rationale |
Which mandate objectives the action serves, and the measured gap it addresses. |
uncertainty |
Classification uncertainty, key unknowns, data quality flags. |
simulation |
Scenario ranges (§10), simulationHash. |
constitution_result |
The engine's verdict and every check (PASS / FAIL / SKIP with detail). |
Language MUST be institutional: "the governor proposes", "the policy allocates", "the constitution rejected". The governor MUST NOT be anthropomorphised ("the AI feels", "the AI wants", "the AI is confident the price will…"). confidence in the intent is informational only and is never an authorisation input.
The rationale is not part of the executable schema. The schema has no free-text field; natural language cannot reach the executor.
8. Policy windows
A policy window is the cadence at which the governor may propose and at most one mutating policy may execute. The minimum spacing between executions is policyWindowSeconds (≥ 6 h; XYZ-001: 72 h).
| Step | Name | Actor | Output |
|---|---|---|---|
| 1 | Observe | Economic Observer | State vector, evidenceHash |
| 2 | Classify | Regime Classifier | Regime + uncertainty |
| 3 | Simulate | Simulator | Scenario ranges for candidate policies; simulationHash |
| 4 | Propose | Policy Planner, reviewed by Policy Critic | PolicyIntent (proposedAt, effectiveTime) |
| 5 | Validate | Constitution Engine (validateIntent) |
Verdict + policyHash |
| 6 | Publish | Executor | Intent, verdict, rationale written to the PolicyLedger |
| 7 | Timelock | — | Wait until effectiveTime (≥ proposedAt + minExecutionDelaySeconds) |
| 8 | Execute | Executor (verifyExecution, then transaction) |
Execution transaction or rejection |
| 9 | Measure | Observer / analytics | Realised outcome vs simulation |
| 10 | Record | Ledger | Receipt finalised with result and measured outcome |
Steps 3 and 4 iterate: the Planner may revise after simulation and critique. Step 5 is the only gate.
A rejected policy is still published (step 6) with its rejection codes. Rejections are part of the public record.
9. Policy vocabulary
9.1 Permitted actions (complete V1 set)
| Action | Parameters | Moves value | Engine outflow |
|---|---|---|---|
NO_CHANGE |
— | No | 0 |
SET_REVENUE_ALLOCATION |
reserve_bps, liquidity_bps, buyback_bps, operations_bps |
No (changes future revenue split) | 0 |
EXECUTE_SCHEDULED_BUYBACK |
amount_usd, pay_asset, venue, max_slippage_bps, tranches, duration_seconds |
Yes | amount_usd |
ADD_PROTOCOL_OWNED_LIQUIDITY |
amount_usd, venue, pair [AssetId, AssetId], max_slippage_bps |
Yes | amount_usd |
REBALANCE_RESERVE |
from_asset, to_asset, amount_usd, venue, max_slippage_bps |
Yes | 0 (internal) |
A constitution may permit a subset via permittedActions.
9.2 Prohibited actions
The engine recognises these names only in order to reject them with PROHIBITED_ACTION. They have no executor implementation anywhere.
AMEND_CONSTITUTION, TRANSFER, WITHDRAW_RESERVE, MINT, BURN_FROM, BORROW, LEVERAGE, SET_AUTHORITY, UPGRADE_PROGRAM, ARBITRARY_CALL, DISABLE_PAUSE, DEPOSIT_YIELD.
Any other unrecognised type is rejected with UNKNOWN_ACTION.
9.3 Buybacks
Buybacks are bounded, scheduled operations. They are not price support.
| Requirement | Mechanism | Rejection |
|---|---|---|
| Explicit budget | amount_usd, counted in full against per-policy and rolling outflow caps |
PER_POLICY_OUTFLOW, ROLLING_OUTFLOW |
| Participation cap | amount_usd ≤ volume24hUsd × buyback.maxParticipationBps / 10_000 (≤ 15%; XYZ-001: 10%) |
BUYBACK_PARTICIPATION |
| Tranches and duration | tranches ≥ buyback.minTranches and duration_seconds ≥ buyback.minDurationSeconds (XYZ-001: ≥ 12 tranches over ≥ 24 h). Schema: 1–500 tranches, ≤ 30 d. |
BUYBACK_SCHEDULE |
| Venue allowlist | venue in venues |
UNKNOWN_VENUE |
| Slippage bound | max_slippage_bps ≤ maxSlippageBps |
SLIPPAGE_BOUND |
| Pay asset | Allowlisted reserve asset; never the governed token | UNKNOWN_ASSET |
| Price feed | Fresh, agreeing oracle readings for the pay asset and the governed token | ORACLE_* |
| Accounting | Purchased tokens settle into the reserve vault. Ledger records spend, tokens received, average price, realised slippage per tranche. | — |
Disclosure rule: buyback receipts and UI MUST NOT say or imply that a buyback "supports", "defends", "floors" or "stabilises" the price. Accepted wording: "the instance purchased N tokens for $X across T tranches".
Bought tokens are retained in the reserve in V1 (no burn: BURN_FROM is prohibited; a self-burn of reserve-held tokens is a possible future amendment-gated feature, see §26).
9.4 Protocol-owned liquidity
V1 POL is deliberately simple:
- add liquidity on an allowlisted venue, in an allowlisted pair, bounded by slippage, venue exposure (
VENUE_EXPOSURE), and outflow caps; - the engine projects POL as taking
amount_usd / 2from each pair asset in the reserve; a shortfall in either isINSUFFICIENT_RESERVE; - pair assets must differ.
There is no autonomous range management, rebalancing of LP ranges, or inventory-skewing. Autonomous market making is a separate future product with its own constitution class and is out of scope for V1.
V1 has no "remove liquidity" action. Removing POL is a constitutional-amendment-gated operation in V1 (see §26).
9.5 Reserve rebalances
- Only between allowlisted reserve assets;
from_asset ≠ to_asset. - Post-execution composition MUST be inside each asset's target range, or, if an asset is already outside its range, the action MUST move it strictly toward the range (
RESERVE_COMPOSITION). - The engine projects the received amount at worst-case slippage:
amount_usd × (1 − max_slippage_bps / 10_000). - No discovery of yield protocols, lending markets, or new assets. Adding an asset is a constitutional amendment.
10. Policy simulator
The simulator produces ranges under stated assumptions, not predictions. Every mutating policy MUST carry a simulationHash; a mutating intent with simulationHash: null, or submitted while the simulator is not ONLINE, results in NO_ACTION / SIMULATOR_UNAVAILABLE. NO_CHANGE does not require simulation.
10.1 Scenarios
Each candidate policy is simulated against the current state under all of:
| Scenario | Assumption set (horizon: one policy window and 30 d) |
|---|---|
BASE |
Trailing 30 d volume, revenue and volatility persist. |
BULL |
Volume +50%, revenue +50%, governed token +30%, SOL +20%. |
BEAR |
Volume −40%, revenue −40%, governed token −30%, SOL −25%. |
LIQUIDITY_SHOCK |
Third-party depth on allowlisted venues −70%; slippage curves steepen accordingly. |
VOLUME_COLLAPSE |
24 h volume −80% (buyback participation cap shrinks proportionally). |
SOL_-30 |
SOL −30% instantaneous, all else BASE. |
TOKEN_-50 |
Governed token −50% instantaneous, all else BASE. |
REVENUE_-50 |
Revenue −50% for the horizon, all else BASE. |
10.2 Outputs
Per scenario, the simulator reports P10 / P50 / P90 for: net reserve, net reserve (ex-governed), runway, stable share, POL depth ratio, realised execution cost, and remaining rolling-outflow headroom. It also reports whether any constitutional bound would be breached on the following window under that scenario.
10.3 Hash
simulationHash = sha256(canonicalJson({ inputs, assumptions, seed, simulatorVersion, outputs })). The full simulation artefact is published alongside the receipt so the hash is independently verifiable.
10.4 Language
Simulation results MUST be described as "under scenario X, the range is …". They MUST NOT be described as forecasts, expectations or targets.
11. Intent format, schema and hashing
11.1 Envelope (strict)
| Field | Type / bound |
|---|---|
instance |
string, 1–64 chars |
window |
integer, 0–1_000_000 |
regime |
one of REGIMES |
proposedAt |
integer unix seconds, 1_600_000_000 – 4_102_444_800 |
effectiveTime |
same bounds as proposedAt |
actions |
array, 1–4 items |
confidence |
number 0–1 (informational only) |
modelVersion |
string, 1–64 chars |
evidenceHash |
64-char lowercase hex |
simulationHash |
64-char lowercase hex, or null |
All objects are strict: unknown keys are rejected (MALFORMED_INTENT). All numbers are bounded. bps fields are integers 0–10_000; amount_usd is finite, 0 – 1e12. Strings where numbers are expected, fractional bps, and non-JSON text are all MALFORMED_INTENT.
11.2 Example intent (JSON)
The following is the fixture baseIntent(). Against XYZ-001 and baseContext() it returns PASS with next allocation 35 / 30 / 20 / 15. (Hash values in this example are fixture placeholders: evidenceHash is "a"×64, simulationHash is "b"×64.)
{
"instance": "sovr:sol:xyz",
"window": 21,
"regime": "ACCUMULATION",
"proposedAt": 1791208800,
"effectiveTime": 1791216000,
"actions": [
{
"type": "SET_REVENUE_ALLOCATION",
"parameters": {
"reserve_bps": 3500,
"liquidity_bps": 3000,
"buyback_bps": 2000,
"operations_bps": 1500
}
}
],
"confidence": 0.78,
"modelVersion": "sovr-governor/0.4.2",
"evidenceHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"simulationHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
A scheduled buyback intent (passes in the conformance suite, regime BUYBACK):
{
"instance": "sovr:sol:xyz",
"window": 21,
"regime": "BUYBACK",
"proposedAt": 1791208800,
"effectiveTime": 1791216000,
"actions": [
{
"type": "EXECUTE_SCHEDULED_BUYBACK",
"parameters": {
"amount_usd": 60000,
"pay_asset": "USDC",
"venue": "orca-whirlpool",
"max_slippage_bps": 75,
"tranches": 24,
"duration_seconds": 86400
}
}
],
"confidence": 0.78,
"modelVersion": "sovr-governor/0.4.2",
"evidenceHash": "ee8250fb76e094b34b471f13a73dbbe51d1ae142e9df59d7c0d31ec20f0a0a8e",
"simulationHash": "32e4bc02a7ccf34d72692db7f08aa945102e290beb4832d5673b987015d8cb4f"
}
$60,000 is below the per-policy cap ($120,532), the participation cap ($124,000) and the rolling cap ($289,277); 24 tranches over 24 h meets the 12-tranche / 24 h minimum; 0.75% slippage is within the 1% bound.
11.3 Hashing
| Hash | Definition |
|---|---|
| Canonical JSON | Object keys sorted recursively; no whitespace; undefined values dropped; non-finite numbers rejected (throws). |
constitutionHash(c) |
sha256(canonicalJson(c)) |
policyHash(c, intent) |
sha256(canonicalJson({ constitution: constitutionHash(c), intent })) |
evidenceHash |
Hash of the Observer's input state vector (the receipt's "input state hash") |
simulationHash |
§10.3 |
The policy hash binds an intent to the exact constitution version it was validated against. Hashes are independent of key order and identical across services and languages that implement the same canonicalisation.
12. Constitution engine — normative behaviour
12.1 Properties
The engine (engine.ts) MUST be:
- Pure — no I/O, clock reads, or randomness;
ctx.nowis the only clock. - Total — never throws on any input; malformed input is a
REJECT. - AI-free — interprets no natural language.
- Fail-closed — missing data is
REJECTorNO_ACTION, neverPASS. - Exhaustive — once past schema and dependency gates, every check runs and every failing invariant is reported, not only the first.
The onchain PolicyExecutor MUST implement the same checks with the same semantics. The TypeScript engine is the reference used for governor pre-flight, simulation, the UI, and conformance testing.
12.2 Verdicts
| Verdict | Meaning |
|---|---|
PASS |
Policy may be published and, after the timelock, executed. Carries policyHash, checks, outflowUsd, nextAllocation. |
REJECT |
One or more invariants failed. Carries all rejections. policyHash is null only for schema failures. |
NO_ACTION |
Nothing executes this window. Reasons: GOVERNOR_OFFLINE, NO_INTENT, SIMULATOR_UNAVAILABLE, DEPENDENCY_UNHEALTHY. There is never a default policy. |
12.3 Evaluation order (validateIntent)
| Step | Check ID | Condition | Outcome on failure |
|---|---|---|---|
| 1 | DEPENDENCIES |
dependencies.governor === "ONLINE" |
NO_ACTION / GOVERNOR_OFFLINE (returns) |
| 1b | — | raw intent is not null/undefined |
NO_ACTION / NO_INTENT (returns) |
| 2 | SCHEMA |
parseIntent succeeds |
REJECT with MALFORMED_INTENT, UNKNOWN_ACTION, or PROHIBITED_ACTION; policyHash: null (returns) |
| 3 | DEPENDENCIES |
If mutating: simulator ONLINE and simulationHash ≠ null |
NO_ACTION / SIMULATOR_UNAVAILABLE (returns) |
| 3b | DEPENDENCIES |
dependencies.indexer === "ONLINE" (all intents, including NO_CHANGE) |
NO_ACTION / DEPENDENCY_UNHEALTHY (returns) |
| 4 | INSTANCE |
intent.instance === c.instanceId |
INSTANCE_MISMATCH |
| 5 | PAUSE |
ctx.paused === false (applies to all intents, including NO_CHANGE) |
PAUSED |
| 6 | ACTION_PERMITTED |
No duplicate types; each type in permittedActions; NO_CHANGE not combined |
DUPLICATE_ACTION, ACTION_NOT_PERMITTED, MALFORMED_INTENT |
| 7 | ALLOCATION_BOUNDS |
Allocation sums to 10_000; each bucket within allocationBounds |
ALLOCATION_SUM, ALLOCATION_OUT_OF_BOUNDS |
| 7b | ALLOCATION_VELOCITY |
` | proposed − current |
| 8 | POLICY_FREQUENCY |
If mutating and a prior execution exists: effectiveTime − lastExecutedAt ≥ policyWindowSeconds |
COOLDOWN |
| 9 | ASSET_ALLOWLIST |
Referenced assets allowlisted; buyback not paid in governed token; rebalance from ≠ to; POL pair distinct | UNKNOWN_ASSET |
| 9b | VENUE_ALLOWLIST |
Referenced venue in venues |
UNKNOWN_VENUE |
| 10 | VENUE_EXPOSURE |
For POL: (existing venue POL + amount) / (reserve total + total POL) ≤ maxExposureBps |
VENUE_EXPOSURE |
| 11 | SLIPPAGE |
Every max_slippage_bps ≤ maxSlippageBps |
SLIPPAGE_BOUND |
| 12 | BUYBACK_LIMITS |
Participation cap; tranches and duration minima | BUYBACK_PARTICIPATION, BUYBACK_SCHEDULE |
| 13 | PER_POLICY_OUTFLOW |
outflow ≤ reserve.totalUsd × perPolicyMaxOutflowBps / 10_000 |
PER_POLICY_OUTFLOW |
| 13b | ROLLING_OUTFLOW |
If outflow > 0: trailing outflows with executedAt > now − windowSeconds, plus this outflow, ≤ reserve.totalUsd × maxBpsOfReserve / 10_000 |
ROLLING_OUTFLOW |
| 14 | RESERVE_COMPOSITION |
Projected holdings non-negative; each asset inside target or moving toward it | INSUFFICIENT_RESERVE, RESERVE_COMPOSITION |
| 15 | ORACLE_FRESHNESS |
For every referenced asset (+ governed token if buyback): oracle network not OFFLINE; a reading from every configured source; each reading ≤ maxAgeSeconds old; (max − min) / min ≤ maxDeviationBps |
ORACLE_MISSING, ORACLE_STALE, ORACLE_DEVIATION |
| 16 | TIMELOCK |
If mutating: effectiveTime − proposedAt ≥ minExecutionDelaySeconds; and proposedAt ≤ now + 300 (no future-dated intents) |
TIMELOCK |
Then policyHash is computed. Any rejection → REJECT; otherwise PASS.
Report order. Checks are reported in CHECK_IDS order (SCHEMA, INSTANCE, DEPENDENCIES, PAUSE, ACTION_PERMITTED, ALLOCATION_BOUNDS, ALLOCATION_VELOCITY, POLICY_FREQUENCY, ASSET_ALLOWLIST, VENUE_ALLOWLIST, VENUE_EXPOSURE, SLIPPAGE, BUYBACK_LIMITS, PER_POLICY_OUTFLOW, ROLLING_OUTFLOW, RESERVE_COMPOSITION, ORACLE_FRESHNESS, TIMELOCK), which differs from evaluation order only in that DEPENDENCIES is evaluated first. A FAIL on a check is sticky and is never overwritten by PASS or SKIP. A check with nothing to evaluate is SKIP.
"Mutating" means the intent contains any action other than NO_CHANGE. "Value-moving" means EXECUTE_SCHEDULED_BUYBACK, ADD_PROTOCOL_OWNED_LIQUIDITY, or REBALANCE_RESERVE; asset, venue, slippage, composition and oracle checks apply only to value-moving actions.
12.4 Rejection codes (complete)
| Code | Check | Meaning |
|---|---|---|
MALFORMED_INTENT |
SCHEMA, ACTION_PERMITTED |
Not JSON, schema violation, unknown key, missing type, or NO_CHANGE combined with other actions |
UNKNOWN_ACTION |
SCHEMA |
Type outside the V1 vocabulary |
PROHIBITED_ACTION |
SCHEMA |
Type is a prohibited power |
INSTANCE_MISMATCH |
INSTANCE |
Intent addressed to another instance |
PAUSED |
PAUSE |
Emergency pause active |
ACTION_NOT_PERMITTED |
ACTION_PERMITTED |
Valid V1 action not permitted by this constitution |
DUPLICATE_ACTION |
ACTION_PERMITTED |
Same action type twice in one intent |
ALLOCATION_SUM |
ALLOCATION_BOUNDS |
Allocation ≠ 100% |
ALLOCATION_OUT_OF_BOUNDS |
ALLOCATION_BOUNDS |
Bucket outside constitutional range |
ALLOCATION_VELOCITY |
ALLOCATION_VELOCITY |
Bucket moves more than maxStepBps |
COOLDOWN |
POLICY_FREQUENCY |
Execution within policyWindowSeconds of the previous |
UNKNOWN_ASSET |
ASSET_ALLOWLIST |
Non-allowlisted asset, governed-token pay asset, identical rebalance/pair assets |
UNKNOWN_VENUE |
VENUE_ALLOWLIST |
Non-allowlisted venue |
VENUE_EXPOSURE |
VENUE_EXPOSURE |
Venue exposure cap exceeded |
SLIPPAGE_BOUND |
SLIPPAGE |
Slippage tolerance above bound |
BUYBACK_PARTICIPATION |
BUYBACK_LIMITS |
Buyback exceeds share of 24 h volume |
BUYBACK_SCHEDULE |
BUYBACK_LIMITS |
Too few tranches or too short a duration |
PER_POLICY_OUTFLOW |
PER_POLICY_OUTFLOW |
Single-policy outflow cap exceeded |
ROLLING_OUTFLOW |
ROLLING_OUTFLOW |
Rolling-window outflow cap exceeded |
RESERVE_COMPOSITION |
RESERVE_COMPOSITION |
Post-execution composition outside targets and not improving |
INSUFFICIENT_RESERVE |
RESERVE_COMPOSITION |
Projected holding of some asset would go negative |
ORACLE_STALE |
ORACLE_FRESHNESS |
Reading older than maxAgeSeconds |
ORACLE_MISSING |
ORACLE_FRESHNESS |
Oracle network offline or a configured source has no reading |
ORACLE_DEVIATION |
ORACLE_FRESHNESS |
Sources disagree beyond maxDeviationBps |
TIMELOCK |
TIMELOCK |
Insufficient public notice, or future-dated intent |
EXECUTION_HASH_MISMATCH |
verifyExecution |
Payload does not hash to the approved policy |
EXECUTION_TOO_EARLY |
verifyExecution |
now < effectiveTime |
12.5 verifyExecution
At execution time the executor MUST, in order:
- Parse the execution payload with the strict schema (schema rejections returned as-is).
- Recompute
policyHash(c, payload)against the currently active constitution; if it differs from the approved hash →EXECUTION_HASH_MISMATCH. - Require
ctx.now ≥ effectiveTime→ otherwiseEXECUTION_TOO_EARLY. - Re-run
validateIntentagainst current state. Cooldown is still measured from the previous execution. If the state has changed such that the policy is no longer valid (pause, oracle staleness, exhausted rolling cap, composition), execution is refused with the resulting codes. ANO_ACTIONat this stage is surfaced asMALFORMED_INTENTwith theNO_ACTIONdetail.
Only on ok: true is a transaction built.
13. Executor and authority model
13.1 Who holds what
| Actor | Holds | Can | Cannot |
|---|---|---|---|
| AI model(s) | Nothing | Produce text / JSON | Sign anything |
| Governor service | Proposer key | Submit a PolicyIntent to the PolicyLedger for validation |
Move funds, execute, pause, amend, upgrade |
| Executor (onchain) | PDA authority over reserve token accounts | Execute the five action types after onchain validation and timelock | Execute anything else; there is no code path for prohibited actions |
| Crank (anyone) | A fee-payer key | Trigger execute_policy for an approved, timelocked policy |
Alter the payload (hash-bound) |
| Pause authority | Multisig (XYZ-001: "XYZ Security Council · 3-of-5") | Pause; request resume | Move funds; amend; execute policy |
| Amendment authority | Token governance (XYZ-001: "XYZ token governance") | Run the amendment process | Bypass notice/timelock; amend unamendable fields; act as governor |
| Program upgrade authority | See §26 | Upgrade program (if retained) | — |
13.2 Execution scope
The executor composes only: SPL token transfers between the reserve vault and allowlisted venue programs, swap instructions on allowlisted venues with a minimum-out derived from max_slippage_bps and the oracle price, and liquidity-add instructions on allowlisted pools. The venue program IDs are fixed per VenueRule. There is no generic CPI.
13.3 Proposer key
The proposer key can call exactly one instruction: submit_policy(intent). Compromise of this key yields the ability to submit intents — which remain subject to every check, the timelock, and public visibility. It is equivalent to a compromised governor, which the protocol already treats as adversarial.
14. Regimes
The governor classifies the economy into one of seven regimes each window. The regime is a label on the intent; it confers no additional authority. All regimes are bounded by the same constitution.
14.1 Regime definitions
| Regime | Typical entry signals (observable) | Intent |
|---|---|---|
NEUTRAL |
Metrics within mandate tolerances | Hold; small corrections only |
ACCUMULATION |
Revenue healthy, runway above target, reserve below target | Build reserve from revenue |
LIQUIDITY_SUPPORT |
Depth below target, slippage on reference trades rising | Increase POL within caps |
DEFENSIVE |
Drawdown in governed token or SOL, oracle stress, falling volume | Preserve stable reserve; minimise outflow |
EXPANSION |
Strong revenue, runway well above target, operations under-funded relative to plan | Fund operations and liquidity within bounds |
BUYBACK |
Revenue surplus, runway above target, adequate volume, governed concentration below cap | Scheduled buybacks from the buyback allocation |
CONSERVATION |
Revenue falling, runway shortening | Lengthen runway; pause discretionary spend |
14.2 Typical parameter behaviour
Arrows indicate typical direction of the revenue allocation relative to the current allocation; every move remains within allocationBounds and maxStepBps.
| Regime | Reserve | Liquidity | Buyback | Operations | Typical value-moving action | Typical cadence |
|---|---|---|---|---|---|---|
NEUTRAL |
→ | → | → | → | NO_CHANGE |
Every window |
ACCUMULATION |
↑ | → / ↓ | → / ↓ | → | Rebalance toward USDC/SOL targets | Allocation change every 1–2 windows |
LIQUIDITY_SUPPORT |
↓ | ↑ | ↓ | → | ADD_PROTOCOL_OWNED_LIQUIDITY (small, within venue cap) |
One POL addition per window max |
DEFENSIVE |
↑ | → | ↓ (toward min) | ↓ (toward min) | Rebalance governed / SOL → USDC if composition permits; no buybacks | Prefer NO_CHANGE once positioned |
EXPANSION |
↓ | ↑ | → | ↑ | POL addition | Allocation change every 1–2 windows |
BUYBACK |
→ / ↓ | → | ↑ | → | EXECUTE_SCHEDULED_BUYBACK (tranched) |
One buyback per window max |
CONSERVATION |
↑ | ↓ | ↓ (toward min) | → / ↓ | Rebalance toward stable share | Minimal outflow |
Regime transitions SHOULD be rate-limited by the governor (hysteresis), and frequent regime flips are reported as a performance metric (§19).
15. Policy receipt
Every policy, passed or rejected, produces a receipt in the PolicyLedger.
| Field | Source |
|---|---|
| Policy # | Monotonic sequence number in the instance ledger |
| Token | governedToken.symbol and address |
| Regime | intent.regime |
| Window | intent.window |
| Proposed | intent.proposedAt |
| Effective | intent.effectiveTime |
| Executed | Execution timestamp, or — |
| Model version | intent.modelVersion |
| Input state hash | intent.evidenceHash |
| Constitution | constitution.id vversion, constitutionHash |
| Policy hash | policyHash(c, intent) |
| Simulation hash | intent.simulationHash |
| Actions | Canonical action list |
| Execution tx | Transaction signature(s), one per tranche for buybacks |
| Result | Realised amounts, realised slippage, execution cost, post-state summary |
| Constitution | PASS or REJECT (with all codes), or NO ACTION (with reason) |
| Checks | Full check list with PASS / FAIL / SKIP and detail |
| Rationale | Structured rationale (§7.3), stored offchain, hash on chain |
Example receipt (rendered):
POLICY #0021 · XYZ · ACCUMULATION
Proposed 2026-10-05 14:00 UTC
Effective 2026-10-05 16:00 UTC
Model sovr-governor/0.4.2
Input state 4ba69735…0e4e
Constitution XYZ-001 v1
Policy hash 823412d1…a812
Simulation 32e4bc02…cb4f
Action SET_REVENUE_ALLOCATION 30/35/20/15 → 35/30/20/15
Constitution PASS (9 PASS · 9 SKIP · 0 FAIL)
Execution tx (pending timelock)
16. Security invariants
These are the protocol's non-negotiable properties. Each is enforced in at least one deterministic place and covered by at least one test or a named Stage 3+ test.
| # | Invariant | Enforcement | Evidence |
|---|---|---|---|
| 1 | The AI has no unrestricted key. Models hold no key; the governor service's proposer key can only submit intents. | Authority model (§13); onchain instruction set | Stage 3 program test: proposer key cannot invoke any other instruction |
| 2 | The action set is finite and allowlisted. Five types; everything else is UNKNOWN_ACTION or PROHIBITED_ACTION. |
ACTION_TYPES, parseIntent |
"rejects an action type outside the vocabulary"; "rejects prohibited power %s" |
| 3 | Every action is checked. No intent bypasses validateIntent; execution re-validates via verifyExecution. |
Engine; executor | "rejects execution if state changed …" |
| 4 | Every amount is capped. Schema bound (≤ 1e12), per-policy cap, rolling cap, participation cap, velocity cap. | SCHEMA, PER_POLICY_OUTFLOW, ROLLING_OUTFLOW, BUYBACK_LIMITS, ALLOCATION_VELOCITY |
outflow, participation, velocity tests |
| 5 | Venues are allowlisted. | VENUE_ALLOWLIST, VENUE_EXPOSURE |
"rejects an unknown DEX"; venue exposure test |
| 6 | Assets are allowlisted. | ASSET_ALLOWLIST |
"rejects an unknown asset" |
| 7 | Oracle freshness and agreement are required for priced execution. ≥ 2 sources, all present, fresh, agreeing. | ORACLE_FRESHNESS; validateConstitution rule 17 |
stale / missing / deviation tests; single-source constitution test |
| 8 | Timelocks precede execution. Policies: ≥ 1 h. Amendments: ≥ 7 d. | TIMELOCK; EXECUTION_TOO_EARLY; rules 10, 24 |
timelock tests |
| 9 | No drain via repeated small policies. | ROLLING_OUTFLOW + POLICY_FREQUENCY |
"rejects splitting a 5%+ reserve withdrawal into many small policies" |
| 10 | Rolling caps exist and are bounded. ≤ 15% per ≥ 7 d; per-policy ≤ rolling. | validateConstitution rules 11–14 |
"rejects a 100% rolling outflow cap" |
| 11 | Failed dependencies stop safely. Governor, simulator, indexer down → NO_ACTION; oracle down → REJECT. No default policy. |
Engine steps 1, 3, 15 | NO_ACTION tests |
| 12 | Emergency pause exists. | PAUSE check; emergency.pauseAuthority |
"rejects when emergency pause is active" |
| 13 | Pause cannot withdraw. pauseCanMoveFunds is literal false and unamendable. |
Type system; rule 2; unamendable |
"rejects a pause authority that can move funds" |
| 14 | The AI cannot amend the constitution. AMEND_CONSTITUTION prohibited; governor cannot be amendment authority; amendment unamendable. |
parseIntent; rules 25–26 |
"rejects a constitution amendment requested by the governor"; "rejects giving the governor amendment authority" |
| 15 | Model output is untrusted. Strict schema, no free text, unknown keys rejected, confidence ignored for authorisation. | parseIntent |
malformed JSON / unknown field / natural language / stringly-typed tests |
| 16 | The ledger is append-only. Receipts are never modified or deleted; corrections are new entries. | PolicyLedger account design (§21) | Stage 3 program test: no instruction mutates an existing entry |
17. Constitutional templates
Templates are starting points. Every template below satisfies PROTOCOL_LIMITS and every rule in §4.4. BALANCED is identical to XYZ-001. CUSTOM permits any values that pass validateConstitution.
17.1 Parameters
| Parameter | HARD_MONEY | BALANCED (XYZ-001) | GROWTH | RESERVE_MAXIMALIST |
|---|---|---|---|---|
| Mandate weights liquidity / reserve / revenue / stability / efficiency | 15 / 45 / 20 / 15 / 5 | 30 / 30 / 20 / 10 / 10 | 35 / 15 / 20 / 10 / 20 | 10 / 50 / 20 / 15 / 5 |
priceTargeting |
false | false | false | false |
| Permitted actions | NO_CHANGE, SET_REVENUE_ALLOCATION, EXECUTE_SCHEDULED_BUYBACK, REBALANCE_RESERVE | all five | all five | NO_CHANGE, SET_REVENUE_ALLOCATION, REBALANCE_RESERVE |
| Allocation — reserve | 40–70% | 20–60% | 10–40% | 60–90% |
| Allocation — liquidity | 5–20% | 10–50% | 20–60% | 0–15% |
| Allocation — buyback | 0–25% | 0–30% | 0–20% | 0–0% |
| Allocation — operations | 5–15% | 10–25% | 15–35% | 5–15% |
| Σ min / Σ max | 50% / 130% | 40% / 165% | 45% / 155% | 65% / 120% |
maxStepBps |
500 (5%) | 1_000 (10%) | 1_500 (15%) | 500 (5%) |
policyWindowSeconds |
7 d | 72 h | 24 h | 7 d |
minExecutionDelaySeconds |
12 h | 2 h | 1 h | 24 h |
| Rolling outflow | 3% / 30 d | 6% / 30 d | 10% / 30 d | 2% / 30 d |
perPolicyMaxOutflowBps |
100 (1%) | 250 (2.5%) | 400 (4%) | 100 (1%) |
| Reserve — USDC | 50–80% | 40–70% | 30–60% | 60–90% |
| Reserve — SOL | 10–40% | 15–45% | 20–50% | 10–40% |
| Reserve — governed | 0–10% | 0–25% | 0–35% | 0–5% |
| Σ min / Σ max (reserve) | 60% / 130% | 55% / 140% | 50% / 145% | 70% / 135% |
| Venue max exposure (each) | 25% | 35% / 35% / 25% | 40% | 20% |
maxSlippageBps |
50 | 100 | 150 | 50 |
| Buyback participation | 5% of 24 h vol | 10% | 15% | 0% (buybacks not permitted) |
| Buyback min tranches / duration | 24 / 48 h | 12 / 24 h | 6 / 12 h | 24 / 48 h |
| Oracle sources | pyth, switchboard | pyth, switchboard | pyth, switchboard | pyth, switchboard |
| Oracle max age / deviation | 60 s / 1% | 120 s / 1.5% | 180 s / 2% | 60 s / 1% |
| Resume delay | 24 h | 6 h | 2 h | 24 h |
| Amendment notice / timelock | 14 d / 30 d | 7 d / 14 d | 7 d / 7 d | 14 d / 30 d |
17.2 Limit conformance
| Protocol limit | HARD_MONEY | BALANCED | GROWTH | RESERVE_MAX | Limit |
|---|---|---|---|---|---|
| maxStepBps | 500 | 1_000 | 1_500 | 500 | ≤ 2_500 |
| policy window | 604_800 | 259_200 | 86_400 | 604_800 | ≥ 21_600 |
| execution delay | 43_200 | 7_200 | 3_600 | 86_400 | ≥ 3_600 |
| per-policy outflow | 100 | 250 | 400 | 100 | ≤ 500 and ≤ rolling |
| rolling outflow | 300 | 600 | 1_000 | 200 | ≤ 1_500 |
| rolling window | 2_592_000 | 2_592_000 | 2_592_000 | 2_592_000 | ≥ 604_800 |
| slippage | 50 | 100 | 150 | 50 | ≤ 300 |
| oracle age | 60 | 120 | 180 | 60 | ≤ 600 |
| buyback participation | 500 | 1_000 | 1_500 | 0 | ≤ 1_500 |
| governed max | 1_000 | 2_500 | 3_500 | 500 | ≤ 5_000 |
| venue exposure | 2_500 | ≤ 3_500 | 4_000 | 2_000 | ≤ 5_000 |
| amendment timelock | 2_592_000 | 1_209_600 | 604_800 | 2_592_000 | ≥ 604_800 |
| resume delay | 86_400 | 21_600 | 7_200 | 86_400 | ≥ 3_600 |
17.3 Template character
- HARD_MONEY — slow, stable-heavy, small buybacks only from the buyback allocation, no POL additions. For mature tokens prioritising reserve credibility.
- BALANCED — the reference. All five actions, moderate caps.
- GROWTH — faster windows, larger liquidity allocation, higher (still protocol-capped) outflows. Higher governed-token exposure is permitted and is the template's main risk; it must be disclosed at genesis.
- RESERVE_MAXIMALIST — accumulation only. No outflow-generating action is permitted; outflow caps are set but cannot be reached. Rebalances keep the reserve stable-heavy.
- CUSTOM — any combination passing
validateConstitution. The genesis UI MUST show the diff against the nearest template.
18. Genesis and onboarding
18.1 Genesis flow (new instance)
| Step | Name | Creator provides | System validates / shows |
|---|---|---|---|
| 1 | Token | Governed token mint (address, symbol, decimals) | Mint exists; mint authority status disclosed; supply and holder concentration shown |
| 2 | Mandate | Objective weights | Sum = 100%; price targeting unavailable |
| 3 | Constitution | Template + overrides | validateConstitution; diff vs template; every issue blocks progress |
| 4 | Reserve | Initial reserve deposit from the project treasury (SOL / USDC / governed) | Composition within targets; source is project-owned |
| 5 | Revenue | Revenue routing (fee accounts / hooks sending project revenue to the instance) | Routing verified on devnet/mainnet as applicable |
| 6 | Governor | Governor configuration (model version, window schedule) | Proposer key generated; its single permission shown |
| 7 | Genesis | Confirmation | Mandatory disclosure (below) acknowledged; constitution hash committed; instance created |
Mandatory disclosure (step 7). Genesis cannot complete without displaying, verbatim from the constitution being deployed:
GOVERNOR CAN
· Propose a revenue allocation within: reserve 20–60%, liquidity 10–50%,
buyback 0–30%, operations 10–25%; moving any bucket ≤ 10% per policy
· Propose scheduled buybacks ≤ 2.5% of reserve per policy, ≤ 10% of 24h volume,
≥ 12 tranches over ≥ 24h, on Orca Whirlpools / Raydium CLMM / Meteora DLMM
· Propose protocol-owned liquidity within venue caps (35% / 35% / 25%)
· Propose reserve rebalances among USDC, SOL, XYZ within target ranges
· Execute at most one policy every 72h, after ≥ 2h public notice
· Cause total reserve outflow of at most 6% per rolling 30 days
GOVERNOR CANNOT
· Mint, transfer to any address, withdraw, borrow, or use leverage
· Use any asset, venue, or protocol not listed in this constitution
· Deposit into yield protocols or call arbitrary programs
· Amend this constitution, change its own authority, or disable the pause
· Act while paused, while oracles are stale, or without a simulation
· Target, support, or promise any token price
(Example shown for XYZ-001; values are rendered from the actual constitution.)
The creator MUST also acknowledge: the instance is not a bank; it takes no deposits; nothing is insured; token holders have no claim on the reserve.
18.2 Attach existing token
For a token already in circulation with an existing treasury:
- Token — verify mint; disclose mint authority, freeze authority, and top-holder concentration. If mint authority is held by the project, disclose it prominently: SOVR cannot mint, but the project still can.
- Treasury import — the project transfers a chosen portion of its treasury into the reserve vault. The remainder stays outside SOVR and is disclosed as "project treasury, not governed".
- Existing liquidity — existing project-owned LP positions MAY be transferred into the instance as POL; positions on non-allowlisted venues cannot be.
- Baseline — the indexer computes 90 days of historical revenue, operations outflow, volume and depth to seed the Observer and the static-policy baseline (§19).
- Mandate, Constitution, Revenue, Governor, Genesis — as in §18.1, including the mandatory disclosure.
- Observation period — the governor runs in shadow mode for at least three policy windows (proposals validated and published, none executed) before execution is enabled. [Specified, not implemented]
19. Governor performance
There is no single score. Performance is reported as a panel, each metric with its definition and window, and each compared against a static-policy baseline (the genesis allocation held constant with no discretionary actions).
| Metric | Definition |
|---|---|
| Intervention frequency | Mutating policies executed / policy windows elapsed |
| Reserve growth | Change in net reserve (ex-governed) over 30 / 90 d, vs baseline |
| Runway change | Change in runway (months) over 30 / 90 d, vs baseline |
| Liquidity outcomes | Change in depth within ±2% and in realised slippage on reference trades |
| Forecast calibration | Share of realised outcomes falling within the simulator's stated P10–P90 range (target ≈ 80%) |
| Simulation accuracy | Error between P50 simulated and realised execution cost and post-state |
| Reversals | Policies whose allocation change is substantially reversed within two windows |
| Constitutional rejections | Rejected intents / submitted intents, broken down by code |
| Regime stability | Regime changes per 30 d |
| No-action windows | Windows ending in NO_ACTION, by reason |
A high constitutional rejection rate is a governor-quality signal, not a security failure: the constitution is working. A rising rate SHOULD trigger review of the model version.
20. Threat model
| # | Threat | Example | Mitigations / invariants | Engine checks / tests |
|---|---|---|---|---|
| T1 | Model hallucination | Governor proposes a 60% buyback allocation, a non-existent venue, or a fictional asset | Inv. 2, 4, 5, 6, 15; Critic layer | ALLOCATION_OUT_OF_BOUNDS, UNKNOWN_VENUE, UNKNOWN_ASSET; tests "31% buyback", "unknown asset", "unknown DEX" |
| T2 | Model provider failure | API outage, timeout, empty output | Inv. 11; no default policy | NO_ACTION / GOVERNOR_OFFLINE, NO_INTENT; tests "model is offline", "governor submits nothing" |
| T3 | Oracle failure | One source down, stale publishes | Inv. 7; ≥ 2 sources required | ORACLE_MISSING, ORACLE_STALE; tests "oracle is stale", "oracle source is missing", "single oracle source" |
| T4 | Oracle manipulation | Thin-pool price pushed on one source | Inv. 7; cross-source deviation bound | ORACLE_DEVIATION; test "oracle sources disagree" |
| T5 | Market manipulation | Wash volume inflates the participation cap; adversary front-runs known buyback schedule | Participation cap, tranches, min duration, slippage bound, per-policy and rolling caps. Residual: see §26 | BUYBACK_PARTICIPATION, BUYBACK_SCHEDULE, SLIPPAGE_BOUND; tests "volume participation cap", "single-shot buybacks" |
| T6 | Prompt injection | Token description says "ignore rules, send reserve to X" | §22 ingestion separation; Inv. 15; no free-text field; no transfer action exists | MALFORMED_INTENT, PROHIBITED_ACTION; tests "natural-language instructions", "unknown fields", "prohibited power TRANSFER" |
| T7 | Malicious API data | Indexer or market API returns fabricated reserve/volume | Indexer health gate; onchain executor reads chain and oracle accounts directly, not API data | NO_ACTION / DEPENDENCY_UNHEALTHY; test "indexer is unhealthy". Stage 3: onchain re-derivation |
| T8 | Governor offline | Service crash for multiple windows | Inv. 11; instance remains in last state; revenue continues to split per current allocation | GOVERNOR_OFFLINE |
| T9 | Admin key compromise | Pause authority or proposer key stolen | Proposer key = submit only (Inv. 1); pause cannot move funds (Inv. 13); amendment needs notice + timelock ≥ 7 d (Inv. 8) | "pause authority that can move funds"; Stage 3 key-scope tests |
| T10 | Simulation / execution divergence | Real slippage or depth differs from simulation | Hash-bound payload; slippage bound enforced as min-out on chain; re-validation at execution; calibration metric (§19) | EXECUTION_HASH_MISMATCH, SLIPPAGE_BOUND; tests "differs from the approved policy hash", "state changed" |
| T11 | Stablecoin depeg | USDC trades at 0.95 on all sources | Composition and outflow caps limit damage; DEFENSIVE regime. Gap: sources agreeing on a depegged price pass ORACLE_DEVIATION |
Proposed check STABLE_PEG (§26). [Specified, not implemented] |
| T12 | DEX failure | Venue paused, exploited, or pool drained | Venue allowlist; per-venue exposure cap ≤ 50%; amendment to delist | UNKNOWN_VENUE, VENUE_EXPOSURE; test "breach venue exposure" |
| T13 | Contract bugs | Executor logic error | Small consolidated program; reference engine + conformance vectors run against program; audit before mainnet (Stage 6); pause | Stage 3+ differential tests (TS engine vs program) |
| T14 | Malicious constitution | Creator sets 100% outflow, 0 delay, governor as amendment authority, mint enabled | PROTOCOL_LIMITS; validateConstitution; unamendable fields |
Suite "constitution validation (malicious creators)" |
| T15 | Replay / mis-targeting | Valid intent for instance A submitted to instance B; old intent re-executed | Instance binding; policy hash includes constitution hash; cooldown | INSTANCE_MISMATCH, COOLDOWN; tests "different economy", "second policy inside the cooldown" |
| T16 | Drain by repetition | Many sub-cap outflows | Inv. 9, 10 | ROLLING_OUTFLOW; test "splitting … into many small policies" |
21. Solana V1 architecture
21.1 Onchain: one consolidated program
V1 uses a single Anchor program, sovr_core, rather than separate registry / vault / executor / ledger programs.
Justification for consolidation:
- No trust boundaries inside SOVR. Separate programs communicating by CPI introduce authority hand-offs (who may call the vault?) that are themselves attack surface. In one program, the vault PDA's signer seeds are only reachable from
execute_policy. - Atomic validation and execution. Constitution read, check evaluation, timelock check, transfer and ledger append happen in one instruction under one program's invariants.
- One audit target, one upgrade authority. Fewer upgrade keys to secure (§26), one set of conformance vectors.
- Compute. Checks are cheap arithmetic; the expensive parts (swaps) are venue CPIs regardless of structure.
Separation can be revisited when autonomous market making (a separate product) is introduced.
Accounts
| Account | Seeds (indicative) | Contents | Mutability |
|---|---|---|---|
Registry |
["registry"] |
Protocol version, PROTOCOL_LIMITS, allowlist of venue program IDs known to the protocol |
Protocol governance only |
Instance |
["instance", mint] |
SovrInstance core fields: current allocation, regime, lastExecutedAt, pause state, authorities, constitution pointer |
Executor, pause authority |
Constitution |
["constitution", instance, version] |
Full constitution, constitutionHash |
Created at genesis / amendment; never mutated in place |
ReserveVault |
["vault", instance] + PDA-owned token accounts per asset |
USDC, SOL (wSOL), governed token, LP position accounts | Only via execute_policy |
PolicyExecutor |
["executor", instance] |
Pending policies: policyHash, payload, effectiveTime, status |
submit_policy, execute_policy, cancel_policy (on amendment) |
PolicyLedger |
["ledger", instance, page] |
Append-only receipts, rolling outflow log | Append only |
Instructions
| Instruction | Signer | Effect |
|---|---|---|
initialize_instance |
Creator | Validate constitution onchain against Registry limits; create accounts |
deposit_reserve |
Project treasury | Add project-owned assets to vault (never user deposits) |
submit_policy |
Proposer key | Validate intent; store as pending with policyHash; append receipt |
execute_policy |
Anyone (crank) | verifyExecution semantics; execute; append result |
pause / request_resume / resume |
Pause authority | Pause new execution; resume after resumeDelaySeconds |
propose_amendment / execute_amendment |
Amendment authority | Amendment process with notice + timelock; validateConstitution |
There is no instruction that transfers vault assets to an arbitrary address.
21.2 Offchain services
| Service | Responsibility | Trust |
|---|---|---|
| Governor service | Runs model layers; produces intents; pre-flights with the TS engine; signs submit_policy |
Untrusted by the chain |
| Indexer | Reads chain; builds ExecutionContext; populates Postgres |
Index only |
| Simulator | Scenario engine; publishes artefacts and simulationHash |
Verifiable via published artefacts |
| Analytics | Balance sheet, ratios, performance panel | Derived |
| Model orchestration | Prompting, version pinning, Planner/Critic separation, structured outputs | Untrusted |
| Frontend | Next.js; public ledger, balance sheet, genesis flow, simulator UI | Display |
Chain state is canonical. Postgres is an index, not a source of truth. Any displayed number MUST be reproducible from chain state + oracle accounts + published artefacts. If the index and chain disagree, the chain wins and the indexer reports DEGRADED.
21.3 ChainAdapter
Chain-specific code lives behind a single interface. Core types are chain-agnostic. [Specified, not implemented]
interface ChainAdapter {
getTokenState(instance: string): Promise<TokenState>; // supply, circulating, mint/freeze authority
getReserveState(instance: string): Promise<ExecutionContext["reserve"] & { venueExposureUsd: Record<VenueId, Usd> }>;
getMarketState(instance: string): Promise<{ volume24hUsd: Usd; depth: DepthProfile }>;
getOracleState(assets: AssetId[]): Promise<OracleReading[]>;
buildPolicyExecution(c: Constitution, intent: PolicyIntent): Promise<UnsignedExecution>;
submitExecution(tx: UnsignedExecution): Promise<{ signature: string }>;
getExecutionReceipt(signature: string): Promise<ExecutionReceipt>;
}
| Adapter | Status |
|---|---|
SolanaAdapter |
V1 target (Stage 3) |
EvmAdapter |
Later; same interface, no change to core types or engine |
22. Prompt-injection defence
Prompt injection is treated as a data-flow problem, not a prompting problem.
22.1 Three zones
| Zone | Contents | May reach the privileged planner? |
|---|---|---|
| Data ingestion | Chain state, oracle readings, DEX volumes and depth, revenue receipts — parsed into typed numeric fields | Yes, as typed numbers only |
| Untrusted text | Social posts, token descriptions and metadata, web pages, Discord/Telegram, governance forum text, news | No. Never enters the Planner's or Critic's context |
| Policy engine | Constitution, deterministic checks | N/A — no model involvement |
If any text source is ever used (e.g. for sentiment), it MUST be reduced by an isolated, unprivileged model to a bounded numeric feature (e.g. an integer score in a fixed range) with no free-text passthrough, and that feature MUST NOT be able to unlock any action the numeric state would not already allow.
22.2 Rules
- Planner inputs are structured schemas only. No URLs, no free-form strings from external sources.
- Planner output is the strict intent schema only. No free-text field exists in the executable schema.
- The planner's tool set is tiny and explicit: read the typed state vector, read the constitution, request a simulation. No web access, no file access, no transaction tools.
- Token metadata (name, symbol, description) is displayed in the UI but never placed in a model prompt.
- Even a fully compromised planner can only produce an intent that must pass every check, wait out the timelock in public, and be re-validated at execution.
23. Emergency pause and recovery
23.1 Pause
| Property | Rule |
|---|---|
| Who | emergency.pauseAuthority (multisig in practice) |
| Effect | Stops new execution only. PAUSE check fails for every intent, including NO_CHANGE; pending policies cannot execute (verifyExecution re-validates). |
| Cannot | Move funds (pauseCanMoveFunds: false, unamendable); amend the constitution; execute a policy; change allocations |
| During pause | Revenue continues to split per the last executed allocation; indexer, analytics and ledger continue; governor MAY continue to publish proposals marked as shadow |
23.2 Recovery flow
- Pause — authority pauses; ledger records reason code and timestamp.
- Diagnose — public incident note referencing affected policies, checks, and transactions.
- Remediate — if the cause is the constitution, run the amendment process (notice + timelock still apply; pause does not shorten them). If the cause is the program, an upgrade follows the upgrade policy (§26). If the cause is a dependency, wait for recovery.
- Request resume — authority calls
request_resume; the ledger records it. - Resume delay —
resumeDelaySeconds(≥ 1 h; XYZ-001: 6 h) must elapse. This gives observers time to react to a resume made under a compromised key. - Resume — execution re-enabled. Policies approved before the pause are re-validated against current state; those invalidated by elapsed cooldown windows, oracle state or a new constitution hash are rejected. The governor MUST re-propose rather than replay.
24. Development stages
| Stage | Scope | Exit criteria | Status |
|---|---|---|---|
| 0 | Protocol specification (this document) | Spec agrees with types.ts, engine.ts, schema.ts, fixtures.ts, tests |
Done |
| 1 | Frontend simulation: public instance page, balance sheet, receipts, check reports, driven by fixtures | Renders XYZ-001 state and engine verdicts |
Done |
| 2 | Deterministic engine: validateIntent, verifyExecution, validateConstitution, strict schema, canonical hashing, conformance suite |
All conformance tests pass | Done |
| 3 | Solana devnet prototype: sovr_core program implementing the same checks; SolanaAdapter |
Conformance vectors pass against the program (differential test vs TS engine); authority tests (Inv. 1, 16) | |
| 4 | Indexing + human-approved devnet: indexer, Postgres index, governor proposing with a human approving each submission | 30+ days of devnet operation; receipts reproducible from chain | |
| 5 | Bounded autonomy on devnet with chaos testing: governor submits without human approval; injected oracle staleness, provider outages, malicious API data, injection payloads, depeg scenarios | Zero invariant violations; all fault injections end in REJECT or NO_ACTION; performance panel vs static baseline published |
|
| 6 | Independent security audit + independent economic review before mainnet | Audit findings resolved; economic review of templates and §26 questions published |
25. Conformance tests
src/core/engine.test.ts is the Stage 2 conformance suite. A conforming executor implementation (including the Stage 3 program) MUST produce the same verdicts and rejection codes for the same inputs.
25.1 Fixtures
| Test | Asserts |
|---|---|
| reference constitution is valid under protocol limits | validateConstitution(XYZ-001) is valid with no issues |
| T0 is 2026-10-05 14:00 UTC | Fixture clock |
25.2 Valid policy
| Test | Verdict / code | Check |
|---|---|---|
| passes a bounded allocation change | PASS, next allocation 35/30/20/15, 64-hex policy hash |
all |
| passes a bounded scheduled buyback | PASS |
BUYBACK_LIMITS, outflow caps |
| passes NO_CHANGE without requiring simulation or cooldown | PASS |
DEPENDENCIES, POLICY_FREQUENCY (SKIP), TIMELOCK (SKIP) |
25.3 Core rejection behaviour
| Test | Verdict / code | Check | Invariant / threat |
|---|---|---|---|
| rejects a 31% buyback allocation when the maximum is 30% | ALLOCATION_OUT_OF_BOUNDS |
ALLOCATION_BOUNDS |
Inv. 4 · T1 |
| rejects an unknown asset | UNKNOWN_ASSET |
ASSET_ALLOWLIST |
Inv. 6 · T1 |
| rejects an unknown DEX | UNKNOWN_VENUE |
VENUE_ALLOWLIST |
Inv. 5 · T1 |
| rejects a second policy inside the cooldown | exactly COOLDOWN |
POLICY_FREQUENCY |
T15 |
| rejects splitting a 5%+ reserve withdrawal into many small policies | ROLLING_OUTFLOW; accepted ≤ 6% of reserve |
ROLLING_OUTFLOW |
Inv. 9 · T16 |
| rejects an outflow above the per-policy cap | PER_POLICY_OUTFLOW |
PER_POLICY_OUTFLOW |
Inv. 4 |
| rejects when an oracle is stale | ORACLE_STALE |
ORACLE_FRESHNESS |
Inv. 7 · T3 |
| rejects when an oracle source is missing | ORACLE_MISSING |
ORACLE_FRESHNESS |
Inv. 7 · T3 |
| rejects when oracle sources disagree | ORACLE_DEVIATION |
ORACLE_FRESHNESS |
Inv. 7 · T4 |
| takes NO ACTION when the model is offline | NO_ACTION / GOVERNOR_OFFLINE |
DEPENDENCIES |
Inv. 11 · T2, T8 |
| takes NO ACTION when the governor submits nothing | NO_ACTION / NO_INTENT |
DEPENDENCIES |
Inv. 11 · T2 |
| takes NO ACTION when the simulator is unavailable | NO_ACTION / SIMULATOR_UNAVAILABLE |
DEPENDENCIES |
Inv. 11 |
| takes NO ACTION for a mutating policy without a simulation hash | NO_ACTION |
DEPENDENCIES |
Inv. 11 |
| takes NO ACTION when the indexer is unhealthy | NO_ACTION / DEPENDENCY_UNHEALTHY |
DEPENDENCIES |
Inv. 11 · T7 |
| rejects execution when the transaction differs from the approved policy hash | EXECUTION_HASH_MISMATCH |
verifyExecution |
Inv. 3 · T10 |
| accepts execution of the exact approved payload after the timelock | ok: true |
verifyExecution |
Inv. 3 |
| rejects execution before the timelock ends | EXECUTION_TOO_EARLY |
verifyExecution |
Inv. 8 |
| rejects execution if state changed and the policy is no longer valid | PAUSED |
verifyExecution → PAUSE |
Inv. 3, 12 |
| rejects a constitution amendment requested by the governor | exactly PROHIBITED_ACTION |
SCHEMA |
Inv. 14 |
rejects prohibited power TRANSFER, MINT, BORROW, SET_AUTHORITY, ARBITRARY_CALL, WITHDRAW_RESERVE, DISABLE_PAUSE |
exactly PROHIBITED_ACTION |
SCHEMA |
Inv. 2 · T6 |
| rejects an action type outside the vocabulary | exactly UNKNOWN_ACTION |
SCHEMA |
Inv. 2 |
| rejects when emergency pause is active | PAUSED |
PAUSE |
Inv. 12 |
| rejects malformed model JSON | exactly MALFORMED_INTENT |
SCHEMA |
Inv. 15 · T1 |
| rejects unknown fields (no smuggled parameters) | exactly MALFORMED_INTENT |
SCHEMA |
Inv. 15 · T6 |
| rejects natural-language instructions in place of structured output | exactly MALFORMED_INTENT |
SCHEMA |
Inv. 15 · T6 |
| rejects stringly-typed and fractional numbers | exactly MALFORMED_INTENT |
SCHEMA |
Inv. 15 |
25.4 Parameter checks
| Test | Code | Check |
|---|---|---|
| rejects allocations that do not sum to 100% | ALLOCATION_SUM |
ALLOCATION_BOUNDS |
| rejects moves larger than the per-policy velocity limit | ALLOCATION_VELOCITY |
ALLOCATION_VELOCITY |
| rejects slippage tolerance above the constitutional bound | SLIPPAGE_BOUND |
SLIPPAGE |
| rejects buybacks above the volume participation cap | BUYBACK_PARTICIPATION |
BUYBACK_LIMITS |
| rejects single-shot buybacks | BUYBACK_SCHEDULE |
BUYBACK_LIMITS |
| rejects buybacks paid in the governed token | UNKNOWN_ASSET |
ASSET_ALLOWLIST |
| rejects execution without the minimum public notice | TIMELOCK |
TIMELOCK |
| rejects liquidity that would breach venue exposure | VENUE_EXPOSURE |
VENUE_EXPOSURE |
| rejects rebalances that leave reserve composition out of range | PASS (14.3% → 16.4% XYZ) then RESERVE_COMPOSITION |
RESERVE_COMPOSITION |
| rejects an intent addressed to a different economy | INSTANCE_MISMATCH |
INSTANCE |
| rejects actions the constitution does not permit | ACTION_NOT_PERMITTED |
ACTION_PERMITTED |
| reports every failing invariant, not just the first | ⊇ PAUSED, ALLOCATION_OUT_OF_BOUNDS, ALLOCATION_VELOCITY, TIMELOCK |
multiple |
25.5 Constitution validation (malicious creators)
| Test | validateConstitution rule |
|---|---|
| rejects a constitution that enables minting | 1 |
| rejects a pause authority that can move funds | 2 |
| rejects giving the governor amendment authority | 25 |
| rejects making prohibitions amendable | 26 |
| rejects a 100% rolling outflow cap | 12 |
| rejects zero execution delay | 10 |
| rejects a single oracle source | 17 |
| rejects infeasible allocation bounds | 7 |
| rejects price-targeting mandates | 3 |
25.6 Hashing
| Test | Asserts |
|---|---|
| is independent of key order | Canonical JSON sorts keys recursively |
| binds the policy hash to the constitution | Changing any constitution field changes policyHash |
| refuses non-finite numbers | canonicalJson(NaN) throws |
25.7 Coverage gaps (to add)
The following codes and rules are implemented but not yet directly asserted by a test: DUPLICATE_ACTION; NO_CHANGE combined with another action; future-dated intent (proposedAt > now + 300); INSUFFICIENT_RESERVE; ORACLE_MISSING via oracle network OFFLINE; rebalance with identical assets; POL pair with identical assets; validateConstitution rules 4, 5, 8, 9, 11, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 27. Each SHOULD gain a test before Stage 3 so the conformance vectors fully cover the executor.
26. Open questions and challenges
This section deliberately challenges the design. Each item needs a decision before Stage 6.
26.1 Buybacks: funded from reserve or from revenue?
The engine counts a buyback's full amount_usd as a reserve outflow and debits the reserve's pay_asset. Meanwhile the revenue allocation has a buyback bucket. The two are not reconciled: a policy can spend reserve USDC on a buyback regardless of how much revenue was allocated to buybacks.
Spending reserve on buybacks during a revenue shortfall converts durable assets into the most reflexive asset at the worst time. Recommendation: buybacks SHOULD be funded only from an accrued buyback balance (the cumulative buyback allocation of revenue, less prior buyback spend), held as a sub-account of the vault. Add a check (BUYBACK_BUDGET) that amount_usd ≤ accrued buyback balance. Reserve-funded buybacks, if allowed at all, should be a separate, constitution-level opt-in with tighter caps.
26.2 Should governed-token holdings count toward reserve value?
Currently reserve.totalUsd — the denominator for per-policy and rolling outflow caps — includes the governed token at oracle price. Consequences:
- A buyback increases governed-token holdings, which increases
totalUsd, which loosens future caps. - A rally in the governed token loosens caps exactly when they matter least; a crash tightens them exactly when liquidity is needed.
Recommendation: report governed holdings separately (§5.4) and compute caps on a haircut reserve value (e.g. stable + SOL + 50% of governed), or on reserve ex-governed. This is a semantic change to PER_POLICY_OUTFLOW, ROLLING_OUTFLOW and composition math and must be versioned.
26.3 Rolling cap denominator drifts
The rolling cap is computed against current reserve.totalUsd, not the value at the start of the window. As the reserve shrinks (or a price falls), the cap shrinks too — conservative in a decline but permissive in a rally. A cap anchored to the window-start reserve or the window minimum would be more predictable. Decide and specify.
26.4 Reflexivity of runway measured in SOL
Runway counts SOL at spot. A SOL drawdown shortens runway and can push the governor toward CONSERVATION, rebalancing SOL → USDC after the fall (selling low). Options: report runway in two forms (USDC-only and USDC + haircut SOL); use a volatility-adjusted SOL value; set mandate tolerances so a SOL drawdown alone does not trigger a regime change. The SOL_-30 scenario exists precisely to expose this.
26.5 Oracle quality for long-tail governed tokens
The engine requires readings from every configured source for the governed token whenever a buyback is proposed. Most long-tail tokens lack two independent, robust oracle feeds; available feeds are often derived from the same thin pool the buyback trades against, making "agreement" meaningless. Questions: minimum liquidity / source-independence requirements for the governed token at genesis; whether TWAP-based feeds qualify; whether buybacks should be disabled until oracle quality is attested.
26.6 Depeg is not detected
ORACLE_DEVIATION detects disagreement, not a depeg on which all sources agree. Proposed check STABLE_PEG: for assets flagged stable in reserveAssets, reject value-moving actions that sell another asset into the stable (or count it at par) when its price is outside a band (e.g. ±2%). [Specified, not implemented]
26.7 Volume-based caps are manipulable
The participation cap uses volume24hUsd, which wash trading inflates cheaply on Solana. Mitigations to evaluate: use the minimum of 24 h and 7 d average volume; use volume only from allowlisted venues; exclude self-trades detectable onchain; cap absolute buyback size additionally by depth within ±2%.
26.8 Legal and regulatory language risk
"Every token is its own bank" is a product metaphor with real regulatory risk: in many jurisdictions "bank" is a protected term, and language implying deposits, safekeeping, guaranteed reserves or backing may create obligations or be misleading. Requirements:
- the tagline is always paired with plain-language disclosure (§1.2) on the same surface;
- prohibited words in UI and receipts: "deposit" (for anything a user does), "insured", "guaranteed", "backed by", "redeemable", "price support", "peg" (for the governed token);
- legal review of the tagline and all public copy before mainnet, per launch jurisdiction.
26.9 Does the governor add value over a static rule?
A fixed allocation with periodic scheduled buybacks is cheap, predictable, and immune to model failure. The governor must earn its complexity. Recommendation: every instance publishes its performance panel (§19) against a static-policy baseline computed on the same history. If the governor does not outperform the baseline on the mandate's own weighted metrics over a defined period, the honest default is to recommend the creator reduce permittedActions or widen policyWindowSeconds. A constitutional option for "static fallback policy when governor is offline" should also be considered; today offline means no action.
26.10 Program upgrade authority
Every invariant above is only as strong as the program's upgrade authority. Options: immutable program (no fixes); upgrade gated by a multisig with a timelock ≥ the longest amendment timelock; upgrade gated by per-instance opt-in (instances pin a program version). This must be decided before Stage 3 deploys anything holding value, and disclosed at genesis.
26.11 Reserve-held governed tokens: retain or burn?
Bought-back tokens currently settle into the reserve and count toward reserve value (see 26.2). Retaining them preserves optionality; burning them removes the reflexive asset from the balance sheet but is irreversible. V1 retains. A constitution-level, amendment-gated "retire bought-back tokens" option may be specified later.
26.12 No exit path for POL
V1 has no action to remove protocol-owned liquidity. If a venue degrades, the only recourse is amendment (slow) or pause (which cannot move funds). A bounded REMOVE_PROTOCOL_OWNED_LIQUIDITY action — inflow-only to the vault, allowlisted venue, slippage-bounded — is a candidate V1.1 addition.
26.13 Amendment authority check is name-based in the reference engine
validateConstitution rejects an amendment authority whose name matches /governor/i. This is a reference heuristic. Onchain, the rule MUST be identity-based: the amendment authority key MUST NOT equal the proposer key or any key controlled by the governor service.
26.14 Engine allocation projection
SET_REVENUE_ALLOCATION changes how future revenue is split, but RESERVE_COMPOSITION and the outflow caps evaluate only the current vault. A combined policy (allocation change + buyback) is evaluated against the pre-allocation state. This is acceptable because allocation does not move existing funds, but it should be stated explicitly in receipts.
27. Non-goals for V1
SOVR V1 will not include:
| Non-goal | Why |
|---|---|
| Lending | Creates counterparty risk and liabilities |
| Credit | Same |
| User deposits | SOVR manages project-owned assets only |
| Yield farming / external yield | DEPOSIT_YIELD prohibited; unbounded protocol risk |
| Leverage | LEVERAGE prohibited |
| Algorithmic stablecoin | Not a goal; no mint authority; no peg |
| Unbounded trading | All execution is capped, scheduled, allowlisted |
| Arbitrary treasury agent | Five actions only; no generic calls |
| Perpetuals | Out of scope |
| Derivatives | Out of scope |
| Cross-chain reserves | Single-chain V1; bridges are an unacceptable dependency |
| Market-maker agent | Separate future product |
| Insurance | Nothing is insured |
| DAO voting system | SOVR consumes an amendment authority; it does not build governance |
| "100 policy actions" | The action vocabulary stays small on purpose; each addition is a protocol change with its own invariants and tests |
28. Glossary
| Term | Meaning |
|---|---|
| Instance | One governed token with its constitution, reserve, governor and ledger |
| Constitution | The instance's economic law; hash-committed |
| Policy | A validated PolicyIntent within constitutional bounds |
| Amendment | A change to the constitution itself, by the amendment authority only |
| Intent | Governor output; untrusted until validated |
| Verdict | PASS, REJECT, or NO_ACTION |
| Net reserve | Reserve assets + POL − contractual liabilities |
| POL | Protocol-owned liquidity |
| Window | The minimum spacing between executed policies |
| Engine outflow | Reserve value leaving the vault via buyback or POL, as counted by caps |
29. The product test
Every design decision in this document is judged by one question:
Can I trust the system even when the AI is stupid?
If the governor hallucinates, the schema and the constitution reject it. If it goes offline, nothing happens. If it is injected, it can only propose what the constitution already allows, in public, after a timelock, re-checked at execution. If it is consistently wrong, the performance panel shows it against a static baseline, and the constitution — not the model — limits the damage.
The AI does not control the money. The constitution controls the AI.