Skip to content

Latest commit

 

History

History
115 lines (100 loc) · 8.74 KB

ARCHITECTURE.md

File metadata and controls

115 lines (100 loc) · 8.74 KB

Architecture

The map. Each module's manual (src/<module>/AGENTS.md) says how it works; each flow doc under flows/ follows one path across modules; the ADRs say why.

Topology

A worker is one daemon owning the simulators and emulators (and enrolled phones) on one machine. A gateway owns no device; workers dial out to it and it fronts them as one fleet. mode in the config picks which a daemon is.

agent ─spawns─> simlock CLI ──┐
MCP client ─> stdio MCP ──────┼─ simlock/client ── unix socket ──┐
                              │                                  ▼
remote agent ── bearer token ─> HTTP frontend ── in process ──> Dispatcher ── handlers
console (browser) ────────────┘                                  │
                                                  worker: leasing + core ── drivers ── simctl / adb / devicectl
                                                  gateway: fleet coordinator ── worker views
                                                                  ▲
worker daemon ── uplink (WebSocket to /v1/uplink, worker dials) ──┘
  • One contract, one Dispatcher per daemon, every transport. HTTP calls the dispatcher in process, never through the socket.
  • A gateway is a second set of handlers for the same contract. Every frontend works against either mode unchanged.
  • State lives on the worker (state.db, its registry tables written only by core's Registry; the warm pool's learned kinds in its own tables). A gateway keeps only its tokens, its instance id and its drained-worker set on disk (state.db); everything else is rebuilt from the workers that reconnect.

Modules

Module Does Manual
contract Every operation, schema, role, error code, protocol range contract
modules Optional-module contract and the host that loads each turned-on module modules
module-registry The list of optional modules, read by the composition root and the CLI module-registry
daemon Composition root, socket server, Dispatcher, sessions, uplink dialler daemon
leasing Every lease rule: admit, queue, plan, grant, renew, end leasing
core Registry, device lifecycle, reclaim, quarantine, cleanup, components, doctor core
core/warm-pool What stays booted, and warm targets warm-pool
core/capacity Limits, RAM budget, reservations capacity
core/usage Usage figures from the history rows, read on a thread usage
drivers The Driver port's two implementations drivers · android · android screen · ios
gateway Fleet queue, routing, forwarding to workers, relay gateway · routing
alerts Gateway only: chosen fleet events POSTed to webhook destinations alerts
link Byte channels over the uplink link
agent-device Optional edge: the two agent-device daemons (simulators, phones) beside a worker, its HTTP front agent-device
stream Optional edge: a running device's live screen over HTTP, relayed by a gateway stream
auth Credential → identity: tokens, admin secret, hello, uplink join token, HTTP strategies auth
http HTTP frontend: routes, SSE http
cli Operator command surface cli
mcp stdio MCP server for one agent session mcp
simlock-client, client, admin The typed programmatic client (simlock/client, simlock/admin) simlock-client · client · admin
updater The detached process that installs a Simlock version with npm, restarts the daemon and rolls back updater
lease-policy Client-side renew timer lease-policy
bus Post-commit business events and their in-memory ring bus
state state.db: opens it, refuses an old file, damage and a newer version, migrates each owner's tables state
history The durable event history: history.db, prune, set-aside, projections (usage: request, minute and device fact rows) history
ports Every external API behind an interface, with a fake ports
host-load CPU, memory and disk load samples host-load
instructions The static rules text for agents: simlock instructions, the MCP server instructions and resource; simlock setup, which installs the router skill instructions
agent-identity Default requester id for CLI and MCP agent-identity
daemon-protocol Socket framing and the protocol version daemon-protocol
test-support Shared test helpers test-support
ui/ Web console, served by the daemon: data, model, state, design system and screens in layers ui · console.mdx

Dependencies

Arrows point from the optional to the required (architecture rule 15). pnpm lint (.dependency-cruiser.mjs, ADR 0027) enforces each line:

Rule Means
past-index Outside a component, import only its index file (tests may also import its testing file)
core-not-leasing Core declares a port; the composition root connects leasing to it
core-not-drivers Core and leasing reach a platform only through the Driver interface
no-fs-or-process Everything under src/ but ports/ uses ports, never fs or child_process
definition-is-light A module's definition file imports its own module only for types or by dynamic import
auth-imports-little src/auth imports only ports and contract
modules-imports-little, modules-imports-bus-types-only src/modules imports only ports, contract and core, and the bus for types
not-modules Core, leasing, drivers, gateway and contract import nothing from modules or module-registry
not-agent-device Core, leasing and drivers import nothing from agent-device
host-load-imports-ports-only, not-host-load host-load stands alone
alerts-imports-bus-ports-core-only, not-alerts alerts imports the bus, ports and core only; only src/daemon/main.ts imports it
not-stream, stream-not-edges Nothing required imports stream; stream imports no frontend, gateway or driver
gateway-not-edges, gateway-only-dispatch Gateway imports no driver or frontend, and from daemon only src/daemon/dispatch.ts
only-main-knows-gateway In daemon, only src/daemon/main.ts imports the gateway
link-is-portable, link-imports-port-types-only The link uses no Node module or package, and only the types of ports
contract-imports-nothing The contract imports nothing outside itself

A directory under src/ with an index file is a component. Two components never import each other, directly or through a third.

Flows

Flow Doc
A lease, from request to reclaim leasing
A lease through a gateway gateway
Which device a request gets device-selection
Startup and stop startup
device.exec, local and through a gateway device-exec
agent-device through simlock agent-device
A screen stream, local and through a gateway stream

Rules that cut across modules

  • One place enforces a rule (architecture rule 10).
  • Events go out after the commit that made them true (events.md).
  • Simlock destroys only what its registry owns, and never touches a leased device (safety.md).
  • Every external API sits behind a port with an in-memory fake (architecture rule 9).