HOVEL // chapter 19
Chapter 19 Part 04 / Module Development

// portable post-exploitation interface

Payload Provider Contract

hovel.payload/v1 is the language-neutral contract for describing, selecting, generating, transporting, and managing typed payload artifacts. It is additive: modules written for Hovel's existing lifecycle and legacy Go payload-provider interface remain valid while the 1.0 contract is completed.

Compatibility is the first rule. Do not rename or remove an existing RPC method, field, enum value, lifecycle behavior, or SDK entry point. Tightening validation must not reject a module that Hovel accepts today. New behavior belongs in optional interfaces, new fields with safe defaults, or a separately versioned schema.

Contract status

SurfaceImplemented nowStill to integrate
Portable schemahovel.payload/v1 descriptors, variants, target/load constraints, artifacts, and the closed content union.Freeze golden cross-version fixtures as the release contract evolves toward 1.0.
Provider SDKsGo, Python, and Rust dispatch the same payload.* methods and expose equivalent authoring types.Keep language mappings in parity and publish compatibility evidence for every supported version.
Daemon artifactsThe daemon owns an immutable, content-addressed artifact store and validates payload schema, digests, sizes, and content choice.Wire every operator workflow to capability-to-artifact resolution and frozen confirmation values.
Post-connect lifecycleThe SDK contract models listener preparation, connect, inspect, cleanup, and existing installed-payload records.Replace example-specific orchestration and route real reconnect, inspect, cleanup, and command calls through the daemon.

Provider descriptor

payload.describe returns one structured PayloadProviderDescriptor. Its schemaVersion is hovel.payload/v1; providerId and version identify the implementation; operations advertises only supported optional behavior; and payloads contains selectable variants. Provider-specific data belongs under namespaced extensions keys.

Each PayloadVariant has a stable id, display name, version, kind, format, capabilities, and two compatibility records. target declares operating system, architecture, and optional ABI, endianness, and minimum OS. load declares the execution model and optional entry contract, relocation model, and dependencies. A provider must not claim a target/load combination it cannot produce.

Typed artifact content

A PayloadArtifactV1 binds a named role and exact variant to a media type, byte size, SHA-256 digest, and content source. Content is a closed tagged union: exactly one of the following is present.

Content memberUseOwnership
inlineSmall encoded content returned with the response.The daemon decodes and validates it before recording.
artifactAn immutable content-addressed object referenced by id.The daemon owns storage, identity, permissions, and retrieval.
streamA provider-owned handle for bounded chunked transfer.The provider serves bytes; the daemon validates the completed artifact.
Artifact storage is intentionally plaintext. Hovel uses owner-only filesystem permissions, content addressing, and integrity checks; it does not add at-rest encryption to payload artifacts. Workspace PKI private key custody is a separate security boundary described in TLS and Workspace PKI.

Optional operations

Advertised operationRPC methodPurpose
resolvepayload.resolveSelect one compatible variant without producing bytes.
generatepayload.generateProduce one validated PayloadArtifactV1.
read-artifactpayload.artifact.readRead bounded chunks from provider-owned content.
prepare-listenerpayload.listener.prepareCreate provider-owned listener state before connection.
connectpayload.connectTurn an installed-payload reconnect record into a session.
inspectpayload.inspectRefresh provider-owned installed state without inventing daemon state.
cleanuppayload.cleanupRemove or deactivate an installed payload using its cleanup descriptor.
commandsExisting command surfacesDescribe and run typed provider-owned post-connect actions.

Unsupported operations fail explicitly. Providers do not implement no-op methods merely to look complete, and callers do not infer support from module names. Squatter and Picblobs are examples, not protocol switches.

Equivalent authoring surfaces

SDKv1 shapeCompatibility path
GoImplement PayloadDescriber and only the optional operation interfaces advertised by the descriptor.The monolithic PayloadProvider interface and its legacy RPC methods remain supported.
PythonReturn the frozen dataclass values from the matching HovelModule payload methods.Base lifecycle modules and installed-payload results retain their existing shape.
RustReturn the equivalent structs from optional Module trait methods.Existing lifecycle defaults continue to fail unsupported calls explicitly.

See the Go, Python, and Rust chapters for language syntax. The module wire protocol is authoritative for JSON-RPC envelopes; generated API references are authoritative for exact symbols and signatures.

Proving compatibility

The SDK report requires 100% branch coverage for Go, Python, and Rust. That is necessary but not sufficient: branch coverage proves exercised implementation, not wire compatibility. The 1.0 gate should also run frozen request/response fixtures against current and previous SDK releases, round-trip every union and optional field in all three languages, launch legacy modules unchanged, and compare accepted/rejected cases across daemon versions. Publish those matrices beside the existing coverage evidence in the site test report.

  • Keep golden JSON fixtures for minimal, complete, unknown-extension, and malformed descriptors and artifacts.
  • Test every optional operation independently and in combinations; omission must remain valid.
  • Run legacy Go providers and ordinary Go, Python, and Rust modules without recompiling their source.
  • Ratchet semantic compatibility separately from line and branch coverage.