Permissions
Configure the permission guard's risk policy and per-app overrides.
The Permission Guard decides, for every tool call, whether to run it, notify you, ask you, or refuse. The decision is a pure function of the tool's declared risk and the policy below.
Risk → action
| Risk | Default action |
|---|---|
low | auto — run silently |
medium | notify — run and surface a notification |
high | confirm — suspend until you decide |
critical | deny — refuse |
Default policy
defaultPolicy: {
low: "auto",
medium: "notify",
high: "confirm",
critical: "deny",
}This is the policy the daemon runs with. The mapping is defined in @jarvis/permission-guard and applied to every tool call.
It is not yet user-configurable. The guard is constructed with its defaults, and config.json has no permissions section — an earlier version of these docs described a permissions.rules array that does not exist. Do not add one and expect it to take effect.
Per-app overrides
The guard supports per-app overrides in its configuration shape:
appPermissions: {
"filesystem": { write: true, execute: false },
"untrusted-mcp": { write: false, execute: false },
}How each flag maps:
| Risk | write: true | write: false |
|---|---|---|
medium | notify | confirm |
high | confirm | deny |
| Risk | execute: true | execute: false |
|---|---|---|
critical | confirm | deny |
low is always auto.
The overrides are evaluated when present, but nothing in the daemon currently reads them from disk, so they apply only to code that constructs the guard with them.
Voice mode
Voice execution is stricter regardless of risk: read-only actions are auto-allowed, everything else requires confirmation. A spoken request is easier to trigger by accident than a typed one.
Approval requests
When the action is confirm, the guard does not hold the decision in memory:
- An
approval_requestis persisted with an action digest, risk, scope, and expiry - The run is suspended
- The UI shows the request — approve, deny, or remember
- The decision resumes or fails the run
Endpoints:
GET /api/approvals
GET /api/approvals/:id
POST /api/approvals/:id/approve
POST /api/approvals/:id/deny
POST /api/approvals/:id/remember
POST /api/approvals/batch/approve
POST /api/approvals/batch/deny
POST /api/approvals/expire-stale"This request is stale" is a real state: unattended queues accumulate requests nobody answered, and expire-stale closes them rather than leaving runs suspended forever.
Permission memories
"Remember" stores a decision against the action digest — the concrete action, not the tool name. The same action is not asked again; a different path, command, or parameter set produces a different digest and is asked again.
Audit
Every decision is written to audit_log and is queryable:
GET /api/audit?actor=jarvis&action=tool.execute&riskLevel=high&since=2026-10-01T00:00:00Z&limit=50An entry records actor, action, resource, riskLevel, permissionDecision, confirmedByUser, result, metadata, and createdAt.
Next Steps
- Permission Guard — the model behind the policy
- Security Model — enforcement layers
- Review the Audit Trail — working with logs