Schema architecture
Program manifests, layout IDs, Hopper IDL, Codama JSON, lossless Solana IDL projection, 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. They assemble one
ProgramManifest, from which Hopper renders the rich Hopper manifest plus nine
client/interoperability outputs:
- Six clients: TypeScript, Kotlin, Python, Go, C, and off-chain Rust
- Hopper's public IDL subset
- Codama JSON
- A Solana IDL v0.1.0 projection, only when the wire contract is lossless
These outputs share a source, but they are not interchangeable formats.
Code-First Doctrine
Canonical truth lives in code. The authoritative declarations are:
hopper_layout!-- fields, offsets, sizes, versions, fingerprintshopper_segment!/ segmented layouts -- segment structure, roleshopper_dispatch!-- instruction set, discriminatorshopper_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 inspecthopper compat/hopper diff/hopper planhopper manager(program introspection)- Receipt rendering and migration planning
- Docs generation and test tooling
File format
hopper.manifest.json
The version string in this structure is an illustrative 0.2.1 fixture value, not the current Hopper package version.
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 Hopper IDL is a public-facing Hopper schema for:
- TypeScript client generation
- Kotlin, Python, and Rust client generation
- External integrations
- Hopper-aware tooling
It is not the Solana IDL v0.1.0 format.
File format
hopper.idl.json
This snippet follows the current IdlJsonFromManifest shape. It continues the
same illustrative 0.2.1 fixture; the values are examples, not a release
artifact.
Structure
{
"name": "hopper_registry",
"version": "0.2.1",
"description": "Segmented registry example",
"instructions": [
{
"name": "deposit",
"tag": 1,
"args": [
{ "name": "amount", "type": "u64", "size": 8, "encoding": "fixed" }
],
"accounts": [
{ "name": "depositor", "writable": false, "signer": true },
{ "name": "vault", "writable": true, "signer": false, "layoutRef": "Vault" }
]
}
],
"accounts": [
{
"name": "Vault",
"disc": 1,
"version": 1,
"layoutId": "a1b2c3d4e5f60718",
"totalSize": 57,
"fieldCount": 3,
"fields": [
{ "name": "authority", "type": "[u8;32]", "size": 32, "offset": 16, "intent": "authority" },
{ "name": "balance", "type": "WireU64", "size": 8, "offset": 48, "intent": "balance" },
{ "name": "bump", "type": "u8", "size": 1, "offset": 56, "intent": "custom" }
],
"semanticFingerprint": "0000000000000000000000000000000000000000000000000000000000000000"
}
],
"events": [
{
"name": "DepositEvent",
"tag": 1,
"fields": [
{ "name": "authority", "type": "[u8;32]", "size": 32, "offset": 0, "intent": "authority" },
{ "name": "amount", "type": "u64", "size": 8, "offset": 32, "intent": "balance" }
]
}
],
"fingerprints": [
{ "layoutId": "a1b2c3d4e5f60718", "name": "Vault" }
]
}
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; Hopper IDL exposes the clean public subset; and the
Codama projection is generated directly from the same ProgramManifest, not by
round-tripping through Hopper IDL.
hopper schema export --manifest @hopper.manifest.json
hopper schema export --idl @hopper.manifest.json
hopper schema export --codama @hopper.manifest.json
Solana IDL v0.1.0 projection
Solana IDL is a separate, lossless-only interoperability target:
hopper schema export --anchor-idl @hopper.manifest.json \
--program-id <PROGRAM_ID>
The projection preserves Hopper's instruction, account, and event
discriminators. Account bodies declare custom hopper-zero-copy-v1
serialization, so generic Anchor-Borsh account decoders cannot decode Hopper
state. Export fails closed on dynamic account tails, unsupported bounded wire
encodings, remaining-account contracts, unresolved or unsafe PDA seeds,
unresolved fixed addresses, and ambiguous discriminator prefixes.
hopper compile --emit idl emits Hopper IDL; it does not invoke this Solana
IDL projection. hopper publish-idl validates the Solana projection before a
dry run or live Program Metadata publication.
Generation Pipeline
Rust declarations (hopper_layout!, hopper_dispatch!, etc.)
|
v
Schema extraction (hopper-schema crate)
|
v
ProgramManifest
|
v
Rich manifest + 6 clients + Hopper IDL + Codama JSON
+ lossless-only Solana IDL v0.1.0
|
v
CLI / Manager / Planner / Receipts / external tooling
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 # Hopper public IDL (generated)
hopper.codama.json # Codama projection (generated)
program.solana-idl.json # Optional validated Solana IDL v0.1.0 projection
src/
lib.rs # Canonical code declarations
docs/
SCHEMA_ARCHITECTURE.md
PROC_MACRO_POLICY.md
