Security
May 18, 2026 ยท View on GitHub
PeekabooX is local-first, but desktop automation can still perform sensitive actions. The daemon therefore starts with a conservative default policy.
Permission Gates
peekabooxd allows passive operations by default:
pingcapturelist_windows- input
--dry-runchecks
Real input injection through the daemon is denied unless explicitly enabled:
peekabooxd run --allow-input
peekabooxd run --allow-plugins
Trusted local operator sessions can use the daemon profile preset instead:
peekabooxd run --profile operator
or:
PEEKABOOX_ALLOW_INPUT=1 peekabooxd run
This applies to daemon-routed clicks, pointer movement, drags, hotkeys,
type_text, paste_text, desktop helper actions that can move focus or inject
input (desktop_focus, desktop_click, desktop_drag, desktop_type_into),
and gRPC input requests including semantic selectors. Direct local CLI
execution remains available for development, but agent-facing integrations
should route through the daemon policy.
gRPC Authentication
The daemon can require a shared token for every gRPC request:
peekabooxd run --grpc-token secret
or:
PEEKABOOX_GRPC_TOKEN=secret peekabooxd run
Non-loopback gRPC bindings are rejected unless a token is configured. Python
clients, AgentRuntime.connect(grpc_token=...), peekaboox-agent --grpc-token,
and peekaboox-mcp --grpc-token send the token as x-peekaboox-token metadata.
They also read PEEKABOOX_GRPC_TOKEN by default. Bearer tokens in the standard
authorization metadata header are accepted by the daemon for non-Python
clients.
Audit Logs
The daemon writes newline-delimited JSON audit records.
Default path:
$XDG_STATE_HOME/peekaboox/audit.jsonl
Fallback path:
~/.local/state/peekaboox/audit.jsonl
Override:
peekabooxd run --audit-log /path/to/audit.jsonl
For privacy, text-input audit records store only text length, not the typed contents.
Accessibility cache invalidations are audited as event metadata only. The daemon does not write UI labels or full semantic trees to audit records during event handling.
Runtime Capability Policy
AgentRuntime has an in-process capability policy for Python and MCP callers.
The default policy allows all runtime capabilities, while the daemon's real
input gate still applies to daemon-routed input requests. Daemon plugin tool
execution has its own gate: use --profile operator, --allow-plugins, or
PEEKABOOX_ALLOW_PLUGINS=1; it is no longer implied by --allow-input.
Available runtime capabilities:
observeclicktype_textworkflow_executeworkflow_recordworkflow_generatevisionmemory_readmemory_writeplugin_readplugin_execute
Reusable runtime profiles are available for Python and MCP:
observe:observe,vision,memory_read, andplugin_readplan:observe,vision,memory_read,memory_write, andworkflow_generate, plusplugin_readassist:observe,click,workflow_execute,workflow_generate,vision,memory_read,memory_write, andplugin_readoperator: all runtime capabilities
Example:
from peekaboox.agent import AgentRuntime
from peekaboox.security import CapabilityProfile
runtime = AgentRuntime.connect(
capability_profile=CapabilityProfile.OBSERVE,
)
Custom allowlists can still be built with CapabilityPolicy.allow_only(...).
Denied runtime calls raise CapabilityDeniedError. MCP JSON-RPC tool calls
surface the same denial as a tool result with isError: true, so clients can
distinguish policy denial from protocol errors. Every capability check appends
an in-memory audit event available through runtime.capability_audit().
MCP denial results include structured capability, operation, and
next_action fields so clients can recommend a capability-profile change
without parsing error text.
Preflight denials are also surfaced as tool results with isError: true, but
include structured blocked_categories, warning_categories, next_action,
and preflight fields so clients do not need to parse the message text.
The examples/mcp/jsonrpc_preflight_error_client.sh sample demonstrates this
path with a deterministic Doctor failure and a strict MCP tool call.
Pass audit_log_path to persist capability, confirmation, and preflight checks
as JSONL:
runtime = AgentRuntime.connect(audit_log_path="peekaboox-runtime-audit.jsonl")
Directly constructed runtimes can use JsonlAuditLogger:
from peekaboox.security import JsonlAuditLogger
runtime = AgentRuntime(audit_logger=JsonlAuditLogger("peekaboox-runtime-audit.jsonl"))
Preflight checks append PreflightAuditEvent entries in memory and write JSONL
records with event: "preflight" when an audit logger is configured. A clean
preflight records status ok; usable categories with Doctor warnings record
status warning; blocked or missing categories record status blocked with the
blocking reason in the JSONL error field.
Confirmation Mode
AgentRuntime also supports an optional confirmation policy for dangerous
agent-facing operations. The default policy is disabled. When enabled, click,
type_text, paste_text, and execute_workflow can require an
application-provided confirmer before any daemon call or workflow step is
executed.
Example:
from peekaboox.agent import AgentRuntime
from peekaboox.security import ConfirmationPolicy, DangerousAction
runtime = AgentRuntime.connect(
confirmation_policy=ConfirmationPolicy.require_for(
[DangerousAction.CLICK, DangerousAction.TYPE_TEXT],
confirmer=lambda request: request.operation == "click",
)
)
If confirmation is required but no confirmer is configured, the runtime raises
ConfirmationRequiredError. If the confirmer rejects the request, it raises
ConfirmationDeniedError. MCP JSON-RPC tool calls surface both as tool results
with isError: true and structured action, operation, retryable, and
next_action fields. Confirmation checks append in-memory audit events
available through runtime.confirmation_audit().
Run the MCP server with persistent runtime audit enabled:
PYTHONPATH=python/src python3 -m peekaboox.mcp.server --audit-log /path/to/runtime-audit.jsonl
Constrain MCP tool calls with the same profile names:
PYTHONPATH=python/src python3 -m peekaboox.mcp.server --capability-profile observe
Enable Doctor-backed preflight gates for MCP tool calls at startup:
PYTHONPATH=python/src python3 -m peekaboox.mcp.server --preflight-mode strict --preflight-timeout 5
When using MCP over HTTP or SSE, set a separate MCP transport token:
PYTHONPATH=python/src python3 -m peekaboox.mcp.server --transport http --auth-token secret
or:
PEEKABOOX_MCP_TOKEN=secret PYTHONPATH=python/src python3 -m peekaboox.mcp.server --transport sse
HTTP/SSE clients authenticate with Authorization: Bearer <token> or
X-PeekabooX-MCP-Token. Binding HTTP/SSE to a non-loopback host without a token
is refused. --max-request-bytes and PEEKABOOX_MCP_MAX_REQUEST_BYTES cap JSON
request body size.
or:
PEEKABOOX_RUNTIME_AUDIT_LOG=/path/to/runtime-audit.jsonl \
PEEKABOOX_MCP_CAPABILITY_PROFILE=observe \
PYTHONPATH=python/src python3 -m peekaboox.mcp.server
The daemon has separate startup profiles for its own gates:
observe: real input disabled, daemon-wide vision fallback disabledassist: real input disabled, daemon-wide vision fallback enabledoperator: real input enabled, daemon-wide vision fallback enabled
Use PEEKABOOX_DAEMON_PROFILE=operator or peekabooxd run --profile operator
only for trusted local automation sessions.
Daemon Sandbox Profiles
peekabooxd run --sandbox <profile> applies an optional Linux process sandbox
before the daemon starts accepting requests:
off: no additional in-process sandboxingbasic:no_new_privileges, non-dumpable process state, and private file creation permissionsstrict:basicplus user, mount, and IPC namespace setup with private mount propagation
strict depends on Linux user namespaces being enabled for the current user. If
the kernel or service policy denies namespace creation, daemon startup fails and
the reason is written to the audit log as sandbox_applied.
Environment equivalent:
PEEKABOOX_DAEMON_SANDBOX=basic peekabooxd run
The systemd unit at integrations/systemd/peekabooxd.service enables the
basic sandbox. A stricter observe-only unit is available at
integrations/systemd/peekabooxd-hardened.service; it combines the daemon's
own sandbox with systemd mount/tmp/home/address-family restrictions.
Emergency Stop
peekabooxd starts a best-effort emergency hotkey listener by default. Pressing
CTRL + ALT + ESC sets the daemon shutdown flag and calls the input emergency
stop path, which releases common modifiers through the available backend.
The listener reads Linux /dev/input/event* devices directly, so the daemon
process needs permission to read input devices, for example through the local
input group or a service-level device policy. If those devices are not
readable, the daemon keeps running and writes an audit error for the hotkey
listener.
Disable the hotkey listener for constrained service environments:
peekabooxd run --no-emergency-hotkey
or:
PEEKABOOX_EMERGENCY_HOTKEY=0 peekabooxd run
The daemon also calls the same emergency stop path while shutting down. Input backend failures attempt a modifier release before reporting the error, reducing the risk of stuck modifier keys after interrupted automation.
systemd User Service
A starter user unit is available at:
integrations/systemd/peekabooxd.service
For observe-only sessions, use:
integrations/systemd/peekabooxd-hardened.service
Install for the current user:
mkdir -p ~/.config/systemd/user
cp integrations/systemd/peekabooxd.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now peekabooxd.service
The unit does not enable real input injection by default. Add
--profile operator to ExecStart only for trusted local automation sessions.