A Graph, Not a Chat

Strix's agent architecture answers a problem that appears as soon as one security task becomes large: how can several autonomous workers investigate in parallel without turning the scan into several incompatible stories? This chapter focuses on the root agent, its specialist children, and the shared state that connects them. The useful judgment is not whether Strix can create many agents; it is whether each agent has a bounded responsibility, an addressable history, and a way to return evidence to a coordinator that still understands the target boundary.

A single model conversation is a poor representation of a penetration test. It mixes planning, reconnaissance, payload selection, interpretation, and reporting in one stream. When the model loses context or changes direction, it becomes difficult to tell which surface it covered, which hypothesis it abandoned, and which conclusion someone else can rely on. Strix treats the scan as a graph of work instead: nodes have identities and statuses, edges express parentage and messages, and shared stores preserve the facts that must outlive any one turn.

The root coordinates; specialists investigate

The root agent has an intentionally unusual role. Its system prompt says that it is responsible for orchestration rather than hands-on testing. It reads the verified scope, decomposes the target, creates focused subagents, monitors progress, maintains shared state, and eventually compiles the report. It is not supposed to run scanners, crawl endpoints, or send injection payloads itself. Child agents carry out those actions under specific assignments.

This division is a control choice, not only a performance trick. If the same model both decides how to divide the work and performs every test, there is no clean boundary between coordination and investigation. A root that delegates can ask one child to map an application, another to analyze access control, and another to validate a particular candidate. Each assignment can name its target and expected result. The root can then compare reports rather than trying to reconstruct what happened from a long stream of mixed intentions.

A child is created with a name, a concrete task, an optional instruction to inherit the parent's recent context, and a small set of skills. Inherited context gives the specialist the background needed to avoid rediscovering the scope and known observations, but the child receives its own identity and task. The system explicitly tells it not to continue the parent's work wholesale. This is an important distinction: shared background reduces duplication; separate responsibility prevents every worker from becoming a copy of the root.

Skills refine competence without creating a new runtime. Strix packages knowledge about vulnerability classes, frameworks, protocols, technologies, reconnaissance, cloud environments, and testing tools. The prompt renderer adds the requested skill content to the common agent instructions, then overlays run-mode guidance, browser and Python knowledge, counterevidence and severity discipline, root coordination, and white-box guidance when applicable. A specialist can therefore reason deeply about one area while using the same shell, browser, proxy, reporting, coverage, and lifecycle interfaces as another specialist.

The distinction between competence and authority is easy to miss. Loading an authorization-testing skill changes what an agent knows to look for; it does not, by itself, grant a new credential or expand the authorized target list. Conversely, a custom skill directory can shadow packaged skills, so changing knowledge content is also a trust decision. The loader validates names, rejects ambiguous selections, and limits the number of skills per agent, which keeps specialization legible. It does not prove that every custom skill is benign. A skill is behavior-shaping software in a broader sense, even when it is stored as Markdown.

The factory gives every agent a common tool surface and then appends the role-specific lifecycle function. Reporting, proxy, coverage, threat-model, graph, filesystem, and shell tools are part of the same environment, while the root receives finish_scan and a child receives agent_finish. Tool results are bounded before they enter history, and tool schemas can be adapted for different model routes. This shared contract matters because coordination should change who does the work, not silently change how a finding is stored or how a tool failure is interpreted.

The graph also gives delegation a shape. A child can create a more focused descendant when its own assignment reveals a distinct validation problem, but the parent remains responsible for knowing that work exists and for accounting for its result. The tree is therefore hierarchical without requiring every decision to travel synchronously through the root. A specialist can continue testing while another specialist works, then hand back a completion message when its own boundary is closed. The parent can use that message to decide whether to pursue an open item, stop redundant work, or incorporate a filed report into the overall assessment.

This structure is especially important when two vulnerability classes look similar from a distance. “Authentication testing” is too broad to tell a child which surface, role, or expected outcome it owns. A focused task can instead ask for a particular login flow, authorization layer, or object boundary. The graph tools' guidance favors checking the existing tree before spawning another specialist, because duplicate agents do not automatically create independent evidence. They can repeat the same requests, compete for shared state, or produce reports that need to be reconciled. Specialization is valuable only when the assigned boundaries are distinct enough that the results can be compared.

The lifecycle boundary makes the difference between work in progress and completed work visible. A child that reaches a conclusion calls its completion function; plain prose does not end its execution. If the child has a confirmed issue, the issue belongs in the shared report path before the completion message. If a service could not be started or a credential was unavailable, the child can return that gap rather than implying that its surface was clean. The root can then treat the graph state, the filed report, and the unresolved item as three related but different facts. That separation is what allows parallelism to improve throughput without turning the scan's final account into a guess.

classDiagram class RootAgent { orchestration scope decomposition finalization } class ChildAgent { focused testing task specialist skills completion report } class Coordinator { graph and statuses mailboxes resume snapshots } class SDKSession { agent history tool exchanges } class Skill { domain instructions validation guidance } class ThreatModel { trust boundaries attack surface severity context } class LifecycleTool { create_agent wait_for_agents agent_finish finish_scan } RootAgent --> ChildAgent : spawns RootAgent --> Coordinator : monitors ChildAgent --> Coordinator : reports and waits RootAgent --> SDKSession : owns ChildAgent --> SDKSession : owns Skill --> RootAgent : shapes instructions Skill --> ChildAgent : shapes instructions RootAgent --> ThreatModel : establishes ChildAgent --> ThreatModel : reads and amends LifecycleTool --> Coordinator : changes state

The graph is useful because it assigns ownership. The root owns the decision to delegate and the final account. A child owns the focused investigation and the obligation to close it honestly. The coordinator owns the graph facts: who exists, who is waiting, who failed, which messages are pending, and which parent must be notified. Each agent owns a separate SDK session, so its reasoning and tool exchanges can be resumed or compacted without pretending that all work happened in one undifferentiated conversation.

Shared state is the coordination substrate

Parallel work becomes accountable only when conclusions cross a deliberate interface. Strix's graph tools allow the root to inspect the tree, create an agent, send a focused message, wait for a child, or stop a subtree gracefully. A child ends by calling its completion tool, which marks it complete and sends a structured report to its parent. That report carries a summary, findings for narrative context, open items, recommendations, and the vulnerability reports the child actually filed. The filed reports are read from shared report state rather than trusted solely from the child's prose.

That last detail prevents a subtle accounting error. A child might say that it found an access-control weakness, but the claim is not a stored finding until it uses the reporting interface. A parent receiving a completion report can use the associated report information to inspect what was actually filed, while treating the narrative as an explanation of work and unresolved questions. This preserves a separation between “the agent said this” and “the scan's report store contains this.”

Messages are deliberately not the default communication channel for everything. The graph guidance recommends them for essential facts, focused questions, course corrections, and requests to wrap up. Children already inherit relevant history, and normal completion flows through agent_finish, so routine status pings would add noise and consume context. This is a small design choice with a large effect: coordination messages become meaningful events rather than another stream that must be mined for truth.

Waiting also has an explicit meaning. A child that has spawned workers and has nothing useful to do until their replies can call wait_for_agents. In an interactive run it parks in a waiting state and wakes when a message arrives. In a non-interactive run, the wait is bounded because a finished agent cannot be woken by a human. If no other agent is active, the tool reports that no message can arrive instead of allowing the root to sleep indefinitely. If an agent repeatedly ends a turn without a lifecycle call, the execution layer nudges it toward a tool call and eventually marks the stalled condition rather than treating plain text as completion.

The coordinator makes these transitions concrete. It tracks running, waiting, completed, stopped, failed, and crashed conditions, along with parent links, task metadata, pending counts, errors, and recovery counters. When a child fails or stalls, a notice can reach the parent so the parent does not wait for a report that will never come. When the root stops a subtree, cancellation is graceful: the current turn is allowed to finish before the child is settled. These behaviors are less glamorous than spawning, but they are where a parallel security workflow avoids silently dropping a surface.

The threat model is another shared state object, and it explains why graph consistency matters. Strix expects a scan-wide model of what the target is, who the relevant actors are, where trust boundaries sit, which inputs are attacker-controlled, what the attack surface contains, and what different severity levels mean in that target. Agents should read the model instead of independently inventing those assumptions. A source-aware agent and a deployment-testing agent can then use the same target identity even when one sees a checkout path and the other sees a URL.

The model is amendable because the first version is necessarily incomplete. A source-derived model may miss an operational role; a black-box model may infer a boundary that later testing disproves. An agent can append an attributed amendment when it finds a new endpoint, a reachable boundary, or a wrong severity assumption. Replacing the whole model is reserved for folding those amendments back into a new baseline. This is a useful pattern for autonomous systems: the shared model is a working hypothesis with a controlled correction path, not an authority that becomes true merely because it was written first.

Continuity extends the graph beyond the life of a process. Each agent uses an SDK-backed SQLite session, while an atomic coordinator snapshot preserves statuses, parent relationships, metadata, pending messages, and budget flags. A resumed run restores the root and eligible children, reattaches their sessions, and reconstructs the task topology. The point is not merely convenience. If a scan loses the agent that held the only evidence for a candidate, the resulting report can be incomplete while looking finished. Persistent identity makes unfinished work visible and recoverable.

Long conversations create a related risk. Strix can compact older history into a structured checkpoint, retaining recent turns and preserving tool-call/result pairing so the provider still receives valid input. The compaction instructions emphasize findings, credentials, endpoints, failed attempts, and next steps because those are the facts a security agent cannot safely forget. Yet the summary is still generated by a model and bounded by a token budget. It can lose nuance. The design reduces that risk; it does not turn summarization into lossless storage.

The execution loop handles several other continuity failures. It can seed initial input into the session before the first run cycle, bound image history, strip images after a provider rejects them, retry transient model or connection errors, and salvage streamed history after a crash. It distinguishes interactive from autonomous recovery: an interactive agent can wait for a human message, whereas a headless agent must either reach a lifecycle state or fail visibly. These conditions make the lifecycle part of the security result. A scan that stops because a provider failed or a child exhausted recovery is not equivalent to one in which every assigned surface reached a conclusion.

There is a trade-off in this architecture. More agents can increase coverage and reduce the cognitive load on any one context, but they also create more states, more handoffs, and more opportunities for duplicate or inconsistent work. Inheritance can reduce repeated reconnaissance, but a large inherited history can make the child harder to steer. Shared state improves convergence, but it becomes a coupling point that must be updated carefully. Strix addresses these tensions with focused tasks, skill limits, graph inspection, explicit parent reports, status notifications, and resumable sessions. Those are coordination controls, not evidence that parallelism is free.

A graph, then, is not simply a visual map in a dashboard. It is the structure that makes autonomous work addressable. The next question is what those addressable agents do with their tools: how source-aware hints, browser interactions, proxy captures, and recovery paths combine into a testing lifecycle in which observations can become proof rather than just more model activity.

References