glm-5.2 5a4ed9e8e3 Exclude internal artifacts from crates.io package
Trim the published package from 64 → 52 files (865.7KiB → 715.3KiB,
202.8KiB → 153.0KiB compressed) by excluding internal-only files:
.opencode/ agent configs, docs/reviews/, docs/research/, and
docs/sdd_process.md. Keeps docs/architecture/ ADRs and OQs, which
document the public design.

Verification:
- cargo test --release: 396 tests pass
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo publish --dry-run --allow-dirty: clean (52 files, 153.0KiB)
2026-08-11 09:45:15 +00:00

alktype

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.

alktype is a standalone crate with two dependencies: jsonschema (for validation) and serde_json (for schema parsing). No tokio, no platform deps, no unsafe. Compiles to wasm32-unknown-unknown.

What it is

A JSON Schema annotated 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 / packed layout)
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.

Usage

Build the schema with the fluent Rust builder (ADR-009), compile it once into an [AlkTypeEngine], then read/write fields at computed offsets:

use alktype::{AlkTypeEngine, Endian, LayoutMode, Schema, FieldValue};

// Channels' 8-byte chunk header: big-endian, packed mode.
let mut schema = Schema::struct_()
    .endian(Endian::Big)
    .field("channel_id", Schema::uint32())
    .field("length",     Schema::uint32())
    .build();

let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;

// Write a frame into a buffer. For fixed-size structs, the byte
// positions are a direct read off the layout — channel_id at 0,
// length at 4. (For variable-length fields, use LayoutBuilder to
// compute positions from known data sizes.)
let mut buf = vec![0u8; 8];
alktype::data_access::write_u32(&mut buf, 0, 42, "channel_id", Endian::Big)?;
alktype::data_access::write_u32(&mut buf, 4,  7, "length",     Endian::Big)?;

// Validate the bytes against the schema in one call.
engine.validate_bytes(&buf)?; // materializes a Value, then validates

// Read the frame back sequentially (packed mode is sequential by
// construction — variable-length fields shift subsequent fields).
let mut reader = engine.sequential_reader().expect("packed mode");
let (name, value) = reader.read_next(&buf)?.expect("first field");
assert_eq!(name, "channel_id");
assert_eq!(value, FieldValue::U32(42));
# Ok::<(), alktype::AlkTypeError>(())

Schemas may also be authored as plain serde_json::json!{...} literals and passed directly to AlkTypeEngine::compile — the builder is a construction convenience, not a requirement.

The 19 AlkType:* kinds

Kind Rust type Size Notes
AlkType:Int8 i8 1
AlkType:Int16 i16 2 endian-sensitive
AlkType:Int32 i32 4 endian-sensitive
AlkType:Int64 i64 8 endian-sensitive; JSON precision caveat (ADR-005)
AlkType:Uint8 u8 1
AlkType:Uint16 u16 2 endian-sensitive
AlkType:Uint32 u32 4 endian-sensitive; also the enum/string/bytes length-prefix width
AlkType:Uint64 u64 8 endian-sensitive; JSON precision caveat (ADR-005)
AlkType:Float32 f32 4 endian-sensitive; NaN/inf rejected by validator
AlkType:Float64 f64 8 endian-sensitive; NaN/inf rejected by validator
AlkType:Boolean bool 1
AlkType:Enum u32 index 4 index into the schema's "enum" array
AlkType:String length-prefixed UTF-8 4 + N [length: u32][bytes] by default
AlkType:Bytes length-prefixed raw bytes 4 + N [length: u32][bytes] by default
AlkType:Timestamp length-prefixed RFC 3339 4 + N non-strict string check (see inline docs)
AlkType:Struct record of fields composite nested; field paths are dotted ("header.version")
AlkType:Union tagged union composite byte-offset or field-name discriminator
AlkType:Array repeated element composite fixed-size elements with stride, or variable count
AlkType:Record string-keyed map composite [count: u32][key, value]...

The engine recognizes a kind when the schema object has a key starting with AlkType: whose value is true (the boolean shorthand) or an annotation object (e.g. { "AlkType:String": { "encoding": "offset-indirect" } }).

Two layout modes

The consumer selects the layout mode at engine construction time via AlkTypeEngine::compile(schema, mode). The same schema can be compiled in either mode. Decided in ADR-002.

Mode Use case Read API Write API
Packed (LayoutMode::Packed) Protocol wire formats (SFTP, channels, TTY) — fields packed with no alignment padding; variable-length fields shift subsequent fields [SequentialReader] (walks fields in order) [LayoutBuilder] (computes positions from known data sizes)
Aligned (LayoutMode::Aligned) mmap-friendly formats (metatensor, safetensors) — fixed positions with natural alignment padding; variable-length data lives outside the static layout [OffsetMap] (random access by field path) OffsetMap (write at known offsets)

Variable-length handling

  • Packed mode: [length: u32][data] inline by default. The LayoutBuilder takes actual data sizes to compute positions; the SequentialReader reads the length prefix to find the data extent.
  • Aligned mode: a 4-byte length prefix sits at a known offset; the variable data is not part of the static layout. Offset indirection (the metatensor blob pattern: {offset, length} pointing into a separate data region) is opt-in via the encoding annotation.

TUnion discriminators

AlkType:Union supports two discriminator kinds (ADR-003):

  • Byte-offset — a fixed-size integer at a known byte offset. The SFTP Packet pattern: byte 0 is the type byte, bytes 1..N are the variant struct. Mapping keys are stringified integers.
  • Field-name — a named field within the struct. The TypeBox typedef.ts pattern. Mapping keys are string values matching the discriminator field's value.

Endianness

Per-schema, default little-endian. Set "endian": "big" on the top-level schema (or via Schema::endian(Endian::Big)) and the engine byte-swaps every multi-byte read/write accordingly. SFTP consumers specify big-endian; channels' chunk header is big-endian.

Validation

Two entry points on [AlkTypeEngine], one underlying jsonschema validator (ADR-010):

  • validate_json(&Value) / is_valid_json(&Value) — for already-parsed JSON (call's payload schemas).
  • validate_bytes(&[u8]) — materializes a Value tree from the bytes via the layout engine, then validates that Value. Single-call binary buffer validation.

The validator is compiled once at load time; access-time validation is a fast is_valid() check. High-throughput paths can skip validation; security-sensitive paths can validate every frame.

Crate independence

alktype does not depend on any application or networking crate. It defines its own types (AlkTypeError, AlkTypeEngine, FieldValue, etc.) and is usable in contexts where networking doesn't exist — CLI tools, test harnesses, schema-building utilities, and WASM targets. The upcoming alkcall crate (the alknet-call + alknet-channels unification) depends on alktype for both binary layout and JSON payload schemas; alktype knows nothing about alkcall.

Schemas as untrusted input

The crate treats schemas as untrusted input. A malformed schema returns AlkTypeError::Schema / AlkTypeError::Offset from any engine path — never a panic. This matters for hub/spoke topologies where the remote peer provides the schema (e.g. alkcall accepting an OperationSpec from an arbitrary internet peer). All unreachable!() sites in production code were converted to Err ahead of v0.1.0 (review #002, L2).

Documentation

Architecture documentation lives under docs/architecture/:

  • Overview — purpose, "schema is the format" principle, dependencies, consumers, scope boundaries
  • Schema layer — the 19 kinds, jsonschema custom keyword integration, schema annotations
  • Layout engine — offset computation, the two layout modes, alignment, endianness
  • Data access — read/write functions, TUnion dispatch, field paths, zero-copy access
  • Validation — custom keyword validators, AlkTypeError, load-time vs access-time validation
  • Builder — fluent Rust API for constructing alktype JSON Schemas at runtime
  • Architecture decisions (ADRs) — purpose/scope, two layout modes, schema annotations, error handling, int64/uint64 kinds, packed-mode read factory, TUnion in aligned mode, builder API, validate_bytes

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Description
No description provided
Readme 885 KiB
Languages
Rust 100%