Skip to main content

Crate holger_plugin_abi

Crate holger_plugin_abi 

Source
Expand description

holger’s package-handler plugin contract — the one wire format a handler speaks whether it is linked into the server or loaded from a .wasm at startup.

Rickard’s ask was that every package handler should be writable as native or WASM. Before this crate holger had no wasm path at all: the sixteen repository backends are unconditional path dependencies of server/lib, so adding a package handler meant editing server/lib/Cargo.toml and rebuilding the server.

§The two seams

A RepositoryBackendTrait is a Rust trait object and cannot cross a wasm boundary, so this crate splits a handler in two along the line that actually matters:

  • PackageHandlerthe format logic: coordinate parsing, the HTTP path scheme, listing, content types. This is what varies per ecosystem and what a plugin author writes. It is pure and target-independent, so the one implementation compiles for the host and for wasm32-unknown-unknown.
  • BlobStorethe bytes. A wasm module has no filesystem and no socket. The host owns storage and hands it in.

That split is what makes “the wasm crate is ABI glue only” true rather than aspirational: the native backend implements BlobStore over the filesystem, the wasm shim implements it over four host imports, and both call the same PackageHandler. It is the same shape as znippy’s maven pair, where host-decompressors compiles the threaded ljar fan-out out of the wasm build and leaves the single-threaded linflate both sides share — same logic, different plumbing underneath.

§Which verbs cross, and why

RepositoryBackendTrait (traits/src/lib.rs:404) has twelve methods. Six cross:

verbwhy it is in
plugin_manifestthe module must name itself, or registration needs a config file and the whole point of scanning a directory is lost
fetchthe read. Non-defaulted on the trait.
putthe write. Non-defaulted on the trait.
listenumeration; the UI and retention planner both read it
coordinate_for_paththe serve-time quarantine gate calls it before bytes go out, and it is pure path logic — the cheapest and most format-specific thing a handler owns
handle_http2_requestnon-defaulted, and it is the actual door a package client knocks on. A handler that could not answer it would not be a package handler.

Six do not, and the host supplies the trait’s own default for each:

verbwhy it is out
name / format / is_writableconstants. Read once from the manifest at load, not per call.
archive_files / archive_info / has_archivethese describe a znippy archive handle. A wasm module has none, and the trait already defaults them to “no archive” — which is the truth here, not a stub.
delete_artifactthe trait default fails closed, and the contract requires the implementer to recompute the stored digest and refuse a mismatch. A sandboxed module cannot be trusted to have done that, and a wrong answer deletes bytes. Failing closed is the correct answer, not a missing feature.

§ABI shape

Every exported verb takes a (ptr, len) pair into linear memory and returns a pointer; the byte length of that result is read back with result_len(), exactly as znippy’s loader does. Payloads are length-prefixed (codec) — never JSON, whose separators and missing null representation are a corruption source rather than a parser bug.

Re-exports§

pub use codec::DecodeError;
pub use codec::Reader;
pub use codec::Writer;
pub use wire::WireArtifactEntry;
pub use wire::WireArtifactId;
pub use wire::WireHttpRequest;
pub use wire::WireHttpResponse;
pub use wire::WireListRequest;
pub use wire::WireManifest;
pub use wire::WirePutRequest;
pub use wire::ABI_VERSION;

Modules§

codec
The byte codec both surfaces share. One writer, one reader — the host links these functions and so does every guest, so a field cannot be encoded one way and decoded another (LAW 5, by construction rather than by a guard watching two copies agree).
exports
Exports a .wasm package handler must provide. The host names the missing one when a module falls short, so a half-built module is a loud load failure rather than a backend that answers None to everything.
guest
The guest side of the ABI, written once and generated per module.
imports
Host functions a module imports from env to reach storage. The host owns every byte; the module owns only the format logic.
wire
The values that cross the boundary, and their encoders.

Macros§

export_package_handler
Generate the eight ABI exports for a crate::PackageHandler.

Traits§

BlobStore
The bytes behind a handler. Implemented natively over a directory and in wasm over the imports host functions — the handler logic above it never learns which.
PackageHandler
The format logic of one package handler. Everything here is pure over a BlobStore, so the single implementation compiles for the host and for wasm32-unknown-unknown unchanged. A handler crate implements this once; the wasm crate beside it adds no logic, only the ABI shim.