nono-cli 0.52.2

CLI for nono capability-based sandbox
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://nono.sh/schemas/nono-profile.schema.json",
  "title": "nono Profile",
  "description": "A nono profile definition that configures sandbox capabilities, filesystem access, network policy, and other security settings for running untrusted processes.",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "JSON Schema URI for editor validation and autocompletion."
    },
    "extends": {
      "oneOf": [
        { "type": "string" },
        {
          "type": "array",
          "items": { "type": "string" },
          "minItems": 1
        }
      ],
      "description": "Name of a base profile (or array of base profiles) to inherit from. All fields from the base profile are used as defaults, with this profile's values taking precedence. When an array is given, bases are merged left-to-right before the child is applied. Shared transitive bases are deduplicated: each profile is resolved at most once."
    },
    "meta": {
      "$ref": "#/$defs/ProfileMeta",
      "description": "Profile metadata including name, version, description, and author."
    },
    "security": {
      "$ref": "#/$defs/SecurityConfig",
      "description": "Security configuration for process-level isolation modes (signal, IPC, process info, capability elevation, WSL2 proxy policy)."
    },
    "groups": {
      "$ref": "#/$defs/GroupsConfig",
      "description": "Group composition: include and exclude policy group names resolved from policy.json."
    },
    "commands": {
      "$ref": "#/$defs/CommandsConfig",
      "description": "Command allow/deny lists applied during sandbox setup."
    },
    "filesystem": {
      "$ref": "#/$defs/FilesystemConfig",
      "description": "Filesystem access rules specifying which directories and files the sandboxed process can read or write."
    },
    "network": {
      "$ref": "#/$defs/NetworkConfig",
      "description": "Network access configuration including proxy settings, credential injection, and port allowlists."
    },
    "env_credentials": {
      "$ref": "#/$defs/SecretsConfig",
      "description": "Maps keystore account names (or op://, apple-password://, env:// URIs) to environment variable names. Secrets are loaded from the system keystore at startup."
    },
    "secrets": {
      "$ref": "#/$defs/SecretsConfig",
      "description": "Alias for env_credentials. Maps keystore account names to environment variable names."
    },
    "environment": {
      "oneOf": [
        { "$ref": "#/$defs/EnvironmentConfig" },
        { "type": "null" }
      ],
      "description": "Controls which environment variables are passed to the sandboxed process. By default all variables are inherited."
    },
    "workdir": {
      "$ref": "#/$defs/WorkdirConfig",
      "description": "Working directory access configuration controlling whether and how the current working directory is shared with the sandboxed process."
    },
    "hooks": {
      "$ref": "#/$defs/HooksConfig",
      "description": "Hook configurations for target applications. Maps application names to their hook definitions."
    },
    "rollback": {
      "$ref": "#/$defs/RollbackConfig",
      "description": "Rollback snapshot configuration controlling which files are excluded from undo snapshots."
    },
    "undo": {
      "$ref": "#/$defs/RollbackConfig",
      "description": "Alias for rollback. Controls which files are excluded from undo snapshots."
    },
    "open_urls": {
      "oneOf": [
        { "$ref": "#/$defs/OpenUrlConfig" },
        { "type": "null" }
      ],
      "description": "Supervisor-delegated URL opening configuration for OAuth2 login flows and similar operations. When present, replaces the base profile's config entirely. When absent or null, inherits from the base profile. To clear inherited permissions, provide an explicit empty object with allow_origins: [] and allow_localhost: false."
    },
    "allow_launch_services": {
      "oneOf": [
        { "type": "boolean" },
        { "type": "null" }
      ],
      "description": "Opt-in gate for temporary direct LaunchServices opens on macOS. Must be paired with the CLI flag --allow-launch-services. Set to null to inherit from the base profile."
    },
    "allow_gpu": {
      "oneOf": [
        { "type": "boolean" },
        { "type": "null" }
      ],
      "description": "Opt-in gate for GPU access (Metal/IOKit on Apple Silicon macOS, render nodes on Linux). Intel Macs are not yet supported. Must be paired with the CLI flag --allow-gpu. Set to null to inherit from the base profile."
    },
    "interactive": {
      "type": "boolean",
      "description": "Deprecated. Parsed for backward compatibility but ignored. Supervised mode preserves TTY by default.",
      "deprecated": true
    },
    "packs": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Pack dependencies verified at launch. Each entry is a namespace/name reference to an installed pack."
    },
    "command_args": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Extra arguments appended to the child command at launch. Supports variable expansion (e.g. $NONO_PACKAGES)."
    },
    "unsafe_macos_seatbelt_rules": {
      "type": "array",
      "items": { "type": "string" },
      "description": "macOS-only. Expert escape hatch: raw Seatbelt S-expression rules applied verbatim to the sandbox policy. Each entry must be a valid S-expression, e.g. \"(allow iokit-open)\". Rules are validated at load time and rejected if malformed. Ignored on Linux. Surfaced prominently in 'nono profile show' output. If a rule pattern becomes common, prefer promoting it to a typed first-class capability instead."
    }
  },
  "$defs": {
    "ProfileMeta": {
      "type": "object",
      "description": "Profile metadata.",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "description": "Human-readable name of this profile."
        },
        "version": {
          "type": "string",
          "description": "Version string for this profile definition."
        },
        "description": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Optional description of what this profile is for."
        },
        "author": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Optional author or maintainer of this profile."
        }
      }
    },
    "SecurityConfig": {
      "type": "object",
      "description": "Security configuration for process-level isolation modes.",
      "additionalProperties": false,
      "properties": {
        "signal_mode": {
          "oneOf": [
            { "$ref": "#/$defs/ProfileSignalMode" },
            { "type": "null" }
          ],
          "description": "Signal isolation mode controlling whether the sandboxed process can signal other processes. When null, inherits from the base profile (defaults to isolated)."
        },
        "process_info_mode": {
          "oneOf": [
            { "$ref": "#/$defs/ProfileProcessInfoMode" },
            { "type": "null" }
          ],
          "description": "Process inspection mode controlling whether the sandboxed process can read process info for other processes. When null, defaults to isolated."
        },
        "ipc_mode": {
          "oneOf": [
            { "$ref": "#/$defs/ProfileIpcMode" },
            { "type": "null" }
          ],
          "description": "IPC mode controlling whether the sandboxed process can use POSIX semaphores (needed for multiprocessing). When null, defaults to shared_memory_only."
        },
        "capability_elevation": {
          "oneOf": [
            { "type": "boolean" },
            { "type": "null" }
          ],
          "description": "Enable runtime capability elevation via seccomp-notify (Linux). When true, the supervisor intercepts file opens and can grant access to paths not in the initial capability set. When false, the sandbox is static."
        },
        "wsl2_proxy_policy": {
          "oneOf": [
            { "$ref": "#/$defs/Wsl2ProxyPolicy" },
            { "type": "null" }
          ],
          "description": "WSL2 proxy fallback policy. Controls behavior when proxy-only network mode cannot be kernel-enforced on WSL2 (seccomp notify returns EBUSY). Default: error (refuse to run). Set to insecure_proxy to allow degraded execution where the credential proxy runs but network is not locked down."
        }
      }
    },
    "Wsl2ProxyPolicy": {
      "type": "string",
      "enum": ["error", "insecure_proxy"],
      "description": "WSL2 proxy fallback policy. 'error' (default) refuses to run if proxy-only mode cannot be kernel-enforced. 'insecure_proxy' allows degraded execution where the credential proxy injects credentials but the sandboxed process is not prevented from bypassing the proxy."
    },
    "ProfileSignalMode": {
      "type": "string",
      "enum": ["isolated", "allow_same_sandbox", "allow_all"],
      "description": "Signal isolation mode for the sandboxed process."
    },
    "ProfileProcessInfoMode": {
      "type": "string",
      "enum": ["isolated", "allow_same_sandbox", "allow_all"],
      "description": "Process inspection mode for the sandboxed process."
    },
    "ProfileIpcMode": {
      "type": "string",
      "enum": ["shared_memory_only", "full"],
      "description": "IPC mode for the sandboxed process. 'shared_memory_only' allows POSIX shared memory but denies semaphores. 'full' enables both shared memory and semaphores (required for Python multiprocessing and similar runtimes)."
    },
    "FilesystemConfig": {
      "type": "object",
      "description": "Filesystem access rules for the sandboxed process.",
      "additionalProperties": false,
      "properties": {
        "allow": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Directories with read+write access."
        },
        "read": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Directories with read-only access."
        },
        "write": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Directories with write-only access."
        },
        "allow_file": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Single files with read+write access."
        },
        "read_file": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Single files with read-only access."
        },
        "write_file": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Single files with write-only access."
        },
        "deny": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Paths denied filesystem access. Denies apply regardless of other allow rules."
        },
        "bypass_protection": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Paths exempted from group-level deny rules. Each path must also appear in an allow/read/write section to actually grant access; bypass_protection only removes the deny."
        },
        "suppress_save_prompt": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Paths whose runtime denials should not be offered in the save-profile prompt. Does not grant access, remove deny rules, or hide diagnostic output."
        },
        "ignore": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Alias for suppress_save_prompt. Prefer filesystem.suppress_save_prompt in new profiles."
        }
      }
    },
    "GroupsConfig": {
      "type": "object",
      "description": "Group composition for the sandboxed process.",
      "additionalProperties": false,
      "properties": {
        "include": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Policy group names to include. Groups are resolved against policy.json."
        },
        "exclude": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Policy group names to remove from the resolved group set (subtractive composition)."
        }
      }
    },
    "CommandsConfig": {
      "type": "object",
      "description": "Command allow/deny lists applied during sandbox setup.",
      "additionalProperties": false,
      "properties": {
        "allow": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Deprecated in v0.33.0. Startup-only command allowlist override. Not enforced for child processes; prefer resource-based controls instead.",
          "deprecated": true
        },
        "deny": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Deprecated in v0.33.0. Startup-only command denylist extension. Not enforced for child processes; prefer resource-based controls instead.",
          "deprecated": true
        }
      }
    },
    "NetworkConfig": {
      "type": "object",
      "description": "Network access configuration including proxy, credential injection, and port allowlists.",
      "additionalProperties": false,
      "properties": {
        "block": {
          "type": "boolean",
          "description": "Block network access. Network is allowed by default; set to true to deny all outbound traffic."
        },
        "network_profile": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Network proxy profile name from network-policy.json. When set, outbound traffic is filtered through the proxy. Set to null to clear an inherited value."
        },
        "allow_domain": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Additional domains to allow through the proxy on top of the network profile hosts."
        },
        "proxy_allow": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Legacy alias for allow_domain."
        },
        "allow_proxy": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Legacy alias for allow_domain."
        },
        "credentials": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Credential service names to enable via the reverse proxy."
        },
        "proxy_credentials": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Legacy alias for credentials."
        },
        "open_port": {
          "type": "array",
          "items": { "type": "integer" },
          "description": "Localhost TCP ports to allow bidirectional IPC (connect + bind)."
        },
        "port_allow": {
          "type": "array",
          "items": { "type": "integer" },
          "description": "Legacy alias for open_port."
        },
        "allow_port": {
          "type": "array",
          "items": { "type": "integer" },
          "description": "Legacy alias for open_port."
        },
        "listen_port": {
          "type": "array",
          "items": { "type": "integer" },
          "description": "TCP ports the sandboxed child may listen on."
        },
        "custom_credentials": {
          "type": "object",
          "additionalProperties": {
            "$ref": "#/$defs/CustomCredentialDef"
          },
          "description": "Custom credential definitions for services not in network-policy.json. Keys are service names used with --credential."
        },
        "upstream_proxy": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Upstream proxy address (host:port) for enterprise proxy passthrough."
        },
        "external_proxy": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Legacy alias for upstream_proxy."
        },
        "upstream_bypass": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Hosts to bypass the upstream proxy and route directly. Supports exact hostnames and wildcard suffixes (e.g., *.example.com)."
        },
        "external_proxy_bypass": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Legacy alias for upstream_bypass."
        }
      }
    },
    "CustomCredentialDef": {
      "type": "object",
      "description": "Custom credential route definition for the reverse proxy. Defines how to route requests and inject credentials for a service. Must have either credential_key or auth (mutually exclusive).",
      "additionalProperties": false,
      "required": ["upstream"],
      "properties": {
        "upstream": {
          "type": "string",
          "description": "Upstream URL to proxy requests to (e.g., \"https://api.telegram.org\"). Must use HTTPS, or HTTP only for loopback addresses."
        },
        "credential_key": {
          "type": "string",
          "description": "Keystore account name for the credential (e.g., \"telegram_bot_token\"), or a 1Password op:// URI, apple-password:// URI, file:// URI, or env:// URI (e.g., \"env://MY_TOKEN\"). Mutually exclusive with auth."
        },
        "auth": {
          "$ref": "#/$defs/OAuth2Config",
          "description": "OAuth2 client_credentials configuration. When present, the proxy handles token exchange automatically. Mutually exclusive with credential_key."
        },
        "inject_mode": {
          "$ref": "#/$defs/InjectMode",
          "description": "Credential injection mode. Determines how the credential is inserted into outbound requests.",
          "default": "header"
        },
        "inject_header": {
          "type": "string",
          "description": "HTTP header name to inject the credential into. Only used when inject_mode is \"header\".",
          "default": "Authorization"
        },
        "credential_format": {
          "type": "string",
          "description": "Format string for the credential value, using {} as placeholder. Only used when inject_mode is \"header\".",
          "default": "Bearer {}"
        },
        "path_pattern": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Pattern to match in incoming URL path, using {} as placeholder for the phantom token. Required when inject_mode is \"url_path\"."
        },
        "path_replacement": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Pattern for outgoing URL path, using {} as placeholder for the real credential. Defaults to path_pattern if not specified. Only used when inject_mode is \"url_path\"."
        },
        "query_param_name": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Name of the query parameter to add or replace with the credential. Required when inject_mode is \"query_param\"."
        },
        "proxy": {
          "oneOf": [
            { "$ref": "#/$defs/ProxyInjectConfig" },
            { "type": "null" }
          ],
          "description": "Optional proxy-side overrides for phantom token parsing. When omitted, proxy behavior uses top-level custom credential fields."
        },
        "env_var": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Explicit environment variable name for the phantom token (e.g., \"OPENAI_API_KEY\"). Required when credential_key is a URI manager reference (op://, apple-password://, or file://). Optional for env:// (derived from the URI)."
        },
        "endpoint_rules": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/EndpointRule"
          },
          "description": "L7 endpoint rules for method+path filtering. When non-empty, only requests matching at least one rule are allowed (default-deny). When empty or absent, all endpoints are permitted.",
          "default": []
        },
        "tls_ca": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Path to a PEM-encoded CA certificate file for upstream TLS. When set, the proxy trusts this CA in addition to system roots. Required for upstreams with self-signed or private CA certificates (e.g. a Kubernetes API server). Supports tilde (~) expansion."
        },
        "tls_client_cert": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Path to a PEM-encoded client certificate for upstream mutual TLS (mTLS). When set together with tls_client_key, the proxy presents this certificate during the TLS handshake. Required for upstreams that enforce client-certificate authentication."
        },
        "tls_client_key": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Path to the PEM-encoded private key corresponding to tls_client_cert. Must be set together with tls_client_cert."
        }
      }
    },
    "EndpointRule": {
      "type": "object",
      "description": "HTTP method+path access rule for restricting which API endpoints an agent can access through a credential route.",
      "additionalProperties": false,
      "required": ["method", "path"],
      "properties": {
        "method": {
          "type": "string",
          "description": "HTTP method to match (e.g., \"GET\", \"POST\") or \"*\" for any method."
        },
        "path": {
          "type": "string",
          "description": "URL path glob pattern. Uses standard glob syntax: \"*\" matches one path segment, \"**\" matches zero or more segments."
        }
      }
    },
    "InjectMode": {
      "type": "string",
      "enum": ["header", "url_path", "query_param", "basic_auth"],
      "description": "Credential injection mode for the reverse proxy.",
      "default": "header"
    },
    "ProxyInjectConfig": {
      "type": "object",
      "description": "Optional proxy-side overrides for phantom token parsing. These settings only affect incoming client request validation, not outbound upstream credential injection.",
      "additionalProperties": false,
      "properties": {
        "inject_mode": {
          "oneOf": [
            { "$ref": "#/$defs/InjectMode" },
            { "type": "null" }
          ],
          "description": "Override inject mode for proxy-side phantom token parsing. Falls back to parent inject_mode when omitted."
        },
        "inject_header": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Override header name for proxy-side header/basic_auth modes. Falls back to parent inject_header when omitted."
        },
        "credential_format": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Override credential format for proxy-side header mode. Falls back to parent credential_format when omitted."
        },
        "path_pattern": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Override path pattern for proxy-side url_path mode. Falls back to parent path_pattern when omitted."
        },
        "path_replacement": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Override path replacement for proxy-side url_path mode. Falls back to parent path_replacement when omitted."
        },
        "query_param_name": {
          "oneOf": [
            { "type": "string" },
            { "type": "null" }
          ],
          "description": "Override query parameter name for proxy-side query_param mode. Falls back to parent query_param_name when omitted."
        }
      }
    },
    "OAuth2Config": {
      "type": "object",
      "description": "OAuth2 client_credentials configuration for automatic token exchange. The proxy exchanges client_id + client_secret for an access_token, caches it, and refreshes automatically before expiry.",
      "additionalProperties": false,
      "required": ["token_url", "client_id", "client_secret"],
      "properties": {
        "token_url": {
          "type": "string",
          "description": "Token endpoint URL (e.g., \"https://auth.example.com/oauth/token\"). Must use HTTPS, or HTTP only for loopback addresses."
        },
        "client_id": {
          "type": "string",
          "description": "Client ID — plain value or credential reference (env://, file://, op://)."
        },
        "client_secret": {
          "type": "string",
          "description": "Client secret — credential reference (env://, file://, op://)."
        },
        "scope": {
          "type": "string",
          "description": "OAuth2 scopes (space-separated). Empty or omitted means no scope parameter is sent.",
          "default": ""
        }
      }
    },
    "SecretsConfig": {
      "type": "object",
      "description": "Maps keystore account names (or op://, apple-password://, env:// URIs) to environment variable names. Secrets are loaded from the system keystore under the service name \"nono\".",
      "additionalProperties": {
        "type": "string",
        "description": "Environment variable name to inject the secret value into."
      }
    },
    "EnvironmentConfig": {
      "type": "object",
      "description": "Controls which environment variables are passed to the sandboxed process. By default all variables are inherited. Precedence (highest to lowest): 1. Hardcoded dangerous vars (always stripped), 2. deny_vars (stripped even if in allow_vars), 3. allow_vars (if non-empty, only matching vars pass).",
      "additionalProperties": false,
      "properties": {
        "allow_vars": {
          "type": "array",
          "items": { "type": "string", "pattern": "^([^*]+\\*?|\\*)$" },
          "description": "Allow-list of environment variable names passed to the sandboxed process. Supports exact names (\"PATH\") and prefix patterns ending with * (\"AWS_*\"). When empty or absent, all variables are allowed."
        },
        "deny_vars": {
          "type": "array",
          "items": { "type": "string", "pattern": "^([^*]+\\*?|\\*)$" },
          "description": "Deny-list of environment variable names stripped from the sandboxed process. Supports exact names (\"GH_TOKEN\") and prefix patterns ending with * (\"GITHUB_*\"). Denied vars are stripped even if they also appear in allow_vars."
        }
      }
    },
    "WorkdirConfig": {
      "type": "object",
      "description": "Working directory access configuration.",
      "additionalProperties": false,
      "properties": {
        "access": {
          "$ref": "#/$defs/WorkdirAccess",
          "description": "Access level for the current working directory.",
          "default": "none"
        }
      }
    },
    "WorkdirAccess": {
      "type": "string",
      "enum": ["none", "read", "write", "readwrite"],
      "description": "Working directory access level controlling whether and how the current working directory is shared with the sandboxed process."
    },
    "HooksConfig": {
      "type": "object",
      "description": "Hook configurations for target applications. Keys are application names (e.g., \"claude-code\"), values define the hook to install.",
      "additionalProperties": {
        "$ref": "#/$defs/HookConfig"
      }
    },
    "HookConfig": {
      "type": "object",
      "description": "Hook definition for a target application.",
      "additionalProperties": false,
      "required": ["event", "matcher", "script"],
      "properties": {
        "event": {
          "type": "string",
          "description": "Event that triggers the hook (e.g., \"PostToolUseFailure\")."
        },
        "matcher": {
          "type": "string",
          "description": "Regex pattern to match tool names (e.g., \"Read|Write|Edit|Bash\")."
        },
        "script": {
          "type": "string",
          "description": "Script filename from data/hooks/ to install."
        }
      }
    },
    "RollbackConfig": {
      "type": "object",
      "description": "Rollback snapshot configuration controlling which files are excluded from undo snapshots.",
      "additionalProperties": false,
      "properties": {
        "exclude_patterns": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Patterns to exclude from rollback snapshots. Matched against path components (exact match) or as substrings of the full path if they contain a slash."
        },
        "exclude_globs": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Glob patterns to exclude from rollback snapshots. Matched against the filename using standard glob syntax."
        }
      }
    },
    "OpenUrlConfig": {
      "type": "object",
      "description": "Configuration for supervisor-delegated URL opening, used for OAuth2 login flows and similar operations.",
      "additionalProperties": false,
      "properties": {
        "allow_origins": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Allowed URL origins (scheme + host, e.g., \"https://console.anthropic.com\"). The supervisor validates each URL open request against this list. An empty list means no URLs are allowed."
        },
        "allow_localhost": {
          "type": "boolean",
          "description": "Allow opening http://localhost and http://127.0.0.1 URLs, typically for OAuth2 callback handlers."
        }
      }
    }
  }
}