Blolabel
← Notes
analysis 8 min read

ZoneClaw cuts persistent-memory attacks from 372 of 480 to 6; phoneshell's screen memory stores 16,815 on-screen labels as first seen, with no record of who wrote them

ZoneClaw's point is that an agent's memory is where untrusted content goes to become trusted. We counted what phoneshell's screen memory holds on the rig: 808 screens, 16,815 labels, no provenance. Only 178 can reach a prompt today, and one of them carries a Wi-Fi network name, text anyone in radio range can choose.

ZoneClaw, posted on 30 September 2026 by researchers at the National University of Singapore, shows that text a computer-use agent remembers acquires authority over later tasks: 372 of 480 persistent-memory attacks succeeded against an undefended OpenClaw-style agent, and separating memory into trust zones cut that to 6. phoneshell's screen memory on our rig holds 16,815 control labels from 808 screens, stored as first seen with no record of whether Apple, an app, an ad network or a stranger's notification wrote them.

Abstract technical illustration of layered translucent storage cells, one stratum faintly tinted where external fragments settle among uniform blocks

What ZoneClaw found

Haokai Ma, Chieh Lin, Yupeng Qiu and Ee-Chien Chang start from a probe on 30 Workspace-Bench tasks. A writing convention seeded only into the agent's memory file was adopted in 14 of 30 tasks, with task quality barely moved (43.5% rubric pass against 46.4%). A user instruction overriding it brought adoption to 0 of 30. Remembering, by itself, confers authority. The attacks then run across four scenarios (BCC, Chat Mirror, Source Redirection, Marketplace), two injection settings (user-triggered and scheduled) and four backbones. The chain is "malicious context, memory injection, malicious execution": the attacker never touches the later task.

The defense does not filter what gets remembered. External claims still persist, in a low-trust zone, and acquire the power to guide action only by crossing an explicit authority boundary, where promotion is cross-checked against zones the attacker cannot write. Role-specific processes enforce it so that no process both ingests external content and acts outward. On Claude Sonnet 4.6, attack success was 0 of 15 in all eight scenario and setting combinations. The authors also report that each of four existing defenses works in some settings and fails in others.

ZoneClaw: attacks that succeed, and tasks that still work (out of 480) count of 480 with ZoneClaw 0 100 200 300 400 500 trials 372 (no defense) 6 attack success 458 utility retained (ZoneClaw) Source: Ma, Lin, Qiu, Chang, ZoneClaw, arXiv:2610.00450 (30 September 2026), abstract and Table 1.

phoneshell's memory, counted

phoneshell/memory.py exists to skip work. Its docstring prices a step at 159 ms for a tree read, 72 ms for a screenshot, 504 ms for the stability gate and 1 to 3 s for the model, so the gain is "not asking the model at all". For each screen it stores a layout fingerprint, the controls, which control led to which screen, and how long the screen took to settle. On the next visit the agent gets a MEMORY: block, and with skip_image_on_known: true in the rig's config the screenshot can be skipped. We counted the rig's store on 4 October 2026.

MeasureValueSource
App files28 (Apple apps plus Grab, Gmail, YouTube, Facebook, X, others)runtime/memory/*.json
Screens remembered808; 348 seen at least twice (trusted at min_visits_to_trust: 2)runtime/memory, runtime/config.yaml
Controls stored16,815; 11,719 with an accessibility identifierruntime/memory
Eligible for a hint178 (tapped at least once and led somewhere), 69 of them in Settings; 91 keyed by identifiermemory.py hint(); runtime/memory
Identifier keys with more than one stored label across screens89 of 1,248runtime/memory
Largest caseone SpringBoard notification key, 19 different notification texts (messages, news pushes)runtime/memory
Ad labels stored17 ("Advertisement, ..." App Store slots and similar); 0 tappedruntime/memory
Agent-written names and notes0 named screens, 0 notesruntime/memory
Provenance or trust fieldnone in the schemamemory.py ControlMemory, ScreenMemory
What phoneshell's memory holds, and how much of it reaches a prompt stored controls16,815 with an identifier11,719 hint-eligible (tapped, led somewhere)178 (1.06%) eligible, keyed by identifier91 808 screens in 28 app files; 348 seen 2+ times, so trusted. Named screens: 0. Notes: 0. 1,248 identifier keys; 89 stored with more than one label across screens. One SpringBoard notification key: 19 different texts, from messaging and news apps. 17 labels start with "Advertisement" or name an ad; none tapped, so none eligible yet. Source: our count over ~/Desktop/projects/pro-phoneshell/runtime/memory/*.json, 4 October 2026 (local data, not published).

How a label from the screen becomes a line under MEMORY

Three details in memory.py make the chain. see() creates a control the first time it appears, with label=(e.label or e.name or e.text)[:60], and on later visits updates only its position. control_key() hashes the element type with the normalised identifier when there is one, and uses the label only when there is none, so a control keeps its key while its text changes. And hint() prints the stored label, not the live one: [{live id}] 'stored label' -> where (Nx), under the heading "what these controls did last time".

Same chain, two agents: read, persist, re-present as authority ZoneClaw threat model (OpenClaw-style CUA) external contentweb page, email, chat written to MEMORY.mdsame privilege as the user reloaded next sessionprepended to context governs a later task372/480 attacks succeed ZoneClaw: external claims live in a low-trust zone, promotion is cross-checked: 6/480, utility kept in 458/480 phoneshell screen memory (runtime/memory/*.json) accessibility labelads, notifications,Wi-Fi names, app data see(): stored oncelabel frozen at first sight;later visits update x, y only screen seen 2+ timesmin_visits_to_trust 2;screenshot may be skipped hint(): "MEMORY: ...[12] 'old label' -> ..."stored text, live id Key = element type + normalised identifier, label ignored when an identifier exists, so the live control and the remembered one can carry different text while sharing one id in the prompt. No zone, no provenance field: a label from an ad and a label from Apple's Settings chrome are stored alike. Sources: Ma, Lin, Qiu, Chang, arXiv:2610.00450; phoneshell/memory.py see(), control_key(), hint() (read 4 October 2026).

The live case on the rig is mild and instructive. The Wi-Fi row on the Settings root screen (identifier com.apple.settings.wifi) is hint-eligible on two remembered screens seen 42 and 59 times, and each stores the row's label as it read on first sight: "Wi-Fi, " followed by the network name the phone was on. The network name is text chosen by whoever runs the access point, and any nearby access point can broadcast one. Today it can reach the prompt only as the label of a control the agent tapped once, under a 60-character cap. That is a small channel, but it is the ZoneClaw shape exactly: environment-written text, persisted without a source, re-presented later as the agent's own experience. The notification and ad labels sit in the same store, one tap away from eligibility.

The store also raises a plainer problem. The 19 notification texts under one SpringBoard key are messages and news pushes from other people, kept on disk in runtime/memory indefinitely. We do not quote them here. A memory built to fingerprint layouts does not need them; the fingerprint already normalises volatile text, but the stored label does not.

What to change, and what it costs

Check it yourself

What would prove this wrong

Our claim is that a label stored from a third party can reach phoneshell's prompt unchanged after it has changed on screen. Test: rename the phone's Wi-Fi network, open Settings twice, and read the observation notes. If the MEMORY block shows the new network name rather than the stored one, our reading of hint() is wrong. We expect it to show the old name on phoneshell as of commit eed3fb1, and we expect phoneshell to either print live labels or add a provenance field by 31 March 2027; if neither has happened, this note's recommendation was not taken.

Sources

  1. Ma, Lin, Qiu, Chang, ZoneClaw: Mitigating Persistent Memory Attacks by Establishing Memory-Zoning in OpenClaw-Style Computer-Use Agents, arXiv:2610.00450, 30 September 2026
  2. ZoneClaw code release
  3. Blolabel, earlier note on iOS identifiers that carry instance data
  4. phoneshell source: phoneshell/memory.py, phoneshell/config.py (read 4 October 2026); counts from the rig's local runtime/memory store and runtime/config.yaml