# iOS embedded kcp — plan Status: 2026-10-30. Lead: atirc-ios-client-dev. Canonical cross-repo plan: socialweb-computer-kcp `docs/plans/0038-ios-embedded-kcp.yaml` (plan owner linux-kcp). This file is the app-side milestone plan; 0038 is the system view. Item mapping: M1/M2 = I1-BOOT + I2-RUNTIME (done), M2 = I3-PERSIST, M5 = I5-OAUTH, M3 = I4-SOCIALWEB-IOS (linux-kcp) + I6-POLICY-SANDBOX (linux-kcp) + I7-AGENTS, M4 = I8-ASK-OWNER, device e2e = I9 (linux-kcp). Goal: run the real kcp control plane and the socialweb requester inside the AT-IRC iOS app so the app can launch and manage agents through the same market request flow the web page uses. ## Architecture (locked decisions) - **One Go runtime per process.** A combined `AtircGo.xcframework` (package `github.com/kcp-dev/kcp/contrib/wasm/ios/atircgo`, built with gomobile) contains both the app's IRC helpers and the kcp bridge. Linking a second gomobile framework (e.g. a separate KCPGo) crashes at launch: duplicate Go runtimes. Never add another Go framework; extend `atircgo`. - **No sockets.** `contrib/wasm/real` boots kcp over an in-process `net.Pipe` listener/dialer with `memory://` storage. The apiserver never binds a port. - **Storage.** The patched apiserver memory backend (`KCP_PATCH_MODE=native`, `k8s.io/apiserver` replaced with the patched copy) plus `contrib/wasm/storage` snapshot/restore. kcp root lives in `Application Support/kcp`; the service-account key is ECDSA P-256. - **Memory.** `KCP_MEMORY_STORAGE=main`, `KCP_LOW_MEMORY=1` (watch cache off, short watch history), `debug.SetGCPercent(25)` after ready, and a `debug.SetMemoryLimit(1 GiB)` soft limit. iOS jetsam is the limit, not wasm's tab heap; measure on a device and raise the limit if the requester needs it. - **Bridge API** (Swift): `AtircgoKCPStart(root, snapshot)`, `AtircgoKCPRequest(method, path, headersJSON, body)`, `AtircgoKCPSnapshot()`, `AtircgoKCPReady()`, `AtircgoKCPStatus()`, `AtircgoKCPStop()`. - **Build.** Regenerate the native patched modfile, then `gomobile bind -target=ios,iossimulator -ldflags="-s -w"` into `GoLib/AtircGo.xcframework`. The app links `AtircGo` and `-lresolv` (Go's BIND resolver symbols). Build artifacts are large; keep the release dirs pruned. ## Milestones 1. **M1 — boots (done).** kcp boots in-process on darwin and iOS; `/version` and `/apis` return 200; combined framework builds; app compiles and shows a kcp status bar. 2. **M2 — simulator e2e (done).** Launch in simulator, kcp reaches ready, status bar shows kcp state. Snapshot persistence implemented: save after ready and on scenePhase background to `Application Support/kcp/snapshot.b64` (1.3 MB after a cold boot), restore on launch, fail-closed drop of an unreadable snapshot. Verified across terminate/relaunch; kcp ready again. 3. **M3 — socialweb + agents (I4 integrated).** The Agents tab starts kcp lazily (8.7 s in the simulator), then the embedded socialweb requester against the same kcp (ready ~2.7 s later), and shows "socialweb requester ready". The Agents API creates the session EphemeralInfra and applies ComputeContract entries with spec.agent; Release/revoke remain for I7. Launching is still unavailable/denied until (a) linux-kcp's I6 policy sandbox runs the requester's tangled-vouch, and (b) pkg/embedded exposes the page's `PutContractSecrets` (the ATIRC_TOKEN bundle written before the contract). Both requested. 4. **M4 — ask-owner UI (done, runtime pending deploy).** Pending list, answer sheet, availability toggle, decided/ADR rows, local notification when mode is available. Uses the account-level minted atirc token per path (a). Live calls 401 until atirc-dev deploys the server change that adds the five ask NSIDs to the minted token scope. 5. **M5 — OAuth scopes (done in simulator).** iOS metadata and authorize request carry 28 scopes = exact requester-scope.txt (27, r131) plus `rpc:chat.atirc.revoke?aud=did:web:irc.fedfork.com%23atirc`; dedupe when socialweb rc133 generates revoke itself. Scripted alice sign-in in the simulator succeeded end to end (OAuth -> loopback -> DPoP -> mint, 28/28 granted). Device signing/TestFlight pending. ## Workstreams and owners - kcp core, framework build, app integration: atirc-ios-client-dev (lead). - ask-owner iOS UI + notification: subagent ask-owner (scope now iOS-only; server/CLI owned by atirc-dev/atirc-ask). - OAuth full scopes + Agents nav: subagent scopes-agents. - Server XRPC (ask/answer/decide/questions/owner_mode), token scopes: atirc-dev / atirc-ask. - Wasm reference flow (page) and native-overlay guidance: wasm; source tree `/Users/johnandersen777/src/linux-kcp/kcp-wasm-mac` is read-only. ## Risks / open decisions - Memory: watch cache off + GOGC 25; measure with a VM lifecycle. Watch jetsam. - Snapshot size/write cost: write on a debounce (page uses >=15 s gap + 3 s trailing), never on every mutation. - Background: iOS suspends the app; kcp and the requester stall. A release asked before suspension finishes on the next launch from persisted state; ask iOS for background time to finish it first (0038 agents decision). - Policy: the requester runs `requester-tangled-vouch`; iOS has no deno, so it fails closed until linux-kcp's iOS policy sandbox provider (I6) sets `PolicyEngine spec.sandbox` with a Prober/Fenced condition. No contract is created while the policy cannot run. - Credentials: `~/.aliceoa_bsky_social_source` is sourced only inside the sign-in command; values never printed/logged/copied; sign-in URLs never posted. - App repo is not a git checkout; keep edits local and coordinate files between the lead and subagents. ## Verification - Host smoke: `go build -modfile=.go.memory.mod ./contrib/wasm/ios/cmd/kcpsmoke` then run; expect `real: ready`, `/version` 200, `/apis` APIGroupList. - Framework: `gomobile bind ... atircgo`; headers expose `AtircgoKCP*`. - App: `xcodebuild -project AT-IRC.xcodeproj -scheme AT-IRC -destination 'generic/platform=iOS Simulator' build`; install/launch in the booted simulator; screenshot status bar shows `kcp: ready · `.