Skip to content

Architecture

The SDK is organised as a strict tier ladder: a module may import only from a lower tier, never a higher or equal one. This is enforced in CI by scripts/check-layers.mjs rather than merely documented, which is what makes the subpath imports below a real guarantee instead of a convention.

TierModulesRole
0core, log, worker, wasm, types-ambientprimitives with no SDK dependencies
1cryptofield arithmetic, Poseidon, Jubjub
2fmd, keys, noteskey derivation, detection, note encryption
3protocol, circuitwire contracts and circuit shapes
4permit2, chain, prover, servicesoutside world — chains, provers, HTTP
5bundle, synctransaction assembly, note synchronisation
6walletthe WalletApi surface
7presets, x402opinionated entry points

The three enforced rules

  1. No importing from a higher tier. The ladder is acyclic by construction, which is what lets a browser bundler drop services when an app only uses primitives.
  2. No export * anywhere. Every re-export is named. This is why api-surface.json can be a meaningful snapshot: a symbol is public if and only if a barrel forwards it by name.
  3. No leaf module below tier 3 may import a domain barrel. Low-level code imports the specific file it needs, so pulling in crypto does not drag a barrel's whole transitive closure with it.

Why this shows up in the API

Two consequences you will notice as a consumer:

  • Subpath imports are meaningful. @lelantos-org/sdk/crypto really is tier 1 and pulls in nothing above it. The 30 subpaths in the exports map are not cosmetic packaging.
  • Branded types live in tier 0. AssetId, CircuitAmount, and Hex32 are declared in core precisely so every tier above can speak them without a cycle. See Amounts.

Why this matters for bundle size

The ladder being acyclic is what lets a bundler drop whole tiers. An application that imports only @lelantos-org/sdk/crypto pulls in tier 0 and tier 1 and nothing else — no HTTP clients, no chain adapter, no prover. Importing the root barrel pulls in everything.

If bundle size matters, import from the narrowest subpath that has what you need.

Next