HOVEL // modules

// developer entry point

Build a Module or Provider

Start with an ordinary module. Add provider, Mesh, credential-delivery, or listening-post hooks only when the capability owns that behavior. This page maps each integration shape to the implemented daemon and SDK surfaces.

Most modules need no provider or credential contract. A survey or exploit that reads explicit configuration, contacts a target, and returns outputs, findings, artifacts, or sessions implements only the base module lifecycle. Using TLS inside that operation does not by itself make the module a Hovel credential provider.

Choose the narrowest contract

What the capability owns Implement now Do not add
A survey, exploit, or tool invocation against one target The base Python HovelModule, Go Module, or Rust Module lifecycle Mesh or credential hooks merely because the tool uses a network, TLS, or target login
Planner-visible capability steps Python step.* overrides or Go StepProvider A Rust step provider: Rust does not dispatch step.* today
Payload discovery, generation, reconnect, cleanup, and chunking Go PayloadProvider; any language may still return installed-payload descriptors from normal execution A Python or Rust payload-provider RPC implementation: those SDKs do not dispatch that provider surface today
Provider-owned nodes, routes, beacons, tasks, streams, or listening posts mesh.describe plus only the optional Mesh operations actually supported No-op topology, task, listener, or stream methods added only to mimic a full provider
Consumption or transformation of Hovel-managed workspace PKI material credential.describe plus only the advertised runtime, files, encoding, or stamping hook A credential descriptor on an ordinary module that never receives assignment-bound material
An existing TCP or UDP client reused through a provider route A daemon OpenMeshBridge call and the language SDK's authenticated bridge dial helper Mesh-provider hooks in the consumer merely to understand provider topology
Build from the base contract outward
  1. base module
  2. one required extension
  3. daemon discovery and validation
  4. package and contract tests

The complete lifecycle, packaging format, test loop, and safety contract are in Module Development. The method-level message shapes are in the module wire protocol.

Credentials: ordinary input versus workspace PKI

Hovel uses the word credential for two different domains. A target account created or observed during an assessment is chain evidence represented by CredentialCapability. Workspace PKI is daemon-owned encrypted custody for certificate generations, keys, trust, revocation, assignments, and short-lived provider delivery. Never copy one model into the other. See target credential evidence, workspace PKI custody, and TLS and Workspace PKI.

Need Current path Implementation status
A module authenticates to a target with explicit operator or chain input Declare normal requirements or consume chain capabilities in the operation's own contract No workspace-PKI provider hook is required
A Mesh listener, task, stream, or bridge needs an active PKI assignment Advertise strict slots and runtime; the daemon resolves selectors and delivers material before the Mesh method in the same provider process Implemented through the privileged daemon API for direct certificate DER, public-key SPKI, and private-key PKCS #8 projections
A provider consumes invocation-scoped protected files Advertise files and implement credential.files The typed domain, SDK dispatch, validation, and bookkeeping exist; the current external Mesh selector does not initiate file delivery
A provider performs a bounded native encoding or stamps an artifact/deployment Advertise a matching encoding schema or stamping capability and implement credential.encode or credential.stamp The contracts and provider dispatch exist; the daemon OpenAPI currently exposes list/inspect bookkeeping, not a production initiation method
An ordinary non-Mesh execute call needs automatic workspace-PKI injection No general delivery path is wired to base module execution Future integration point; do not document or depend on implicit injection

A provider advertises only slots and modes it really consumes. In Go, implement CredentialDescriber and only the small execution interfaces needed. In Python and Rust, override only the corresponding methods; unsupported defaults fail explicitly. If the same descriptor appears in both credential.describe and MeshDescriptor.credentialDelivery, Hovel requires their canonical digests to match. Start with the Credential Provider Development guide, then use the daemon API chapter for the currently callable control-plane surface.

Consume a Mesh bridge as local TCP or UDP

Use a bridge when a working client already speaks to a host and port and the Mesh provider can preserve TCP byte-stream or UDP datagram semantics. The control plane chooses the provider, route or node, destination, and provider-defined remote protocol. The separate localNetwork field selects the daemon's tcp or udp loopback adapter. The consumer does not need provider topology types.

Use RunMeshTask instead when the provider owns a native survey, command, upload-and-execute, or loader operation. Use OpenMeshStream and session APIs for provider-defined flows that should not become a local socket. ICMP, raw IP, GRE, and custom protocols have no raw local adapter today. A provider may still expose one of those remote protocols through a local TCP byte stream or UDP datagram association when its stream contract defines that mapping.

POST /hovel.daemon.v1.DaemonService/OpenMeshBridge
Content-Type: application/json

{
  "moduleId": "lab-relay-mesh@v0.1.0",
  "localNetwork": "tcp",
  "request": {
    "runId": "run-lab-1",
    "nodeId": "relay-west",
    "destinationHost": "192.0.2.25",
    "destinationPort": 443,
    "protocol": "tcp"
  }
}
{
  "operationId": "mesh-op-43",
  "sessionId": "run-lab-1-session-1",
  "localHost": "127.0.0.1",
  "localPort": 43117,
  "localNetwork": "tcp",
  "localAddress": "127.0.0.1:43117",
  "capability": "uL8wGvVukVynTng2uR6U4oqxm8v0cMxfR7VaJkW_1eM"
}
The bridge capability is a bearer secret. Keep it in memory and out of logs, results, transcripts, and durable config. The SDK helper sends it as the first newline-terminated TCP preface or first complete UDP datagram, then returns a normal connection. A raw connection that omits the preface is rejected.

Use Go's NewMeshBridgeEndpoint and DialMeshBridge, Python's MeshBridgeEndpoint and connect_mesh_bridge, or Rust's MeshBridgeEndpoint and TCP/UDP connect helpers. Open one bridge per independent TCP connection or UDP peer association and close it through CloseMeshBridge when the consumer is finished.

Separate a listening post from the provider adapter

A Mesh listener is the provider-reported listening post or rendezvous resource. The module process handling mesh.listener.start is an adapter call: Hovel shuts it down after the response. Therefore a provider-managed listener that must survive the call needs durable provider-owned runtime state outside that short-lived adapter process.

Concern Owner Rule
Provider control adapter Module process Validates and forwards list/start/stop calls; it is not automatically a long-lived listener host
Listening-post data plane Provider-owned embedded deployment, separate service, or external manager Must preserve callbacks and state for the advertised lifecycle
Lifecycle identity Caller and provider Start and stop are idempotent for the stable caller-selected listenerId; daemon-wide identity is module ID plus listener ID
Configuration and credentials Provider operation input Write-only: never echo them in listener read models, logs, errors, or daemon operation records

Choose deployment: separate when the listening post needs an independent process, host, update cadence, failure boundary, or scale profile. Choose management: external when Hovel should report but not control its lifecycle. An embedded listener may share a durable provider deployment, but it still cannot depend on one start-RPC subprocess remaining alive. A routed stream is different: Hovel keeps the provider process while its returned session is active. See Developing Mesh Providers.

Language starting points

Language Base module Provider and bridge entry points Current boundary
Python HovelModule, run, serve Override supported Mesh/credential hooks; connect_mesh_bridge; API reference Supports step.*; no payload-provider RPC dispatch
Go Module, Run, Serve Small optional Mesh/credential interfaces; DialMeshBridge; API reference Most complete provider surface, including steps and payload providers
Rust Module, run, serve Override supported trait methods; TCP/UDP bridge helpers; API reference Mesh and credential dispatch are implemented; no step.* or payload-provider RPC dispatch

Completion checklist

  • The module starts with valid, stable metadata and explicit chain/target requirements.
  • handshake, schema, and every advertised describe method are deterministic, offline, and side-effect free.
  • Stdout contains only framed module RPC; progress uses structured SDK logging.
  • Only implemented provider methods are advertised, and unsupported optional methods fail explicitly.
  • Workspace-PKI slots constrain purpose, consumer, profile, projection, form, size, and private-material policy; secret bytes, paths, capabilities, and references never enter logs or durable results.
  • Mesh bridge consumers use the authenticated SDK helper and preserve TCP stream or UDP datagram semantics.
  • Long-lived listening posts survive individual adapter calls; stable listener IDs make start/stop retries converge.
  • Outputs, findings, artifacts, sessions, capabilities, and installed-payload descriptors use their intended ownership boundaries.
  • Dangerous behavior is tagged and remains behind Hovel's persisted-plan, confirmation, and --allow-dangerous safeguards.
  • Direct logic tests and framed-RPC contract tests cover success, malformed input, unsupported hooks, failures, cleanup, and secret non-disclosure.
  • The package manifest selects a real launcher, and hovel module check validates discovery without executing the module.
  • In-repository work finishes through the strongest available Task-backed slice check.

Continue with the detailed guides