Overview

Components and flows

Gateway (daemon)

Clients (mac app / CLI / web admin)

Nodes (macOS / iOS / Android / headless)

Protocol details: Gateway protocol

WebChat

Connection lifecycle (single client)

sequenceDiagram
    participant Client
    participant Gateway

    Client->>Gateway: req:connect
    Gateway-->>Client: res (ok)
    Note right of Gateway: or res error + close
    Note left of Client: payload=hello-ok<br>snapshot: presence + health

    Gateway-->>Client: event:presence
    Gateway-->>Client: event:tick

    Client->>Gateway: req:agent
    Gateway-->>Client: res:agent<br>ack {runId, status:"accepted"}
    Gateway-->>Client: event:agent<br>(streaming)
    Gateway-->>Client: res:agent<br>final {runId, status, summary}

Wire protocol (summary)

Pairing and local trust

Details: Gateway protocol, Pairing, Security.

Protocol typing and codegen

Remote access

Operations snapshot

Timed work and shutdown

The Gateway kernel owns one GatewayScheduler for registered maintenance and cron wakeups. Owners receive that instance and register jobs; one host timer drives the next wake. For durable work, stores retain deadlines and their owners reconstruct schedules at startup rather than persisting a second scheduler state.

After sleep, a late wake dispatches each runnable, due registration once for that wake. A periodic registration waits for its callback and tracked work to finish before starting its next interval; missed ticks are coalesced. Wall time catches sleep, while elapsed time keeps relative delays and cadences moving through a backward clock correction. Rescheduling replaces a waiting job by default; mode: "earliest" preserves earlier wall and elapsed deadlines for the same job ID so a stale read cannot postpone an already promised wake.

beginClose() closes scheduling admission, cancels pending wakes, and signals shutdown. stop() joins callbacks already running and work tracked by their async scope; the Gateway lifecycle owns the outer shutdown budget and resource teardown. Request deadlines, stream-local timers, and child-process cleanup stay with their operation owners. SQLite WAL checkpoint timers stay with the storage owner.

Plugins receive the versioned PluginServiceSchedulerV1 capability through their service or channel-account lifetime. Its beginClose() cancels pending work and closes admission; stop() joins running callbacks and child scopes before the host releases the lifetime. Plugins still own domain cleanup ordering, including socket closure and durable flushes. See Service scheduling.

Invariants

Related