Prove the indexed bytes are right.

An indexer passes every freshness and throughput check while writing empty or malformed accounts. In the incident this project came out of, green liveness hid empty Kamino payloads for 158.91 hours. karat67 shapes the exact bytes your index stored, reconciles them against chain, and exits non-zero when they disagree.

Pick a case

One live, four constructed

Byte field

What the IDL declares, what was written

IDL, Obligation
3344 bytes
Indexer wrote
3344 bytes

Loading the WebAssembly module.

The verdict comes from the karat67 crate compiled to WebAssembly, running in this tab.

Your own payload and endpoint

Passed through unmodified. No discriminator is rebuilt and nothing is padded to an IDL length, so a truncated or empty row stays truncated or empty. Fill this in and it replaces the selected constructed case.

Labels the result and keys the chain fetch. The live case needs it; shape alone does not.

Empty means PublicNode's free public endpoint, solana-rpc.publicnode.com. This site has no backend, so an endpoint you type goes only to that host and is kept in this browser's localStorage. Your provider must allow browser requests (CORS) from this origin.

Enables slot-lag tolerance on a byte mismatch. Without it, any mismatch is a hard fail rather than skipped.

Endpoint in this browser: none set

How the gate decides

One decision point, karat67::gate::check_account, is what the CLI, the Carbon processor hook, the MCP tool, and this page all call. None of them can reach a verdict the others would not.

  1. 01

    Shape

    The exact indexed bytes against the account registry: an 8-byte Anchor discriminator that resolves to a known type, then that type's declared length, exactly. Obligation 3344, UserMetadata 1032, Reserve 8624, LendingMarket 4664.

    A non-Pass shape returns alone. The fetcher is never invoked, so no RPC call goes out and nothing downstream can paper over bytes that are already wrong.

  2. 02

    Reconcile

    Shape passing earns the fetch. The same key comes back through getMultipleAccounts and the bytes are diffed. Equal bytes pass. A mismatch within the slot-lag window is skipped with its reason, never passed. Beyond the window, or with no write slot, it fails.

  3. 03

    Completeness

    Indexed slots against getBlocks over the same range, which catches the gap a per-account check cannot see: rows that are individually correct and collectively missing.

  4. 04

    Exit code

    Exit 0 only when the result set is non-empty and every status is Pass. Skipped is not Pass, so a check that could not run still exits 1, and the readout says which of the two happened.

Deploy the check

Shape needs no RPC and no secrets, so it runs in CI as it does in this tab. Reconcile and completeness stay opt-in behind KARAT_RPC_URL.

name: Shape check (offline)

on:
  push:
    branches: [main]
  pull_request:

jobs:
  shape:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Install karat
        run: cargo install --locked --git https://github.com/SGDlab67/karat67 karat67
      - name: Shape check, no RPC
        run: karat shape --account-type Obligation --len 3344

karat67 is not on crates.io yet, so both snippets install from GitHub.

Or call the crate

cargo add karat67 --git https://github.com/SGDlab67/karat67