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(packagegithub.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; extendatircgo. - No sockets.
contrib/wasm/realboots kcp over an in-processnet.Pipelistener/dialer withmemory://storage. The apiserver never binds a port. - Storage. The patched apiserver memory backend (
KCP_PATCH_MODE=native,k8s.io/apiserverreplaced with the patched copy) pluscontrib/wasm/storagesnapshot/restore. kcp root lives inApplication 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 adebug.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"intoGoLib/AtircGo.xcframework. The app linksAtircGoand-lresolv(Go's BIND resolver symbols). Build artifacts are large; keep the release dirs pruned.
Milestones
- M1 — boots (done). kcp boots in-process on darwin and iOS;
/versionand/apisreturn 200; combined framework builds; app compiles and shows a kcp status bar. - 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. - 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. - 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.
- 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-macis 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) setsPolicyEngine spec.sandboxwith a Prober/Fenced condition. No contract is created while the policy cannot run. - Credentials:
~/.aliceoa_bsky_social_sourceis 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/kcpsmokethen run; expectreal: ready,/version200,/apisAPIGroupList. - Framework:
gomobile bind ... atircgo; headers exposeAtircgoKCP*. - App:
xcodebuild -project AT-IRC.xcodeproj -scheme AT-IRC -destination 'generic/platform=iOS Simulator' build; install/launch in the booted simulator; screenshot status bar showskcp: ready · <gitVersion>.