> Source: https://upvera.tech/research/rules-released-like-software/
> Upvera releases every rule that moves UPV, computes Upvera Value, limits disclosure or runs the watcher as a versioned, hashed entry in one append-only registry, so every UPV movement, receipt and value can be traced to the rule and build that produced it.

UTR-26-06 · Method note · Live

# Rules released like software

Upvera releases every rule that moves UPV, computes Upvera Value, limits disclosure or runs the watcher as a versioned, hashed entry in one append-only registry, so every UPV movement, receipt and value can be traced to the rule and build that produced it.

## The problem

A number on a screen is only as honest as the rule behind it. If a rule can change quietly, two receipts written a week apart can disagree, and nobody can say which rule was in force when UPV moved or a value was computed. Rules kept loosely in code and configuration drift without a trace.

Upvera handles rules the way careful teams handle software releases. Each rule has a name and a version number. Each version carries a fingerprint of its content and a fingerprint of the code that runs it. Versions are never edited, only superseded. A new value rule runs beside the live one before it replaces it. And a fingerprint of the registry is sealed every day in public.

## One registry, one grammar

Every money, value, privacy and watcher rule lives in a single registry, one row per version. Names follow one grammar, `<family>@<n>`, with optional sub-families:

-   `money@20`: the rules that move UPV, Upvera's in-app credit, which stays inside Upvera and cannot be cashed out
-   `value@1`: how Upvera Value is computed
-   `trust.disclosure@1`: which event types may ever be opened to the public
-   `block@1`: how minute blocks are sealed
-   `days@1`: how build days and streaks are counted
-   `watch@13`: the watcher's own thresholds and lists

Each row holds the same fields:

| Field | What it holds |
| --- | --- |
| `body` | the parameters, as integers and strings only, never fractional numbers |
| `body_hash` | `sha256` of the body serialized as canonical JSON (JCS, RFC 8785) |
| `engine` | a fingerprint of the implementation that runs the body |
| `effective_from` | when the version takes effect, in UTC microseconds |
| `published_at` | when it was written |
| `chain` | `sha256(prev ‖ body_hash ‖ engine ‖ effective_from)` |
| `reason` | one sentence that points to the methodology |

Three properties follow. The registry is append-only, so a rollback is a new version that carries the old body. There are no retroactive rules: `effective_from` can never be earlier than `published_at`, and the database enforces that with a check constraint, not a habit. And the registry is a hash chain: every `chain` value commits to the one before it, so rewriting or deleting an old version breaks every later link.

A transaction uses the highest version whose `effective_from` is at or before its own moment. Every UPV movement, receipt, value run and watcher run records the rule it ran under. A new version applies to everyone from publication onward; values and receipts published before that moment never change. As of 2026-10-20 the registry holds 54 versions in 10 root families.

## A fingerprint for the code, not only the rules

A body hash shows which parameters were in force. It says nothing about the code that read them. So every version also carries an `engine` fingerprint.

For the value engine, the fingerprint names the compiled WebAssembly artifact and the commit it was built from: `value@1` records `wasm:505bc44c…+git:83289608…`. The build is reproducible on a fixed toolchain and platform: clean builds of one commit produce the same hash, and Upvera's CI checks that the artifact shipped with the engine matches a clean build of the commit it names.

For money rules, which run as SQL inside the database, the fingerprint is a hash over the function surface: the name, arguments, result, language, settings and body of each function that can move UPV. `money@20` records `pg-surface:f1387ea1…`. The surface has three layers: the functions that move UPV, the helpers whose behavior changes a UPV outcome without moving anything themselves, and the parameters those functions read. Parameters that no such function reads are left out on purpose, so an unrelated setting cannot raise a false alarm.

Any change to that surface must publish a new `money@n` in the same database transaction. Every hour, a watcher recomputes the live fingerprint and compares it with the version in force. A mismatch means code changed without a release, and it raises a critical alarm. The watcher is fingerprinted as well: its own functions are checked against the `watch@n` in force, so a quiet edit to the watcher trips the same alarm.

## The Core Contract

The registry is one chapter of a larger text, the Core Contract: the binding rules every module of Upvera's value network is built on. Version 1 holds 133 rules in 15 families, covering identity, units, rounding, determinism, rule versions, accounts, transfers, events, signatures, receipts, blocks, data sources, support modules, extensibility and publication.

It was locked on 2026-10-09. The locked text has a sha256 digest beginning `e4940a14`, and Upvera's tests recompute it on every run. Changes are append-only amendments below the lock line, each naming the rule it touches, what it changes and why. A rule's identity is permanent; a rule whose meaning changes gets a new identity.

Three contract rules matter most here. Hashed and signed structures use a strict JCS subset: ASCII keys, integers below 2⁵³, amounts as strings, no floats. UPV arithmetic is exact integer math, quantized once. And the value engine is deterministic: its WebAssembly and native builds produce byte-identical results on a shared set of test vectors.

## Shadow first

A new value rule never goes live cold. The release path is fixed: simulation, then shadow, then release, then the watcher compares the running code with the registry.

In shadow, the candidate runs side by side with the live rule on the same events, and every shadow run is recorded. The candidate is itself a registry entry in the `value.shadow@n` family, with its own body hash and engine fingerprint. Recalibrating it means publishing a new shadow version, never editing the old one. Shadow results are never published per startup, so nobody can act early on a startup's value under a rule that does not exist yet. At the switch, actions that depend on a startup's value wait until its value under the new rule is computed, so nobody acts on the old value either.

`value@1` took this path. The shadow engine began computing on 2026-10-11, and `value@1` went live on 2026-10-18. Its successor, `value@2`, entered shadow the same day as `value.shadow@3`. It is in shadow now.

## What the public can see

Upvera publishes a daily root chain at github.com/upvera-co/ledger-roots, with an OpenTimestamps proof for every day file. Each file carries a `rules_hash`: the registry's `chain` value at the end of that day. The rule bodies are not published, but every version's fingerprints and chain are, in the [release archive](/releases/), and anyone can recompute the chain there and match its head to this hash. It moves only when a rule version is published.

Read from the repository on 2026-10-20:

| Day | `rules_hash` begins | Changed |
| --- | --- | --- |
| 2026-10-13 | `5c5f8344` | first day file |
| 2026-10-14 | `eeb75cb5` | yes |
| 2026-10-15 | `eeb75cb5` | no |
| 2026-10-16 | `eeb75cb5` | no |
| 2026-10-17 | `988850cc` | yes |
| 2026-10-18 | `5ae2f2cf` | yes |
| 2026-10-19 | `5ae2f2cf` | no |

The registry matches: one money version on 2026-10-14, a recalibrated shadow candidate on 2026-10-17, and twelve versions on 2026-10-18, among them `value@1`, `money@19`, `money@20` and the `value@2` shadow candidate. Any later account of the rules in force on those days has to chain to hashes that are already timestamped.

Two more checks are open today. Anyone can verify the whole day chain in the browser, on [this site](/verify/) or at upvera.co/dogrula, and each person can confirm that their own events and receipts sit inside a sealed day, at app.upvera.co under Settings › Security › Proofs. The methodology is public in plain language at app.upvera.co/value/guide.

## Limits and what's next

This system has clear edges. The value engine is not open source and the rule bodies are not published, so nobody outside Upvera can recompute a startup's value or rebuild an engine from its fingerprint. The public chain shows which versions changed and when, not what is inside them. The watcher that compares fingerprints is Upvera's own. Fingerprints also depend on the database environment, so moving to a new one needs a new release that says why.

Next, `value@2` stays in shadow until a release decision is made. If it is released, the release will be visible the same way as every other: a new registry entry, an engine fingerprint naming its build, and a new `rules_hash` in that day's file.

## References

-   [upvera-co/ledger-roots](https://github.com/upvera-co/ledger-roots)
-   [Verify a day on Upvera](https://upvera.dev/verify/)
-   [Check the day chain in your browser](https://upvera.co/dogrula)
-   [How Ranks value works](https://app.upvera.co/value/guide)
-   [RFC 8785, JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785)
-   [OpenTimestamps](https://opentimestamps.org)

[Next · UTR-26-07**Kestrel: measure before you fix**](/research/kestrel-measure-before-you-fix/)
