Infrastructure — Core Runtime Services
Core Runtime Services
The Infrastructure module provides the foundational runtime layer for OpenClaw: data store abstractions (SQLite and atomic JSON persistence), an in-memory caching layer with TTL-based eviction, a cron-style heartbeat scheduler, process supervision across Linux/macOS/Windows, a resilient outbound delivery pipeline, and system-level utilities including SSRF-safe networking and sandboxed filesystem operations.
Persistence Resilience Scheduling Security
Architecture
The infrastructure layer sits beneath every other OpenClaw module. Configuration flows down from config/, while channels, agents, and commands depend on infra for I/O, scheduling, delivery, and process control. The three sub-modules (store, caching, scheduling) encapsulate the data persistence, in-memory state, and time-based execution concerns respectively.
flowchart LR
subgraph Config["Configuration Layer"]
CFG["OpenClawConfig"]
ENV["Environment / .env"]
end
subgraph Infra["Infrastructure"]
subgraph Store["store/"]
JSON["Atomic JSON I/O"]
SQLITE["node-sqlite"]
AUTH["Device Auth Store"]
QUEUE["Delivery Queue Storage"]
end
subgraph Cache["caching/"]
MEM["In-Memory Maps"]
TTL["TTL Eviction"]
RATE["Rate Limiter"]
DEDUP["Dedup Engine"]
end
subgraph Sched["scheduling/"]
HB["Heartbeat Runner"]
CRON["Active Hours Gate"]
RESTART["Restart Controller"]
RECOVERY["Queue Recovery"]
end
end
subgraph Consumers["Consumer Modules"]
CHAN["Channels"]
AGENTS["Agents"]
CMDS["Commands"]
GW["Gateway"]
end
CFG --> Store
ENV --> Store
Store --> Cache
Cache --> Sched
Sched --> Consumers
Store --> Consumers
Data Flow
A typical request flows through the infrastructure layer from configuration load through state persistence, message normalization, delivery queuing, and finally transport. The pipeline ensures atomic writes, retry on transient failures, and audit logging at each stage.
flowchart TD A["Config Load (io.ts)"] --> B["State Detection (state-migrations.ts)"] B --> C["Session Store Load"] C --> D["Approval Resolution (exec-approvals.ts)"] D --> E["Heartbeat Runner (heartbeat-runner.ts)"] E --> F["Message Normalization (outbound/payloads.ts)"] F --> G["Channel Adapter Resolution"] G --> H["Delivery Queue (delivery-queue-storage.ts)"] H --> I["Transport (APNS / Channel Plugins)"] I --> J["Session Cost Tracking"] J --> K["Update Check (update-runner.ts)"]
How Infrastructure Connects to Config & Commands
Infrastructure owns runtime I/O and scheduling. Config owns schema validation and materialization. Commands own user-facing workflows. The boundary between them is defined by typed interfaces: infra exposes loadJsonFile(), writeJsonAtomic(), acquireGatewayLock(), and scheduleGatewaySigusr1Restart(). Config calls into infra for file persistence; commands call into infra for process control and delivery.
sequenceDiagram
participant CMD as Commands
participant CFG as Config
participant INFRA as Infrastructure
participant FS as Filesystem
CMD->>CFG: loadConfig()
CFG->>INFRA: readJsonFile(configPath)
INFRA->>FS: atomic read with lock
FS-->>INFRA: JSON data
INFRA-->>CFG: typed config object
CFG-->>CMD: OpenClawConfig
CMD->>INFRA: scheduleGatewaySigusr1Restart()
INFRA->>INFRA: cooldown check (30s)
INFRA-->>CMD: ScheduledRestart result
🚶 Step-by-Step Narrated Walkthrough
Follow a single outbound message delivery as it flows through the entire Infrastructure pipeline:
- Config Resolution —
loadConfig()reads the YAML/JSON5 config file via atomic JSON I/O, applies environment substitution, and materializes theOpenClawConfigobject. - Session Lookup — The session store loads the conversation entry from disk using
readJsonFile<SessionEntry>()with file locking to prevent concurrent writes. - Approval Check — If the message triggers a command execution,
exec-approvals.tschecks the allowlist patterns. If no match, it opens a JSONL socket to request user approval (30-minute timeout). - Heartbeat Gate — The heartbeat runner checks active hours, evaluates the reason kind, and decides whether to send or skip based on channel visibility settings and interval configuration.
- Payload Normalization —
outbound/payloads.tsconverts theReplyPayloadinto aNormalizedOutboundPayload, chunking text by markdown paragraphs if needed. - Channel Resolution —
outbound/channel-resolution.tsresolves the channel plugin adapter, validating capabilities (media support, threads, reactions). - Queue Enqueue —
enqueueDelivery()writes a JSON file to${stateDir}/delivery-queue/using atomic writes with PID-based temp files. - Delivery Attempt — The delivery runner reads pending items, attempts send via the channel adapter, and on success calls
ackDelivery()to remove the queue file. - Retry on Failure — Transient errors trigger exponential backoff (5s → 25s → 120s → 600s). Permanent errors (bot blocked, chat not found) move the item to
failed/. - Cost Tracking —
session-cost-usage.tsrecords token usage and calculates per-provider costs for the session transcript. - Process Health — The gateway lock file is refreshed, system presence is broadcast via mDNS, and the update runner checks for new releases on the configured channel.
Key Types & Interfaces
| Type | File | Purpose |
|---|---|---|
ArchiveExtractLimits | archive.ts | Size/entry limits for secure archive extraction (zip/tar) |
ExecApprovalRequest | exec-approvals.ts | Pending approval with ID, payload, creation and expiry timestamps |
ExecApprovalResolved | exec-approvals.ts | Resolution record: allow-once, allow-always, or deny |
HeartbeatSummary | heartbeat-summary.ts | Resolved interval, reason, visibility, and enabled state |
HeartbeatEventPayload | heartbeat-events.ts | Event record with status (sent/skipped/failed) and channel info |
QueuedDelivery | delivery-queue-storage.ts | Queued message with retry count, timestamps, and error tracking |
RestartAttempt | restart.ts | OS-level restart result (launchctl/systemd/schtasks) |
ScheduledRestart | restart.ts | SIGUSR1 restart with PID, delay, cooldown, and coalesce flag |
GatewayLockHandle | gateway-lock.ts | Process lock handle with path, config, and release function |
SafeOpenResult | fs-safe.ts | Validated file handle with real path and stat after symlink checks |
GatewayBonjourAdvertiseOpts | bonjour.ts | mDNS advertisement options for local network discovery |
SystemPresence | system-presence.ts | Host identity with IP, version, platform, roles, and TTL |
Configuration Parameters
| Parameter | Default | Description |
|---|---|---|
OPENCLAW_DISABLE_BONJOUR | false | Disable mDNS service advertisement on local network |
DEFAULT_MAX_ARCHIVE_BYTES_ZIP | 256 MB | Maximum compressed size for zip archive extraction |
DEFAULT_MAX_ENTRIES | 50,000 | Maximum number of files and directories in an archive |
DEFAULT_MAX_EXTRACTED_BYTES | 512 MB | Total decompressed size limit across all entries |
DEFAULT_EXEC_APPROVAL_TIMEOUT_MS | 1,800,000 | Approval request timeout (30 minutes) |
RESTART_COOLDOWN_MS | 30,000 | Minimum interval between gateway restarts |
SIGUSR1_AUTH_GRACE_MS | 5,000 | Grace period for restart authorization tokens |
MAX_DISPATCH_WRAPPER_DEPTH | 4 | Maximum nested dispatch depth to prevent infinite loops |
OPENCLAW_HTTP_PROXY | — | HTTP proxy URL for outbound network requests |
OPENCLAW_GATEWAY_CERT_PIN | — | TLS certificate pin (SHA-256) for gateway connections |
Sub-Modules
store/
Data store abstractions and persistence layer. Atomic JSON I/O, SQLite runtime integration, device auth storage, delivery queue persistence, and safe file operations with symlink protection.
- Atomic writes with UUID-based temp files
- State migration detection for legacy paths
- Sandboxed filesystem with O_NOFOLLOW
caching/
In-memory caching layer with TTL-based eviction, Symbol-based singletons for global state, fixed-window rate limiting, and deduplication utilities for heartbeat and system event payloads.
- System presence cache (200 entries, 5-min TTL)
- Session-scoped event queues (max 20)
- Fixed-window rate limiter with backoff
scheduling/
Cron-style heartbeat scheduling, process supervision across three OS platforms, SIGUSR1-based graceful restarts with cooldown enforcement, and delivery queue recovery with exponential backoff.
- Active-hours time gating for heartbeats
- Supervisor-aware process respawn
- Exponential backoff (5s → 600s) for retries
Code Overview
Atomic JSON Write Pattern
export async function writeJsonAtomic(
filePath: string,
value: unknown,
options?: { mode?: number }
): Promise<void> {
const tmpPath = `${filePath}.${crypto.randomUUID()}.tmp`;
const data = JSON.stringify(value, null, 2);
await fs.writeFile(tmpPath, data, { mode: options?.mode ?? 0o600 });
await fs.rename(tmpPath, filePath);
}
Heartbeat Summary Resolution
export type HeartbeatSummary = {
intervalMs: number;
reason: HeartbeatReasonKind;
visibility: HeartbeatVisibility;
enabled: boolean;
};
export function resolveHeartbeatSummaryForAgent(
agentId: string
): HeartbeatSummary {
const enabled = isHeartbeatEnabledForAgent(agentId);
const intervalMs = resolveHeartbeatIntervalMs(agentId, config);
const reason = resolveHeartbeatReason(agentId);
const visibility = resolveHeartbeatVisibility(agentId);
return { intervalMs, reason, visibility, enabled };
}
Directory Map
Source tree for Infrastructure
src/infra/
├── outbound/ ← Message delivery pipeline (50+ files)
│ ├── deliver.ts ← Delivery orchestration and chunking
│ ├── delivery-queue-storage.ts ← Persistent queue with atomic writes
│ ├── delivery-queue-recovery.ts ← Retry with exponential backoff
│ ├── message.ts ← Send interface (direct & gateway)
│ ├── message-action-runner.ts ← Interactive button/form callbacks
│ ├── target-resolver.ts ← Recipient resolution and validation
│ ├── channel-resolution.ts ← Plugin adapter loading
│ ├── payloads.ts ← ReplyPayload normalization
│ └── session-context.ts ← Session-scoped outbound context
├── net/ ← Network security (15 files)
│ ├── fetch-guard.ts ← SSRF-safe fetch with redirect limits
│ ├── ssrf.ts ← Private network blocking policy
│ ├── proxy-env.ts ← Environment proxy configuration
│ └── hostname.ts ← Hostname normalization
├── tls/ ← TLS certificate handling
│ ├── gateway.ts ← TLS runtime config and self-signed gen
│ └── fingerprint.ts ← SHA-256 cert fingerprinting
├── json-file.ts ← Synchronous JSON file I/O
├── json-files.ts ← Async atomic JSON with locks
├── node-sqlite.ts ← SQLite runtime wrapper
├── state-migrations.ts ← Legacy state detection and migration
├── heartbeat-runner.ts ← Periodic heartbeat orchestration
├── heartbeat-events.ts ← Global heartbeat event bus
├── heartbeat-active-hours.ts ← Time-based heartbeat gating
├── system-events.ts ← Session-scoped ephemeral events
├── system-presence.ts ← mDNS presence broadcasting
├── restart.ts ← SIGUSR1 restart controller
├── process-respawn.ts ← Supervisor-aware respawn
├── gateway-lock.ts ← PID-based single-instance lock
├── exec-approvals.ts ← Command approval workflow
├── exec-approvals-allowlist.ts ← Pattern matching for auto-approval
├── session-cost-usage.ts ← Token usage and cost tracking
├── archive.ts ← Secure zip/tar extraction
├── fs-safe.ts ← Sandboxed file operations
├── retry.ts ← Exponential backoff retry engine
├── backoff.ts ← Backoff policy computation
├── errors.ts ← Error chain traversal and formatting
├── unhandled-rejections.ts ← Global error classification
├── bonjour.ts ← mDNS service advertisement
├── push-apns.ts ← Apple Push Notification Service
├── device-pairing.ts ← Device pairing and auth tokens
├── device-auth-store.ts ← Device credential persistence
├── clawhub.ts ← Package registry client
└── env.ts ← Environment normalization