Skip to main content

agentd/config/v2/
schema.rs

1// SPDX-License-Identifier: Apache-2.0
2//! The **JSON Schema (Draft 2020-12) of the v2 settings document** (RFC 0030
3//! §3) — hand-written (no `schemars`, the moat) and kept faithful to
4//! [`super::Settings`] by the drift tests in `super::tests`. It is the single
5//! source for the path bindings (env `AGENTD_<PATH>` names, `--<path>` flags,
6//! `--help`), for `--config-schema=2`, and for agentctl's admission validation.
7//!
8//! Conventions: every object is `additionalProperties: false` (mirrors
9//! `deny_unknown_fields`); durations are strings (`10m`, `500ms`, bare seconds
10//! also accepted); secrets are strings that MUST be `{{secret:…}}` /
11//! `{{secret-file:…}}` references when they come from a file (§5).
12
13use serde_json::{Map, Value, json};
14
15/// The schema's `x-agentd-contract-version` — the 2.0 config contract.
16pub const SCHEMA_CONTRACT_VERSION: &str = "2.0";
17
18/// The document version this schema describes (`config_version`).
19pub const CONFIG_VERSION: &str = "2";
20
21pub fn schema() -> Value {
22    let duration = json!({ "type": ["string", "integer"], "description": "a duration: `10m`, `90s`, `500ms`, or bare seconds" });
23    let secret = json!({ "type": "string", "description": "a secret — from a file it MUST be a `{{secret:NAME}}` / `{{secret-file:PATH}}` reference; env/flag values may be inline" });
24    let string_map = json!({ "type": "object", "additionalProperties": { "type": "string" } });
25    let tool_select = json!({
26        "oneOf": [
27            { "enum": ["all", "none"] },
28            { "type": "array", "items": { "type": "string" } }
29        ],
30        "description": "`all` | `none` | a list of names"
31    });
32    let budget = json!({
33        "type": "object",
34        "additionalProperties": false,
35        "properties": {
36            "windows": { "type": "array", "items": { "$ref": "#/$defs/BudgetWindow" } },
37            "lifetime_tokens": { "type": "integer", "minimum": 0, "description": "hard ceiling; 0 = unbounded" },
38            "scope": { "type": "array", "items": { "enum": ["instance", "run", "conversation", "principal"] } },
39            "on_exhausted": { "enum": ["wait", "slow", "degrade", "refuse", "fail"] },
40            "slow": { "type": "object", "additionalProperties": false, "properties": { "factor": { "type": "number", "exclusiveMinimum": 0, "maximum": 1 } } },
41            "degrade": { "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" } } },
42            "reserve": { "type": "object", "additionalProperties": false, "properties": {
43                "estimate": { "enum": ["context", "fixed", "none"] },
44                "fixed": { "type": "integer", "minimum": 0 } } }
45        }
46    });
47    let mut properties = Map::new();
48    properties.insert(
49        "config_version".to_string(),
50        json!({ "type": "string", "const": CONFIG_VERSION, "description": "the document version" }),
51    );
52    top_level_properties(
53        &mut properties,
54        &duration,
55        &secret,
56        &string_map,
57        &tool_select,
58        &budget,
59    );
60    let mut defs = Map::new();
61    defs_properties(&mut defs, &secret, &string_map, &budget, &duration);
62    json!({
63        "$schema": "https://json-schema.org/draft/2020-12/schema",
64        "$id": format!("https://agentd.dev/schema/config/{SCHEMA_CONTRACT_VERSION}"),
65        "x-agentd-contract-version": SCHEMA_CONTRACT_VERSION,
66        "title": "agentd settings (v2)",
67        "description": "agentd 2.0 configuration document (YAML or JSON; several files merge in order; every path is also AGENTD_<PATH> and --<path>)",
68        "type": "object",
69        "additionalProperties": false,
70        "properties": Value::Object(properties),
71        "$defs": Value::Object(defs)
72    })
73}
74
75#[allow(clippy::too_many_arguments)]
76fn top_level_properties(
77    m: &mut Map<String, Value>,
78    duration: &Value,
79    secret: &Value,
80    string_map: &Value,
81    tool_select: &Value,
82    budget: &Value,
83) {
84    m.insert("agent".to_string(), json!({
85                "type": "object", "additionalProperties": false,
86                "properties": {
87                    "name": { "type": "string", "description": "instance identity (falls back to the downward-API instance, then the hostname)" },
88                    "instruction": { "type": "string", "description": "static text, or a single-token URI a configured MCP server serves (read + subscribed)" },
89                    "prompt": { "type": "string", "description": "a one-shot task (--prompt): with no workflows configured the generated run executes this, while `instruction` stays the standing policy (the run's system prompt)" },
90                    "preflight": { "enum": ["never", "auto", "always"] },
91                    "wake_on": { "type": "array", "items": { "enum": ["a2a_message", "human_reply", "subagent_result", "workflow_finished", "workflow_failed", "instruction_updated", "budget_resumed"] } },
92                    "on_workflow_finished": { "enum": ["ignore", "note", "think"] },
93                    "tools": { "type": "object", "additionalProperties": false, "properties": {
94                        "internal": tool_select, "mcp": tool_select, "code": tool_select } },
95                    "max_parallel_turns": { "type": "integer", "minimum": 1 },
96                    "conversation_budget": budget,
97                    "ask_human_fallback": { "enum": ["wait", "pause", "idle", "fail", "finish", "stop", "auto"], "description": "what ask_human does with no human channel (and, for auto, on an unanswered gate timeout): wait (park until timeout), fail (default), or auto (an LLM judge answers on the operator's behalf, marked as auto)" }
98                }
99            }));
100    m.insert("intelligence".to_string(), json!({
101                "type": "object", "additionalProperties": false,
102                "properties": {
103                    "endpoints": { "oneOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "string" } ], "description": "ordered endpoint list (failover); one comma-separated string is accepted" },
104                    "model": { "type": "string" },
105                    "dialect": { "enum": ["openai", "anthropic", "bedrock"], "description": "wire dialect; bedrock = native Amazon Bedrock Converse (pair with auth.kind=aws)" },
106                    "token": secret,
107                    "token_file": { "type": "string" },
108                    "headers": string_map,
109                    "auth": { "$ref": "#/$defs/Auth" },
110                    "swap_policy": { "enum": ["finish-on-old", "restart-turn"] },
111                    "structured_output": { "enum": ["auto", "json_schema", "tool", "prompt"] },
112                    "budget": budget,
113                    "pricing": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Pricing" } },
114                    "timeout": duration
115                }
116            }));
117    m.insert(
118        "mcp".to_string(),
119        json!({
120            "type": "object", "additionalProperties": false,
121            "properties": {
122                "servers": { "type": "array", "items": { "$ref": "#/$defs/McpServer" } },
123                "default_timeout": duration
124            }
125        }),
126    );
127    m.insert("tools".to_string(), json!({
128                "type": "object", "additionalProperties": false,
129                "properties": {
130                    "disabled": { "type": "array", "items": { "type": "string" } },
131                    "overrides": { "type": "object", "additionalProperties": { "$ref": "#/$defs/ToolOverride" } }
132                }
133            }));
134    m.insert("store".to_string(), json!({
135                "type": "object", "additionalProperties": false,
136                "properties": {
137                    "kind": { "enum": ["mcp", "http", "file", "memory", "none"] },
138                    "prefix": { "type": "string" },
139                    "mcp": { "$ref": "#/$defs/StoreMcp" },
140                    "http": { "$ref": "#/$defs/StoreHttp" },
141                    "file": { "$ref": "#/$defs/StoreFile" },
142                    "checkpoint": { "type": "object", "additionalProperties": false, "properties": { "debounce_ms": { "type": "integer", "minimum": 0 } } },
143                    "durability": { "type": "object", "additionalProperties": false, "properties": {
144                        "a2a": { "enum": ["strict", "eventual"] }, "steps": { "enum": ["strict", "eventual"] } } },
145                    "on_error": { "enum": ["halt", "degrade"] },
146                    "audit": { "type": "boolean" },
147                    "timeout": duration
148                }
149            }));
150    m.insert(
151        "memory".to_string(),
152        json!({ "type": "object", "additionalProperties": false, "properties": {
153                "max_value_bytes": { "type": "integer", "minimum": 1 },
154                "list_default_limit": { "type": "integer", "minimum": 1 } } }),
155    );
156    m.insert("context".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
157                "compact_at": { "type": "number", "exclusiveMinimum": 0, "maximum": 1 },
158                "keep_last": { "type": "integer", "minimum": 0 },
159                "model_window": { "type": "integer", "minimum": 1, "description": "the model's context window in tokens (overrides the value inferred from intelligence.model)" },
160                "plan": { "type": "object", "additionalProperties": false, "properties": { "max_items": { "type": "integer", "minimum": 1 } } } } }));
161    m.insert("knowledge".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
162                "server": { "type": "string" },
163                "auto_context": { "type": "object", "additionalProperties": false, "properties": {
164                    "on": { "enum": ["turn", "never"] }, "top_k": { "type": "integer", "minimum": 1 }, "max_bytes": { "type": "integer", "minimum": 1 } } } } }));
165    m.insert("search".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": { "server": { "type": "string" } } }));
166    m.insert(
167        "skills".to_string(),
168        json!({ "type": "object", "additionalProperties": false, "properties": {
169                "sources": { "type": "array", "items": { "$ref": "#/$defs/SkillSource" } },
170                "reference_prefix": { "type": "string" },
171                "max_loaded": { "type": "integer", "minimum": 1 },
172                "max_bytes": { "type": "integer", "minimum": 1 } } }),
173    );
174    m.insert("workflows".to_string(), json!({ "type": "array", "items": { "$ref": "#/$defs/WorkflowRef" }, "description": "inline dialect-3 definitions or {name, file|uri} references" }));
175    m.insert("limits".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
176                "max_runs": { "type": "integer", "minimum": 1 },
177                "run": { "type": "object", "additionalProperties": false, "properties": {
178                    "steps": { "type": "integer", "minimum": 1 }, "tokens": { "type": "integer", "minimum": 1 }, "deadline": duration } },
179                "subagents": { "type": "object", "additionalProperties": false, "properties": {
180                    "depth": { "type": "integer", "minimum": 0 }, "breadth": { "type": "integer", "minimum": 1 },
181                    "total": { "type": "integer", "minimum": 1 }, "rate": { "type": "string", "description": "`<burst>/<per>s`, e.g. `8/2s`" } } },
182                "inline_max_bytes": { "type": "integer", "minimum": 1 },
183                "step_timeout": duration } }));
184    m.insert("lifecycle".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
185                "run_until": { "enum": ["auto", "idle", "drained"] },
186                "idle_grace": duration,
187                "drain_timeout": duration,
188                "run_id": { "type": "string" },
189                "exit_code_map": { "type": "object", "additionalProperties": { "type": "integer", "minimum": 0, "maximum": 255 }, "description": "remap the policy exit codes (3/7 only): {\"3\": N, \"7\": N}" },
190                "watch_config": { "type": "boolean" } } }));
191    m.insert("a2a".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
192                "listen": { "type": "string", "description": "https://host:port (loopback http:// for dev)" },
193                "tls": { "type": "object", "additionalProperties": false, "properties": {
194                    "cert": { "type": "string" }, "key": { "type": "string" }, "client_ca": { "type": "string" } } },
195                "bearer": secret,
196                "principals": { "type": "array", "items": { "$ref": "#/$defs/Principal" } },
197                "peers": { "type": "array", "items": { "$ref": "#/$defs/A2aPeer" } },
198                "conversation_ttl": duration,
199                "push": { "type": "object", "additionalProperties": false,
200                    "description": "push notifications: a caller registers a webhook and agentd POSTs its task's updates there. Default-OFF — the URL comes from a peer, so making the request at all is the operator's decision.",
201                    "properties": {
202                    "enabled": { "type": "boolean", "description": "accept CreateTaskPushNotificationConfig and deliver on transitions" },
203                    "allow_private": { "type": "boolean", "description": "permit webhook targets on private / loopback addresses (a separate and larger decision — a peer could otherwise reach agentd's own surfaces or a cloud metadata endpoint)" } } } } }));
204    m.insert("interface".to_string(), json!({ "type": "object", "additionalProperties": false,
205                "description": "The display-client (TUI/web-UI) surface (RFC 0032), served on the A2A listener. Default-OFF.",
206                "properties": {
207                "enabled": { "type": "boolean", "description": "serve the interface methods (SubscribeToEvents, interface.info, …)" },
208                "debug": { "type": "boolean", "description": "expose extra debug information (transcripts, run step detail, the log ring, audit feed events); runtime-togglable via the config.set op" },
209                "origins": { "type": "array", "items": { "type": "string" }, "description": "extra allowed browser origins (scheme://host[:port]) for a hosted web UI; loopback origins never need listing" },
210                "display": { "type": "object", "additionalProperties": false,
211                    "description": "what clients render in their chrome — ordered item lists for the top (header) and bottom (status bar) edges; unknown items are skipped",
212                    "properties": {
213                    "top": { "type": "array", "items": { "type": "string" } },
214                    "bottom": { "type": "array", "items": { "type": "string" } } } },
215                "pairing": { "type": "object", "additionalProperties": false,
216                    "description": "pairing-code login: a rotating 6-digit code (shown to operators) a client exchanges for a session token — the low-friction alternative to copying a bearer",
217                    "properties": {
218                    "enabled": { "type": "boolean" },
219                    "role": { "enum": ["operator", "user", "agent", "anonymous"], "description": "the role a paired session gets (operator or user; default operator)" },
220                    "ttl": { "type": ["string", "integer"], "description": "session-token lifetime (default 12h)" } } } } }));
221    m.insert("webhooks".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
222                "listen": { "type": "string", "description": "https://host:port (loopback http:// for dev) — the inbound webhook surface" },
223                "tls": { "type": "object", "additionalProperties": false, "properties": {
224                    "cert": { "type": "string" }, "key": { "type": "string" }, "client_ca": { "type": "string" } } },
225                "default_auth": { "$ref": "#/$defs/WebhookAuth" } } }));
226    m.insert("goal".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
227                "statement": { "type": "string", "description": "the goal in natural language (the LLM judge reads it)" },
228                "check": { "type": "object", "additionalProperties": false, "properties": {
229                    "every": duration, "condition": { "type": "string", "description": "a cheap CEL predicate over durable state, evaluated first" }, "via": { "enum": ["both", "condition", "agent"] } } },
230                "stuck_after": { "type": "integer", "minimum": 1 },
231                "on_achieved": { "$ref": "#/$defs/GoalAction" },
232                "on_stuck": { "$ref": "#/$defs/GoalAction" } } }));
233    m.insert("observability".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
234                "log_level": { "enum": ["trace", "debug", "info", "warn", "error"] },
235                "log_content": { "type": "boolean" },
236                "otel": { "type": "object", "additionalProperties": false, "properties": {
237                    "endpoint": { "type": "string" }, "traces": { "type": "boolean" }, "metrics": { "type": "boolean" }, "logs": { "type": "boolean" } } },
238                "metrics_addr": { "type": "string" },
239                "health_file": { "type": "string" },
240                "report_file": { "type": "string" },
241                "events_ring": { "type": "integer", "minimum": 1 },
242                "audit": { "type": "object", "additionalProperties": false, "properties": {
243                    "sink": { "type": "array", "items": { "enum": ["log", "store"] } } } },
244                "traceparent": { "type": "string" } } }));
245    m.insert("security".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
246                "allow_trifecta": { "type": "boolean" },
247                "tls_ca": { "type": "string" },
248                "aauth": { "$ref": "#/$defs/AAuth" },
249                "cgroup": { "type": "object", "additionalProperties": false, "properties": {
250                    "spec": { "type": "string" }, "memory_max": { "type": "string" }, "pids_max": { "type": "string" } } },
251                "exec": { "type": "object", "additionalProperties": false,
252                    "description": "The guarded local command runner (default-OFF; needs --features exec).", "properties": {
253                    "enabled": { "type": "boolean" },
254                    "allow": { "type": "array", "items": { "type": "string" }, "description": "allow-listed command names (argv[0])" },
255                    "workdir": { "type": "string" }, "timeout": duration,
256                    "max_output": { "type": "integer" },
257                    "env": { "type": "array", "items": { "type": "string" }, "description": "env var names passed through" } } } } }));
258}
259
260fn defs_properties(
261    m: &mut Map<String, Value>,
262    secret: &Value,
263    string_map: &Value,
264    budget: &Value,
265    duration: &Value,
266) {
267    m.insert("BudgetWindow".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["per"], "properties": {
268                "per": { "enum": ["second", "minute", "hour", "day", "week"] },
269                "tokens": { "type": "integer", "minimum": 1 },
270                "requests": { "type": "integer", "minimum": 1 },
271                "reset": { "type": "string", "pattern": "^[0-9]{2}:[0-9]{2}Z$", "description": "calendar-window reset time (UTC), e.g. 00:00Z" } } }));
272    m.insert("Pricing".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
273                "input_per_1k": { "type": "number", "minimum": 0 }, "output_per_1k": { "type": "number", "minimum": 0 }, "currency": { "type": "string" } } }));
274    m.insert("McpServer".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["name", "endpoint"], "properties": {
275                "name": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" },
276                "endpoint": { "type": "string" },
277                "ns": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$", "description": "tool namespace prefix (`ns.tool`)" },
278                "headers": string_map,
279                "tags": { "type": "object", "additionalProperties": { "type": "array", "items": { "enum": ["untrusted_input", "sensitive", "egress"] } } },
280                "aauth": { "type": "boolean" },
281                "oauth": { "type": "object", "additionalProperties": false, "required": ["token_url", "client_id", "client_secret"], "properties": {
282                    "token_url": { "type": "string" }, "client_id": { "type": "string" }, "client_secret": secret, "scope": { "type": "string" } } },
283                "auth": { "$ref": "#/$defs/Auth" },
284                "timeout": duration } }));
285    m.insert("Auth".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["kind"],
286                "description": "A unified credential provider (RFC 0031).", "properties": {
287                "kind": { "enum": ["static", "oauth2", "aws", "spiffe"] },
288                "issuer": { "type": "string" }, "token_url": { "type": "string" },
289                "device_authorization_url": { "type": "string" }, "authorization_url": { "type": "string" },
290                "client_id": { "type": "string" }, "client_secret": secret,
291                "grant": { "enum": ["device", "authorization_code", "client_credentials"] },
292                "scopes": { "type": "array", "items": { "type": "string" } }, "audience": { "type": "string" },
293                "token": secret, "header": { "type": "string" }, "value": secret,
294                "region": { "type": "string" }, "service": { "type": "string" },
295                "source": { "enum": ["env", "static", "imds", "irsa", "sso"] },
296                "sso_start_url": { "type": "string" }, "account_id": { "type": "string" }, "role_name": { "type": "string" },
297                "svid": { "enum": ["jwt", "x509"] }, "jwt_svid_file": { "type": "string" },
298                "svid_file": { "type": "string" }, "key_file": { "type": "string" } } }));
299    m.insert("ToolOverride".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["server", "tool"], "properties": {
300                "server": { "type": "string" }, "tool": { "type": "string" },
301                "args": { "type": "string", "description": "a JSON template or `CEL: …` producing the MCP tool arguments from `args`/`ctx`" },
302                "result": { "type": "string", "description": "a JSON pointer / template / `CEL: …` mapping the CallToolResult to the internal output schema" } } }));
303    m.insert("WebhookAuth".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
304                "hmac": { "type": "object", "additionalProperties": false, "properties": {
305                    "secret": secret, "header": { "type": "string", "description": "the header carrying the signature (default X-Signature)" }, "algo": { "enum": ["sha256"] }, "prefix": { "type": "string", "description": "a prefix stripped before the constant-time compare, e.g. sha256=" } } },
306                "bearer": secret,
307                "header": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "equals": secret } },
308                "none": { "type": "boolean", "description": "loopback-only, no auth (dev) — explicit opt-in" } } }));
309    m.insert("GoalAction".to_string(), json!({ "oneOf": [
310                { "enum": ["finish", "idle", "replan", "escalate"] },
311                { "type": "object", "additionalProperties": false, "required": ["workflow"], "properties": { "workflow": { "type": "string" } } } ] }));
312    m.insert("StoreOp".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["tool"], "properties": {
313                "tool": { "type": "string" }, "args": { "type": "string" }, "ok": { "type": "string" }, "conflict": { "type": "string" },
314                "value": { "type": "string" }, "keys": { "type": "string" } } }));
315    m.insert("StoreMcp".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["server"], "properties": {
316                "server": { "type": "string" },
317                "put": { "$ref": "#/$defs/StoreOp" }, "get": { "$ref": "#/$defs/StoreOp" },
318                "list": { "$ref": "#/$defs/StoreOp" }, "delete": { "$ref": "#/$defs/StoreOp" } } }));
319    m.insert("HttpOp".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["url"], "properties": {
320                "method": { "enum": ["GET", "PUT", "POST", "DELETE"] }, "url": { "type": "string" }, "body": { "type": "string" },
321                "value": { "type": "string" }, "keys": { "type": "string" }, "conflict_status": { "type": "integer", "minimum": 100, "maximum": 599 } } }));
322    m.insert("StoreHttp".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["base_url"], "properties": {
323                "base_url": { "type": "string" }, "headers": string_map,
324                "get": { "$ref": "#/$defs/HttpOp" }, "put": { "$ref": "#/$defs/HttpOp" },
325                "list": { "$ref": "#/$defs/HttpOp" }, "delete": { "$ref": "#/$defs/HttpOp" } } }));
326    // RFC 0033 §4: the only knob is where the state lives, and it is optional —
327    // an omitted `path` resolves through $AGENTD_STATE_DIR / $XDG_STATE_HOME.
328    m.insert("StoreFile".to_string(), json!({ "type": "object", "additionalProperties": false, "properties": {
329                "path": { "type": "string", "description": "the state root; default $AGENTD_STATE_DIR, else $XDG_STATE_HOME/agentd/state, else $HOME/.local/state/agentd/state, else the OS temp dir" } } }));
330    m.insert("SkillSource".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["server"], "properties": {
331                "server": { "type": "string" }, "discover": { "enum": ["prompts", "resources", "auto"] }, "filter": { "type": "string" } } }));
332    m.insert("WorkflowRef".to_string(), json!({ "type": "object", "required": ["name"], "properties": {
333                "name": { "type": "string" }, "armed": { "type": "boolean" }, "file": { "type": "string" }, "uri": { "type": "string" } },
334                "additionalProperties": true,
335                "description": "a {name, file} / {name, uri} reference, or an inline dialect-3 definition (RFC 0027)" }));
336    m.insert("Principal".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["match", "role"], "properties": {
337                "match": { "type": "object", "additionalProperties": false, "properties": {
338                    "san": { "type": "string" }, "sub": { "type": "string" }, "bearer_ref": { "type": "string" }, "aauth_agent": { "type": "string" }, "any": { "type": "boolean" } } },
339                "role": { "enum": ["operator", "user", "agent", "anonymous"] },
340                "grants": { "type": "array", "items": { "type": "string" } },
341                "quotas": { "type": "object", "additionalProperties": false, "properties": {
342                    "rate": { "type": "string" }, "budget": budget } } } }));
343    m.insert("A2aPeer".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["name", "endpoint"], "properties": {
344                "name": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" }, "endpoint": { "type": "string" },
345                "headers": string_map, "client_cert": { "type": "string" }, "client_key": { "type": "string" },
346                "auth": { "$ref": "#/$defs/Auth" } } }));
347    m.insert("AAuth".to_string(), json!({ "type": "object", "additionalProperties": false, "required": ["provider"], "properties": {
348                "provider": { "type": "string" }, "key_file": { "type": "string" }, "enroll_token": secret,
349                "enroll_assertion_file": { "type": "string" }, "person_server": { "type": "string" } } }));
350}