Status: Scoping — for review Date: 2026-06-17 Parent design: option-b-caching-browser.md §10
This breaks the ticket's three named gaps into concrete, reviewable units of work against the current codebase. No implementation yet.
Current state (what exists today):
Repo(ios/Stone/Models/Repo.swift) — pure data:id,name,fileName,remoteURL. No notion of a user identity or session.CredentialStore(…/Services/CredentialStore.swift) — Keychain, keyed by repo UUID, stores one password per repo. No username, no cookie.FossilEngine(…/Services/FossilEngine.swift) — runsfossilcommands and the loopback server via the C bridge. No HTTP-client capability of its own.RepoStore— clone/sync/rename/delete;urlWithPassword()injects the password into the remote URL's userinfo for Fossil's own sync.- UI is browse/clone/sync only (
RepoListView,RepoDetailView,RepoWebView); no Sessions/Thread/composer views.
Gap 1 — Login + cookie-forward auth
Goal: obtain a Fossil login cookie for a remote and forward it on agent
requests (JSON endpoints, /forume2, session_live_url mint).
What Fossil's login actually is (to verify before building): Fossil's /login
sets a fossil-<projcode> cookie tied to the project. We need to confirm the
exact login form fields and cookie name against a live ollama.openbeagle.org
repo (anonymous capabilities, whether a captcha/anoncap flow is involved, and
whether the JSON endpoints accept the cookie vs. a separate token).
New pieces:
- A
usernamealongside the existing per-repo password. Options: add ausername: String?toRepo(it's not secret) or store auser:passwordpair inCredentialStore. Leaning:usernameonRepo(non-secret, visible in metadata), password stays in Keychain. Decision needed. - A
RemoteSessionservice: performsPOST /login, captures theSet-Cookie, holds it in memory (and optionally Keychain for reuse), exposesauthorizedRequest(for: URL) -> URLRequestthat attaches the cookie. Built onURLSession(native HTTPS — not the Fossil C path; this is client traffic, cleanly separate fromFossilEngine). - CA trust:
URLSessionuses the iOS system trust store, so unlike the Fossil C core it does not need the bundledcacert.pem. (Confirm the host's chain validates against system roots.)
Risks / unknowns: cookie lifetime + refresh-on-401; whether session_live_url
needs the cookie or only the minted HMAC token; CSRF token on the login form.
Estimated surface: ~1 new service file + a small Repo/credential change.
No C-bridge changes.
Gap 2 — Native Sessions view + SSE consumer + reply composer
Goal: a native mobile UI to list sessions/threads, watch a turn live, and post a reply as the human.
Sub-units:
- JSON models + fetch. Decode the agent endpoints into Swift
Codablemodels. Known contracts (maintainer, 2026-06-18):GET <repo>/ext/agent?session_posts=<root>→{ ok, posts: [{ hash, user, mtime, role, html }] }GET <repo>/ext/agent?session_live_url=<root>→ the signed:8443SSE URL (mints the per-project HMAC token). Still want a captured live sample of turn-status JSON to confirm shape.
- Sessions/Thread SwiftUI view. List threads → thread detail showing posts; read-only first.
- SSE consumer. Connect to the
:8443live-events stream (URL minted bysession_live_url, which carries the per-project HMAC token). SSE = a long-livedtext/event-streamURLSessiondata task parsingevent:/data:frames; surfaceturn_started/turn_heartbeat/turn_finishedas live status. Needs lifecycle handling (connect on view appear, tear down on disappear, reconnect/backoff). - Reply composer (the Fossil forum CSRF dance). Posting as the human is
not a single POST — it's Fossil's forum reply flow:
GET <repo>/forumedit?fpid=<H>&reply(authenticated with the login cookie).- Scrape
name="csrf" value=".."out of the returned reply editor HTML. POST <repo>/forume2with thecsrftoken + aRefererheader (preview, then submit). Reference implementation:ollama-codex src/forum_writer.cr. Requires the login cookie and forum-reply capability on that repo (cap 3/4, or s/a). A repo not provisioned with forum caps/enable fails with "no csrf token in reply editor" (observed live onblabl, 2026-06-17). Stone must surface auth/cap failures distinctly (e.g. "not permitted to post on this repo") rather than as a generic error. Write action → confirm before send (consistent with guarding anything that leaves the device).
Risks / unknowns: exact JSON schemas; SSE token lifetime; turn-status polling vs SSE overlap; detecting the no-csrf cap-failure cleanly vs other HTML errors.
Estimated surface: several new files (models, 2–3 views, SSE client). Largest of the three gaps.
Gap 3 — Native vs WKWebView for agent views
Decision, not code. Two viable paths:
| Approach | Pros | Cons |
|---|---|---|
| Native | Best mobile UX; true "optimized client"; offline-cacheable JSON; no CSP/CORS | Most work; must track agent JSON schema changes |
| WKWebView | Fastest to ship; reuses server HTML; low drift | Opaque HTML; need cookie injection; the sluggish/flaky issues that motivated Option B |
Recommendation: native for the live agent surface (Sessions/Thread/SSE/ composer) — that is precisely the ticket's value proposition and avoids the WKWebView pain Option B was created to fix. Keep WKWebView for general Fossil browse/read (working-copy and remote-cached HTML pages). This matches the client/server split in §10.
Proposed build order
- Gap 1 (login + cookie-forward) — unblocks everything; smallest surface.
- Capture payloads — pull real samples of
session_posts, turn-status, the SSE frames, and the/forume2form from a live repo (prerequisite for Gap 2). - Gap 2a — JSON models + read-only Sessions/Thread view.
- Gap 2b — SSE live consumer.
- Gap 2c — reply composer (write path, confirm-before-send).
Gap 3 is decided up front (native) and shapes 2a–2c.
Decisions needed before coding
- D1:
usernameonRepovs.user:passwordin Keychain? (Gap 1) - D2: Persist the login cookie in Keychain for reuse, or re-login per launch?
- D3: Confirm we can obtain real sample payloads from a live
ollama.openbeagle.orgrepo (blocks Gap 2) — turn-status JSON + SSE frames + theforumedit/forume2form. - D4: Confirm Gap 3 = native (assumed above).
Note (2026-06-18): This scoping covers the agent-client (anywhere-coding) plane. The maintainer has since expanded the vision to a multi-plane gateway — see vision-gateway.md. The journaling plane (embedded offline-first write) is positioned as the nearer, lower-risk pillar and is scoped there; the agent-client work here builds on the same auth + the Option B read foundation.