OpenSRE architecture
How the OpenSRE codebase is structured: the eight first-party packages, what each is responsible for, and which may depend on which. These dependency rules are CI-enforced (make check-imports), so they are real invariants rather than
aspirations.
The layer stack
The packages sit in five tiers. Higher tiers may import lower tiers; a lower tier may never import a higher one. Packages on the same tier are peers — the last column says whether peers may import each other.
The shortcut: dependencies point downward only. A surface can reach all the
way down;
config can reach nothing. The single deliberate exception is
core ⟷ infrastructure, a mutually-dependent pair by design (see below).
The arrows show edges between adjacent tiers to keep the diagram readable.
The actual rule is broader: a tier may import any tier below it, not only
the one directly beneath — so a surface may import config directly, and a
tool may import infrastructure. Refer to the “May import” column above for the
complete set of allowed edges.
The layers in detail
Tier 1 — surfaces and gateway
The entry points a human or an external system talks to. Nothing first-party
may import from here, so a surface can be added or removed without touching the
layers below it.
surfaces/— one folder per UI/client:surfaces/cli(the statelessopensre <command>runner),surfaces/interactive_shell(the stateful REPL), andsurfaces/sharedfor code two or more surfaces use. A surface owns its own I/O, prompts, and presentation, and composes lower layers to do the actual work. The two terminal surfaces are peers and do not import each other (tests/shared/test_surface_border.pypins both directions at zero); what both need lives insurfaces/shared—terminal/(output tracking, tables, prompts, banner, health and feedback rendering),llm_setup/,error_handling/— or a lower layer.surfaces/entrypoint.pyis theopensreconsole script: it hands the CLI aCliHost(how to open the shell, how to run the gateway attached) and hands the shell the CLI’s Click group for grounding, so composition happens in one place above both. Slack is not a surface: its inbound transport lives ingateway/transports/slack, outbound delivery inintegrations/slack.gateway/— the standalone messaging gateway for inbound chat platforms (gateway/transports/{telegram,slack,discord,buzz}), plus the FastAPIgateway/web/surface (health, alert intake) and the shared per-turn machinery ingateway/core/{session,storage,middleware}(middleware/holds the inbound-decision, identity, and approval steps every transport composes). A peer ofsurfaces, not a child.
Tier 2 — bootstrap
The composition root for process and harness wiring. Hosts (surfaces,
gateway) cannot import each other, and tools / integrations are peers that
must not import each other — yet registering harness adapters needs both
capability packages. bootstrap/ is the only package allowed to import tools
and integrations together.
Every entrypoint boots through configure_process(<profile>) from
bootstrap/process.py: a profile (CLI, gateway, web, scheduler worker,
embedded) selects which boot steps run, and the step order is fixed by this
package — profiles cannot invent a different sequence. Adapter and
scheduler-runner registration lives in bootstrap/adapters.py and nowhere
else; surfaces and gateway used to keep peer copies — do not reintroduce them.
Host-owned concerns stay out of this package: CLI/gateway logging, Rich product
ports, CLI’s update-tolerant Sentry init.
Tier 3 — tools and integrations
The capability layer — “do a thing against the outside world” — split by
responsibility:
integrations/— the boundary for user config and external clients: per-vendor config normalization, verification (verifier.py), API clients (client.py), the store/catalog that resolves credentials, and integration-local helpers. One folder per vendor (integrations/datadog,integrations/grafana,integrations/github, …) plus cross-cutting pieces likeintegrations/llm_cli.tools/— the vendor-neutral agent-callable boundary: the registry, interactive-shell tool framework,tools/system/for capabilities with no vendor in their domain purpose, and the narrowtools/cross_vendor/bucket for capabilities that genuinely span vendors. A tool tied to one vendor belongs underintegrations/<vendor>/tools/, beside that vendor’s client and configuration. See tool-placement-policy.md.
tools and integrations are peers, so neither may import the other in new
code. The exceptions in .importlinter.strict are tracked migration debt, not
precedent: they cover the existing cross-vendor tools and a few incidental
credential/client edges. Single-vendor tools avoid the peer edge entirely by
living inside their owning integrations/<vendor>/ package. bootstrap
discovers and registers both tool families into the core.tool registry.
Do not reintroduce top-level vendors/ or services/ packages.
Tier 4 — core and infrastructure
The shared runtime and cross-cutting services the capability layer is built on.
core/— the provider-agnostic agent runtime: the think → call tools → observe loop (core.agent.Agent), agent state (core/state) and context-budget enforcement (core/context_budget.py), tool contracts, schema, registry ports, execution, and error reporting (core/tool), tool authoring helpers (core/tool_framework), shared LLM clients (core/llm), agent-harness session handling (core/agent_harness), and pure domain rules (core/domain).infrastructure/— cross-cutting services with no agent logic of their own: guardrails, masking, sandbox, analytics, auth, notifications, observability, scheduler, deployment, and the shared turn host (turn_host) — the singleTurnRunnerthe interactive shell and every gateway transport run turns through, so the two entry paths cannot drift. Deploy-time assets live underinfrastructure/deployment/(EC2/packaging Python tooling plus thecloudflare_install_proxyedge worker); these are not imported by the app.
core reaches infrastructure
for guardrails, masking, observability, and evidence/log compaction, while
infrastructure reaches back into core for the shared state and session types
(core.state, core.agent_harness.session). Splitting them into
separate tiers would forbid that edge, so they share a tier as siblings.
Tier 5 — config
The floor: shared constants, prompts, and UI theme. Everything above may
read from config, but config
imports no other first-party package — keeping it a leaf means constants can be
imported anywhere without dragging runtime along.
Cross-layer flows
Two worked examples showing runtime control flow and how results return to the host. Dashed registry-dispatch arrows are callbacks installed bybootstrap;
they do not represent static imports from core into capability packages.
A chat turn from the interactive shell
- The shell (or a gateway transport) hands the message to the shared agent
harness in
core/agent_harness— the surface never runs agent logic itself. - The harness runs the ReAct agent (
core/agent): think → call tools → observe, under the context budget. bootstraphas already registered vendor-neutral tools fromtoolsand vendor-owned tools fromintegrations/<vendor>/toolsbehind the registry’scorecontract. Dispatch through that registry is a runtime callback, not a forbiddencoreimport of either capability package.- Vendor-owned tools use the client and resolved credentials in their own
integration package;
infrastructuresupplies guardrails, masking, and observability around calls. - The answer flows back up to the surface, which owns how it is presented or delivered.
An inbound gateway message
gateway receives a message, resolves session state from its own storage, then
uses the same boot-registered tier-3 capabilities as a surface — without ever
importing surfaces, since the two are independent tier-1 peers.
Related docs
AGENTS.md— repo map and per-area “files to touch” guides.