keel/v1
- 08 · Map
- 2 entries, keys sorted
- 04 · String
- "task" → "review"
- 07 · Float64
- "progress" → 0.5
- 06 · List
- tags → "racket", "desktop"
max encoded value
Contract v1 · Canonical bytes · MIT
Keel is the transport-neutral application contract shared by Racket desktop runtimes: one value model, one canonical encoding, one error shape — so native and WebView apps speak the same bytes.
max encoded value
BELOW THE PROTOCOL, ABOVE THE BYTES
RVT1 frames a native RPC; GLZ1 frames a WebView invoke. Frames, request ids, and backpressure are transport business — but the payload they carry kept being redefined by each runtime.
Keel owns what travels inside: values, structured errors, lifecycle names, and a schema vocabulary. It sits above a byte stream and below an application protocol, so Rivet and Glaze embed one contract instead of maintaining two.
One value encodes to one byte sequence. Map keys are ordered by their UTF-8 bytes, non-finite floats are rejected, and decoders refuse anything non-canonical.
A schema describes records, commands, events, and states without owning an envelope: Rivet maps a command to an RVT1 RPC, Glaze to a GLZ1 invoke. Names like ready? stay exact.
Every implementation enforces the same budgets — 64 MiB, depth 64, 262,144 nodes — and rejects the same malformed inputs. No runtime is more forgiving than another.
TEN TAGS, NO AMBIGUITY
Every value is one tag byte followed by a fixed body. Integers are little-endian; lengths are unsigned 32-bit.
00Nullnone01Bool · falsenone02Bool · truenone03Int64signed two's-complement, little-endian04Stringlength + well-formed UTF-805Byteslength + uninterpreted bytes06Listitem count + encoded values07Float64finite IEEE-754 binary64 bits08Mapentry count + String pairs, keys sorted09Errorcode + message + data valueError codes are kebab-case and deliberately not a closed list — RVT1, GLZ1, and future transports each publish and negotiate their own taxonomy.
ONE VOCABULARY, TERMINAL ENDS
Early shutdown may jump from starting or ready straight to stopping. Success, failure, cancellation, and timeout are mutually exclusive outcomes — cancellation races completion, and the winner owns the single terminal result.
RACKET REFERENCE IMPLEMENTATION
Install the package, encode a value, get the canonical bytes — the same bytes every conforming runtime produces.
Read the normative Contract v1 →> (require keel)
> (define wire
(encode-app-value
(hash "task" "review" "progress" 0.5)))
> (decode-app-value wire)
'#hash(("progress" . 0.5) ("task" . "review"))
> (make-app-error "invalid-args"
"Expected a non-empty title"
(hash "field" "title"))
COMPATIBILITY DISCIPLINE
tests/golden-v1.txt is the normative byte corpus: changing an existing line is a compatibility break. New union members need a written rationale, new cross-runtime golden vectors, decoder budget tests, and a version decision.
A DELIBERATE BOUNDARY
Keel owns no frame header, request identifier, streaming or backpressure, renderer, permission model, or installer. Rivet keeps RVT1 and its native hosts; Glaze keeps GLZ1 and its WebView adapters; other transports may embed this codec but negotiate their own versions and error taxonomies.