Where attention appears to go across agent threads: collector, web explorer, and native iOS client
agents docs ios.md
9.9 kB
Markdown
at main

iOS feasibility and implementation direction #

Assessment: 2026-09-29. Recommendation: a native Expo/React Native client against the existing read-only backend. Medium lift for a useful personal client; substantially more work for a public product. This is a source-based assessment, not a built prototype or a calendar estimate.

What the existing apps establish #

Reference Inspected source What to reuse What not to inherit
Simmer ~/tangled.org/zzstoatzz.io/simmer, HEAD 6aa94db; App.tsx, src/auth.ts, README, package/build config Expo/RN scaffold, native bottom tabs, selective SwiftUI controls via @expo/ui, virtualized journal, system appearance, EAS workflow Its private ATProto Space and durable write queue. Agents already has a server and is read-only. Its direct OAuth client is a different auth topology.
tuner (“still”) ~/tangled.org/zzstoatzz.io/still, HEAD 8374682; README, package config, verification record Device lifecycle discipline, accessibility/Reduce Motion, preferences, EAS-managed signing with a local-build fallback Audio capture, DSP, permissions and continuous instrument processing. Agents has none of these.
birds.place ~/tangled.org/birds.place/birds.place, HEAD d35d1bc; mobile/utils/session.ts, worker/auth.ts, mobile/README.md, CLAUDE.md, submission docs Closest precedent: server-owned OAuth, native opaque session in SecureStore, Expo Router, shared pure data package; one product rendered twice Social writes, media, maps, moderation and public account lifecycle. Do not blindly copy its bearer token in a custom-scheme callback URL.

All three currently pin Expo 57 / React Native 0.86.3 in source. This is a description of these checkouts, not advice to upgrade to whatever is latest. Simmer and tuner use existing EAS release scripts; tuner documents a processed internal TestFlight build. birds.place records unresolved submission/reviewer-access work. Live App Store Connect or EAS status was not checked in this assessment, and build records do not prove approval.

Options and relative lift #

Option Relative lift Result
Keep web; improve Home Screen installation Smallest Existing UI and auth retained. Useful if launch convenience is the actual need; not a native interaction upgrade.
WKWebView wrapper Small shell, meaningful auth/lifecycle work Reuses HTML/CSS, but inherits browser UI limitations and introduces another cookie context. Doesn't buy much toward the requested native feel.
Expo/RN native renderer, existing backend Medium; recommended Native lists, navigation, sheets, gestures, accessible text and secure credential storage. Reuses backend and pure domain logic.
SwiftUI-only client Larger divergence from these reference apps Strong iOS integration, but duplicates JS calculations/contracts in Swift or moves more logic to the server. No demonstrated need for that cost yet.

Compared with birds.place, this has fewer platform features and no writes/media/social moderation. Compared with tuner, rendering the data is simpler than audio processing, but remote identity/data access is additional work. Compared with Simmer, we avoid sync-conflict/write-queue machinery, but must add a native session transport to our existing Python server.

A plain website wrapper also has App Store minimum-functionality uncertainty; Apple requires an app experience beyond a repackaged website. For a personal owner-only tool, start with a device development/Ad Hoc build; treat public release as a separate decision. Apple itself suggests Xcode/Ad Hoc for limited personal audiences. Apple guidelines.

Reuse and gaps #

Backend: mostly reuse. Keep collection, redaction, Git/Orca context, 30-day history and LLM summaries on the Mac. Existing routes are /api/sessions, /api/attention, /api/attention/digest. Native does not enable collecting other apps' logs on iOS or prove actual attention. It still depends on the Mac and tunnel being available.

Domain logic: extract, don't fork. attention-model.mjs is already browser-independent. Date/range/repo helpers in attention-shared.mjs can move into a pure module; its DOM renderers cannot. Add a shared versioned/validated API contract and fixture-based parity tests. Keep grouping, duration caveats, titles, date boundaries and repo identity decisions shared; views render them. birds.place's src/data/ is the model.

UI: rewrite the rendering layer. Current code is vanilla DOM/CSS, not React: it cannot be imported into RN. Use native tabs/stacks, virtualized lists, shared harness/repo row components, native settings/calendar sheets, and RN SVG for timeline marks. Keep Sessions, Your day, Timeline and Find aligned with the accepted web organization. Do not copy four independent page implementations and gradually let them drift.

Authentication: the first real integration task. WebDashboard.identity() currently reads only __Host-observatory; MCP's bearer auth does not authorize these REST routes. Browser cookies cannot be assumed to transfer from a system authentication browser into native fetch.

Proposed native flow:

  1. App creates a verifier and sends its challenge to a native-specific start endpoint. Server records a bounded, expiring attempt and opens the existing identity-only ATProto flow in the system browser, keeping SDK validation and the DID allowlist.
  2. Callback returns only a short-lived, single-use exchange code to the registered app return URL. Bind that code to the original challenge and intended return target; consume it atomically. Prefer a verified HTTPS app link when feasible; a registered scheme still needs verifier binding.
  3. Native exchanges code + verifier for an opaque app session over HTTPS. Store it in SecureStore; keep server lookup hashes in the existing private AuthStore. Add explicit bearer authentication to the REST routes without changing browser cookie/Origin protections or conflating MCP grants.
  4. Support restore, server-side revocation, cancelled/expired login, replay rejection, wrong verifier, app cold/warm return and server restart. Decide the native session lifetime explicitly: carrying over the current 12-hour limit is safe but may be annoying. No silent extension of existing browser access.

This adapts birds.place's topology while avoiding its direct session-token callback URL. Use platform/SDK browser auth facilities rather than implementing ATProto cryptography. Expo authentication guidance requires an app development build to test real callback schemes; Expo Go is not the acceptance environment.

Refresh and cache: modest but necessary work. Current uncompressed projected files measured about 1.6 MB Sessions, 556 KB timeline, 1.2 MB digest on September 29. Downloading the entire Sessions file every two seconds is a poor native foreground default; at 1.6 MB × 30 that's about 48 MB/minute before transport compression, even without meaningful changes. Measure actual wire traffic before choosing thresholds. Separate list metadata from selected-session excerpts, add bounded pagination/change revision or conditional requests, and refresh on foreground/pull-to-refresh with backoff. A phone background timer is not a data-collection strategy.

Start with memory caching and explicit stale/offline UI. If durable offline excerpts are wanted, choose account-scoped protected storage, retention and deletion semantics deliberately; never put transcripts in SecureStore or a public web cache. Logout/401 must clear all private in-memory views and any eventual disk cache. A backgrounded request must not repaint stale private data after logout.

Sequence and acceptance gates #

  1. Make source durable: establish a versioned project containing backend, web, shared domain code and mobile/. Preserve private data/config outside it. Inventory deployment references before moving paths.
  2. Prove access on a real phone: build the native shell and complete owner sign-in → one validated API response; cancel/retry, kill/relaunch, server restart, expiry and revocation work. No keys or app sessions in URLs/logs.
  3. Deliver a useful slice: Your day → sources → session detail, harness marks and all repo links. Native Back/swipe, loading/error/stale behavior, Dynamic Type and VoiceOver work with real data. Web remains functional.
  4. Reach feature parity: Sessions filtering/archive/subagents, Find, timeline/date/period/grouping/gap controls, settings. Run both renderers through the same domain cases: Chicago DST, isolated signals, thread switches, filtered gaps, mixed harnesses and missing remotes.
  5. Measure and distribute: check network bytes, long-list responsiveness, foreground/background lifecycle, private cache cleanup, physical-device appearance. Reuse EAS setup patterns from Simmer/tuner with a new app identity. Register/sign only as part of the approved implementation; no resources were created here.

App Store work is additional: suitable audience/distribution, reliable backend availability, accurate privacy disclosures, review access that does not expose Nate's real history, screenshots and physical-device verification. Do not broaden the owner allowlist or seed private history for reviewers. Any safe demo mode or multi-user design is a separate scoped implementation.

Deliberately deferred #

Widgets, push notifications, new gamification, microphone/voice capture, agent controls, broader screen-time monitoring and multi-user hosting. None is required to prove that the native client is worth using. Native polish can improve navigation; it cannot turn log timestamps into measured attention.