PATINA docs

The upgrade boundary

Some things are frozen because changing them would rewrite history. Others can be added because they change nothing that already exists. The line between them is this page.

What you will know after this page
  • The eight frozen surfaces, down to which constants and which reason code names are inside them.
  • What an implementation is free to change without telling anyone, and why an additive field is not a removal.
  • Why a new opcode or a new marker version splits implementations even though old parsers survive it.
  • That every bit of the SEED flags byte must be zero, and why it is not a forward compatibility channel.
  • The two conditions software must see before it will build a mainnet transaction.

Frozen

Nothing below may change. A change here would produce different state from the same chain, which means the artifacts people already hold would mean something different than they did when they were made.

Frozen surfaces.
SurfaceIncludes
IdentityMagic PTNA, version byte 0x01, protocol slug
DerivationsThe five domain tags, single SHA-256, the preimage layouts
Marker grammarSingle minimal push, 83 byte ceiling, lowest index selection, duplicate voiding
Opcodes0x01 SEED and 0x02 KEEP, and their payload layouts
Constants144, 4032, 4032, 100000, 10000, 10000, 8, 6, and the eight tier thresholds
Validity rulesThe six SEED checks, KEEP entry rules, the default rule, relic terminality
Reason codesAll eighteen, by name
State semanticsDepth is computed, rings are append only, relic is terminal

Free to change

None of these affect what a transaction means.

  • Indexer internals: storage engine, schema, caching, concurrency, language.
  • Sync speed, memory use, and deployment shape.
  • Additive API surface: new endpoints, new fields on existing responses. Removing or repurposing a field is not additive.
  • Everything about presentation: renders, share cards, wording, this documentation.
  • Client tooling, planners, and wallet integrations.
  • Operational policy: rate limits, retention of invalid events, monitoring.

On the boundary

Three things sit in between and need the process below.

A new opcode

The marker already reserves opcode space, and unknown opcodes are handled: they record MARKER_UNKNOWN_OP and change nothing. That means a new opcode can be introduced without breaking older indexers, but older indexers will disagree with newer ones about state from the moment anyone uses it. Adding an opcode is a protocol change, not a feature.

A new marker version

Same shape. Version 0x02 markers would be ignored with MARKER_UNKNOWN_VERSION by current parsers. Introducing one splits implementations the same way.

The flags byte

SEED reserves one payload byte with no assigned meaning, and every bit of it must be zero at version 1. Assigning a meaning to it later would change how already confirmed transactions are interpreted, so it is a protocol change, and one to avoid.

Flags are not a compatibility channel

A non zero flags byte is SEED_BAD_GRAMMAR, and an implementation must reject it. One that waves it through creates artifacts no other implementation has, which surfaces as a state root mismatch rather than a disagreement about presentation. A future version that wants flags will carry a different version byte instead.

The process for anything frozen or on the boundary

Four steps, in this order. Nothing written at step one ships until step three has vectors that two implementations can both reproduce.

  1. Write it down before writing code

    A change to the baseline is proposed as a change to the baseline file, with the reason, the affected constants, and the effect on existing artifacts.

  2. Record deviations honestly

    Any implementation that already differs from the baseline records the difference in docs/deviations.md, in this repo. A deviation that is not written down is a bug wearing a disguise.

  3. Extend the golden vectors first

    A change is not real until vectors cover it. Two implementations must be able to prove they agree on the new behaviour before it ships. See Golden vectors and state roots.

  4. Bind it to a deployment record

    Deployment records carry spec_sha256. A change to the spec changes that hash, and GET /patina/status publishes it. Anyone can tell which spec an indexer is running without asking.

Networks and activation

Deployments bind a network to the constants and window heights it uses:

{ network, protocol_id: "PTNA", spec_sha256,
  h_open, h_close, grace_end,
  min_carrier_founding, min_carrier_open, commit_min_age }
  • Regtest and signet deployments ship in the repo.
  • Mainnet values stay unset until activation is authorised.
  • Software must refuse to construct mainnet transactions without both PATINA_MAINNET_AUTHORIZED=true and a mainnet deployment record naming at least two approvers.

This is a deliberate speed limit. It means an accidental deploy cannot open a founding window, and it means the window's heights are a published decision rather than a runtime surprise.

What those switches reach, and the things nobody holds a switch over at all, is set out on the public transparency page.

Naming rule

There are no version labels in any name: not in file names, folder names, package names, URLs, or interface copy. The one number that exists is the marker version byte, which is protocol data inside a transaction, not a product name. When you need to say which spec is running, quote spec_sha256.