// 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.
Contract status
| Surface | Implemented now | Still to integrate |
|---|---|---|
| Portable schema | hovel.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 SDKs | Go, 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 artifacts | The 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 lifecycle | The 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 member | Use | Ownership |
|---|---|---|
inline | Small encoded content returned with the response. | The daemon decodes and validates it before recording. |
artifact | An immutable content-addressed object referenced by id. | The daemon owns storage, identity, permissions, and retrieval. |
stream | A provider-owned handle for bounded chunked transfer. | The provider serves bytes; the daemon validates the completed artifact. |
Optional operations
| Advertised operation | RPC method | Purpose |
|---|---|---|
resolve | payload.resolve | Select one compatible variant without producing bytes. |
generate | payload.generate | Produce one validated PayloadArtifactV1. |
read-artifact | payload.artifact.read | Read bounded chunks from provider-owned content. |
prepare-listener | payload.listener.prepare | Create provider-owned listener state before connection. |
connect | payload.connect | Turn an installed-payload reconnect record into a session. |
inspect | payload.inspect | Refresh provider-owned installed state without inventing daemon state. |
cleanup | payload.cleanup | Remove or deactivate an installed payload using its cleanup descriptor. |
commands | Existing command surfaces | Describe 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
| SDK | v1 shape | Compatibility path |
|---|---|---|
| Go | Implement PayloadDescriber and only the optional operation interfaces advertised by the descriptor. | The monolithic PayloadProvider interface and its legacy RPC methods remain supported. |
| Python | Return the frozen dataclass values from the matching HovelModule payload methods. | Base lifecycle modules and installed-payload results retain their existing shape. |
| Rust | Return 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.