Skip to main content
Solana · Anchor · Rust · CI-native

Change an account. Don’t corrupt the chain.

layoutd diffs two versions of your on-chain account layout, generates provably-safe migration code, and blocks the unsafe upgrade in CI — without ever touching the blockchain.

Read the docs
Never emits an unprovable migrationZero false-safe verdictsDeterministic + version-pinned

The problem

Silent corruption is the default.

When your program upgrades, old accounts keep their original byte layout. The chain has no schema registry — no runtime awareness that your struct changed shape.

Your new code reads those old accounts through a modified struct. The deserializer silently interprets the wrong bytes.

V1 account layout (live on chain)

authority0–31
balance32–39
b
0
32
40

Insert a field →

One new field shifts every field after it to the wrong byte offset.

Anchor v1.0 introduced a Migration<From, To> runtime gate. The gate exists. The layer that writes the migration and proves it safe did not. layoutd is that layer.

Live output

See every finding, live.

V1 · UserState

#[account]
pub struct UserState {
pub authority: Pubkey, // 0
pub balance: u64, // 32
pub bump: u8, // 40
}

V2 · UserState

#[account]
pub struct UserState {
pub authority: Pubkey, // 0
+ pub is_active: bool, // 32
pub balance: u64, // 33
pub bump: u8, // 41
}
running…layoutd v0.1 · borsh · deterministic

Three commands

The entire surface area of layoutd.

layoutd diff

--v1 state_v1.rs --v2 state_v2.rs

What changed, and what's risky.

Parses two Anchor account structs, computes byte-level offsets, and classifies every field change as Safe · Review · Danger. Outputs a human-readable report and machine-readable SARIF.

authority — no change
is_active — inserted at offset 32
balance — offset shifted 32 → 33

layoutd gen

--v1 state_v1.rs --v2 state_v2.rs --out migration.rs

Write the safe migration for me.

Emits a complete, compilable Anchor migration instruction for provably-safe cases. Scaffolds dangerous ones behind an explicit acknowledgement. Each output carries a proof annotation.

migrate_user_state.rs — 47 lines
proof: sha256:3a7f… — v0.1 pinned

layoutd check

--v1 state_v1.rs --v2 state_v2.rs

CI gate. Exit code. No drama.

Produces a SARIF 2.1.0 report and exits non-zero on any Danger verdict. Renders inline as GitHub PR annotations. The official GitHub Action wraps this for two-line CI adoption.

exit 1 — 2 DANGER detected
exit 0 — all changes proven safe

The risk model

Three tiers. No grey area.

layoutd never refuses to help —
it refuses to let danger be silent.

SAFE

Proven. Auto-generated.

A provably-correct transformation exists. layoutd emits complete migration logic — no human input required, no offset math to verify.

  • Field appended at struct end
  • Field renamed — same type and position
  • Zero byte shift on existing fields
REVIEW

Probably safe. Human eyes needed.

Migration code is generated with a warning. Correctness depends on context the tool cannot see — CI policy decides whether to auto-pass or require approval.

  • Type widened — u8 → u16, same signedness
  • Mid-struct insert — Borsh, matched by name
  • Field reordered — Borsh only
DANGER

No provable path. Fails CI.

No automatically-correct transformation exists. CI fails by default. Shipping requires an explicit, recorded acknowledgement naming the exact field and change.

  • Field removed — data lost, offsets shift
  • Mid-struct insert — zero-copy alignment required
  • Type reinterpreted — bytes change meaning

Shipping danger requires acknowledgement

A DANGER verdict fails CI by default. To override, the CI config must explicitly name the exact field and change being acknowledged. The danger cannot be silent — it becomes a permanent, audited decision in your version history.

How it works

Five deterministic steps. Zero RPC calls.

  1. 01

    Parse

    Read IDL or raw Rust source for V1 and V2. No network, no RPC, no runtime.

  2. 02

    Layout

    Compute byte offsets, sizes, and alignment padding for every field in both versions.

  3. 03

    Diff

    Produce a structured field-level diff with full offset provenance.

  4. 04

    Classify

    Assign each change Safe, Review, or Danger using the deterministic risk model.

  5. 05

    Emit

    Output a human report, SARIF 2.1.0 file, and migration code.

All steps are pure functions of the two input structs. Fully reproducible offline. No secrets, no network access, no side-effects.

Why it’s trustworthy

Six promises.
All of them structural.

Trust in a security tool isn’t built from copy — it’s built from constraints. layoutd earns trust by refusing to operate outside provable bounds, not by asserting that it’s trustworthy.

Models bytes, not intent

Computes exact byte offsets and alignment padding for every field. Not a guess from names — a proof from layout.

Zero false-safe verdicts

If layoutd cannot prove a migration is safe, it classifies DANGER. Silence is never safe. There are no optimistic assumptions.

Deterministic + version-pinned

The same two structs always produce the same verdict on the same version. Verdicts never change silently across releases.

SARIF 2.1.0 + GitHub Action

Outputs SARIF consumed by GitHub's code scanning pipeline. The official Action wraps check for two-line CI adoption — no YAML plumbing required.

Narrow on purpose

Does one thing: prove whether an account layout migration is safe. Never touches the blockchain. Never holds keys. Never emits transactions.

Danger requires acknowledgement

Shipping a known DANGER requires naming that exact change in CI config. The danger can never be silent again — it becomes an explicit, audited decision.