id: SMH-RESEARCH-TBKDJHVX
type: research
title: "Plugin API Standard Library Lessons"
Plugin API Standard Library Lessons
Grounding for the smith:plugin world: deep lessons from respected standard libraries, adapted to a wasm component guest surface.
Facts and conclusions are candidates, not mandates.
Lua — mechanisms, not policies
Facts:
Entire stdlib is roughly twenty tables; the language plus libraries implement in the order of tens of thousands of lines.
Design motto from the authors: provide mechanisms, not policies; libraries stay embeddable and host-agnostic in intent.
io mirrors C stdio (FILE* handles, read with format verbs); string.format mirrors printf; os exposes execute, getenv, date.
Mechanisms-not-policies is the deepest fit: the host exposes typed primitives, plugins compose policies. No world interface should encode a workflow.
A total surface teachable in one sitting. Budget: every world interface should fit on one page.
Reject:
The C-mirror: printf verbs, stdio metaphors, 1-based indexing. Guest APIs must speak component-model vocabulary (records, resources, results), never host-history vocabulary.
Ambient OS reach (os.*): already impossible under the sandbox; keep it impossible in world design, not just engine config.
Java — contracts, cruft, and the cost of forever
Facts:
java.util.Date: mutable, timezone-hostile, mostly deprecated for decades with no removal path. Stack extends Vector, Properties extends Hashtable: inheritance as API surface locking in implementation detail.
Enumeration→Iterator, Observer/Observable removed in 9, Optional arriving decades after null: corrections layered onto unremovable mistakes.
Counterexample inside the same stdlib: the Collections Framework — interfaces (List, Map, Set) separated from implementations (ArrayList, HashMap), each with documented complexity contracts. It stayed good because the seam was interfaces, not classes.
Interface/implementation separation at the collection level is the same seam as plugin module contracts: publish the contract, swap the implementation.
Documented behavioral contracts (complexity, thread-safety, nullity) as first-class API surface.
Reject:
Inheritance as extension mechanism: world guests get composition only.
Mutable default types: every world record is a value; mutation belongs to resource handles with explicit methods.
The forever-deprecated zombie layer: deprecations must carry a replacement and a removal path from day one (already WASM0001 world-versioning law).
Go — one-method contracts, one grammar
Facts:
io.Reader and io.Writer are single-method interfaces; nearly all byte flow in the ecosystem composes through them.
Proverb canonized by the designers: the bigger the interface, the weaker the abstraction.
context.Context threaded through every cancellable call after 1.7: cancellation became an explicit parameter, retrofit cost enormous.
Inhomogeneity is real: errors as values here, booleans there; per-package conventions drift despite godoc uniformity.
std.io.Reader/Writer compose through duck-typed vtable structs; the 2024 IO redesign generalizes composition further and is still settling — the library is young and messy by its own admission.
Allocation is an explicit parameter everywhere; nothing allocates behind your back; buffers are passed, not grown implicitly.
Error sets are explicit unions in signatures; performance is a stated design constraint of the API shape itself.
No hidden allocation or copying: every world function that moves bytes takes explicit bounds and buffer parameters.
Error sets as precise enumerated failure vocabularies — resist collapsing failures into one error case.
Performance belongs to the contract: batched calls, explicit budgets (our fuel and invocation budget are the same doctrine).
Reject:
Duck typing via anytype: unavailable in WIT; composability must come from shared record types.
The churn of a young redesign: pin the world early, evolve additively, never mid-stream reshape.
Elm — naming discipline, mechanical semver
Facts:
Core is around ten modules; the whole API fits in a practitioner's head. Argument order conventions exist and hold: data first, function last (List.foldl fn acc list).
The style guide bans abbreviations and presumes nothing: names say what they mean, consistently, across the entire ecosystem.
elm/package enforced semantic versioning mechanically: publishing computed the API diff and derived the version bump — breaking changes could not masquerade as patches.
Json.Decode makes untrusted payload parsing an explicit typed pipeline; nothing decodes implicitly.
Data-first parameter ordering for every world function; the convention is free until violated once.
No abbreviations; one glossary for the whole world, enforced in review.
Mechanical semver from API diff is directly transplantable: smith plugin check should diff worlds and classify additive versus breaking, feeding the world-version law instead of trusting authors.
Explicit typed decoding of untrusted provider payloads — already our provider law; the world should expose decode helpers, not implicit conversions.
Reject:
Purity guarantees: paradigm does not transfer; reachability of effects transfers instead (capability grants).
core/std split: the always-available substrate versus the fuller environment layered above.
Naming grammar is codified: as_ borrowed conversions, to_ expensive copies, into_ consuming conversions; #[must_use] marks effects ignoring-is-a-bug.
The API guidelines are a mechanical review checklist: casing tables, doc examples that compile and run, no panics in public API, no unwrap in shipped code.
Coherence rules keep impls from colliding ecosystem-wide.
Two-layer world: a core world every plugin sees, plus granted capability interfaces layered on top — the exact analogue of core/std, and it matches WASM0001 imports-with-grants law.
Prefix grammar for world function names; adopt one and hold it.
A written API-guidelines checklist for the smith world, applied to every world change, with compiling doc examples in the SDK.
Reject:
Allocator assumptions baked into API shapes: guests own linear memory; world signatures express ownership by structure (values in, values out, handles by resource).
OCaml — signatures as the ecosystem unit
Facts:
.mli interface files make the signature a first-class authored artifact; implementations seal against them explicitly.
Functors parameterize modules over modules; the ecosystem (opam) publishes packages whose value is the interface, with competing implementations ( MirageOS protocol providers are the canonical family ).
Interface-first authoring: a plugin's manifest and world exports are authored like an .mli — the contract is the product, the implementation is replaceable.
This is already SPEC0001 law (module contracts, zero/one/many implementations, major-version binding); the research reinforces it: the binding model is the ecosystem's load-bearing wall, not a convenience.
Wasm boundary limitations
Verified constraints and design consequences for the world:
limitation
consequence
mitigation
no generic signatures
contracts name concrete types
SDK-layer generics deferred past 1.0; world stays concrete
no closures or function values
callbacks impossible
named imports; host pulls chunks and re-invokes renewal
no trait objects
no boundary polymorphism
resources with methods are the polymorphism unit; module contracts are resources
values cross by copy
chatty APIs cost
batched calls, explicit buffers
no shared memory
no host–guest aliasing
generation law; copies only
fuel cannot interrupt host calls
guest fuel ≠ host timeout
host effects carry own deadlines and cancellation
32-bit memories
4 GiB per instance
ceilings law
traps poison the instance
guest panic unrecoverable
typed results for ordinary failures; generation reload for traps
result<T, E> only
no exception propagation
precise variant error sets
no variadics or defaults
rigid signatures
none needed if world stays small; SDK optional later
route through host effects; keep grants, fuel, generations coherent
instantiation cost
per-call spin-up is real
instantiate at registration; pool instances
Rust guests keep full generics internally through monomorphization; only signatures crossing the boundary are concrete.
Bindgen maps WIT types to idiomatic guest types per language; unknown future data travels as a tagged list<u8> inside the catch-all variant.
SDK stance: no smith-authored SDK before 1.0; the world must be complete and comfortable through raw bindgen guests alone, and an SDK stays optional forever after.
Synthesis: rules for the smith:plugin world
Minimal total surface; every interface needs two real consumers before entering the world (Zig, Lua).
One naming grammar, one error grammar, one documentation shape across all interfaces (Go, Elm).
Tiny universal contracts over wide interfaces; several one-function interfaces beat one fat one (Go, Zig).
Explicit over implicit: buffers, bounds, cancellation, provenance are parameters, never ambient state (Zig, Go).
Additive-only evolution; every deprecation carries a replacement and removal path (Java, WASM0001).
Performance is part of the contract: batched calls, explicit budgets (Zig, our fuel law).
Two-layer world: core plus granted capability interfaces (Rust core/std).
Data-first parameter order; no abbreviations; one glossary (Elm).
World version bumps are computed from API diffs, not asserted by authors (Elm mechanical semver).
Interfaces are the ecosystem unit: contracts publish, implementations swap, binding is explicit (OCaml, Java Collections).
Mechanisms, not policies: no world interface encodes a workflow (Lua).
World-first: the WIT world is the only API; no smith-authored SDK ships before 1.0, and an SDK stays optional forever — comfort through raw bindgen is a design requirement.
One world major for the host API (smith:plugin@N); plugin-published contracts carry their own majors; versioning every interface separately recreates classpath chaos for no benefit.
Deprecation is metadata with a named replacement and a removal major; enforcement is mechanical at smith plugin check, never a breaking change by itself. New APIs enter @unstable, exempt from append-only until stabilized; a world major ships with zero unstable items.
Flat interfaces inside one world; the fixed namespace:package/interface hierarchy is all the structure a world needs.
Trust flows through the distribution channel — embedded and release-signed, or installed and hash-recorded — never through runtime privilege.
Do Reader/Writer-style byte contracts belong in the world, or only inside HTTP and stream interfaces where chunks already flow?
Which failure vocabulary granularity survives WIT result variants before becoming a Java-style exception taxonomy?
How do plugin-published module contracts compose with world versioning: are they worlds of their own, or interface resources inside the smith world?
Can smith plugin check diff worlds mechanically today (wasm-tools), or does the tooling gap force author-asserted versions initially?
How does a consumer detect additive interface features within one major — unknown catch-alls cover data, not functions?
Which signing scheme fits remote implementation updates, and does it ride the release signature or a separate update key?
---
id: SMH-RESEARCH-TBKDJHVX
type: research
title: "Plugin API Standard Library Lessons"
---
# Plugin API Standard Library Lessons
Grounding for the `smith:plugin` world: deep lessons from respected standard libraries, adapted to a wasm component guest surface.
Facts and conclusions are candidates, not mandates.
## Lua — mechanisms, not policies
Facts:
- Entire stdlib is roughly twenty tables; the language plus libraries implement in the order of tens of thousands of lines.
- Design motto from the authors: provide mechanisms, not policies; libraries stay embeddable and host-agnostic in intent.
- `io` mirrors C `stdio` (`FILE*` handles, `read` with format verbs); `string.format` mirrors `printf`; `os` exposes `execute`, `getenv`, `date`.
- Sources: [Lua 5.4 manual](https://www.lua.org/manual/5.4/), [The Evolution of Lua (HOPL)](https://www.lua.org/doc/hopl.pdf).
Extract:
- Mechanisms-not-policies is the deepest fit: the host exposes typed primitives, plugins compose policies. No world interface should encode a workflow.
- A total surface teachable in one sitting. Budget: every world interface should fit on one page.
Reject:
- The C-mirror: printf verbs, stdio metaphors, 1-based indexing. Guest APIs must speak component-model vocabulary (records, resources, results), never host-history vocabulary.
- Ambient OS reach (`os.*`): already impossible under the sandbox; keep it impossible in world design, not just engine config.
## Java — contracts, cruft, and the cost of forever
Facts:
- `java.util.Date`: mutable, timezone-hostile, mostly deprecated for decades with no removal path. `Stack extends Vector`, `Properties extends Hashtable`: inheritance as API surface locking in implementation detail.
- `Enumeration`→`Iterator`, `Observer`/`Observable` removed in 9, `Optional` arriving decades after null: corrections layered onto unremovable mistakes.
- Counterexample inside the same stdlib: the Collections Framework — interfaces (`List`, `Map`, `Set`) separated from implementations (`ArrayList`, `HashMap`), each with documented complexity contracts. It stayed good because the seam was interfaces, not classes.
- Source: [Java SE API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/module-summary.html).
Extract:
- Interface/implementation separation at the collection level is the same seam as plugin module contracts: publish the contract, swap the implementation.
- Documented behavioral contracts (complexity, thread-safety, nullity) as first-class API surface.
Reject:
- Inheritance as extension mechanism: world guests get composition only.
- Mutable default types: every world record is a value; mutation belongs to resource handles with explicit methods.
- The forever-deprecated zombie layer: deprecations must carry a replacement and a removal path from day one (already WASM0001 world-versioning law).
## Go — one-method contracts, one grammar
Facts:
- `io.Reader` and `io.Writer` are single-method interfaces; nearly all byte flow in the ecosystem composes through them.
- Proverb canonized by the designers: the bigger the interface, the weaker the abstraction.
- `context.Context` threaded through every cancellable call after 1.7: cancellation became an explicit parameter, retrofit cost enormous.
- Inhomogeneity is real: errors as values here, booleans there; per-package conventions drift despite `godoc` uniformity.
- Sources: [io package](https://pkg.go.dev/io), [Go proverbs](https://go-proverbs.github.io/).
Extract:
- Single-purpose contracts: prefer several one-function WIT interfaces over one wide one; guests implement what they use.
- Cancellation as an explicit parameter of every long-running world function — matches our budget and abort laws.
- Uniform documentation grammar across the entire world: same section order, same terminology, machine-checkable.
Reject:
- Implicit interface satisfaction (not expressible in WIT regardless).
- Convention drift between packages: one naming and error grammar for the whole world, reviewed mechanically.
## Zig — explicit allocation, compositional performance
Facts:
- `std.io.Reader`/`Writer` compose through duck-typed vtable structs; the 2024 IO redesign generalizes composition further and is still settling — the library is young and messy by its own admission.
- Allocation is an explicit parameter everywhere; nothing allocates behind your back; buffers are passed, not grown implicitly.
- Error sets are explicit unions in signatures; performance is a stated design constraint of the API shape itself.
- Source: [Zig io guide](https://zig.guide/standard-library/io).
Extract:
- No hidden allocation or copying: every world function that moves bytes takes explicit bounds and buffer parameters.
- Error sets as precise enumerated failure vocabularies — resist collapsing failures into one `error` case.
- Performance belongs to the contract: batched calls, explicit budgets (our fuel and invocation budget are the same doctrine).
Reject:
- Duck typing via `anytype`: unavailable in WIT; composability must come from shared record types.
- The churn of a young redesign: pin the world early, evolve additively, never mid-stream reshape.
## Elm — naming discipline, mechanical semver
Facts:
- Core is around ten modules; the whole API fits in a practitioner's head. Argument order conventions exist and hold: data first, function last (`List.foldl fn acc list`).
- The style guide bans abbreviations and presumes nothing: names say what they mean, consistently, across the entire ecosystem.
- `elm/package` enforced semantic versioning mechanically: publishing computed the API diff and derived the version bump — breaking changes could not masquerade as patches.
- `Json.Decode` makes untrusted payload parsing an explicit typed pipeline; nothing decodes implicitly.
- Sources: [elm/core](https://package.elm-lang.org/packages/elm/core/latest/), [style guide](https://elm-lang.org/docs/style-guide).
Extract:
- Data-first parameter ordering for every world function; the convention is free until violated once.
- No abbreviations; one glossary for the whole world, enforced in review.
- Mechanical semver from API diff is directly transplantable: `smith plugin check` should diff worlds and classify additive versus breaking, feeding the world-version law instead of trusting authors.
- Explicit typed decoding of untrusted provider payloads — already our provider law; the world should expose decode helpers, not implicit conversions.
Reject:
- Purity guarantees: paradigm does not transfer; reachability of effects transfers instead (capability grants).
## Rust — layering, prefix grammars, checklist discipline
Facts:
- `core`/`std` split: the always-available substrate versus the fuller environment layered above.
- Naming grammar is codified: `as_` borrowed conversions, `to_` expensive copies, `into_` consuming conversions; `#[must_use]` marks effects ignoring-is-a-bug.
- The [API guidelines](https://rust-lang.github.io/api-guidelines/) are a mechanical review checklist: casing tables, doc examples that compile and run, no panics in public API, no `unwrap` in shipped code.
- Coherence rules keep impls from colliding ecosystem-wide.
- Source: [std docs](https://doc.rust-lang.org/std/), [API guidelines](https://rust-lang.github.io/api-guidelines/).
Extract:
- Two-layer world: a core world every plugin sees, plus granted capability interfaces layered on top — the exact analogue of `core`/`std`, and it matches WASM0001 imports-with-grants law.
- Prefix grammar for world function names; adopt one and hold it.
- A written API-guidelines checklist for the smith world, applied to every world change, with compiling doc examples in the SDK.
Reject:
- Allocator assumptions baked into API shapes: guests own linear memory; world signatures express ownership by structure (values in, values out, handles by resource).
## OCaml — signatures as the ecosystem unit
Facts:
- `.mli` interface files make the signature a first-class authored artifact; implementations seal against them explicitly.
- Functors parameterize modules over modules; the ecosystem (opam) publishes packages whose value is the interface, with competing implementations ( MirageOS protocol providers are the canonical family ).
- Source: [OCaml modules manual](https://ocaml.org/manual/modules.html).
Extract:
- Interface-first authoring: a plugin's manifest and world exports are authored like an `.mli` — the contract is the product, the implementation is replaceable.
- This is already SPEC0001 law (module contracts, zero/one/many implementations, major-version binding); the research reinforces it: the binding model is the ecosystem's load-bearing wall, not a convenience.
## Wasm boundary limitations
Verified constraints and design consequences for the world:
| limitation | consequence | mitigation |
|---|---|---|
| no generic signatures | contracts name concrete types | SDK-layer generics deferred past 1.0; world stays concrete |
| no closures or function values | callbacks impossible | named imports; host pulls chunks and re-invokes renewal |
| no trait objects | no boundary polymorphism | resources with methods are the polymorphism unit; module contracts are resources |
| values cross by copy | chatty APIs cost | batched calls, explicit buffers |
| no shared memory | no host–guest aliasing | generation law; copies only |
| fuel cannot interrupt host calls | guest fuel ≠ host timeout | host effects carry own deadlines and cancellation |
| 32-bit memories | 4 GiB per instance | ceilings law |
| traps poison the instance | guest panic unrecoverable | typed results for ordinary failures; generation reload for traps |
| `result<T, E>` only | no exception propagation | precise variant error sets |
| no variadics or defaults | rigid signatures | none needed if world stays small; SDK optional later |
| no structural subtyping | version tolerance must be designed | additive-only worlds plus unknown catch-all |
| guest threads experimental | no intra-guest parallelism | host-orchestrated concurrency law |
| async types stabilizing | unstable to depend on | explicit pull model; never await guest futures |
| component-to-component linking complicates ownership | plugin-to-plugin design choice | route through host effects; keep grants, fuel, generations coherent |
| instantiation cost | per-call spin-up is real | instantiate at registration; pool instances |
Rust guests keep full generics internally through monomorphization; only signatures crossing the boundary are concrete.
Bindgen maps WIT types to idiomatic guest types per language; unknown future data travels as a tagged `list<u8>` inside the catch-all variant.
SDK stance: no smith-authored SDK before 1.0; the world must be complete and comfortable through raw bindgen guests alone, and an SDK stays optional forever after.
## Synthesis: rules for the smith:plugin world
1. Minimal total surface; every interface needs two real consumers before entering the world (Zig, Lua).
2. One naming grammar, one error grammar, one documentation shape across all interfaces (Go, Elm).
3. Tiny universal contracts over wide interfaces; several one-function interfaces beat one fat one (Go, Zig).
4. Explicit over implicit: buffers, bounds, cancellation, provenance are parameters, never ambient state (Zig, Go).
5. Vocabulary types with impossible-to-misuse constructors; precise enumerated failure sets (Rust, Zig).
6. Additive-only evolution; every deprecation carries a replacement and removal path (Java, WASM0001).
7. Performance is part of the contract: batched calls, explicit budgets (Zig, our fuel law).
8. Two-layer world: core plus granted capability interfaces (Rust `core`/`std`).
9. Data-first parameter order; no abbreviations; one glossary (Elm).
10. World version bumps are computed from API diffs, not asserted by authors (Elm mechanical semver).
11. Interfaces are the ecosystem unit: contracts publish, implementations swap, binding is explicit (OCaml, Java Collections).
12. Mechanisms, not policies: no world interface encodes a workflow (Lua).
13. World-first: the WIT world is the only API; no smith-authored SDK ships before 1.0, and an SDK stays optional forever — comfort through raw bindgen is a design requirement.
14. One world major for the host API (`smith:plugin@N`); plugin-published contracts carry their own majors; versioning every interface separately recreates classpath chaos for no benefit.
15. Deprecation is metadata with a named replacement and a removal major; enforcement is mechanical at `smith plugin check`, never a breaking change by itself. New APIs enter `@unstable`, exempt from append-only until stabilized; a world major ships with zero unstable items.
16. Flat interfaces inside one world; the fixed `namespace:package/interface` hierarchy is all the structure a world needs.
17. Trust flows through the distribution channel — embedded and release-signed, or installed and hash-recorded — never through runtime privilege.
## Sources
- Lua 5.4 reference manual — <https://www.lua.org/manual/5.4/>
- The Evolution of Lua — <https://www.lua.org/doc/hopl.pdf>
- Java SE API — <https://docs.oracle.com/en/java/javase/21/docs/api/java.base/module-summary.html>
- Go io package — <https://pkg.go.dev/io>
- Go proverbs — <https://go-proverbs.github.io/>
- Zig io guide — <https://zig.guide/standard-library/io>
- Elm core and style guide — <https://package.elm-lang.org/packages/elm/core/latest/>, <https://elm-lang.org/docs/style-guide>
- Rust std and API guidelines — <https://doc.rust-lang.org/std/>, <https://rust-lang.github.io/api-guidelines/>
- OCaml modules — <https://ocaml.org/manual/modules.html>
- Component model and WIT — <https://component-model.design/>
## Unresolved questions
- Do Reader/Writer-style byte contracts belong in the world, or only inside HTTP and stream interfaces where chunks already flow?
- Which failure vocabulary granularity survives WIT result variants before becoming a Java-style exception taxonomy?
- How do plugin-published module contracts compose with world versioning: are they worlds of their own, or interface resources inside the smith world?
- Can `smith plugin check` diff worlds mechanically today (wasm-tools), or does the tooling gap force author-asserted versions initially?
- How does a consumer detect additive interface features within one major — unknown catch-alls cover data, not functions?
- Which signing scheme fits remote implementation updates, and does it ride the release signature or a separate update key?