Skip to content
hopper
Get started
Operations / schema

Schema architecture

Program manifests, layout IDs, Codama projection, Anchor-shaped IDL, and compatibility diffs.

One canonical schema model

Hopper has one source of truth for state and program semantics: the Rust code. Layout macros, instruction declarations, event definitions, and policy bindings are the authoritative definitions. From that single source, Hopper generates two output formats:

  1. Hopper Manifest -- rich internal schema for tooling
  2. Hopper IDL -- lighter public schema for clients and integrations

This keeps one truth while supporting two audiences.

Code-First Doctrine

Canonical truth lives in code. The authoritative declarations are:

  • hopper_layout! -- fields, offsets, sizes, versions, fingerprints
  • hopper_segment! / segmented layouts -- segment structure, roles
  • hopper_dispatch! -- instruction set, discriminators
  • hopper_error! -- error codes and variants
  • Policy constants -- capability-requirement bindings
  • hopper_interface! -- cross-program read-only views

These declarations produce compile-time constants (LAYOUT_ID, LEN, DISC, VERSION) that are deterministic and verifiable. The schema layer reads these constants to build manifests and IDLs without duplicating truth.

Proc Macro Policy

No proc macros are required for correctness or core functionality. Proc macros are allowed only for:

  • Schema derivation (#[derive(HopperSchema)])
  • Manifest export
  • IDL generation
  • Optional boilerplate reduction

See PROC_MACRO_POLICY.md for the full doctrine.

The Hopper Manifest

Purpose

The Manifest is Hopper's rich internal schema. It powers:

  • hopper explain / hopper inspect
  • hopper compat / hopper diff / hopper plan
  • hopper manager (program introspection)
  • Receipt rendering and migration planning
  • Docs generation and test tooling

File format

hopper.manifest.json

Structure

{
  "format": "hopper-manifest",
  "version": 1,
  "program": {
    "name": "hopper_registry",
    "program_id": "...",
    "version": "0.2.1",
    "description": "Segmented registry example"
  },
  "layouts": [
    {
      "name": "Vault",
      "kind": "fixed",
      "version": 1,
      "discriminator": 1,
      "layout_id": "a1b2c3d4e5f60718",
      "size": 57,
      "header_size": 16,
      "fields": [
        { "name": "authority", "type": "[u8;32]", "size": 32, "offset": 16 },
        { "name": "balance", "type": "WireU64", "size": 8, "offset": 48 },
        { "name": "bump", "type": "u8", "size": 1, "offset": 56 }
      ],
      "segments": [],
      "compatibility": {
        "append_safe": true,
        "compatible_from": [1],
        "migration_required_from": []
      }
    }
  ],
  "instructions": [
    {
      "name": "deposit",
      "tag": 1,
      "args": [
        { "name": "amount", "type": "u64", "size": 8 }
      ],
      "accounts": [
        { "name": "depositor", "writable": false, "signer": true },
        { "name": "vault", "writable": true, "signer": false, "layout_ref": "Vault" }
      ],
      "capabilities": ["MutatesState", "MutatesTreasury"],
      "policy_pack": "TREASURY_WRITE",
      "receipt_expected": true
    }
  ],
  "events": [
    {
      "name": "DepositEvent",
      "tag": 1,
      "fields": [
        { "name": "authority", "type": "[u8;32]", "size": 32 },
        { "name": "amount", "type": "u64", "size": 8 }
      ]
    }
  ],
  "policies": [
    {
      "name": "TREASURY_WRITE",
      "capabilities": ["MutatesState", "MutatesTreasury"],
      "requirements": ["Authority", "StateSnapshot", "LamportConservation", "InvariantCheck"]
    }
  ],
  "compatibility": {
    "pairs": [
      {
        "from": "Vault@1",
        "to": "Vault@2",
        "toVersion": 2,
        "policy": "append-only",
        "backwardReadable": true
      }
    ]
  }
}

Segment metadata in manifests

For segmented accounts, each segment entry includes:

Field Type Meaning
name string Segment identifier
role string Core / Extension / Journal / Index / Cache / Audit / Shard
segment_id hex FNV-1a hash of segment name
layout_ref string Layout name for this segment
required bool Must be present in every account instance
append_only bool Only append operations allowed
rebuildable bool Can be reconstructed from other data
immutable bool Cannot be modified after init

The Hopper IDL

Purpose

The IDL is the public-facing schema for:

  • TypeScript client generation
  • Kotlin, Python, and Rust client generation
  • Block explorers
  • External integrations
  • Codama-compatible tooling

File format

hopper.idl.json

Structure

{
  "format": "hopper-idl",
  "version": 1,
  "program": {
    "name": "hopper_registry",
    "program_id": "...",
    "version": "0.2.1"
  },
  "instructions": [
    {
      "name": "deposit",
      "discriminator": [1],
      "args": [{ "name": "amount", "type": "u64" }],
      "accounts": [
        { "name": "depositor", "writable": false, "signer": true },
        { "name": "vault", "writable": true, "signer": false }
      ]
    }
  ],
  "accounts": [
    {
      "name": "Vault",
      "discriminator": [1],
      "size": 57,
      "fields": [
        { "name": "authority", "type": "publicKey", "offset": 16 },
        { "name": "balance", "type": "u64", "offset": 48 },
        { "name": "bump", "type": "u8", "offset": 56 }
      ]
    }
  ],
  "events": [
    {
      "name": "DepositEvent",
      "discriminator": [1],
      "fields": [
        { "name": "authority", "type": "publicKey" },
        { "name": "amount", "type": "u64" }
      ]
    }
  ]
}

What IDL excludes

  • Migration planning data
  • Trust profile internals
  • Policy wiring details
  • Receipt render metadata
  • Unsafe invariant catalog
  • Segment migration hints

These live in the Manifest only.

Codama Compatibility

Hopper is Codama-compatible where it improves developer experience:

  • Client generation
  • Instruction/account metadata for explorers
  • TypeScript ecosystem interop

Hopper does not flatten its richer state model to fit Codama. The Manifest preserves full richness; the IDL exposes the clean public subset; a Codama projection can be generated from the IDL.

hopper schema export --manifest    # Full manifest
hopper schema export --idl         # Public IDL
hopper schema export --codama      # Codama-compatible projection

Generation Pipeline

Rust declarations (hopper_layout!, hopper_dispatch!, etc.)
    |
    v
Schema extraction (hopper-schema crate)
    |
    v
Hopper Manifest + Hopper IDL + Codama projection
    |
    v
CLI / Manager / Clients / Planner / Receipts

The extraction layer lives in hopper-schema. It reads LayoutManifest constants generated by macros and assembles them into the output formats. No runtime reflection. No dynamic discovery. Everything is compile-time deterministic.

File Layout

project/
  hopper.manifest.json    # Rich manifest (generated)
  hopper.idl.json         # Public IDL (generated)
  src/
    lib.rs                # Canonical code declarations
  docs/
    SCHEMA_ARCHITECTURE.md
    PROC_MACRO_POLICY.md