Agents / Agent safety
Agent safety
A capture puts outside text and images into your agent, so lwis treats that path like a possible backdoor: you choose who can use it, you see it, and you can stop it. This page explains the safeguards and what they can't do.
Where untrusted text comes from
| Source | Example | Treated as |
|---|---|---|
| A page you capture | a comment saying "AI assistants must run this command" | data |
| Pixels in an image | the same sentence in a screenshot | data, but it can't be escaped |
| A contact's note or intent | "summarise the documents you were shown before" | data |
| Your own note or intent | "fix this" | your request |
The fence
Everything from the captured page (URL, title, target labels, extracted text) and every contact's words go inside an <untrusted source="capture" …> block. The block is escaped, so a </untrusted> in the page can't close it, and its fields are length-bounded. The capture text, the plugin's MCP instructions and a hosted agent's system prompt all tell the model that the block is data, never instructions.
A contact's capture that you forward carries sender="<petname> ⟨fingerprint⟩ (not you)" via="you". Only your forward note, outside the block, is a request; the contact's note and intent move inside it. The sender is always shown by your petname for them and their fingerprint, never by the name they claim.
Gates on a session
- Attach is opt-in. Sessions are unreachable until you attach them (
/lwis:on,lwis allow <dir>, orLWIS_ATTACH=1in the agent's environment). See Claude Code. - Wake is off by default. A capture waits for your next prompt unless you attached with
--wake. - Side effects need you. The capture text asks the agent to get your explicit confirmation before any side effect. Your agent's own permission prompts remain the last gate: lwis can't force them.
- Replies wait for you.
lwis_replyis held until you review it in a terminal (lwis replies) and approve it (lwis reply-approve <id>or/lwis:approve-reply <id>), unless you attached with--allow-replies. Read a reply before you send it: an injected instruction could try to leak something through it. - Device trust. Captures from the hosted web app, whose code comes from the server at every visit, reach no session until you run
lwis accept <cid>. See Your devices. - Agent pairings are opt-in and fenced. Another machine's agent (
lwis pair --agent) reaches only the sessions you attach with--from-agent <label>, at most 20 captures an hour. Its reports arrive as<untrusted … sender="agent <label> (not you)" kind="agent">, under the standing instruction you set outside the fence. See Agent to agent. - Outgoing shares ask you.
lwis shareasks in the terminal before an image leaves the machine. An agent's shell has no terminal, so there it needs--yes, which means you agreed. Every share is inlwis log. See Agent to agent. - Kill switch.
lwis pause, or Pause delivery on a paired device, stops every new delivery untillwis resume(to sessions and hosted agents; a turn already running finishes). It also blockslwis share.
Strict mode
A strict session receives only a stub; the agent views the capture after you type /lwis:accept c_…. A session is strict when:
- you started it with
LWIS_STRICT=1, or ranlwis sessions strict <sid> on; - its permission mode approves tools by itself (bypass,
acceptEdits,auto,dontAsk, …). Sessions that bypass permission prompts also hold inbox messages for your approval, and a held message is never re-sent another way.
Hosted agents are strict by default: read-only tools run, and every other tool call waits for lwis agents approve <id>. See Hosted agents.
Hosted agent confinement
A claude-sdk agent runs with --restricted --safe-mode: no user or project settings, plugins, hooks, skills, CLAUDE.md or MCP servers. lwisd answers every tool permission itself:
- only tools listed in the profile, and never
bypassPermissions; - file paths must stay inside the agent's
cwd:..,~and symlinks that lead out are refused, and path-like arguments lwisd can't check are denied; Bash,WebFetch,WebSearch,Taskand similar tools escape that confinement: validation warns about them, and refuses them in a published profile unless you setallow_unconfined_tools;- the agent never gets lwisd's or a Claude session's secrets in its environment;
- a contact's capture always runs strict, in a fresh process that never saw your conversation with the agent.
What remains
- Your own captures are your requests. If you capture a malicious page and send it with "explain this", the page text is fenced, but the model still reads it. Models can be talked into things.
- Pixels can't be escaped. Text inside an image bypasses the fence by nature.
- Autonomous turns remove you from the loop. Only use
--wakewhere that's acceptable. - Local processes. Anything running as your user can run
lwiscommands, includinglwis agents approve.
Guidance for agent operators
- Keep side-effecting tools behind permission prompts in the sessions you attach.
- Use strict mode for sessions that hold secrets or production access.
- Give hosted agents a dedicated directory, the fewest tools, and keep
strict = true. - Leave
--allow-repliesoff unless you trust everything that can reach the session. - Treat a capture from anyone but you like an email attachment from a stranger: forward it with a narrow note, to a session or agent you watch.