The blackboard is the single source of truth for a running campaign. It replaces the in-memory hand-offs of a sequential pipeline with a durable, queryable store that every agent reads from and writes to independently. Agents never call each other — they observe the board and react to what they find there. This is what makes the swarm stigmergic rather than orchestrated: coordination is a side-effect of writes, not a result of explicit messaging.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Armur-Ai/Pentest-Swarm-AI/llms.txt
Use this file to discover all available pages before exploring further.
Finding Types
Every item on the blackboard has aFindingType that tells agents what kind of information it represents. The type is also the primary key for trigger predicates — agents subscribe to one or more types and are dispatched only when a matching finding appears.
The full set of types, defined in internal/swarm/blackboard/types.go:
Recon Phase
TARGET_REGISTERED— the initial seed that kicks the swarm offSUBDOMAIN— a discovered subdomainHTTP_ENDPOINT— a reachable web endpointPORT_OPEN— an open TCP/UDP port on a hostSERVICE— a named service running on a portTECHNOLOGY— a detected technology and version
Classification Phase
CVE_MATCH— a confirmed CVE matched to a findingCVSS_SCORE— a scored vulnerabilityMISCONFIGURATION— a security misconfiguration without a CVESECRET_LEAK— a leaked credential or tokenPOTENTIAL_SQLI— a suspected SQL injection surface
Exploit Phase
EXPLOIT_CHAIN— a multi-step attack pathEXPLOIT_RESULT— the outcome of executing an attack stepSESSION— a captured session or credential
Meta & Artefacts
CAMPAIGN_COMPLETE— signals the swarm to wind downAGENT_ERROR— an error emitted by an agentNUCLEI_TEMPLATE_DRAFT— a generated Nuclei template draft for human review
The Finding Struct
Every item written to the blackboard is aFinding. The struct is defined in internal/swarm/blackboard/types.go:
Data field is a JSON-encoded payload whose shape is specific to the Type. The Pheromone field is computed at read time using exponential decay — it is never stored, only returned by Query and Subscribe.
Board Operations
TheBoard interface in internal/swarm/blackboard/board.go defines all operations agents can perform:
Predicate Filtering
ThePredicate struct is how agents express their trigger conditions. All conditions use AND semantics — a zero Predicate matches everything.
Write Options
When writing to the board, agents can customise pheromone behaviour with option functions:Pheromone values are clamped to
[0, 1] on every write, regardless of what the caller provides. This is a defence against MINJA-style pheromone-flood injection attacks where a malicious or buggy agent writes PheromoneBase=9999 to dominate trigger predicates. See internal/swarm/blackboard/injection_test.go for the relevant hardening tests.Memory vs. Postgres Backends
TheBoard interface has two implementations shipped in the codebase.
- MemoryBoard (default)
- PostgresBoard (beta)
MemoryBoard is an in-process implementation backed by a Go slice with a sync.RWMutex. It is used by default in both the sequential runner and the --swarm path.- Zero external dependencies — no database required
- Not durable across restarts; findings are lost when the process exits
Subscribedelivers findings synchronously via in-memory fan-out- Suitable for single-machine campaigns; used in all integration tests
- Pheromone decay computed with
math.Pow(0.5, age/halfLife) - Supports injectable fake clock (
now func() time.Time) for deterministic tests
How Agents Trigger
The scheduler ininternal/swarm/scheduler.go calls board.Subscribe(ctx, agent.Trigger()) for each registered agent at startup. The returned channel delivers every matching finding as it is written. The scheduler then dispatches each finding to agent.Handle(), bounded by agent.MaxConcurrency().
After Handle returns — whether it succeeded or failed — the scheduler calls board.CommitCursor() to record the last-seen finding ID. On restart (or after a crash), the agent resumes from its committed cursor, giving at-least-once delivery semantics. The SinceID field on the predicate is the mechanism: on startup, the scheduler reads the agent’s committed cursor and sets pred.SinceID before subscribing.
Finding Lifecycle
Seed
The engine calls
agents.Seed(), which writes a TARGET_REGISTERED finding to the board. This is the only finding that is not produced by an agent reacting to another finding.Recon writes
The Recon agent triggers on
TARGET_REGISTERED and fans out one finding per discovery: SUBDOMAIN, PORT_OPEN, HTTP_ENDPOINT, SERVICE, and TECHNOLOGY findings land on the board as the underlying tools return results.Classifier reads and writes
The Classifier agent triggers on
SUBDOMAIN, PORT_OPEN, HTTP_ENDPOINT, SERVICE, and TECHNOLOGY findings with pheromone ≥ 0.2. It sends each through the LLM and writes back CVE_MATCH or MISCONFIGURATION findings with pheromone and half-life tuned to the finding’s severity.Exploit reads and writes
The Exploit agent triggers on
CVE_MATCH with pheromone ≥ 0.5. It builds an attack chain and writes EXPLOIT_CHAIN and (on execution) EXPLOIT_RESULT findings. Successful exploit results are written with PheromoneBase: 1.0 to maximise their visibility to downstream agents.The default backend is MemoryBoard — no database is required to run a campaign. To enable the durable Postgres backend (beta), wire a
pgxpool.Pool and pass blackboard.NewPostgresBoard(pool) when constructing the runner. See internal/swarm/blackboard/postgres.go for the full implementation.