Skip to content

System architecture

foxctl is a Go-based multi-agent framework, CLI, and control plane with a hybrid runtime. This page describes the major subsystems, how they interact, and where code lives in the repo.

flowchart TD
CLI[cmd/foxctl]
Web[internal/interfaces/web + API handlers]
Legacy[legacy agent runtime\ninternal/agent + internal/agent/daemon]
V2[v2 services/runtime\ninternal/v2/services + internal/v2/runtime/*]
Jido[Jido bridge\ninternal/v2/adapters/jido]
Stores[storage + Turso projections/CAS]
Context[companion + contextbuilder]
Indexing[indexing/retrieval]
Obs[observability + hooks]
CLI --> Legacy
CLI --> V2
CLI --> Web
Web --> V2
Web --> Legacy
Legacy --> Stores
V2 --> Jido
V2 --> Stores
V2 --> Context
Context --> Stores
V2 --> Indexing
Legacy --> Obs
V2 --> Obs
Jido --> Stores

The diagram above shows the current runtime topology. The CLI routes to both legacy and v2 surfaces. The web API sits in front of both. Storage, context, indexing, and observability are shared layers.

The CLI is the primary user surface. It parses commands, calls services, and prints envelopes or human-readable tables. Key entry points:

  • foxctl run <skill> — job-tracked skill execution
  • foxctl agent spawn — create and start a new agent
  • foxctl index repo build — build the repo graph index
  • foxctl web serve — start the HTTP API server

See the CLI reference for the full command surface.

The legacy runtime is mailbox-driven and still handles some CLI flows:

PackageResponsibility
internal/agentMailbox-driven sessions, overseer hierarchy
internal/agent/daemonClassic foreground daemon loop and mailbox polling
internal/runtime/execution/agentmanagerLegacy spawn/kill and runtime management fallback

These packages are being incrementally replaced by the v2 stack. See Agent lifecycle and Agent orchestration for how agents work today.

The v2 stack is the newer agent/runtime/orchestration lane:

PackageResponsibility
internal/v2/core/*Typed v2 commands and command routing
internal/v2/servicesRun, ask, spawn, list, kill services
internal/v2/runtime/runnerTurn execution and worker lifecycle
internal/v2/runtime/orchestrationEvent-sourced orchestration and staged dispatch
internal/v2/runtime/supervisorWorker supervision and health tracking
internal/v2/runtime/contextbuilderLayered context assembly for sessions

See Runtime architecture for how v2 and Jido fit together.

internal/v2/adapters/jido provides a JSON-RPC bridge to the Jido runtime. It is the optional runtime adapter for BEAM/OTP-backed orchestration. Jido owns agent process lifecycle and parent/child trees; foxctl owns tool semantics, memory, and control-plane state.

PackageResponsibility
internal/context/companionConversation memory and live context assembly
internal/v2/runtime/contextbuilderLayered context building for active sessions
internal/v2/runtime/enrichersAsync derived artifacts and companion bridge integration

See ACA context architecture for the broader context model.

PackageResponsibility
internal/context/memorycoreTyped, provenance-bearing memory records
internal/storage/memoryDurable memory persistence
skills/memory_queryMemory retrieval skill
skills/memory_curator_reportCurator reports and gated apply workflows

See Memory and continuity for how memory works.

PackageResponsibility
internal/storage/*Durable stores, CAS, mailbox/task/session persistence
internal/v2/adapters/turso/*v2 events and projections backed by Turso/libsql

Supported backends include SQLite, libsql, Turso, and PostgreSQL. See Storage and CAS.

The intelligence family spans six sub-concerns that should be routed separately:

SubfamilyPackagesRole
Ingest/buildersintelligence/indexing, intelligence/searchindexPost-review indexing pipelines, persisted retrieval-document model
Search/query/recallintelligence/retrieval, intelligence/retrieval/v2, intelligence/repoquery, intelligence/searchquery, intelligence/searchrankFused search, repo-index recall, query planning, cross-source ranking
Evidence gatheringintelligence/codecontext, intelligence/codemap/contextSnippet extraction and code-evidence collection
Synthesis/planningintelligence/codemap, intelligence/refactor, intelligence/analysis/tasksgraphSemantic codemaps, hotspot analysis, graph-derived planning
Oversightintelligence/analysis/overseerPost-review coordination, task scoring, indexer fanout
Verificationintelligence/verificationClaim-checking and verification-specific pipelines

Placement rules for new intelligence code:

  • Indexing and persisted index work belongs in intelligence/indexing or intelligence/searchindex, not under generic retrieval
  • Query parsing, repo-index recall, lexical/vector fusion belong in the search/query/recall slice
  • Snippet and file evidence collection belongs in evidence-gathering packages, not in synthesis packages
  • Synthesized codemaps and planning artifacts belong in synthesis packages after evidence has already been gathered

See Search and index and Repoindex and DAG grep.

PackageResponsibility
internal/interfaces/webHTTP API server and handlers
internal/interfaces/chatadapterChat platform integrations
internal/interfaces/openapiOpenAPI skill and plugin surfaces
internal/providersLLM provider integrations

See API server and Chat platforms.

PackageResponsibility
internal/runtime/observabilityTrace and event propagation
internal/runtime/hooksHook execution
internal/context/updaterProactive context surfacing

See Observability and Hooks.

The console family splits into session primitives and application/runtime behavior:

PackageResponsibility
internal/consoleConsole session contract, lifecycle, persistence primitives, and correlation tracking
internal/console/appConsole application runtime, LLM runner, stream parsing, and session-turn execution
internal/interfaces/web/consolewsWebSocket transport surface for console sessions

The runtime-terminal family handles pane allocation and mux backends:

PackageResponsibility
internal/runtime/terminal/agentpanePane-scoped PTY wrappers, submit-mode rendering, and mux-agnostic pane allocation
internal/runtime/terminal/tmuxbridgetmux pane/session creation, labeling, and tmux-specific presentation
internal/runtime/terminal/zellijbridgeZellij pane creation and submit behavior
internal/interfaces/gateway/webtermBrowser-facing WebSocket-to-PTY terminal room
internal/interfaces/gateway/sshtermSSH terminal access for the same room model, authenticated through Tailscale WhoIs

The key ownership rule is: agentpane owns runtime-terminal lifecycle (mux-neutral), while webterm and sshterm are interface entrypoints into that runtime, not terminal lifecycle owners.

The context family mixes five distinct concerns that should be routed separately:

SubfamilyPackagesRole
Control planecontext/contextplaneACA-style orientation, proposals, promotion helpers, and task history
Assemblycontext/companionLive prompt/context assembly and conversation memory coordination
Historycontext/transcriptpipeline, context/contextplane/taskhistory, storage/transcriptcacheTranscript import, claim derivation, task-oriented history views, and transcript artifact caching
Runtime helperscontext/sessionkit, context/updater, storage/contextbuffer, storage/contextvarSession utilities, proactive context surfacing, injection buffering, and context-variable persistence
Knowledgecontext/knowledge, storage/knowledgeDurable knowledge registries, sync, and embedded knowledge packs

New context work should not route into internal/v2 — the context family is a peer family, not legacy runtime scaffolding.

PackageResponsibility
internal/domainCore domain contracts and value types
internal/platformCross-cutting platform/config/runtime utilities
internal/protocolWire/envelope/protocol helpers
internal/toolingRuntime-neutral tooling and eval harnesses
PackageResponsibility
internal/domain/identityCanonical principal model and context propagation
internal/authCasbin authorization for HTTP and hook/tool resources
internal/auth/brokerOAuth token lifecycle and encrypted persistence

See Auth and identity for the full identity propagation contract.

Before introducing or extending internal/*, read the package topology map and place code by package family. internal/v2/* is not the default destination for new context, retrieval, storage, or interface code.

Stable families:

FamilyPurposeExamples
internal/domainCore domain contractsdomain/agent, domain/envelope, domain/policy, domain/identity
internal/platformPlatform utilitiesplatform/config, platform/workspace
internal/storageDurable state and CASstorage/*
internal/authAuthn/authz and OAuthauth/*, auth/broker
internal/consoleConsole session primitivesconsole, console/app
internal/runtimeExecution, terminal, hooks, observabilityruntime/engine, runtime/terminal, runtime/hooks, runtime/observability
internal/v2Agent/runtime/orchestrationv2/core, v2/services, v2/runtime, v2/adapters
internal/context/*Context/memory/historycontext/companion, context/contextplane, context/transcriptpipeline
internal/intelligence/*Retrieval and code intelligenceintelligence/indexing, intelligence/retrieval, intelligence/refactor
internal/interfaces/*API and transport layersinterfaces/web, interfaces/chatadapter, interfaces/gateway
internal/toolingStandalone tools and evalstooling/evals, tooling/skillrun, tooling/tools
internal/providersExternal provider integrationsproviders/*, providers/llmcompat

These package families are peers, not legacy scaffolding to be absorbed into internal/v2:

FamilyReason
internal/storage/*Shared persistence layer used by both legacy and v2 paths
internal/context/*Context/memory/history plane, not old runtime scaffolding
internal/intelligence/*Intelligence and retrieval plane
internal/interfaces/*Interface and transport layers
internal/domain, internal/platform, internal/protocolFoundations, not generation-specific runtime code
Legacy packageRoleV2 replacementStatus
internal/agent/runtimeMailbox-driven agent sessionsinternal/v2/runtime/* + internal/v2/services/*Active replacement
internal/agent/daemonClassic foreground daemon loopinternal/v2/runtime/{runner,orchestration,supervisor}Partial replacement
internal/runtime/execution/agentmanagerLegacy spawn/kill fallbackinternal/v2/services/{spawn,kill,list,run}Partial replacement
internal/agent/toolsRuntime-facing tools for classic agent stackShould delegate to internal/v2/servicesPartial replacement

Do not do a single large package rename. Use this order:

  1. Keep internal/v2 explicitly scoped to agent/runtime/orchestration work
  2. Stop adding new top-level roots unless they clearly represent a stable family
  3. Move packages only when already touching that area for real work
  4. Consolidate one family at a time
  5. Prefer documenting the target family first, then moving code incrementally

For the full package topology with migration epic details, see the canonical source at docs/architecture/package-topology.md.

InvariantWhy it matters
Envelope contract stability (meta.* and shape)Prevents breakage across hooks, UI, and golden tests
Append-first v2 events plus projection readsKeeps orchestration and ask state replayable
Jido bridge remains adapter-scopedPrevents runtime transport concerns from leaking into v2 core contracts
Workspace-constrained IOPrevents path escapes and unsafe file access
CAS-backed large outputsAvoids oversized envelopes and preserves replayability
Context propagation (context.Context)Enables cancellation and timeouts across both legacy and v2 stacks