OpenClaw Dual-Schema Analysis
TL;DR: OpenClaw uses two validation systems that never talk to each other—Zod for config files, TypeBox for WebSocket RPCs—and session metadata has no validation at all. This comprehensive breakdown reveals where the gaps are: arbitrary properties can be injected into sessions, health data passes through unchecked, and different LLM providers require incompatible JSON schemas that get normalized at runtime. If you're building on OpenClaw or auditing it, this is your schema reference.
OpenClaw Schema System -- Comprehensive Analysis
Table of Contents
- Schema Architecture Overview
- Zod Config Schemas
- TypeBox Protocol Schemas
- Plugin Schema System
- SQLite Memory Schema
- Session Metadata Types
- Validation Infrastructure
- Schema Gaps and Security Implications
- Provider-Specific Schema Normalization
1. Schema Architecture Overview
OpenClaw uses a dual-schema architecture with two distinct validation libraries serving different layers of the system:
Two Schema Systems
| System | Library | Purpose | Validation Style |
|---|---|---|---|
| Config Schemas | Zod | YAML/JSON5 config file validation | Parse-time, strict, with transforms and refinements |
| Protocol Schemas | TypeBox (via @sinclair/typebox) | WebSocket RPC parameter validation | AJV-compiled JSON Schema, runtime type guards |
Validation Chain
Config File (YAML/JSON5)
|
v
[1] Zod: OpenClawSchema.safeParse() -- src/config/validation.ts
| - Legacy migration check
| - Full Zod parse with .strict() enforcement
| - Cross-field refinements (superRefine)
| - Sensitive field registration
|
v
[2] Post-Zod Validation -- src/config/validation.ts
| - Duplicate agent directory check
| - Identity avatar path-traversal check
| - Plugin manifest validation (AJV via schema-validator.ts)
| - Heartbeat target validation
|
v
[3] Defaults Application -- src/config/defaults.ts
| - applySessionDefaults()
| - applyAgentDefaults()
| - applyModelDefaults()
|
v
Runtime Config Object
|
+--> Gateway WebSocket Server
|
v
[4] TypeBox/AJV: Per-method param validation -- src/gateway/protocol/index.ts
| - ajv.compile() for each RPC method
| - assertValidParams() guard in handlers
|
v
Handler executes
Key Architectural Observations
- No shared schema definition: Zod and TypeBox schemas are defined independently. There is no mechanism to generate one from the other or ensure they stay in sync.
- Strict mode everywhere in Zod: Nearly every object uses
.strict(), which rejects unknown properties. Exception:ChannelsSchemauses.passthrough()for extension channels, andHookConfigSchemauses.passthrough()for per-hook custom keys. - Sensitive field registry: Zod schemas use
.register(sensitive)to mark fields for automatic redaction when config is exposed to dashboards. - Session metadata has NO runtime validation:
SessionEntryis a plain TypeScript interface with no Zod or TypeBox schema (security gap #18).
2. Zod Config Schemas
2.1 Root Schema -- zod-schema.ts
File: src/config/zod-schema.ts
Export: OpenClawSchema
The root schema assembles all sub-schemas into the top-level OpenClaw configuration object. Uses .strict() at every level and .superRefine() for cross-field validation of broadcast agent IDs.
| Top-Level Field | Schema Source | Required |
|---|---|---|
$schema | z.string() | optional |
meta | inline (lastTouchedVersion, lastTouchedAt) | optional |
env | inline (shellEnv, vars) -- uses .catchall(z.string()) | optional |
wizard | inline (lastRunAt, lastRunVersion, etc.) | optional |
diagnostics | inline (enabled, flags, otel, cacheTrace) | optional |
logging | inline (level, file, consoleLevel, consoleStyle, redact) | optional |
update | inline (channel: stable/beta/dev, checkOnStart) | optional |
browser | inline (38+ fields across profiles, ssrfPolicy, etc.) | optional |
ui | inline (seamColor, assistant name/avatar) | optional |
auth | inline (profiles, order, cooldowns) | optional |
models | ModelsConfigSchema from core | optional |
nodeHost | inline (browserProxy) | optional |
agents | AgentsSchema from agents | optional |
tools | ToolsSchema from agent-runtime | optional |
bindings | BindingsSchema from agents | optional |
broadcast | BroadcastSchema from agents | optional |
audio | AudioSchema from agents | optional |
media | inline (preserveFilenames) | optional |
messages | MessagesSchema from session | optional |
commands | CommandsSchema from session | optional |
approvals | ApprovalsSchema from approvals | optional |
session | SessionSchema from session | optional |
cron | inline (enabled, store, maxConcurrentRuns, webhook) | optional |
hooks | inline + HookMappingSchema, HooksGmailSchema, InternalHooksSchema | optional |
web | inline (enabled, heartbeat, reconnect) | optional |
channels | ChannelsSchema from providers | optional |
discovery | inline (wideArea, mdns) | optional |
canvasHost | inline (enabled, root, port, liveReload) | optional |
talk | inline (voiceId, modelId, apiKey sensitive) | optional |
gateway | inline (60+ fields across auth, tls, remote, reload, http) | optional |
memory | inline (backend: builtin/qmd, citations, qmd config) | optional |
skills | inline (allowBundled, load, install, limits, entries) | optional |
plugins | inline (enabled, allow, deny, load, slots, entries, installs) | optional |
Cross-field refinement (superRefine):
- Validates that every agent ID referenced in
broadcastmappings exists inagents.list
Notable validation patterns:
HttpUrlSchema:z.string().url().refine()-- ensures http:// or https:// protocol- Memory
searchMode: union of"query","search","vsearch" MemoryQmdLimitsSchema: positive integers for maxResults, maxSnippetChars, etc.- OTEL
sampleRate:z.number().min(0).max(1)
2.2 Core Schema -- zod-schema.core.ts
File: src/config/zod-schema.core.ts
Contains ~50+ exported schemas used as building blocks across the configuration:
| Schema | Key Fields | Constraints |
|---|---|---|
ModelApiSchema | union literal | 7 API types: openai-completions, anthropic-messages, google-generative-ai, etc. |
ModelCompatSchema | supportsStore, maxTokensField, thinkingFormat, etc. | .strict().optional() |
ModelDefinitionSchema | id, name, api, reasoning, input, cost, contextWindow, maxTokens, compat | id/name: .min(1), contextWindow/maxTokens: .positive() |
ModelProviderSchema | baseUrl, apiKey(sensitive), auth, api, models | baseUrl: .min(1), models: array of ModelDefinition |
ModelsConfigSchema | mode (merge/replace), providers, bedrockDiscovery | optional top-level |
GroupPolicySchema | enum | "open", "disabled", "allowlist" |
DmPolicySchema | enum | "pairing", "allowlist", "open", "disabled" |
QueueModeSchema | union | 7 modes: steer, followup, collect, steer-backlog, queue, interrupt, etc. |
HexColorSchema | string | regex /^#?[0-9a-fA-F]{6}$/ |
ExecutableTokenSchema | string | .refine(isSafeExecutableValue) -- security check |
TranscribeAudioSchema | command, timeoutSeconds | command[0] validated via isSafeExecutableValue |
TtsConfigSchema | auto, provider, elevenlabs, openai, edge | elevenlabs seed: .min(0).max(4294967295) |
CliBackendSchema | command, args, output format, reliability/watchdog | 25+ fields for CLI agent backends |
BlockStreamingCoalesceSchema | minChars, maxChars, idleMs | positive integers |
RetryConfigSchema | attempts, minDelayMs, maxDelayMs, jitter | jitter: .min(0).max(1) |
ToolsMediaUnderstandingSchema | enabled, scope, maxBytes, models | Nested media understanding config |
LinkModelSchema | type: "cli", command, args, timeoutSeconds | command: .min(1) |
ProviderCommandsSchema | native, nativeSkills | z.union([z.boolean(), z.literal("auto")]) |
Security-relevant schemas:
requireOpenAllowFrom(): Enforces thatdmPolicy="open"requiresallowFromto include"*"-- prevents accidentally exposing DMs to everyoneisSafeExecutableValue: Validates executable paths to prevent command injection
2.3 Agent Runtime Schema -- zod-schema.agent-runtime.ts
File: src/config/zod-schema.agent-runtime.ts
| Schema | Key Fields | Constraints |
|---|---|---|
HeartbeatSchema | every (duration), activeHours, model, session, prompt | Custom superRefine validates duration and HH:MM time format |
SandboxDockerSchema | image, network, capDrop, pidsLimit, memory, binds | Security refinements: blocks network: "host", seccompProfile: "unconfined", apparmorProfile: "unconfined", validates bind mount paths are absolute |
SandboxBrowserSchema | enabled, image, cdpPort, headless, binds | Blocks network: "host" |
ToolPolicySchema | allow, alsoAllow, deny | Refinement: cannot set both allow AND alsoAllow |
AgentToolsSchema | profile, allow, deny, byProvider, elevated, exec, fs, loopDetection | Profile: minimal/coding/messaging/full |
ToolExecSchema | host (sandbox/gateway/node), security (deny/allowlist/full), ask, timeoutSec | approvalRunningNoticeMs only on agent variant |
ToolLoopDetectionSchema | enabled, historySize, warningThreshold, criticalThreshold | Refinement: warning < critical < globalCircuitBreaker |
AgentSandboxSchema | mode (off/non-main/all), workspaceAccess (none/ro/rw), scope (session/agent/shared) | |
ToolsWebSearchSchema | provider (brave/perplexity/grok), apiKey(sensitive), maxResults | |
MemorySearchSchema | enabled, sources, provider (openai/local/gemini/voyage), store, chunking, query, hybrid, mmr, temporalDecay | Complex nested search config |
AgentEntrySchema | id, default, name, workspace, model, skills, tools, sandbox, heartbeat, identity | id: required string |
ToolsSchema | profile, allow, deny, web, media, links, sessions, loopDetection, message, exec, fs | Top-level tools config |
Security-relevant fields:
ToolExecSchema.host: Controls where commands execute (sandbox/gateway/node)ToolExecSchema.security: Controls command execution policy (deny/allowlist/full)AgentSandboxSchema.workspaceAccess: Controls filesystem access levelElevatedAllowFromSchema:z.record(z.string(), z.array(z.union([z.string(), z.number()])))-- provider-keyed allowlists
2.4 Agents Schema -- zod-schema.agents.ts
File: src/config/zod-schema.agents.ts
| Schema | Key Fields | Constraints |
|---|---|---|
AgentsSchema | defaults (lazy -> AgentDefaultsSchema), list (array of AgentEntrySchema) | .strict().optional() |
BindingsSchema | array of {agentId, match: {channel, accountId, peer}} | peer.kind: direct/group/channel/dm(deprecated) |
BroadcastSchema | strategy (parallel/sequential) + catchall z.array(z.string()) | Uses .catchall() for dynamic peer IDs |
AudioSchema | transcription: TranscribeAudioSchema |
2.5 Agent Model Schema -- zod-schema.agent-model.ts
File: src/config/zod-schema.agent-model.ts
AgentModelSchema = z.union([
z.string(), // Simple model name
z.object({
primary: z.string().optional(),
fallbacks: z.array(z.string()).optional(),
}).strict(),
]);
Supports both a simple string model identifier and an object with primary + fallback models.
2.6 Session Schema -- zod-schema.session.ts
File: src/config/zod-schema.session.ts
| Schema | Key Fields | Constraints |
|---|---|---|
SessionSchema | scope (per-sender/global), dmScope, resetTriggers, idleMinutes, reset, maintenance | idleMinutes: positive int |
SessionSendPolicySchema | Created via createAllowDenyChannelRulesSchema() | Allow/deny rules with channel/chatType matching |
SessionResetConfigSchema | mode (daily/idle), atHour, idleMinutes | atHour: .min(0).max(23) |
MessagesSchema | messagePrefix, groupChat, queue, ackReaction, statusReactions, tts | statusReactions has emoji and timing config |
CommandsSchema | native, nativeSkills, bash, config, debug, restart, ownerAllowFrom | Default factory: {native: "auto", nativeSkills: "auto", restart: true, ownerDisplay: "raw"} |
Session maintenance validation (superRefine):
pruneAfter: Validated viaparseDurationMs()with "d" default unitrotateBytes: Validated viaparseByteSize()with "b" default unit
Notable: CommandsSchema uses .optional().default(() => ...) which provides a runtime-safe default even when the entire section is omitted.
2.7 Approvals Schema -- zod-schema.approvals.ts
File: src/config/zod-schema.approvals.ts
| Schema | Key Fields | Constraints |
|---|---|---|
ApprovalsSchema | exec: ExecApprovalForwardingSchema | .strict().optional() |
ExecApprovalForwardingSchema | enabled, mode (session/targets/both), agentFilter, targets | |
ExecApprovalForwardTargetSchema | channel (min 1), to (min 1), accountId, threadId | channel and to are required non-empty |
2.8 Providers Schema -- zod-schema.providers.ts
File: src/config/zod-schema.providers.ts
The aggregator schema that assembles all channel provider configs:
| Field | Schema Source |
|---|---|
defaults | inline (groupPolicy, heartbeat) |
modelByChannel | z.record(z.string(), z.record(z.string(), z.string())) |
whatsapp | WhatsAppConfigSchema (separate file) |
telegram | TelegramConfigSchema |
discord | DiscordConfigSchema |
irc | IrcConfigSchema |
googlechat | GoogleChatConfigSchema |
slack | SlackConfigSchema |
signal | SignalConfigSchema |
imessage | IMessageConfigSchema |
bluebubbles | BlueBubblesConfigSchema |
msteams | MSTeamsConfigSchema |
Critical: Uses .passthrough() instead of .strict() to allow extension channel configs from plugins. This is intentional for extensibility but bypasses Zod's unknown-property rejection for the entire channels object.
2.9 Providers Core Schema -- zod-schema.providers-core.ts
File: src/config/zod-schema.providers-core.ts
Defines the schema for each messaging channel provider. Very large file (~1100 lines).
| Provider | Key Security Fields | Notable Patterns |
|---|---|---|
| Telegram | botToken(sensitive), webhookSecret(sensitive), dmPolicy, allowFrom | Multi-account support, custom commands with superRefine validation, streaming mode normalization |
| Discord | token(sensitive), dmPolicy, allowFrom, guilds, execApprovals | Discord ID validation (must be strings, not numbers), multi-account, voice config |
| Slack | botToken(sensitive), appToken(sensitive), userToken(sensitive), signingSecret(sensitive) | HTTP/socket mode, per-channel tool policies, safeExtend() usage |
| Signal | account, cliPath(ExecutableTokenSchema), dmPolicy, allowFrom | SCP remote host validation via isSafeScpRemoteHost |
| iMessage | cliPath(ExecutableTokenSchema), dmPolicy, allowFrom, attachmentRoots | Path validation via isValidInboundPathRootPattern |
| IRC | password(sensitive), nickserv with register validation | NickServ registration requires email |
| MS Teams | appPassword(sensitive), tenantId, dmPolicy, groupPolicy | SharePoint site ID for file uploads |
| Google Chat | serviceAccount, audience, webhookUrl | Service account can be string or object |
| BlueBubbles | password(sensitive), serverUrl | Actions schema (reactions, edit, unsend, etc.) |
Common security patterns across all providers:
requireOpenAllowFrom(): Called in every provider's superRefine to enforce thatdmPolicy="open"requires explicit allowFrom wildcarddmPolicydefaults to"pairing"(most restrictive default)groupPolicydefaults to"allowlist"- Multi-account pattern: base config +
accounts: z.record(z.string(), AccountSchema)
2.10 Hooks Schema -- zod-schema.hooks.ts
File: src/config/zod-schema.hooks.ts
| Schema | Key Fields | Constraints |
|---|---|---|
HookMappingSchema | id, match (path, source), action (wake/agent), channel, transform | transform.module validated via SafeRelativeModulePathSchema |
InternalHookHandlerSchema | event, module, export | module: safe relative path |
InternalHooksSchema | enabled, handlers, entries, load, installs | entries use .passthrough() for per-hook custom keys |
HooksGmailSchema | account, label, topic, pushToken(sensitive), serve, tailscale, model |
Security: SafeRelativeModulePathSchema prevents:
- Absolute paths
- Tilde-prefixed paths (
~) - URL-ish paths (containing
:) - Parent directory traversal (
..segments)
2.11 Allow/Deny Schema -- zod-schema.allowdeny.ts
File: src/config/zod-schema.allowdeny.ts
Factory function createAllowDenyChannelRulesSchema() used for session send policies and media understanding scopes.
Structure:
{
default?: "allow" | "deny",
rules?: Array<{
action: "allow" | "deny",
match?: {
channel?: string,
chatType?: "direct" | "group" | "channel" | "dm"(deprecated),
keyPrefix?: string,
rawKeyPrefix?: string,
}
}>
}
2.12 Installs Schema -- zod-schema.installs.ts
File: src/config/zod-schema.installs.ts
| Field | Type | Notes |
|---|---|---|
source | `"npm" | "archive" |
spec | string | optional |
sourcePath | string | optional |
installPath | string | optional |
version | string | optional |
resolvedName | string | optional |
resolvedVersion | string | optional |
resolvedSpec | string | optional |
integrity | string | optional, SRI hash |
shasum | string | optional |
resolvedAt | string | optional, ISO timestamp |
installedAt | string | optional, ISO timestamp |
Exported as InstallRecordShape (not a full schema -- used with spread in parent objects).
2.13 Sensitive Field Registry -- zod-schema.sensitive.ts
File: src/config/zod-schema.sensitive.ts
export const sensitive = z.registry<undefined, z.ZodType>();
A Zod registry used to mark fields as sensitive. Fields registered with .register(sensitive) are automatically redacted when config is serialized for the dashboard/control UI. Sensitive fields found across all schemas:
- Auth tokens:
botToken,appToken,userToken,token,webhookSecret,signingSecret - API keys:
apiKey,apiKey(elevenlabs, openai, perplexity, grok, skills) - Passwords:
password,appPassword - Gateway:
gateway.auth.token,gateway.auth.password,gateway.remote.token,gateway.remote.password - Cron:
webhookToken - Hooks:
token,pushToken,sessionKey - Commands:
ownerDisplaySecret
3. TypeBox Protocol Schemas
TypeBox schemas define the WebSocket RPC protocol for client-gateway communication. They compile to JSON Schema and are validated at runtime via AJV.
3.1 Frames -- schema/frames.ts
Core WebSocket frame types:
| Schema | Fields | Purpose |
|---|---|---|
TickEventSchema | ts: Integer(min:0) | Keepalive heartbeat |
ShutdownEventSchema | reason: NonEmptyString, restartExpectedMs? | Graceful shutdown notification |
ConnectParamsSchema | minProtocol, maxProtocol, client, caps, commands, permissions, device, auth, locale | WebSocket handshake params. Client includes id, displayName, version, platform, mode |
HelloOkSchema | type:"hello-ok", protocol, server, features, snapshot, policy | Server handshake response. Features lists available methods and events |
ErrorShapeSchema | code, message, details?, retryable?, retryAfterMs? | Standard error envelope |
RequestFrameSchema | type:"req", id, method, params? | Client-to-server RPC request |
ResponseFrameSchema | type:"res", id, ok, payload?, error? | Server-to-client RPC response |
EventFrameSchema | type:"event", event, payload?, seq?, stateVersion? | Server-to-client push event |
GatewayFrameSchema | Union of Request/Response/Event | Discriminated on type field |
Device pairing in ConnectParams:
device?: {
id: NonEmptyString,
publicKey: NonEmptyString,
signature: NonEmptyString,
signedAt: Integer(min:0),
nonce?: NonEmptyString,
}
3.2 Primitives -- schema/primitives.ts
| Schema | Definition |
|---|---|
NonEmptyString | Type.String({ minLength: 1 }) |
SessionLabelString | Type.String({ minLength: 1, maxLength: SESSION_LABEL_MAX_LENGTH }) |
GatewayClientIdSchema | Union of literals from GATEWAY_CLIENT_IDS |
GatewayClientModeSchema | Union of literals from GATEWAY_CLIENT_MODES |
3.3 Agent -- schema/agent.ts
| Schema | Fields | Methods Validated |
|---|---|---|
AgentEventSchema | runId, seq, stream, ts, data (Record) | agent.event event |
SendParamsSchema | to, message?, mediaUrl?, mediaUrls?, gifPlayback?, channel?, idempotencyKey | agent.send |
PollParamsSchema | to, question, options(2-12 items), maxSelections(1-12), durationSeconds(1-604800) | agent.poll |
AgentParamsSchema | message, agentId?, to?, sessionKey?, thinking?, deliver?, attachments?, timeout?, inputProvenance?, idempotencyKey, label?, spawnedBy? | agent.run |
AgentIdentityParamsSchema | agentId?, sessionKey? | agent.identity |
AgentIdentityResultSchema | agentId, name?, avatar?, emoji? | agent.identity response |
AgentWaitParamsSchema | runId, timeoutMs? | agent.wait |
WakeParamsSchema | mode: "now" | "next-heartbeat", text |
3.4 Agents/Models/Skills -- schema/agents-models-skills.ts
| Schema | Fields | Methods Validated |
|---|---|---|
ModelChoiceSchema | id, name, provider, contextWindow?, reasoning? | models.list response items |
AgentSummarySchema | id, name?, identity? (name, theme, emoji, avatar, avatarUrl) | agents.list response items |
AgentsListResultSchema | defaultId, mainKey, scope, agents[] | agents.list response |
AgentsCreateParamsSchema | name, workspace, emoji?, avatar? | agents.create |
AgentsUpdateParamsSchema | agentId, name?, workspace?, model?, avatar? | agents.update |
AgentsDeleteParamsSchema | agentId, deleteFiles? | agents.delete |
AgentsFilesListParamsSchema | agentId | agents.files.list |
AgentsFilesGetParamsSchema | agentId, name | agents.files.get |
AgentsFilesSetParamsSchema | agentId, name, content | agents.files.set |
ModelsListParamsSchema | (empty object) | models.list |
SkillsStatusParamsSchema | agentId? | skills.status |
SkillsBinsParamsSchema | (empty object) | skills.bins |
SkillsInstallParamsSchema | name, installId, timeoutMs?(min:1000) | skills.install |
SkillsUpdateParamsSchema | skillKey, enabled?, apiKey?, env? | skills.update |
3.5 Config -- schema/config.ts
| Schema | Fields | Methods Validated |
|---|---|---|
ConfigGetParamsSchema | (empty object) | config.get |
ConfigSetParamsSchema | raw (NonEmptyString), baseHash? | config.set |
ConfigApplyParamsSchema | raw, baseHash?, sessionKey?, note?, restartDelayMs? | config.apply |
ConfigPatchParamsSchema | same as Apply | config.patch |
ConfigSchemaParamsSchema | (empty object) | config.schema |
UpdateRunParamsSchema | sessionKey?, note?, restartDelayMs?, timeoutMs? | update.run |
ConfigUiHintSchema | label?, help?, group?, order?, advanced?, sensitive?, placeholder?, itemTemplate? | Part of schema response |
ConfigSchemaResponseSchema | schema (Unknown), uiHints (Record), version, generatedAt | config.schema response |
Note: config.set and config.apply accept raw as a string (the full config YAML/JSON5), not a pre-parsed object. The server parses and validates it internally.
3.6 Sessions -- schema/sessions.ts
| Schema | Fields | Methods Validated |
|---|---|---|
SessionsListParamsSchema | limit?, activeMinutes?, includeGlobal?, includeDerivedTitles?, includeLastMessage?, label?, spawnedBy?, agentId?, search? | sessions.list |
SessionsPreviewParamsSchema | keys (array, minItems:1), limit?, maxChars?(min:20) | sessions.preview |
SessionsResolveParamsSchema | key?, sessionId?, label?, agentId?, spawnedBy?, includeGlobal? | sessions.resolve |
SessionsPatchParamsSchema | key, label?, thinkingLevel?, verboseLevel?, reasoningLevel?, responseUsage?, model?, execHost?, execSecurity?, sendPolicy?, groupActivation?, spawnedBy?, spawnDepth? | sessions.patch |
SessionsResetParamsSchema | key, reason? ("new" | "reset") |
SessionsDeleteParamsSchema | key, deleteTranscript?, emitLifecycleHooks? | sessions.delete |
SessionsCompactParamsSchema | key, maxLines?(min:1) | sessions.compact |
SessionsUsageParamsSchema | key?, startDate? (YYYY-MM-DD pattern), endDate?, mode?, utcOffset? (UTC+/-N pattern), limit?, includeContextWeight? | sessions.usage |
Notable: Date patterns use regex validation: ^\\d{4}-\\d{2}-\\d{2}$ for dates, ^UTC[+-]\\d{1,2}(?::[0-5]\\d)?$ for UTC offsets.
3.7 Cron -- schema/cron.ts
| Schema | Fields |
|---|---|
CronScheduleSchema | Union: {kind:"at", at}, {kind:"every", everyMs, anchorMs?}, {kind:"cron", expr, tz?, staggerMs?} |
CronPayloadSchema | Union: {kind:"systemEvent", text}, {kind:"agentTurn", message, model?, thinking?, timeoutSeconds?, deliver?, channel?, to?} |
CronDeliverySchema | Union: {mode:"none"}, {mode:"announce"}, {mode:"webhook", to} |
CronJobSchema | id, agentId?, name, enabled, schedule, sessionTarget (main/isolated), wakeMode, payload, delivery?, state |
CronJobStateSchema | nextRunAtMs?, runningAtMs?, lastRunAtMs?, lastStatus?, lastError?, consecutiveErrors? |
CronAddParamsSchema | name, schedule, sessionTarget, wakeMode, payload, delivery?, agentId?, description? |
CronUpdateParamsSchema | Union of {id, patch} or {jobId, patch} |
CronRemoveParamsSchema | Union of {id} or {jobId} |
CronRunParamsSchema | id/jobId + mode? (due/force) |
CronRunsParamsSchema | id/jobId + limit?(1-5000) |
CronRunLogEntrySchema | ts, jobId, action:"finished", status?, error?, sessionKey?, durationMs? |
3.8 Exec Approvals -- schema/exec-approvals.ts
| Schema | Fields |
|---|---|
ExecApprovalsAllowlistEntrySchema | id?, pattern, lastUsedAt?, lastUsedCommand?, lastResolvedPath? |
ExecApprovalsDefaultsSchema | security?, ask?, askFallback?, autoAllowSkills? |
ExecApprovalsAgentSchema | security?, ask?, askFallback?, autoAllowSkills?, allowlist? |
ExecApprovalsFileSchema | version:1, socket?(path, token), defaults?, agents?(Record) |
ExecApprovalsSnapshotSchema | path, exists, hash, file |
ExecApprovalRequestParamsSchema | id?, command, cwd?, host?, security?, agentId?, resolvedPath?, sessionKey?, timeoutMs?, twoPhase? |
ExecApprovalResolveParamsSchema | id, decision |
Note: security, ask, and decision fields use Type.String() without enum constraints. This means any string value passes TypeBox validation -- the actual enforcement happens in application logic.
3.9 Devices -- schema/devices.ts
| Schema | Methods |
|---|---|
DevicePairListParamsSchema | device.pair.list |
DevicePairApproveParamsSchema | device.pair.approve (requestId) |
DevicePairRejectParamsSchema | device.pair.reject (requestId) |
DevicePairRemoveParamsSchema | device.pair.remove (deviceId) |
DeviceTokenRotateParamsSchema | device.token.rotate (deviceId, role, scopes?) |
DeviceTokenRevokeParamsSchema | device.token.revoke (deviceId, role) |
DevicePairRequestedEventSchema | Event with requestId, deviceId, publicKey, displayName?, platform?, roles?, scopes?, remoteIp? |
DevicePairResolvedEventSchema | Event with requestId, deviceId, decision, ts |
3.10 Logs/Chat -- schema/logs-chat.ts
| Schema | Fields |
|---|---|
LogsTailParamsSchema | cursor?(min:0), limit?(1-5000), maxBytes?(1-1000000) |
LogsTailResultSchema | file, cursor, size, lines[], truncated?, reset? |
ChatHistoryParamsSchema | sessionKey, limit?(1-1000) |
ChatSendParamsSchema | sessionKey, message, thinking?, deliver?, attachments?, timeoutMs?, idempotencyKey |
ChatAbortParamsSchema | sessionKey, runId? |
ChatInjectParamsSchema | sessionKey, message, label?(maxLength:100) |
ChatEventSchema | runId, sessionKey, seq, state (delta/final/aborted/error), message?, usage?, stopReason? |
3.11 Nodes -- schema/nodes.ts
| Schema | Methods |
|---|---|
NodePairRequestParamsSchema | node.pair.request (nodeId, displayName?, platform?, caps?, commands?) |
NodePairListParamsSchema | node.pair.list |
NodePairApproveParamsSchema | node.pair.approve (requestId) |
NodePairRejectParamsSchema | node.pair.reject (requestId) |
NodePairVerifyParamsSchema | node.pair.verify (nodeId, token) |
NodeRenameParamsSchema | node.rename (nodeId, displayName) |
NodeListParamsSchema | node.list |
NodeDescribeParamsSchema | node.describe (nodeId) |
NodeInvokeParamsSchema | node.invoke (nodeId, command, params?, timeoutMs?, idempotencyKey) |
NodeInvokeResultParamsSchema | node.invoke.result (id, nodeId, ok, payload?, error?) |
NodeEventParamsSchema | node.event (event, payload?) |
NodeInvokeRequestEventSchema | Event pushed to nodes (id, nodeId, command, paramsJSON?) |
3.12 Wizard -- schema/wizard.ts
| Schema | Fields |
|---|---|
WizardStartParamsSchema | mode? (local/remote), workspace? |
WizardAnswerSchema | stepId, value? |
WizardNextParamsSchema | sessionId, answer? |
WizardStepSchema | id, type (note/select/text/confirm/multiselect/progress/action), title?, message?, options?, initialValue?, placeholder?, sensitive?, executor? |
WizardStartResultSchema | sessionId, done, step?, status?, error? |
WizardNextResultSchema | done, step?, status?, error? |
WizardStatusResultSchema | status (running/done/cancelled/error), error? |
3.13 Snapshot -- schema/snapshot.ts
| Schema | Fields |
|---|---|
PresenceEntrySchema | host?, ip?, version?, platform?, deviceFamily?, mode?, lastInputSeconds?, tags?, deviceId?, roles?, scopes? |
HealthSnapshotSchema | Type.Any() -- no validation |
SessionDefaultsSchema | defaultAgentId, mainKey, mainSessionKey, scope? |
StateVersionSchema | presence: Integer(min:0), health: Integer(min:0) |
SnapshotSchema | presence[], health, stateVersion, uptimeMs, configPath?, sessionDefaults?, authMode?, updateAvailable? |
Gap: HealthSnapshotSchema = Type.Any() -- health data passes through with zero validation.
3.14 Push -- schema/push.ts
| Schema | Fields |
|---|---|
PushTestParamsSchema | nodeId, title?, body?, environment? ("sandbox"/"production") |
PushTestResultSchema | ok, status (Integer), apnsId?, reason?, tokenSuffix, topic, environment |
3.15 Channels -- schema/channels.ts
| Schema | Fields |
|---|---|
TalkModeParamsSchema | enabled, phase? |
TalkConfigParamsSchema | includeSecrets? |
TalkConfigResultSchema | config: {talk?, session?, ui?} |
ChannelsStatusParamsSchema | probe?, timeoutMs? |
ChannelAccountSnapshotSchema | accountId, name?, enabled?, configured?, linked?, running?, connected?, lastError?, mode?, dmPolicy?, allowFrom?, etc. -- uses additionalProperties: true |
ChannelUiMetaSchema | id, label, detailLabel, systemImage? |
ChannelsStatusResultSchema | ts, channelOrder[], channelLabels, channels (Record Unknown), channelAccounts, channelDefaultAccountId |
ChannelsLogoutParamsSchema | channel, accountId? |
WebLoginStartParamsSchema | force?, timeoutMs?, verbose?, accountId? |
WebLoginWaitParamsSchema | timeoutMs?, accountId? |
Note: ChannelAccountSnapshotSchema uses additionalProperties: true -- intentionally schema-light so new channels can ship without protocol updates.
4. Plugin Schema System
Manifest Structure
File: src/plugins/manifest.ts
Plugin manifests are declared in openclaw.plugin.json files:
type PluginManifest = {
id: string; // Required, non-empty
configSchema: Record<string, unknown>; // Required, JSON Schema object
kind?: "memory"; // Optional plugin kind
channels?: string[]; // Channel IDs this plugin provides
providers?: string[]; // Provider IDs this plugin provides
skills?: string[]; // Skill IDs this plugin provides
name?: string;
description?: string;
version?: string;
uiHints?: Record<string, PluginConfigUiHint>;
};
Config Validation Flow
- Manifest loading (
loadPluginManifest()): Reads JSON, validates requiredidandconfigSchemafields - Registry assembly (
loadPluginManifestRegistry()): Scans bundled, global, workspace, and config-specified plugin paths - Enable resolution (
resolveEnableState()): Checks allow/deny lists, memory slot decisions - Schema validation (
validateJsonSchemaValue()): Uses AJV to validate plugin config values against the manifest'sconfigSchema
Plugin Config Schema Interface
File: src/plugins/types.ts
type OpenClawPluginConfigSchema = {
safeParse?: (value: unknown) => { success: boolean; data?: unknown; error?: ... };
parse?: (value: unknown) => unknown;
validate?: (value: unknown) => PluginConfigValidation;
uiHints?: Record<string, PluginConfigUiHint>;
jsonSchema?: Record<string, unknown>;
};
Plugins can provide validation via:
safeParse(Zod-compatible)parse(throwing validation)validate(custom validation returning{ok, errors})jsonSchema(raw JSON Schema for AJV)
UI Hints System
File: src/plugins/types.ts and src/gateway/protocol/schema/config.ts
UI hints are metadata attached to config fields to drive control UI rendering:
type PluginConfigUiHint = {
label?: string; // Human-readable field label
help?: string; // Help text / tooltip
advanced?: boolean; // Hide in "basic" view
sensitive?: boolean; // Mask input value
placeholder?: string; // Input placeholder text
};
The protocol schema extends this with additional fields:
ConfigUiHintSchema = {
label?, help?, group?, // Grouping for UI layout
order?: Integer, // Sort order within group
advanced?, sensitive?,
placeholder?,
itemTemplate?: Unknown // Template for array items
};
UI hints flow through:
- Plugin manifest declares
uiHintskeyed by config path config.schemaRPC method returns schema + uiHints to the control UI- Control UI renders form fields based on JSON Schema type + uiHints metadata
5. SQLite Memory Schema
File: src/memory/memory-schema.ts
Tables
| Table | Columns | Key | Purpose |
|---|---|---|---|
meta | key TEXT, value TEXT | key (PK) | Key-value metadata store |
files | path TEXT, source TEXT DEFAULT 'memory', hash TEXT, mtime INTEGER, size INTEGER | path (PK) | Tracks indexed files and their hashes |
chunks | id TEXT, path TEXT, source TEXT DEFAULT 'memory', start_line INTEGER, end_line INTEGER, hash TEXT, model TEXT, text TEXT, embedding TEXT, updated_at INTEGER | id (PK) | Text chunks with embeddings |
{embeddingCacheTable} | provider TEXT, model TEXT, provider_key TEXT, hash TEXT, embedding TEXT, dims INTEGER, updated_at INTEGER | (provider, model, provider_key, hash) composite PK | Caches computed embeddings |
{ftsTable} | text, id UNINDEXED, path UNINDEXED, source UNINDEXED, model UNINDEXED, start_line UNINDEXED, end_line UNINDEXED | FTS5 virtual table | Full-text search over chunk text |
Indices
| Index | Table | Column(s) |
|---|---|---|
idx_embedding_cache_updated_at | embeddingCacheTable | updated_at |
idx_chunks_path | chunks | path |
idx_chunks_source | chunks | source |
FTS5 Setup
The FTS5 virtual table is created conditionally (ftsEnabled parameter):
- Only the
textcolumn is indexed for search - All other columns (id, path, source, model, start_line, end_line) are
UNINDEXED-- stored in the FTS table but not searchable, available for result retrieval - FTS creation is wrapped in try/catch -- gracefully degrades if FTS5 extension is not available
- Returns
{ftsAvailable: boolean, ftsError?: string}to callers
Schema Migration
The ensureColumn() helper function handles additive migrations:
- Uses
PRAGMA table_info()to check column existence - Adds missing columns via
ALTER TABLE ADD COLUMN - Currently migrates:
files.source,chunks.source(added after initial schema)
6. Session Metadata Types
File: src/config/sessions/types.ts
SessionEntry Interface
The SessionEntry interface is a plain TypeScript type with NO runtime validation schema. It is used directly with Partial<SessionEntry> patches and object spread merging.
| Field | Type | Purpose |
|---|---|---|
sessionId | string | UUID session identifier |
updatedAt | number | Last update timestamp (ms) |
sessionFile | string? | Path to JSONL transcript file |
spawnedBy | string? | Parent session key (sandbox scoping) |
spawnDepth | number? | Subagent depth (0=main, 1=sub, 2=sub-sub) |
systemSent | boolean? | Whether system prompt has been sent |
abortedLastRun | boolean? | Whether last run was aborted |
chatType | SessionChatType? | direct/group/channel |
thinkingLevel | string? | Per-session thinking override |
verboseLevel | string? | Per-session verbosity |
reasoningLevel | string? | Per-session reasoning effort |
elevatedLevel | string? | Elevated permissions level |
ttsAuto | TtsAutoMode? | Text-to-speech mode |
execHost | string? | Execution host override |
execSecurity | string? | Execution security policy override |
execAsk | string? | Execution approval mode override |
execNode | string? | Node override for execution |
responseUsage | string? | Token usage display mode |
providerOverride | string? | Model provider override |
modelOverride | string? | Model override |
authProfileOverride | string? | Auth profile override |
groupActivation | string? | "mention" or "always" |
sendPolicy | string? | "allow" or "deny" |
queueMode | string? | Message queue mode |
inputTokens / outputTokens / totalTokens | number? | Token usage tracking |
totalTokensFresh | boolean? | Whether token count is from latest run |
cacheRead / cacheWrite | number? | Cache usage |
model / modelProvider | string? | Active model info |
contextTokens / compactionCount | number? | Context window usage |
label | string? | User-assigned session label |
channel / groupId / groupChannel / space | string? | Messaging context |
origin | SessionOrigin? | Where the session originated |
lastChannel / lastTo / lastAccountId / lastThreadId | various | Last delivery target |
skillsSnapshot | SessionSkillSnapshot? | Cached skills state |
systemPromptReport | SessionSystemPromptReport? | Context weight analysis |
Security Gap #18
The SessionEntry interface has no Zod or TypeBox schema for runtime validation. The mergeSessionEntry() function performs a simple object spread:
function mergeSessionEntry(existing, patch) {
return { ...existing, ...patch, sessionId, updatedAt };
}
This means:
- Arbitrary properties can be injected into session metadata
- String fields like
execSecurity,execHost,sendPolicyaccept any string -- no enum validation spawnDepthis typed asnumber?but could receive any value at runtimequeueModelists valid values in the TypeScript type but has no runtime check- Session entries are persisted to disk and read back without validation
7. Validation Infrastructure
7.1 Plugin Schema Validator -- src/plugins/schema-validator.ts
Uses AJV (Another JSON Schema Validator) to validate plugin config values:
const ajv = new Ajv({ allErrors: true, strict: false, removeAdditional: false });
function validateJsonSchemaValue(params: {
schema: Record<string, unknown>; // JSON Schema from plugin manifest
cacheKey: string; // Cache key for compiled validator
value: unknown; // Config value to validate
}): { ok: true } | { ok: false; errors: string[] }
Key behaviors:
- Compiled validator caching: Validators are cached by
cacheKeyand recompiled only if the schema object reference changes strict: false: Does not enforce strict JSON Schema validation (allows unknown keywords)removeAdditional: false: Does not strip unknown properties- Error formatting: Converts AJV error paths from
/foo/bartofoo.bardot notation
7.2 Config Validation -- src/config/validation.ts
Three tiers of config validation functions:
| Function | What It Does |
|---|---|
validateConfigObjectRaw() | Legacy check + Zod parse + duplicate agent dirs + avatar paths. Returns raw validated config (no defaults). |
validateConfigObject() | Calls Raw, then applies applySessionDefaults, applyAgentDefaults, applyModelDefaults |
validateConfigObjectWithPlugins() | Calls validateConfigObject + validates plugin manifests, channel IDs, heartbeat targets, plugin config schemas |
Plugin validation within validateConfigObjectWithPluginsBase():
- Validates channel keys against known channel IDs (built-in + plugin-registered)
- Validates heartbeat targets against known channels
- For each plugin with an explicit config entry:
- Loads plugin manifest registry
- Resolves enable/disable state
- Validates config value against manifest's
configSchemavia AJV - Warns if plugin is disabled but has config present
7.3 RPC Parameter Validation -- src/gateway/protocol/index.ts
The protocol index file creates AJV validators for every RPC method by compiling TypeBox schemas:
const ajv = new Ajv({ allErrors: true, strict: false, removeAdditional: false });
export const validateConnectParams = ajv.compile<ConnectParams>(ConnectParamsSchema);
export const validateAgentParams = ajv.compile(AgentParamsSchema);
// ... 70+ more validators
7.4 Handler-Level Validation -- src/gateway/server-methods/validation.ts
The assertValidParams() function is called at the top of each RPC handler:
function assertValidParams<T>(
params: unknown,
validate: Validator<T>, // AJV compiled validator
method: string, // Method name for error messages
respond: RespondFn, // Response callback
): params is T
On validation failure, responds with ErrorCodes.INVALID_REQUEST and formatted validation error details. Returns false to allow early handler exit.
Validation Chain Summary
Layer 1: Config File Load
Zod (strict parse + refinements + transforms)
|
v
Layer 2: Post-Parse Config Validation
Custom checks (duplicates, avatar paths, plugin schemas via AJV)
|
v
Layer 3: Runtime Defaults
applySessionDefaults -> applyAgentDefaults -> applyModelDefaults
|
v
Layer 4: RPC Ingress (per-request)
TypeBox schemas compiled to JSON Schema -> AJV validators
|
v
Layer 5: Application Logic
Type assertions, manual checks (no schema)
8. Schema Gaps and Security Implications
Gap 1: SessionEntry Has No Runtime Validation
Files: src/config/sessions/types.ts
Severity: High
SessionEntry is a TypeScript interface only. Fields like execSecurity, execHost, sendPolicy, queueMode, and elevatedLevel accept arbitrary strings at runtime despite having defined enum values in the type. mergeSessionEntry() performs unguarded object spread, allowing property injection.
Impact: A crafted session patch (e.g., via sessions.patch RPC) could inject arbitrary metadata fields or set security-sensitive fields to unexpected values. While SessionsPatchParamsSchema validates the RPC parameters, the actual session store merge has no schema enforcement.
Gap 2: HealthSnapshotSchema = Type.Any()
File: src/gateway/protocol/schema/snapshot.ts
Severity: Medium
HealthSnapshotSchema = Type.Any() means health data passes through the protocol with zero validation. Any structure or value is accepted and forwarded to clients.
Gap 3: ChannelAccountSnapshotSchema additionalProperties: true
File: src/gateway/protocol/schema/channels.ts
Severity: Low-Medium
ChannelAccountSnapshotSchema uses additionalProperties: true, intentionally allowing any extra fields. While this supports extensibility, it means channel status responses can contain arbitrary data.
Gap 4: Exec Approvals Use Unvalidated Strings
File: src/gateway/protocol/schema/exec-approvals.ts
Severity: Medium
ExecApprovalsDefaultsSchema and ExecApprovalsAgentSchema define security, ask, and askFallback as Type.Optional(Type.String()) without enum constraints. The ExecApprovalResolveParamsSchema.decision is also a bare NonEmptyString. Validation of valid values happens only in application logic.
Gap 5: No Response Schema Validation
Severity: Medium
While every RPC method has a params validator (ajv.compile(ParamsSchema)), there is no equivalent validation of outbound response payloads in most cases. Response schemas exist (e.g., AgentsListResultSchema, LogsTailResultSchema) but are not systematically compiled and checked before sending responses. This means server bugs could send malformed responses to clients.
Gap 6: channels.passthrough() Bypasses Strict Validation
File: src/config/zod-schema.providers.ts
Severity: Low
ChannelsSchema uses .passthrough() instead of .strict(). While this is intentional for plugin extensibility, it means typos in channel config keys are silently accepted. The post-parse validation in validation.ts does check channel keys against known IDs, partially mitigating this gap.
Gap 7: Plugin Config Uses AJV strict:false
File: src/plugins/schema-validator.ts
Severity: Low
Plugin config validation uses strict: false in AJV, which does not reject unknown JSON Schema keywords. A malformed plugin configSchema could behave unexpectedly.
Gap 8: ConnectParams auth.token and auth.password Are Type.String()
File: src/gateway/protocol/schema/frames.ts
Severity: Low
Auth credentials in WebSocket connection params accept any string (including empty strings) because they use Type.Optional(Type.String()) rather than NonEmptyString. The actual auth check happens in application logic, but malformed auth params pass schema validation.
Gap 9: HookConfigSchema Uses .passthrough()
File: src/config/zod-schema.hooks.ts
Severity: Low
HookConfigSchema uses .passthrough() to allow per-hook custom keys. This means hook configs are only partially validated (enabled/env are typed, everything else is accepted).
Gap 10: No Schema for Session File (JSONL) Content
Severity: Medium
Session transcript files are JSONL format with no schema validation on read. Corrupted or crafted session files are processed without validation, relying on application-level error handling.
9. Provider-Specific Schema Normalization
File: src/agents/pi-tools.schema.ts
The normalizeToolParameters() function adapts tool schemas to work across different LLM providers, each of which has different JSON Schema requirements:
Provider Compatibility Matrix
| Provider | Issue | Normalization Applied |
|---|---|---|
| Gemini (google-generative-ai) | Rejects several JSON Schema keywords | cleanSchemaForGemini() strips unsupported keywords |
| OpenAI | Rejects function tool schemas unless top-level is type: "object" | Forces type: "object" on schemas with properties but no explicit type |
| Anthropic | Expects full JSON Schema draft 2020-12 compliance | Passes schema through without modification |
Schema Normalization Logic
-
Simple object schemas (has
type+properties, noanyOf):- Gemini: Apply
cleanSchemaForGemini() - Others: Pass through unchanged
- Gemini: Apply
-
Missing type with object-ish fields (has
properties/required, notype, noanyOf/oneOf):- Add
type: "object"to satisfy OpenAI - Gemini: Also clean for Gemini compatibility
- Add
-
Union schemas (top-level
anyOforoneOf):- Flatten union variants: Merge all variant properties into a single
type: "object"schema - Enum merging:
mergePropertySchemas()combines enum values from different variants (e.g., anactionfield with different allowed values in each variant) - Required field computation: Only fields required in ALL variants become required in the merged schema
- Gemini: Also clean the flattened schema
- This is necessary because:
- Gemini does not allow top-level
typetogether withanyOf - OpenAI rejects schemas without top-level
type: "object" - The merged flat schema works across all providers
- Gemini does not allow top-level
- Flatten union variants: Merge all variant properties into a single
Provider Detection
const isGeminiProvider = provider.includes("google") || provider.includes("gemini");
const isAnthropicProvider = provider.includes("anthropic") || provider.includes("google-antigravity");
Note: google-antigravity is treated as Anthropic, suggesting this is a provider alias or internal codename.
Enum Extraction Helper
extractEnumValues() handles multiple JSON Schema enum representations:
- Direct
enumarrays constvalues (wrapped in array)anyOf/oneOfarrays (recursively extracted)
This ensures that when flattening a union type, property-level enums from different variants are merged correctly.
Appendix: Schema File Index
| File | Schema System | Layer |
|---|---|---|
src/config/zod-schema.ts | Zod | Config root |
src/config/zod-schema.core.ts | Zod | Config building blocks |
src/config/zod-schema.agent-runtime.ts | Zod | Agent + tools config |
src/config/zod-schema.agents.ts | Zod | Agent list + bindings |
src/config/zod-schema.agent-model.ts | Zod | Model selection |
src/config/zod-schema.agent-defaults.ts | Zod | Agent defaults |
src/config/zod-schema.session.ts | Zod | Session + messages + commands |
src/config/zod-schema.approvals.ts | Zod | Exec approval forwarding |
src/config/zod-schema.providers.ts | Zod | Channel aggregator |
src/config/zod-schema.providers-core.ts | Zod | Per-provider schemas |
src/config/zod-schema.hooks.ts | Zod | Webhook + internal hooks |
src/config/zod-schema.allowdeny.ts | Zod | Allow/deny rule factory |
src/config/zod-schema.installs.ts | Zod | Plugin/skill install records |
src/config/zod-schema.channels.ts | Zod | Channel heartbeat visibility |
src/config/zod-schema.sensitive.ts | Zod | Sensitive field registry |
src/gateway/protocol/schema/frames.ts | TypeBox | WebSocket frames |
src/gateway/protocol/schema/primitives.ts | TypeBox | Primitive types |
src/gateway/protocol/schema/agent.ts | TypeBox | Agent RPC methods |
src/gateway/protocol/schema/agents-models-skills.ts | TypeBox | CRUD + catalog methods |
src/gateway/protocol/schema/config.ts | TypeBox | Config RPC methods |
src/gateway/protocol/schema/sessions.ts | TypeBox | Session management methods |
src/gateway/protocol/schema/cron.ts | TypeBox | Cron job methods |
src/gateway/protocol/schema/exec-approvals.ts | TypeBox | Exec approval methods |
src/gateway/protocol/schema/devices.ts | TypeBox | Device pairing methods |
src/gateway/protocol/schema/logs-chat.ts | TypeBox | Log tailing + chat methods |
src/gateway/protocol/schema/nodes.ts | TypeBox | Node management methods |
src/gateway/protocol/schema/wizard.ts | TypeBox | Setup wizard methods |
src/gateway/protocol/schema/snapshot.ts | TypeBox | State snapshots |
src/gateway/protocol/schema/push.ts | TypeBox | Push notification methods |
src/gateway/protocol/schema/channels.ts | TypeBox | Channel status + talk methods |
src/memory/memory-schema.ts | Raw SQL | SQLite memory index |
src/config/sessions/types.ts | TypeScript only | Session metadata (NO runtime validation) |
src/plugins/schema-validator.ts | AJV | Plugin config validation |
src/config/validation.ts | Zod + custom | Config validation pipeline |
src/gateway/protocol/index.ts | AJV (from TypeBox) | RPC param validators |
src/gateway/server-methods/validation.ts | AJV | Handler-level validation guard |
src/agents/pi-tools.schema.ts | Custom | Provider schema normalization |
src/plugins/manifest.ts | Manual | Plugin manifest loading |
src/plugins/types.ts | TypeScript | Plugin type definitions |