--- status: draft last_updated: 2026-08-11 --- # alktype — Overview The binary struct engine: a small Rust crate that takes a JSON Schema with `AlkType:*` custom keywords and produces an offset map, read/write functions, and validation — all driven by the schema. The schema is the format definition; the engine is generic. This document covers the crate's purpose, the "schema is the format" principle, its dependency edges, consumers, and scope boundaries. Component details are in the sibling documents. ## What `alktype` is a library crate that consumes JSON Schemas annotated with `AlkType:*` custom keywords (the same kinds defined in TypeBox's `typedef.ts`, plus `AlkType:Bytes`, `AlkType:Int64`, and `AlkType:Uint64` as alktype additions) and produces three capabilities: 1. **An offset map** — walks the schema, computes byte offsets for each field based on type sizes, field order, and alignment. 2. **Read/write functions** — given a `&[u8]` buffer and a field path, read the field's bytes at its offset (zero-copy for fixed-size types). Given a `&mut [u8]` buffer, write a value at its offset. 3. **Validation** — via `jsonschema` custom keywords, validates that a buffer's bytes match the schema's type constraints. The heavy lifting is done by the `jsonschema` crate (validation) and `serde_json` (schema parsing). The novel code is the offset computation — a recursive walk of the schema JSON that computes byte positions for each field. The custom keyword implementations are small (a few lines each, generated from shared macros — see [validation.md](validation.md)). The crate replaces two prior attempts that built their own jsonschema engines — typebox-rs (~8,400 lines) and the @alkdev/alktype prototype (~5,600 lines) — with `jsonschema` + an offset map + small custom keyword implementations. See [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md). ## Why The crate's purpose is to be a binary struct engine for components that read or write binary data at computed offsets. Instead of per-protocol serde structs (russh-sftp's 29 packet types), per-handler wire format code (TTY's 5-byte format parser), or per-format offset computation (metatensor's tensor access), all of these become instances of the same engine with different schemas. The guiding insight: > **The schema is the format.** A JSON Schema with `AlkType:Float32`, > `AlkType:Struct`, `AlkType:Union` etc. is both the validation spec and > the layout spec. No separate format definition, no separate parser, no > separate validator. One schema, three uses: validate, compute offsets, > access data. This is the convergence of three threads identified in the call-channels-unification research: the `typedef.ts` schema kinds from TypeBox, the russh-sftp protocol packets, and the metatensor format. The common pattern: a JSON Schema describes the shape of binary data, and the binary data is the struct's bytes at computed offsets. The crate was bumped up in the timeline when the call-channels-unification research surfaced that channels, TTY, and the binary call protocol are all variations on the same wire-format family — `[discriminant][length][payload]`. The alktype engine makes the "channels is call with a binary data plane" unification concrete: the binary data plane's wire format is the call protocol's own schema system, just binary-encoded. The `channel_open` marker says "use binary framing"; the alktype engine says "here's how to read/write the binary payload." ## The "Schema Is the Format" Principle A JSON Schema with `AlkType:*` custom keywords serves three roles simultaneously: | Role | Mechanism | When | |------|-----------|------| | **Validation spec** | `jsonschema` custom keywords | Load time (build validator), access time (validate buffer) | | **Layout spec** | Offset computation from type sizes + field order | Load time (build offset map) | | **Data access** | Read/write at computed offsets | Access time (read field, write field) | No separate format definition, no separate parser, no separate validator. The schema is the single source of truth for the binary format. Adding a new field to a protocol is adding a property to the schema JSON — the engine computes the new offsets automatically. This is the same principle as `#[repr(C)]` struct field access, but at runtime from a portable JSON Schema instead of at compile-time from language-specific annotations. The schema is the ABI contract. ## Dependencies ``` alktype ├── jsonschema (v0.46.5, Draft 2020-12) — validation engine, custom keyword support ├── serde_json (with preserve_order) — schema parsing; field order is load-bearing └── (no tokio, no platform deps) — WASM-clean by construction ``` `alktype` is dependency-light: `jsonschema` + `serde_json` only. No tokio, no platform deps. Compiles to `wasm32-unknown-unknown` for browser use. The `jsonschema` crate is already in the workspace at `@alkdev/alknet: jsonschema/` — alktype is its first consumer. `serde_json` requires the `preserve_order` feature because field order is load-bearing for binary layouts. The order of properties in the schema JSON determines the order of fields in the binary struct. ## Consumers | Consumer | Schema describes | Engine provides | |----------|-----------------|-----------------| | **alkcall** (v0.1.0 first consumer) | channels' `ChunkHeader { channel_id: u32 BE, length: u32 BE }` + call's `OperationSpec.input_schema` / `output_schema` / `error_schemas` | `validate_bytes` for the 8-byte chunk header; `validate_json` for call's JSON payloads; builder API for both | | russh-sftp | 29 packet structs + Packet union (byte discriminator) | Read/write SFTP frames from bytes | | metatensor | Model layout (ConvNet struct, tensor refs) | Offset map for mmap'd tensor access | | binary call frames | `call.requested` / `call.responded` / etc. structs | Read/write binary call frames | | TTY negotiation | `NegotiateRequest` / `NegotiateResponse` structs | Read/write TTY control frames | | channels wire | `ChunkHeader { channel_id, length }` | 8-byte chunk header (now in scope for alkcall; was "trivial" pre-v0.1.0) | alkcall — the merged `alknet-call` (call protocol) + `alknet-channels` (channel multiplexing) extraction from `@alkdev/alknet` — is the consumer that bumped the builder API (OQ-003) and generalized validation (ADR-010) into v0.1.0. It uses alktype for two distinct schema roles (binary layout + JSON payloads) from one library. See [builder.md](builder.md) and [validation.md](validation.md) §"validate_bytes". The russh-sftp case is the most instructive and the highest-value POC target. The `Packet` enum's `TryFrom<&mut Bytes>` impl is a hand-written dispatch on a type byte followed by serde deserialization. Under alktype, the dispatch is `TUnion` with a byte-offset discriminator — the schema says "byte 0 is the discriminator, bytes 1..N are the variant struct." The engine reads the discriminator, looks up the variant schema, computes offsets, reads fields. Same result, no per-packet-type code. ## Scope Boundaries (What This Is Not) These boundaries are decided in [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md). - **Not metatensor.** alktype is the binary struct *engine*. Metatensor is a *format* (8-byte header + JSON header + binary data) that uses the alktype engine for its offset computation and tensor access. - **Not a Value system.** TypeBox's `Value.Diff`, `Value.Migrate`, `Value.Convert` — schema evolution — is out of scope for v1. The engine should not do anything that explicitly blocks adding a Value system later. - **Not a code generator.** typebox-rs's `codegen/` module is a separate concern. The alktype engine consumes schemas; it does not generate them. - **Schema builder is in scope as of v0.1.0.** A fluent Rust API for constructing schemas at runtime, producing `serde_json::Value`, is shipped in v0.1.0 ([ADR-009](decisions/009-builder-api.md), resolves OQ-003). The builder covers AlkType kinds and standard JSON Schema; see [builder.md](builder.md). Schemas may still be authored in TypeBox, generated by ujsx components, or hand-written — the builder is an additional construction path, not a replacement. - **Not a serialization framework.** The alktype engine is not a general-purpose serde replacement. It operates on raw byte buffers at computed offsets — no intermediate `Value` tree, no reflection, no dynamic dispatch per field. For JSON data, use serde. For binary data with a known schema, use alktype. ## Architecture (component pointers) - **[schema-layer.md](schema-layer.md)** — the 19 `AlkType:*` kinds, jsonschema custom keyword integration, TypeBox interop, schema annotations (endianness, alignment, encoding, TUnion discriminators). - **[layout-engine.md](layout-engine.md)** — offset computation, the two layout modes (packed sequential vs aligned static), alignment, endianness, variable-length field handling. - **[data-access.md](data-access.md)** — read/write functions, TUnion dispatch, field paths, zero-copy access for fixed-size types, length-prefix reading for variable-length types. - **[validation.md](validation.md)** — custom keyword validators for all 19 `AlkType:*` kinds, `AlkTypeError`, load-time vs access-time validation, `AlkTypeEngine` as the compiled form of a schema. `validate_json` for JSON values; `validate_bytes` for binary buffers (ADR-010). - **[builder.md](builder.md)** — fluent Rust API for constructing alktype JSON Schemas at runtime, producing `serde_json::Value`. Covers AlkType kinds and standard JSON Schema (ADR-009). ## Design Decisions | Decision | ADR | Summary | |----------|-----|---------| | Purpose, scope, and the jsonschema engine | [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md) | What the crate is/isn't; why jsonschema not a custom engine; "schema is the format" principle; scope boundaries | | Two layout modes | [ADR-002](decisions/002-two-layout-modes-packed-vs-aligned.md) | Packed sequential (`LayoutBuilder`/`SequentialReader`) for protocols; aligned static (`OffsetMap`) for mmap formats | | Schema annotations | [ADR-003](decisions/003-schema-annotations.md) | Endianness (schema-level, default LE), alignment (struct + field-level), encoding (length-prefixed vs offset-indirect), TUnion discriminators (byte-offset vs field-name) | | Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `AlkTypeError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping | | Int64/Uint64 kinds | [ADR-005](decisions/005-int64-uint64-first-class-kinds.md) | 64-bit integers as first-class kinds (SFTP offsets, metatensor data_offsets) | | Non-final inline variable fields | [ADR-006](decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md) | Rejected in aligned mode (would clobber subsequent fields) | | Packed-mode read factory | [ADR-007](decisions/007-packed-mode-read-factory.md) | `engine.sequential_reader()` returns an owned fresh reader | | TUnion in aligned mode | [ADR-008](decisions/008-reject-tunion-in-aligned-mode.md) | Rejected for v1 (broken semantics; no current consumer needs it) | | Builder API | [ADR-009](decisions/009-builder-api.md) | Fluent Rust API producing `serde_json::Value`; covers AlkType kinds + standard JSON Schema; resolves OQ-003 | | Generalized validation — `validate_bytes` | [ADR-010](decisions/010-generalized-validation-validate-bytes.md) | Single-call binary-buffer validation on `AlkTypeEngine`; materialize `Value` from bytes, then validate | ## Open Questions See [open-questions.md](open-questions.md) for full details. - **OQ-001** (deferred(scope)): Arrays of variable-length-element structs. - **OQ-002** (deferred(scope)): `no_std` + `alloc` support. - **OQ-003** (resolved by [ADR-009](decisions/009-builder-api.md)): Builder API for schema construction. Shipped in v0.1.0; see [builder.md](builder.md). ## References - `@alkdev/alknet: docs/research/alknet-typedef/findings.md` — POC results (26 tests passing, two layout modes, TUnion dispatch, endianness) - `@alkdev/alknet: docs/research/call-channels-unification/findings.md` §"alknet-typedef: JSON Schema as the binary struct engine" — the origin of this research thread - `@alkdev/alknet: typebox/example/typedef/typedef.ts` — the TypeBox schema kinds (619 lines) - `@alkdev/alknet: jsonschema/` — the jsonschema crate (v0.46.5, Draft 2020-12) - `@alkdev/alknet: alknet-typedef-poc/` — the POC code (disposable) - `@alkdev/alknet: typebox-rs/` — prior attempt, replaced by alktype - `@alkdev/alknet: alktype-prototype/` — prior attempt (the @alkdev/alktype prototype; not to be confused with this crate, which reuses the name but is backed by the `jsonschema` crate) > **Note**: The research findings, POC code, and prior-attempt paths above > refer to the parent `@alkdev/alknet` workspace where this crate originated. > They are preserved here as historical context for the architectural > decisions; the artifacts themselves are not part of this standalone repo.