Domain Model
Hovel revolves around these concepts:
Workspace
Operation
Target
Fact
Provider
Mesh
MeshNode
MeshLink
MeshRoute
MeshDestination
MeshTask
MeshListener
Beacon
Trigger
Service
Module
Payload
PayloadStamp
InstalledPayload
Artifact
Chain
Phase
Step
Throw
Run
Job
Event
Finding
Transport
Session
Listener
Credential
Capability
CleanupHandle
Workspace
A workspace is the local project/database context for operations, targets, throws, modules, services, structured events, artifacts, installed payload inventory, and provider state.
Current workspace storage is SQLite plus workspace-owned files for artifacts, logs, modules, throws, services, and provider-owned state.
Workspace data:
- Configuration.
- Registry metadata.
- Throw history.
- Target inventory.
- Facts.
- Artifacts.
- Installed payload records and state-change history.
- Structured events.
- Provider cache.
- Logs rendered from structured events.
- Listener and session state.
Operation
An operation is the top-level operator context. It is the thing a red teamer picks up, puts down, and comes back to later. Operations own chains, chain log topics, target inventory, target sets/groups, throws, artifacts, and the operator-facing transcript rendered from structured events.
op is the primary command spelling. operation may remain as a readable alias, but product copy and examples should prefer op.
Operation responsibilities:
- Name and describe the active work context.
- Own one or more chains.
- Own target inventory, per-target configuration, and target sets/groups shared by those chains.
- Keep each client attachment's active chain separate from the shared chain state.
- Preserve chain logs and throw records by operation.
- Keep findings, artifacts, and structured events tied to the operation that produced them.
An attached client can work inside one operation at a time. The operation state is daemon-owned shared state; the active chain selected by the implemented CLI or by future TUI, REST, or MCP clients is client-local attachment state.
Target
A target represents something the operator is authorized to test.
id: target-uuid
name: router-01
addresses:
- 10.41.32.2
ports:
- 22/tcp
labels:
env: lab
owner: will
facts: []
Fact
Facts are typed observations about a target.
type: os.name
value: linux
source: ssh-survey
confidence: 0.95
Facts must be mergeable, source-attributed, and queryable.
Module
A module is a typed user-facing unit of bounded functionality.
Initial module types:
survey
exploit
payload_provider
The type set must remain expandable after the first mocked UI stage.
A module may live in-tree or out-of-tree, but production execution should still go through ordinary module descriptors, planning, validation, logging, event, artifact, and runtime contracts. A reusable composition of modules is a chain, not another module type.
Every module may declare required and optional typed configuration keys. Configuration scopes are chain for global chain configuration and target for per-target configuration.
Service
A service is a long-lived or reusable capability managed by Hovel.
Current source has service descriptor value objects and validation, but not a general service discovery, launch, health-check, or stop runtime. Implemented provider behavior is module-backed today; the service model below is the target shape for longer-lived capabilities.
Service examples:
- Payload build service.
- PIC blob generation service.
- Listening post.
- Session broker.
- Credential broker.
- Artifact HTTP server.
- Callback listener.
- Target inventory sync service.
The key distinction:
Module: run this bounded operation.
Service: start this capability, keep it alive, expose typed operations, manage lifecycle.
Provider
A provider supplies typed resources to modules, chains, and services.
Provider types in the product model:
PayloadProvider
ArtifactProvider
FactProvider
CredentialProvider
ListenerProvider
SessionProvider
MeshProvider
The first general provider implementation should only require PayloadProvider and ArtifactProvider. Mesh providers are SDK-level contracts for node operations and may be module-backed before a general service lifecycle exists. A MeshListener is a Mesh-specific listening-post resource and does not introduce the broader ListenerProvider abstraction shown above. Facts may be plain module outputs at first, and general credential, listener, and session providers should wait until service lifecycle, policy review, and artifact/event capture are stable.
Future providers may include build, encoder, shellcode, stager, and implant providers.
Payload generation is outside the Hovel core. Hovel resolves payload bytes through provider interfaces, tags them with declared metadata, records hashes and artifacts, then passes the bytes to the next module in the chain. When a payload provider or installing module reports a successful install, Hovel core validates the explicit install descriptor and persists the installed payload record in the workspace database.
Mesh
A Mesh is a provider-owned node operations plane that can be one node, a chain, or a routed tree/graph of nodes.
Mesh vocabulary:
MeshNode addressable participant such as controller, relay, agent, or implant
MeshLink communication edge between nodes
MeshRoute ordered path across nodes and links
MeshDestination host or service reachable through a node or route
MeshTask node, route, or destination operation such as survey, upload, execute, command, load, or stream
MeshListener provider-reported listening post, separate from provider control and daemon local bridges
Beacon node liveness or status signal
Trigger provider-declared condition that can cause mesh work or state changes
Transport endpoints and tunnels are narrower byte-movement concepts. A Mesh may expose a stream that Hovel bridges to a local IP and port, but the Mesh also owns listening posts, tasking, surveys, triggers, and beacons. A Mesh Listener may be embedded or separately deployed and provider- or externally managed; stable caller-selected ids and daemon lifecycle methods let future external control planes manage it without speaking the module protocol. Destination-scoped Mesh requests identify the pivot with nodeId or route and the reached service with destinationHost, destinationPort, and protocol.
Payload
A payload is provider-described capability that can produce tagged bytes. In the target provider model, available payloads are discovered live from provider contracts rather than cached as durable workspace state. In the current operator command path, payloads available lists configured payload-provider modules; Go provider modules already expose the richer list_payloads RPC method for provider integrations and contract tests.
Initial payload kinds:
shell
shellcode
library
executable
Payload metadata:
id: payload-uuid
name: sshplant-x86_64-linux
kind: elf
arch: x86_64
os: linux
format: bytes
entrypoint: default
size: 123456
sha256: abc123
capabilities:
- interactive_session
- stdio_transport
constraints:
requiresExec: true
writesDisk: false
requiresPython: false
Generated payload bytes become stamped payload artifacts. A stamp is Hovel's durable handle for a generated artifact set: it links provider identity, payload metadata, generation inputs, target, transport, artifact hashes, and later install records. A stamped artifact is not an installed payload until a provider or installing module explicitly reports a successful target-side install.
Installed Payload
An installed payload is a durable workspace record for one target-side deployed payload agent, executable, service, or listener-capable instance. It is the object operators inspect, reconnect to, refresh, clean up, or manually mark removed. It is distinct from generated payload artifacts and from live sessions.
handle: p1
id: installed-payload-uuid
provider: squatter
payloadId: squatter/windows/x86/xp-sp3/tcp-bind/pe-exe
payloadVersion: 0.1.0
state: installed
target: 10.0.0.42
targetId: t1
transport: tcp-bind
endpoint: 10.0.0.42:9101
stampId: payload-stamp-uuid
provenance:
operationId: op-uuid
chainId: c1
targetId: t1
throwId: throw-uuid
runId: run-uuid
artifactIds: [artifact-uuid]
reconnect:
providerId: squatter
schema: squatter.reconnect.tcp_bind
schemaVersion: "1"
descriptor: { "host": "10.0.0.42", "port": 9101 }
Operator-facing handles use short names such as p1 and
p2, backed by stable internal identifiers. Records are global
workspace inventory with operation, chain, target, throw, run, and artifact
provenance. They remain discoverable after switching chains or restarting
Hovel.
Installed payload states for v1:
- installed
- connected
- unreachable
- removed
Listing installed payloads reads last-known state from SQLite and does not probe targets by default. Reconnect or refresh actions ask the current provider to validate the stored reconnect descriptor and may update state. If the provider is missing or rejects the descriptor version, the record remains visible with reconnect marked unavailable.
Capability
A capability is typed, durable chain state that one step produces and later steps may consume. Hovel owns the core capability schemas and validates module outputs against them. Module contracts match by concrete type, required attributes, and allowed states.
RemoteExecutionCapability
CredentialCapability
PayloadArtifact
PayloadInstance
TransportEndpoint
SessionRef
CleanupHandle
Capabilities have stable ids, schema versions, producer step ids, current state, attributes, optional namespaced extensions, and append-only event history. Evidence remains separate from capability state.
Payload Instance Capability
A PayloadInstance capability is runtime chain state produced by an
install step. When it contains an explicit installed-payload descriptor, Hovel
may persist or update an InstalledPayload inventory record. The
capability itself is still scoped to the throw and capability graph; the
installed payload record is the durable cross-chain inventory object.
id: cap_payload_instance_q8m2v4
provider: squatter
target: 10.0.0.42
transport: smb-named-pipe
endpoint: cap_endpoint_smb_pipe_c4a7nx
state: installed
installedPayload: p1
cleanupHandles:
- cap_cleanup_74m2wq
Cleanup Handle
A cleanup handle is a first-class capability for target or provider-side state
that can be removed or verified later. It records the owner module, cleanup
step id, required inputs, current cleanup state, and evidence for each attempt.
Cleanup is retriable until it reaches removed,
cleanup_verified, or the operator marks it
abandoned.
Listener
A listener or listening post is a managed service that receives callbacks, brokers sessions, serves staged payloads, or controls post-execution sessions.
Lifecycle states:
- registered
- starting
- listening
- healthy
- degraded
- stopping
- stopped
- failed
Chain
A chain is an operation-owned workflow record with ordered steps. Product docs may group those steps into conceptual phases, but the current stored chain model is step-based. Chains are CRUD resources: operators can create, select, rename, inspect, list, and delete them through shared application services.
Chain definitions should be modular and loadable. They are collections of module references and chain-scoped configuration, not modules themselves.
Chains do not own target records. Targets are operation-owned assets, while chain records keep target bindings by target ID for default planning. A throw selects targets from --target, --target-set/--target-group, or the active chain's bindings. Chain records own ordered steps, chain-scoped configuration, target bindings, and logs; target config follows the target across chains in the same operation.
Chains also own their logging topic. The canonical topic shape is operation/<operation>/chain/<chain>/logs. A front end only renders logs for the chain selected by its attachment, while daemon-side storage and the structured logging rail keep those logs available for other clients attached to the same operation and chain.
Conceptual workflow phases:
- service_prepare
- survey
- prepare
- access
- deliver
- execute
- post
- collect
- cleanup
- service_cleanup
Throw
A throw is the operator-facing execution record for a chain or module against one or more targets. throw is both the verb and the singular command root used for listing and inspection.
Throw states:
- created
- planning
- ready
- throwing
- paused
- succeeded
- failed
- cancelled
- partial
- cleaning_up
Throws must keep the reviewed intent, inputs, targets, chain version, module versions, artifacts, structured event records, transcript rendering inputs, errors, and final result.
Run
A run is an internal runtime execution detail. Operator-facing commands, transcripts, records, and docs should say throw unless the code is describing a low-level module runtime API.
Runtime internals may reuse the throw state machine, but the durable product language remains operation, chain, step, throw, structured event, artifact, and finding.