Configuration
OpenVole uses a single vole.config.json file at the project root — plain JSON, no imports, no build step.
Full Example
{
"brain": "@openvole/paw-brain",
"paws": [
{ "name": "@openvole/paw-brain", "allow": { "network": ["*"], "env": ["BRAIN_PROVIDER", "BRAIN_API_KEY", "BRAIN_MODEL", "OLLAMA_HOST", "OLLAMA_MODEL", "OLLAMA_API_KEY"] } },
{ "name": "@openvole/paw-memory", "allow": { "network": ["*"] } },
{ "name": "@openvole/paw-session" },
{ "name": "@openvole/paw-compact" },
{ "name": "@openvole/paw-telegram", "allow": { "network": ["*"], "env": ["TELEGRAM_BOT_TOKEN", "TELEGRAM_ALLOW_FROM"] } },
{ "name": "@openvole/paw-shell", "allow": { "filesystem": ["./"], "env": ["VOLE_SHELL_ALLOWED_DIRS"], "childProcess": true } }
],
"skills": ["clawhub/summarize"],
"loop": {
"maxIterations": 25,
"confirmBeforeAct": false,
"taskConcurrency": 1,
"compactThreshold": 50,
"toolHorizon": true,
"maxContextTokens": 128000,
"responseReserve": 4000,
"costTracking": "auto",
"costAlertThreshold": 1.00,
"rateLimits": {
"llmCallsPerMinute": 30,
"llmCallsPerHour": 500,
"toolExecutionsPerTask": 100,
"tasksPerHour": { "telegram": 20, "cli": 100 }
}
},
"heartbeat": {
"enabled": true,
"intervalMinutes": 30,
"runOnStart": false
},
"toolProfiles": {
"telegram": { "deny": ["shell_exec", "fs_write", "fs_delete"] },
"heartbeat": { "allow": ["memory_search", "memory_write", "telegram_send", "shell_exec"] }
},
"security": {
"sandboxFilesystem": true,
"allowedPaths": ["/home/user/projects"],
"docker": {
"enabled": false,
"image": "node:20-slim",
"memory": "512m",
"cpus": "1.0",
"scope": "session",
"network": "none"
}
},
"agents": {
"researcher": {
"role": "Research assistant",
"instructions": "Search the web and summarize findings. Do not execute code.",
"allowTools": ["web_fetch", "scrape_page", "memory_write"],
"maxIterations": 10
}
},
"net": {
"enabled": true,
"instanceName": "my-vole",
"role": "coordinator",
"port": 9700,
"peers": [
{ "url": "http://192.168.1.50:9701", "trust": "full", "allowBrain": false }
],
"share": { "tools": true, "memory": true, "session": false },
"routing": { "shell_*": "worker-1", "db_*": "db-worker" }
}
}Config Sections
brain
Which Brain Paw handles the Think phase of the agent loop.
{ "brain": "@openvole/paw-brain" }The unified paw-brain supports all providers — set BRAIN_PROVIDER env var to ollama, openai, anthropic, gemini, xai, claude-code, or antigravity. Set BRAIN_PROVIDER=mock for a free, network-free brain (replies via BRAIN_MOCK_REPLY, or scripted tool calls via BRAIN_MOCK_SCRIPT) — handy for testing the dashboard, VoleNet, or the agent loop.
paws
Array of paws to load. Each entry is either a package name string or an object with permissions.
String shorthand — no special permissions:
{ "paws": ["@openvole/paw-memory", "@openvole/paw-session"] }Object form — with explicit sandbox permissions:
{
"paws": [
{
"name": "@openvole/paw-brain",
"allow": {
"network": ["*"],
"env": ["BRAIN_PROVIDER", "BRAIN_API_KEY", "BRAIN_MODEL",
"OLLAMA_HOST", "OLLAMA_MODEL", "OLLAMA_API_KEY",
"OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"]
}
}
]
}Paw Permission Object (allow)
Each paw runs in a sandboxed subprocess. The allow field controls what the paw can access:
| Key | Type | Description | Example |
|---|---|---|---|
network | string[] | Outbound network access. ["*"] for any, or specific domains. | ["api.openai.com", "api.telegram.org"] |
listen | number[] | Ports the paw can bind (for servers like dashboard). | [3001] |
filesystem | string[] | Additional filesystem paths beyond .openvole/. | ["./", "/tmp"] |
env | string[] | Environment variables passed to the subprocess. | ["TELEGRAM_BOT_TOKEN"] |
childProcess | boolean | Allow spawning child processes. Required for shell, browser, MCP paws. | true |
Hook Configuration
Paw hooks can be configured with ordering and pipeline behavior:
{
"name": "@openvole/paw-memory",
"hooks": {
"perceive": { "order": 1, "pipeline": true }
},
"allow": { "network": ["*"] }
}Common Paw Permissions
| Paw | Needs | Config |
|---|---|---|
paw-brain | LLM API access | "network": ["*"], env vars for provider |
paw-shell | Spawn processes | "childProcess": true, "filesystem": ["./"] |
paw-browser | Spawn Chrome | "childProcess": true, "network": ["*"] |
paw-database | Native addons | Disable sandbox: "sandboxFilesystem": false |
paw-dashboard (deprecated — use vole serve) | Bind HTTP port | "listen": [3001] |
paw-telegram | Telegram API | "network": ["api.telegram.org"] or ["*"] |
paw-memory | Embedding API | "network": ["*"] |
paw-compact | LLM for compaction | "network": ["*"] |
paw-mcp | Spawn MCP servers | "childProcess": true |
paw-filesystem | Read/write files | "filesystem": ["./"] |
paw-image | Native addons (sharp) | Disable sandbox: "sandboxFilesystem": false |
skills
Array of skill names to load. Skills are context-aware prompt templates that activate based on available tools.
{ "skills": ["clawhub/summarize", "clawhub/email-triage", "local/my-workflow"] }Skills from clawhub/ are fetched from the VoleHub registry. Skills from local/ are loaded from .openvole/skills/.
loop
Controls the agent loop — how the Brain thinks, acts, and manages context.
| Option | Type | Default | Description |
|---|---|---|---|
maxIterations | number | 10 | Max loop iterations per task. Resets on successful tool execution. |
confirmBeforeAct | boolean | false | If true, ask user confirmation before executing tools. |
taskConcurrency | number | 1 | Max tasks running in parallel. |
compactThreshold | number | 50 | Message count that triggers compact hooks. 0 to disable. |
toolHorizon | boolean | true | Brain starts with core tools only, discovers others via discover_tools. Reduces context bloat. |
maxContextTokens | number | 128000 | Max context window size in tokens. Core trims messages by priority to fit. |
responseReserve | number | 4000 | Tokens reserved for the Brain's response output. |
costTracking | string | "auto" | "auto": track for cloud providers. "enabled": always track. "disabled": off. |
costAlertThreshold | number | — | Warn when a single task exceeds this USD amount. |
rateLimits | object | — | Rate limiting (see below). |
Rate Limits
{
"loop": {
"rateLimits": {
"llmCallsPerMinute": 30,
"llmCallsPerHour": 500,
"toolExecutionsPerTask": 100,
"tasksPerHour": {
"telegram": 20,
"cli": 100,
"heartbeat": 6
}
}
}
}| Option | Description |
|---|---|
llmCallsPerMinute | Max Brain (LLM) calls per minute across all tasks. |
llmCallsPerHour | Max Brain calls per hour. |
toolExecutionsPerTask | Max tool executions within a single task. |
tasksPerHour | Per-source task rate limits. Keys are source names (cli, telegram, heartbeat, etc.). |
Context Budget
The ContextBudgetManager trims messages by priority when the context exceeds maxContextTokens:
- Old tool results (lowest priority — trimmed first)
- Old error messages
- Old assistant/brain messages
- Session history
Never trimmed: system prompt, first user message, last 2 brain responses.
heartbeat
Periodic autonomous wake-up. The agent reads HEARTBEAT.md and acts on scheduled jobs without user input.
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable heartbeat scheduling. |
intervalMinutes | number | 30 | Minutes between heartbeat wake-ups. Intervals of 60+ are mapped onto whole hours; a day or more runs once daily at midnight UTC. |
cron | string | — | Cron expression for the wake-up. Takes precedence over intervalMinutes — use it for anything an interval can't express. |
runOnStart | boolean | false | Run a heartbeat immediately on startup. |
{ "heartbeat": { "enabled": true, "intervalMinutes": 15, "runOnStart": true } }
{ "heartbeat": { "enabled": true, "cron": "0 12 * * *" } } // daily at noon UTC
{ "heartbeat": { "enabled": true, "cron": "0 9 * * 1-5" } } // weekdays at 09:00 UTCNOTE
An unparseable schedule disables the heartbeat with a logged error — the agent still starts and runs normally.
Because cron cannot express "every N days", any intervalMinutes of 1440 or more becomes a single daily run at midnight UTC. If you want a specific time of day, set cron instead.
Common intervals:
| Use Case | Interval | Description |
|---|---|---|
| DevOps monitoring | 10 | Health checks, alerts every 10 min |
| Personal assistant | 30 | Email/calendar checks every 30 min |
| Data monitoring | 15 | Watch for changes every 15 min |
| Content automation | 360 | Content cycle every 6 hours |
| Research aggregator | 720 | Daily research report every 12 hours |
The heartbeat instructions live in .openvole/HEARTBEAT.md. The agent reads this file each wake-up and decides what actions to take.
toolProfiles
Restrict which tools are available per task source. Useful for limiting what external channels (Telegram, Slack) can trigger.
{
"toolProfiles": {
"telegram": {
"deny": ["shell_exec", "fs_write", "fs_delete"]
},
"heartbeat": {
"allow": ["memory_search", "memory_write", "telegram_send", "web_fetch"]
},
"cli": {}
}
}| Field | Description |
|---|---|
allow | Allowlist — only these tools can be used. If set, everything else is denied. |
deny | Denylist — these tools are blocked. Everything else is allowed. |
If both allow and deny are set, deny takes precedence. Profile keys match the task source: cli, telegram, slack, heartbeat, api, etc.
security
Controls the subprocess sandbox and isolation.
| Option | Type | Default | Description |
|---|---|---|---|
sandboxFilesystem | boolean | true | Enable Node.js --permission sandbox for paw subprocesses. |
allowedPaths | string[] | [] | Extra absolute filesystem paths granted (read + write) to sandboxed paw subprocesses, beyond the agent dir / .openvole/. |
docker | object | — | Docker container sandbox (optional, stronger isolation). |
{
"security": {
"sandboxFilesystem": true,
"allowedPaths": ["/home/user/data"]
}
}Docker Sandbox
Runs paw subprocesses inside Docker containers for stronger isolation.
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable Docker sandboxing. |
image | string | "node:20-slim" | Base Docker image. |
memory | string | "512m" | Memory limit per container. |
cpus | string | "1.0" | CPU limit per container. |
scope | string | "session" | "session": container per task session. "shared": one container reused. |
network | string | "none" | Docker network mode: "none", "bridge", or "host". |
allowedDomains | string[] | — | Outbound domains allowed when network: "bridge". |
{
"security": {
"docker": {
"enabled": true,
"image": "node:20-slim",
"memory": "256m",
"cpus": "0.5",
"network": "bridge",
"allowedDomains": ["api.openai.com"]
}
}
}agents
Named sub-agent profiles for the spawn_agent tool — not to be confused with your server's agents (the isolated engines vole serve manages).
Named agent profiles for sub-agent spawning via the spawn_agent core tool. Each profile defines a restricted execution context.
| Option | Type | Default | Description |
|---|---|---|---|
role | string | — | Human-readable role description (injected into context). |
instructions | string | — | Additional instructions for the sub-agent. |
allowTools | string[] | — | Tools this agent can use (allowlist). |
denyTools | string[] | — | Tools this agent cannot use (denylist, takes precedence). |
maxIterations | number | 10 | Max loop iterations for this agent. |
{
"agents": {
"researcher": {
"role": "Research assistant",
"instructions": "Search the web and summarize findings. Do not execute shell commands.",
"allowTools": ["web_fetch", "scrape_page", "memory_write", "memory_search"],
"maxIterations": 15
},
"coder": {
"role": "Code generator",
"denyTools": ["telegram_send", "email_send"],
"maxIterations": 20
}
}
}The Brain can spawn these via spawn_agent({ profile: "researcher", task: "..." }).
TIP
In the control-plane dashboard (vole serve), agent profiles are editable as structured form fields under Config → AGENTS — role, instructions, allowTools, denyTools, and maxIterations — no raw JSON.
net (VoleNet)
Distributed agent networking — connect multiple OpenVole instances across machines.
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable VoleNet. |
instanceName | string | "vole" | Human-readable name for this instance. |
role | string | "peer" | "coordinator", "worker", or "peer". |
port | number | 9700 | WebSocket/HTTP port for peer communication. |
hostname | string | first non-internal IPv4 | Host advertised to peers in the discovery endpoint. Set to your public domain when using TLS so it matches the cert (env override: VOLE_NET_HOSTNAME). |
publicUrl | string | — | Full endpoint advertised to peers instead of hostname:port — for reverse-proxy setups where the raw VoleNet port stays unexposed, e.g. "https://club.example.com/mesh" (env override: VOLE_NET_PUBLIC_URL). See Behind a reverse proxy. |
keyPath | string | .openvole/net/vole_key | Path to Ed25519 keypair. |
peers | array | [] | Peer connections (see below). |
share | object | — | What to share with peers. |
routing | object | — | Tool-to-peer routing rules. |
brainSource | string | "local" | "local", "remote", or a specific peer name. |
leader | string | "auto" | "auto" (lowest instance ID) or a specific instance name. |
heartbeatMode | string | "leader" | "leader": only leader runs heartbeat. "independent": each instance runs its own. |
brainMode | string | "local" | "local": handle own tasks. "loadbalance": route to least-loaded brain. |
taskOverflow | string | "reject" | "reject": reject when queue full. "forward": forward to least-loaded peer. |
maxQueuedTasks | number | 10 | Max queued tasks before overflow triggers. |
tls | object | — | { cert, key } file paths — enables https/wss transport. Pair with hostname. See Transport encryption. |
relay | object | — | Blind relay. Hub: { enabled, maxPerMinutePerPair (30), maxBytes (65536) } — forward sealed member↔member envelopes the hub cannot read. Member: acceptFrom — who may reach you over a relay: unset = only peers you approve or already trust (default deny); "*" = any hub member; or a list of names / instanceId prefixes. v1 carries end-to-end encrypted chat only. See Relay. |
discovery | string | "manual" | Peer discovery method: "manual" or "mdns". |
encrypt | boolean | false | Direct end-to-end encryption — seal direct-mesh messages to capable peers with the hybrid X25519 + ML-KEM-768 (post-quantum) KEM, independent of TLS. Opportunistic: older peers still get plaintext. See Relay §1. |
publishNames | boolean | false | Include peer display names (live announced instanceName) in the public /volenet/info response. Off by default — names are an enumeration surface. Turn on for a public hub whose members are meant to be seen, so external tooling can read live names without the authenticated dashboard. |
Peer Configuration
{
"net": {
"peers": [
{
"url": "http://192.168.1.50:9701",
"trust": "full",
"allowBrain": false,
"allowTools": ["shell_exec"],
"denyTools": ["vault_read"]
}
]
}
}| Field | Type | Description |
|---|---|---|
url | string | Peer endpoint URL. |
trust | string | "full": all access. "tool": specific tools only. "read": memory search only. |
allowTools | string[] | Tools this peer can execute on our instance. Glob patterns supported (shell_*). If set, only matching tools are allowed. |
denyTools | string[] | Tools this peer cannot use on our instance. Glob patterns supported. Takes precedence over allowTools. |
allowBrain | boolean | Allow this peer to delegate thinking to our Brain (LLM cost on us). Default: false — off even for trust: "full". |
A peer may call our tools at all only if it has explicit trust: "tool" or trust: "full" in peers, or we set share.tools: true (below). Per-peer allowTools/denyTools then refine which tools. By default tools are not exposed to peers.
Sharing
{
"net": {
"share": {
"tools": true,
"memory": true,
"session": false
}
}
}| Field | Description |
|---|---|
tools | Advertise our local tools to peers and accept remote tool calls from them. This is a blanket grant — a peer with explicit trust: "tool"/"full" in peers can call our tools even without it. Default: tools not exposed. |
memory | Propagate memory writes to peers and accept remote memory searches. |
session | Sync session transcripts between peers (shared conversation). |
Public Join
Let unknown peers self-register over HTTP and join at a restricted guest trust level — for running a public mesh hub that anyone can connect to. Off by default. Self-joined guests are never granted "full" trust.
{
"net": {
"publicJoin": {
"enabled": false,
"trustLevel": "tool",
"allowBrain": false,
"maxPeers": 200,
"ratePerMinute": 5,
"requireApproval": false
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Accept HTTP self-join requests from unknown peers. |
trustLevel | string | "tool" | Trust granted to self-joined guests: "read" or "tool". Never "full". |
allowBrain | boolean | false | Let guests delegate thinking to our Brain (LLM cost on us). |
maxPeers | number | 200 | Max trusted peers before new joins are refused. |
ratePerMinute | number | 5 | Join requests allowed per minute per IP. |
requireApproval | boolean | false | Queue joins for manual vole net trust instead of auto-trusting. |
Chat Retention
Retention for node-to-node chat sessions (the volenet:<peer> transcripts persisted via paw-session). Unlike brain sessions these have no TTL, so this bounds their growth.
{
"net": {
"chatRetention": {
"maxMessages": 1000,
"maxAgeDays": 90
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
maxMessages | number | 1000 | Max messages kept per peer transcript; the oldest are trimmed on each new message. |
maxAgeDays | number | 90 | Chat sessions idle longer than this are cleared (swept ~every 6h). 0 disables age pruning. |
Files (VoleDrop)
End-to-end encrypted file transfer between voles — see VoleNet → File Transfer.
{
"net": {
"files": {
"acceptFrom": ["orchestrator", "video-editor"],
"inboxDir": ".openvole/workspace/drop"
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Turn the whole feature off (false auto-rejects inbound offers). |
inboxDir | string | .openvole/net/inbox | Where accepted files land (relative to the agent root). Point it at a watch folder to feed an agent pipeline. |
acceptFrom | "*" | string[] | unset | Auto-accept policy. Unset: every offer waits for an explicit accept. "*" or a name/id-prefix list auto-accepts (your own fleet). |
maxBytes | number | 2147483648 | Largest file accepted over a direct transfer (2 GiB). 0 means no limit — transfers are chunked and streamed to disk, so the disk is then the only boundary (and a receiver declines an offer it has no room for). |
relayMaxBytes | number | 536870912 | Largest single blob hosted when this node acts as a relay hub (512 MiB). Deliberately independent of maxBytes: raising what you accept for yourself should not turn your hub into unbounded storage for other people. |
relayQuotaBytes | number | 536870912 | Total relay storage per peer pair. |
relayTtlHours | number | 24 | How long a relayed blob survives before the sweep removes it. |
maxConcurrent | number | 4 | Max simultaneous transfer streams. |
offerTtlMinutes | number | 60 | Pending offers, transfer tokens, and unfinished transfers expire after this. |
chunkBytes | number | 4194304 | Sender-side encryption chunk size (advanced). |
relayQuotaBytes | number | 536870912 | Hub: max stored ciphertext per sender→receiver pair. |
relayTtlHours | number | 24 | Hub: stored relay blobs are deleted after this. |
Routing
Route tool calls to specific peers by glob pattern:
{
"net": {
"routing": {
"shell_*": "server-worker",
"db_*": "db-worker",
"scrape_*": "web-scraper"
}
}
}When multiple peers share the same tool, the Brain can target a specific peer using <peerName>/<toolName> syntax (e.g. us-monitor/shell_exec).
TIP
The whole net section is editable as structured form fields under Config → NET in the control-plane dashboard (vole serve) — including an on/off toggle for enabled, plus peers, share (tools/memory/session), TLS, routing, and the various modes. No raw JSON.
VoleNet Setup
# 1. Generate identity on each instance
vole net init my-instance
# 2. Exchange keys
vole net show-key # on instance A
vole net trust "vole-ed25519 ..." # on instance B (paste A's key)
# 3. Configure peers in vole.config.json (see above)
# 4. Start both instances
vole serve.openvole Directory Structure
.openvole/
├── paws/
│ ├── paw-memory/ ← memory data
│ │ ├── MEMORY.md
│ │ └── user/, paw/, heartbeat/
│ ├── paw-session/ ← session transcripts
│ │ └── cli:default/, telegram:123/
│ ├── paw-brain/ ← brain paw data
│ │ └── BRAIN.md ← system prompt (scaffolded on first run)
│ └── paw-mcp/ ← MCP config
│ └── servers.json
├── net/ ← VoleNet identity (if enabled)
│ ├── vole_key ← Ed25519 private key
│ ├── vole_key.pub ← public key
│ └── authorized_voles ← trusted peer keys
├── workspace/ ← agent scratch space
├── skills/ ← local and clawhub skills
├── logs/ ← log files
│ └── vole.log
├── vault.json ← encrypted key-value store
├── schedules.json ← persistent cron schedules
├── SOUL.md ← agent personality
├── USER.md ← user profile
├── AGENT.md ← operating rules
└── HEARTBEAT.md ← recurring job definitionsEach paw gets its own data directory at .openvole/paws/<name>/. The installed npm package stays immutable — all user data lives in the local paw directory.
Workspace
Every agent is scaffolded with a .openvole/workspace/ directory — the agent's writable scratch and project area. It's the sanctioned place for anything the agent produces or fetches that isn't memory, config, or a paw's own data:
- Internal projects and their files (drop source material or media here to work on it)
- Drafts, notes, and downloaded docs or instructions
- Generated outputs (reports, renders, exports)
The core workspace_read, workspace_write, workspace_list, and workspace_delete tools operate on this directory and confine every path to it — they run in-core, so the agent can use them without any sandbox grant.
Since 4.13.1 the system prompt tells the agent about the workspace by absolute path, so it treats it as the working directory without any AGENT.md instruction. The prompt also spells out the one trap: shell commands start in the agent root, not the workspace, so they need an absolute path (or a cd) — otherwise a relative > notes.txt lands next to vole.config.json. The agent root and .openvole/ itself are declared off-limits (config, identity, memory, and paw data live there).
- Gitignored by default (the scaffolded
.gitignoreignores.openvole/), so it's safe for large or throwaway files. Version anything you want to keep elsewhere. - Not for secrets — store credentials in the vault, never here.
- Sandboxed paws don't get workspace access automatically; a paw subprocess that needs it must be granted the path via
allow.filesystem(see Security).
Identity Files
Customize agent behavior with markdown files in .openvole/:
| File | Purpose | Used By |
|---|---|---|
BRAIN.md | Custom system prompt — overrides the default prompt entirely. | Brain Paw |
SOUL.md | Agent personality, tone, and identity. | System Prompt |
USER.md | User profile, preferences, timezone. | System Prompt |
AGENT.md | Operating rules and behavioral constraints. | System Prompt |
HEARTBEAT.md | Recurring job definitions for heartbeat wake-ups. | Heartbeat Task |
These files are loaded into the system prompt on every iteration. Edit them to shape how the agent behaves.
Environment Variables
Global environment variables that affect OpenVole core:
| Variable | Description |
|---|---|
VOLE_LOG_LEVEL | Log level: debug, info, warn, error. Default: info. |
VOLE_LOG_FILE | Path to log file. Default: .openvole/logs/vole.log. |
VOLE_DASHBOARD_PORT | Control-plane dashboard (vole serve) HTTP port. Default: 3000. |
VOLE_HOME | Explicit OpenVole root for vole serve (overrides current-directory resolution). |
VOLE_DEBUG | Enable debug mode (true/false). |
VOLE_IPC_TIMEOUT_MS | IPC timeout for paw communication in ms. Default: 300000. Applies to tool calls and lifecycle hooks — not to the brain's think, which is deliberately unbounded (see Timeouts). |
VOLE_COMPACT_MODEL | Explicit model for LLM compaction (if unset, uses simple compaction). |
VOLE_UPLOAD_MAX_BYTES | Largest browser → agent upload accepted by the dashboard's file spool, in bytes. Default: 4294967296 (4 GiB). Streams to disk, so this bounds disk use rather than memory. |
VOLE_EVENT_LOG_DAYS | Days of daily event log kept under <root>/.openvole/logs/ (vole serve). Default: 30. 0 keeps everything. See Live Events. |
Provider-specific env vars are passed to paws via the allow.env config — they are not globally available to all paws.
