Merge nucleic/zesty-opal-robin-p6sk into dev
This commit is contained in:
@@ -0,0 +1,846 @@
|
||||
# WINDOWS_PORT — Nucleic for Windows, full implementation plan
|
||||
|
||||
> **Status:** Approved plan, 2026-07-28. This document is the working spec for the native
|
||||
> Windows port. It is written to be self-contained: an agent picking up any work item below
|
||||
> should be able to execute it from this document plus the referenced source files, without
|
||||
> access to the planning conversation.
|
||||
>
|
||||
> **Goal:** full feature parity with the macOS app **except** the macOS-VM / GUI-Linux-VM
|
||||
> computer-use subsystem (`Sources/NucleicCore/MacVM/`), which cannot exist on Windows.
|
||||
> Reuse as much existing Swift as possible via the official Swift-for-Windows toolchain.
|
||||
> The UI is **WinUI 3 (C#)**. Anything not covered by existing Swift is written in **C#**,
|
||||
> per Microsoft's recommendation for the WSL Container API.
|
||||
|
||||
---
|
||||
|
||||
## 0. Locked decisions (user-approved — do not relitigate)
|
||||
|
||||
| # | Decision |
|
||||
|---|---|
|
||||
| D1 | **Headless Swift core + C# renderer.** `nucleic-hostd.exe` (Swift, Windows toolchain) owns sessions, git, storage, approvals, and sync. The WinUI 3 app is a *renderer* over the existing Noise/CBOR sync protocol via local loopback — the same "N renderers, one authority" pattern the iPhone app uses. No Swift-in-C#-process embedding of the core. |
|
||||
| D2 | **Control-only.** Windows supports ONLY Nucleic Control projects. Every agent session runs inside a WSL container. There is no host-spawned agent path on Windows. (Control will be forced on macOS soon too; plan accordingly.) |
|
||||
| D3 | **Containerization = WSL Container API** (`Microsoft.WSL.Containers` NuGet, WinRT; public preview now, GA fall 2026), driven from a C# broker process. Not full VMs, not Docker, not `wsl --import` distros. |
|
||||
| D4 | **MacVM subsystem excluded** (both macOS-guest and GUI-Linux-VM computer-use paths). MDM (`NucleicMDM` server role) and `NucleicPowerHelper` are also not ported (macOS-VM-only / macOS-concept). |
|
||||
| D5 | **Distribution:** MSIX packaging + per-channel `.appinstaller` sideload feeds on Cloudflare R2, mirroring the five-channel Sparkle model (dev/canary/beta/rc/stable). Authenticode via Azure Trusted Signing. Store/winget deferred to post-beta. |
|
||||
| D6 | **On-device AI assists:** Windows AI Foundry / Phi Silica (Windows App SDK) where hardware supports it; graceful degradation to the existing heuristic fallbacks elsewhere (the same behavior as a Mac without Apple Intelligence). |
|
||||
| D7 | **Renderer protocol client = Swift DLL.** `NucleicProtocol` (+ transcript projections) compiled into `NucleicProtocolC.dll` exposing a thin flat C ABI; C# P/Invokes it. No C# reimplementation of CBOR/Noise/envelope. |
|
||||
| D8 | **Repos on NTFS.** Control clones + worktrees live at `%USERPROFILE%\.nucleic\control`, bind-mounted into containers via `ContainerVolume`. Host git ops keep using `GitRunner`/`WorktreeManager` with Windows `git.exe` (bundled MinGit), unchanged. |
|
||||
| D9 | **Full remote parity:** DNS-SD advertise + QR pairing for the iPhone remote over LAN, the relay transport, AND tailnet (libtailscale built for Windows as a DLL). |
|
||||
| D10 | **x64 + ARM64 from day one.** The naros-agent OCI image gains a multi-arch (amd64+arm64) build. |
|
||||
| D11 | **Self-hosted Windows CI runners** (one x64 first, ARM64 later) for the container e2e suite; hosted runners lack nested virtualization. |
|
||||
| D12 | **The Windows app is also a remote client.** It can pair with and render any Nucleic host (its own hostd, or a Mac) — host-picker UI, reusing the same pairing/connection code. |
|
||||
|
||||
---
|
||||
|
||||
## 1. Background: what exists today (verified 2026-07-28)
|
||||
|
||||
### 1.1 The codebase already has the port seams
|
||||
|
||||
- `Package.swift` (swift-tools-version 6.2) computes `onLinux` at manifest-eval time
|
||||
(`Package.swift:62-64`), drops `darwinOnlyTargets` (`:70`, `:402`), conditionalizes
|
||||
dependency lists (`:219`, `:266`), and excludes Apple-framework sources from
|
||||
`NucleicCore` via a per-target `exclude:` list (`:352-381`). The manifest is also
|
||||
channel-aware (`NUCLEIC_CHANNEL` env → product name + `NUCLEIC_DEV/CANARY/BETA/RC/STABLE`
|
||||
compile define).
|
||||
- Source-level gating is almost entirely `#if canImport(...)` (only 3 `#if os(...)` in all
|
||||
of `Sources/`: `Claude/ClaudeCredentialBroker.swift:360`,
|
||||
`Codex/CodexCredentialBroker.swift:166`, `Project.swift:465`).
|
||||
- `Sources/NucleicCore/LinuxSupport.swift` (~443 LOC) provides stub actors
|
||||
(`ContainerManager`, `MacVMManager` mirrors that throw `LinuxUnsupported`) plus an
|
||||
`os.Logger` shim, so shared call sites compile unchanged on non-Darwin. **This is the
|
||||
template for `WindowsSupport.swift`.**
|
||||
- `Sources/nucleicd/` (645 LOC: `Nucleicd.swift`, `ControlEndpoint.swift`) is a proven
|
||||
headless host over `NucleicCore` for Linux (the Covalence Cloud Runner): one `AppStore`
|
||||
graph, `startSyncServer()`, pairing mint, signal-driven shutdown. **This is the template
|
||||
for `nucleic-hostd`.**
|
||||
- `NucleicProtocol` builds for macOS, iOS, and Linux already. On non-Apple platforms it
|
||||
swaps CryptoKit→swift-crypto and URLSession-WebSocket→SwiftNIO via `#if canImport`.
|
||||
|
||||
### 1.2 Target portability inventory
|
||||
|
||||
| Target | LOC (≈) | Verdict for Windows |
|
||||
|---|---|---|
|
||||
| `NucleicProtocol` (57 files) | 13,200 | **Portable as-is.** AgentEvent model, ClientMsg/HostMsg envelope (`Sync/MessageEnvelope.swift` ~1,079 LOC; `Sync/WireMessages.swift` ~831), custom CBOR codec (`CBOR/`), Noise (XXpsk0 pairing + IK reconnect, `Noise/`), `SyncClient.swift` (generic over `FrameChannel`), STUN/UDP punch (`Sync/Direct/`), relay WebSocket (NIO off-Apple). |
|
||||
| `NucleicCore` (177 files) | 79,600 | **The battleground.** Exclude `Container/` engine files + all `MacVM/` engine files (~15.7k LOC, mirroring the Linux exclude list); shim the rest (see §4). |
|
||||
| `NucleicApp` (76 files) | 30,000 | **Do not port.** SwiftUI/AppKit cockpit — this is the WinUI 3 rewrite target. Includes SwiftTerm terminal, Sparkle updater, Dock-bounce/NSSound hooks, AppleIntelligence UI. |
|
||||
| `NucleicTailnet` | 640 | Portable Swift over `#if canImport(TailscaleKit)`; Windows needs a libtailscale DLL leg (D9, §9.1). |
|
||||
| `NucleicMDM` | 2,540 | Not ported (macOS-VM-only role, D4). Crypto/ASN.1 parts are portable if ever needed. |
|
||||
| `nucleicd`, `nucleic-smoke`, `nucleic-punch-harness` | ~1,200 | Portable; `nucleic-smoke` becomes the Windows e2e driver (§14). |
|
||||
| `NucleicPowerHelper` / `NucleicPowerProtocol` | ~215 | Not ported; replaced by `SetThreadExecutionState` in hostd (§4.3). |
|
||||
| `ios/NucleicRemote` | ~18,300 | Not ported, but its `Models/RemoteStore.swift` (3,396 LOC, pure host-state projection) + `Models/HostConnection.swift` (1,294 LOC, owns `SyncClient` over a `FrameChannel`) are **the structural template for the C# renderer** (§7). Its `Views/Transcript/` projection files are compiled into the host-testable `NucleicRemoteProjection` SPM target — reused by the DLL (§6). |
|
||||
|
||||
External dependencies (from `Package.resolved`): GRDB 7.11 (**Windows support since 7.10**),
|
||||
swift-crypto, swift-nio(+ssl), swift-certificates/asn1, swift-log, swift-system,
|
||||
swift-collections etc. — all portable. `SwiftTerm` and `Sparkle` are App-target-only
|
||||
(replaced on Windows). `third_party/containerization` (Apple's framework, vendored/patched)
|
||||
and `TailscaleKit.xcframework` are Apple-only binaries.
|
||||
|
||||
### 1.3 NucleicCore internals: what needs what
|
||||
|
||||
| Area | Files (representative) | Windows disposition |
|
||||
|---|---|---|
|
||||
| App engine / state | `AppStore.swift` (14k LOC, `@MainActor @Observable`, conforms to `SyncHostBridge`) | Runs headless inside hostd as the sync authority (as nucleicd proves). |
|
||||
| Session engine | `SessionController.swift` (2,071), `Transcript.swift`, `TranscriptProjection` | Portable. |
|
||||
| Agent backends | `Claude/` (9.5k), `Codex/` (2.4k), `Grok/` (1.2k), `Backend.swift`, `ACP/` | Portable; container-exec path only (D2). |
|
||||
| Approvals / control plane | `Claude/MCPApprovalServer.swift` (~3.9k), `Claude/ApprovalServerRegistry.swift` | Portable logic; needs a NIO `ByteConn` (§5). |
|
||||
| Git | `Git/GitRunner.swift`, `Git/WorktreeManager.swift` (1,079), `Git/GitHubCredentials.swift`, `Git/ProjectCloner` | Shells out to `git`/`gh` CLI — portable (D8). Credentials → SecretStore (§4.2). |
|
||||
| Process spawning | `ProcessHost.swift` (578: `ProcessSpec`/`ProcessHandle`/`ChildProcess`, plain pipes, **no PTY**), `LoginShellPATH/Env` | Foundation `Process` works on Windows; signals + login-shell probing need shims (§4.3). |
|
||||
| Persistence | `Persistence/GRDBMetadataStore.swift` (978, WAL `DatabasePool` under Application Support), `SessionMetadataStore` protocol | Portable; path mapping to `%APPDATA%` (§4.4). |
|
||||
| Sync host | `Sync/SyncHost.swift` (706), `ConnectionHandler.swift` (1,146), `SyncHostBridge.swift` (473) | Portable; LAN transport files are Network.framework and already Linux-excluded — NIO rewrite (§4.3). |
|
||||
| Containers | `Container/{ContainerEngine,ContainerEngine+Rootfs,ContainerManager,ContainerizedProcessHandle,MemoryBalloon,OCIArtifact,CommandInterceptor}.swift` | Engine = Apple-only, replaced by wslc broker (§3). **Manager = policy, ported** (§3.1). `CommandInterceptor.swift` is pure values — keep. |
|
||||
| MacVM | `MacVM/` (28 files, 12.8k) | Excluded (D4); keep pure value files (`MacVMSpec`, `MacVMSurface`) compiling, stub `MacVMManager`. |
|
||||
| Crypto | CryptoKit sites | Free via existing `#if canImport(CryptoKit)` → swift-crypto seams. |
|
||||
| Keychain | 24 `Security` sites in ~14 files | SecretStore protocol + DPAPI backend (§4.2). |
|
||||
| Logging | 12 `os`/OSLog sites | Lift the Linux Logger shim into a shared file (§4.3). |
|
||||
| On-device AI | `Intelligence.swift` (1,295, provider seam + heuristic fallbacks), `AFMRequestQueue.swift` | Windows Foundry provider (§9.2). |
|
||||
|
||||
### 1.4 The Control/container subsystem (what wslc replaces)
|
||||
|
||||
Authoritative docs: `docs/VSOCK_CONTROL_PLANE.md`, `docs/CONTAINER_ISOLATION.md`,
|
||||
`docs/NAROS.md`, `docs/NASH.md`, `docs/RUNTIME_ARCHITECTURE.md`.
|
||||
|
||||
**Two orthogonal virtualization stacks — do not conflate:**
|
||||
1. **The agent sandbox** (`Container/`): Apple `containerization` framework, headless Linux
|
||||
microVMs. → **This maps to wslc.**
|
||||
2. **Computer-use VMs** (`MacVM/`): full `Virtualization.framework` GUI VMs. → **Excluded.**
|
||||
|
||||
Key mechanics that carry over unchanged (all guest-side / protocol-level, OS-agnostic):
|
||||
|
||||
- **Control plane protocol:** host `MCPApprovalServer` serves HTTP JSON-RPC at `POST /mcp`
|
||||
(Streamable-HTTP MCP: `initialize`, `ping`, `tools/list`, `tools/call` with SSE for gated
|
||||
calls), bearer-token multiplexed per session, plus plain JSON `POST /git-event`,
|
||||
`/gh-event`, `/command-event`, `/shell-event` (dispatch at
|
||||
`MCPApprovalServer.swift:1903-1916`). Agent-side MCP config
|
||||
(`mcpConfigJSON`, `:1611`) always points at `http://127.0.0.1:9099/mcp`.
|
||||
- **Guest bridge:** `containers/nucleic-sandbox/control-bridge.js` listens on guest
|
||||
loopback `9099` (`ContainerSpec.controlBridgePort`) and forwards byte-for-byte to the
|
||||
host endpoint. On macOS that endpoint is a per-container host AF_UNIX socket relayed
|
||||
into the guest over virtio-vsock via Apple `UnixSocketConfiguration(.into)` — **the only
|
||||
macOS-specific piece of the entire control plane.** A second optional forwarder
|
||||
(`proxyBridgePort` 9098) serves the opt-in Claude token proxy
|
||||
(`Claude/ClaudeTokenProxy.swift`).
|
||||
- **Interceptors:** Node git/gh shims installed at `/usr/local/bin/{git,gh}` (sources are
|
||||
Swift string literals: `ContainerEngine+Rootfs.swift:394` and `:466`), exec the real
|
||||
binary then POST `{argv, cwd, exitCode, subcommand, sessionId}` to
|
||||
`NUCLEIC_GIT_HOOK_URL`/`NUCLEIC_GH_HOOK_URL` with a bearer token. Optional bash command
|
||||
tracer + batch poster in `Container/CommandInterceptor.swift` (also home of
|
||||
`CommandInterceptor.hookEnv(...)`, the single source of interceptor endpoint env, and
|
||||
`agentShellArgv`/`hostShellArgv` for nash selection). The nash shell (`shell/`, Rust)
|
||||
reports `/shell-event`.
|
||||
- **Agent run flow** (`Claude/ClaudeCodeBackend.swift` `runLoop`, `:766-1087`; Codex/Grok
|
||||
backends identical shape): resolve approval server via `ApprovalServerRegistry` →
|
||||
`containerManager.ensureRunning(cspec)` → mint per-session bearer token →
|
||||
`server.start(unixSocketPath: run.container.controlSocketHostPath)` → register handlers
|
||||
keyed by token (`register`, `registerGitReport`/`GhReport`/`CommandReport`/`ShellReport`,
|
||||
`registerNucleicTools`) → argv `claude -p --output-format stream-json
|
||||
--input-format stream-json --verbose --permission-prompt-tool mcp__nucleic__approve
|
||||
--mcp-config <mcpConfigJSON>` → `sandbox.exec(name:workdir:env:argv:uid:gid:)` returning
|
||||
a `ProcessHandle` (`Container/ContainerizedProcessHandle.swift` adapts framework stdio
|
||||
onto the same NDJSON `AsyncThrowingStream` interface a host `ChildProcess` provides).
|
||||
- **ContainerSpec** (`Backend.swift:54`): `name`, `image`, `mounts: [Mount]`
|
||||
(host/container/readOnly), `workdir`, `env`, `idleTimeout`,
|
||||
`claudeHomeStaging`/`claudeHomeWritable`, `installGitInterceptor`, `cpus`, `memoryGiB`,
|
||||
`runAsUID`(=`narosAgentUID` 501)/`runAsGID`, `controlSocketHostPath`,
|
||||
`proxySocketHostPath`. Constants: `controlSocketGuestPath = "/run/nucleic/control.sock"`,
|
||||
`controlBridgePort = 9099`, `proxyBridgePort = 9098`. Built by
|
||||
`SessionController.containerSpec()` (`SessionController.swift:643`) from
|
||||
`Project.sandbox` (`ProjectSandbox`, `Project.swift:77`).
|
||||
- **Images:** agent rootfs `ghcr.io/abkslm/naros-agent:26.07` (narOS, built from `os/` by
|
||||
`.github/workflows/naros.yml`; contract in `docs/NAROS.md`: `naros-init` PID 1,
|
||||
`agent` uid 501, nash as `/bin/sh`, `NAROS_BRIDGE=1` supervises control-bridge); legacy
|
||||
`ghcr.io/abkslm/nucleic-sandbox:v7` (`containers/nucleic-sandbox/Dockerfile`). The
|
||||
**kernel OCI artifact and patched vminitd initfs are microVM concepts — NOT needed on
|
||||
WSL** (WSL supplies kernel + init). GHCR auth: `ContainerEngine.registryAuth(for:)`
|
||||
pattern (`NUCLEIC_REGISTRY_TOKEN` or app GitHub token with `read:packages`).
|
||||
- **Lifecycle policy** (`Container/ContainerManager.swift`, 687 LOC): busy ref-counting,
|
||||
idle timers, shared-container naming (`sharedControlContainerName`, per-backend
|
||||
claude/codex/xai splits, `channelSuffix`), teardown/GC, `reconcile(activeSessions:)`,
|
||||
`updateAgentCLIs`, `checkImageUpdate`, `controlContainerStatus/Usage/DownloadProgress`,
|
||||
`probeControlPlane`, `requestControlPlaneRecovery`. Its public surface is mirrored
|
||||
byte-for-byte by the `LinuxSupport.swift` stubs — **it IS the port contract.**
|
||||
|
||||
### 1.5 The WSL Container API (wslc) — facts
|
||||
|
||||
Source: learn.microsoft.com/windows/wsl/wsl-container, wsl.dev/api-reference,
|
||||
samples at aka.ms/wslc-samples.
|
||||
|
||||
- NuGet **`Microsoft.WSL.Containers`** (C# projection + C++/WinRT headers). Namespace
|
||||
`Microsoft.WSL.Containers`. **Public preview; breaking changes possible; GA fall 2026.**
|
||||
CLI twin: `wslc.exe` (docker-like: `wslc run / image ls / container ps / stop`, `-p`, `-v`).
|
||||
- Object model: `WslcService` (static entry: `GetMissingComponents() → ComponentFlags`,
|
||||
component install, service version) → **`Session`** (a WSL-backed VM host;
|
||||
`SessionSettings(name, dataDir)` + `CpuCount`, `MemoryMB`, timeout, VHD via
|
||||
`VhdOptions`; manages images: `PullImageAsync(PullImageOptions)` with progress,
|
||||
import/load/push/tag/delete; `session.CreateContainer(...)`; `session.Terminate()`) →
|
||||
**`Container`** (`ContainerSettings(image)` + `Name`, `InitProcess`, networking mode
|
||||
(`ContainerNetworkingMode`), hostname/domain, volumes (`ContainerVolume`,
|
||||
`ContainerNamedVolume`), port mappings (`ContainerPortMapping`); `Start()`,
|
||||
`Stop(Signal, TimeSpan)`, `Delete(DeleteContainerFlags)`, state queries, run additional
|
||||
processes) → **`Process`** (`ProcessSettings`: `CmdLine`, env, workdir,
|
||||
`OutputMode = ProcessOutputMode.Event`; `OutputReceived` events, stdin writes, signals
|
||||
(SIGTERM/SIGKILL/...), exit events (`ProcessExitHandler`), crash info
|
||||
(`ProcessCrashInformation`)).
|
||||
- OCI images pulled directly from registries (docker.io, GHCR); multi-arch manifests
|
||||
resolve per host arch. GPU access supported. Interactive stdin/stdout streaming supported.
|
||||
- Session-level termination handler (`SessionTerminationHandler`); wslc objects are
|
||||
service-backed (state survives a client-process crash — verify in M1 spike).
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture
|
||||
|
||||
### 2.1 Process topology
|
||||
|
||||
```
|
||||
┌─────────────────────────── MSIX package (per channel) ───────────────────────────┐
|
||||
│ │
|
||||
│ Nucleic.exe (WinUI 3, C#) nucleic-hostd.exe (Swift, full trust) │
|
||||
│ ┌───────────────────────┐ Noise/CBOR ┌──────────────────────────────────┐ │
|
||||
│ │ RendererStore (C#) │◄────────────►│ AppStore (headless) + SyncHost │ │
|
||||
│ │ ▲ P/Invoke callbacks │ loopback │ ApprovalServerRegistry (NIO) │ │
|
||||
│ │ NucleicProtocolC.dll │ TCP frames │ GitRunner / WorktreeManager │ │
|
||||
│ │ (Swift, C ABI) │ │ GRDB @ %APPDATA%\Nucleic\<chan> │ │
|
||||
│ └───────────────────────┘ │ ContainerManager (policy, Swift) │ │
|
||||
│ │ └─ WslcBrokerClient (JSON-RPC) │ │
|
||||
│ └───────────────┬──────────────────┘ │
|
||||
│ NDJSON JSON-RPC │ stdio │
|
||||
│ ┌────────────────▼─────────────────┐ │
|
||||
│ │ nucleic-brokerd.exe (C#/.NET 9) │ │
|
||||
│ │ Microsoft.WSL.Containers (WinRT) │ │
|
||||
│ └────────────────┬─────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────┼───────────────────────┘
|
||||
WinRT/COM │
|
||||
┌──────────────────────▼──────────────────────┐
|
||||
│ wslc Session "nucleic-<channel>" (WSL VM) │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ naros-agent containers (shared/split) │ │
|
||||
│ │ control-bridge.js :9099 ──TCP──► host │ │
|
||||
│ │ git/gh shims, nash, claude/codex/grok │ │
|
||||
│ │ /work ◄─ContainerVolume─ NTFS worktree│ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────┘
|
||||
|
||||
iPhone (NucleicRemote) / other renderers ──► LAN (DNS-SD + QR) / relay / tailnet ──► SyncHost
|
||||
Windows app in remote-cockpit mode (D12) ──► same transports ──► any Nucleic host
|
||||
```
|
||||
|
||||
### 2.2 Division of labor
|
||||
|
||||
| Concern | Owner | Why |
|
||||
|---|---|---|
|
||||
| Sessions, transcripts, approvals, risk classification, Autoship + merge queue, LockManager, nvrsion, quotas, idea inbox, activity/streaks | Swift hostd (existing `NucleicCore`, logic unchanged) | Maximum Swift reuse (D1). |
|
||||
| Container lifecycle **policy** (refcounts, idle, shared naming, reconcile, CLI updates) | Swift hostd (`ContainerManager`, retargeted onto `SandboxEngine`) | Policy stays single-sourced across macOS/Windows. |
|
||||
| Container **mechanism** (wslc Session, image pull, create/exec/stdio/signals) | C# `nucleic-brokerd` | wslc API is WinRT; Microsoft recommends C# (D3). |
|
||||
| Wire protocol (CBOR, Noise, envelope, transcript projection) | Swift `NucleicProtocolC.dll` | Single-sourced wire format (D7). |
|
||||
| UI, toasts, taskbar flash, theming, Phi Silica calls | C# `Nucleic.exe` | WinUI 3 mandate. |
|
||||
|
||||
### 2.3 Broker process model
|
||||
|
||||
**Separate exe, child of hostd, NDJSON JSON-RPC 2.0 over stdio.** Rationale:
|
||||
- Containers must outlive the UI window; broker lifetime must match hostd, not Nucleic.exe.
|
||||
- hostd already has everything needed to run it: `ChildProcess`
|
||||
(`Sources/NucleicCore/ProcessHost.swift`) for spawn + NDJSON line streams, and
|
||||
`Sources/NucleicCore/JSONRPCConnection.swift` for framing. Zero new IPC surface — no
|
||||
named-pipe ACLs, no COM registration.
|
||||
- Crash containment: wslc is preview software. hostd supervises brokerd with
|
||||
exponential-backoff restart. On reattach, brokerd re-opens the named wslc `Session` and
|
||||
re-enumerates containers (wslc state is service-backed); hostd then runs
|
||||
`ContainerManager.reconcile(activeSessions:)` against the fresh listing. In-flight execs
|
||||
fail with a distinguishable `brokerLost` error, mapped to the same recovery path
|
||||
`ContainerizedProcessHandle.forceCloseStreams()` serves today.
|
||||
- Broker sends a `capabilities` hello (wslc/NuGet version, supported features) so hostd can
|
||||
degrade gracefully across preview→GA churn.
|
||||
|
||||
### 2.4 Lifetime & single-instance
|
||||
|
||||
- `Nucleic.exe` launches hostd at startup if not running. Single-instance guard: named
|
||||
mutex `Local\nucleic-hostd-<channel>` (hostd also self-guards).
|
||||
- hostd writes a rendezvous file `%LOCALAPPDATA%\Nucleic\<channel>\hostd.json` =
|
||||
`{pid, port, localPSK}` with an **owner-only DACL**. The renderer (and `nucleic-smoke`)
|
||||
reads it to connect. Stale detection: pid liveness + connect probe.
|
||||
- hostd outlives the renderer window while any session is active or the Autoship queue is
|
||||
non-empty; the renderer exposes explicit "Quit host". Clean shutdown via
|
||||
`SetConsoleCtrlHandler` (CTRL_CLOSE/LOGOFF/SHUTDOWN) routed into the same shutdown path
|
||||
nucleicd drives from SIGTERM (`Nucleicd.swift:255-263` gets a Windows leg).
|
||||
- MSIX declares `runFullTrust`; hostd + brokerd + DLL + Swift runtime + MinGit ship in the
|
||||
package payload.
|
||||
|
||||
---
|
||||
|
||||
## 3. Container subsystem port
|
||||
|
||||
### 3.1 The `SandboxEngine` seam (Darwin-first refactor)
|
||||
|
||||
New file `Sources/NucleicCore/Container/SandboxEngine.swift` — a platform-neutral protocol
|
||||
capturing exactly the mechanism surface `ContainerManager` and the backends use today
|
||||
(extracted from `ContainerEngine.swift:513, :660, :697, :727-:924`):
|
||||
|
||||
```swift
|
||||
public protocol SandboxEngine: Sendable {
|
||||
/// Idempotent create-or-start; returns the container name and the address at which
|
||||
/// the guest can reach the host (macOS: vmnet gateway; Windows: WSL vEthernet gateway).
|
||||
func ensureRunning(_ spec: ContainerSpec) async throws -> (name: String, hostGateway: String)
|
||||
func exec(name: String, workdir: String, env: [String: String],
|
||||
argv: [String], uid: UInt32, gid: UInt32) async throws -> any ProcessHandle
|
||||
func runCapturing(name: String, workdir: String, env: [String: String],
|
||||
argv: [String]) async -> (exitCode: Int32, output: String)?
|
||||
func stop(name: String) async
|
||||
func restart(name: String) async -> Bool
|
||||
func remove(name: String) async -> Bool
|
||||
func isRunning(name: String) async -> Bool
|
||||
func containerExists(name: String) async -> Bool
|
||||
func list() async -> [ControlContainerEntry]
|
||||
func sampleResourceUsage(name: String) async -> ContainerResourceSample?
|
||||
func probeControlPlane(name: String) async -> ControlPlaneProbe
|
||||
func diagnoseKill(name: String) async -> String?
|
||||
func reclaimMemoryNow() async
|
||||
func pullImage(_ ref: String) async throws // progress via existing download-progress plumbing
|
||||
func checkImageUpdate(_ ref: String) async -> Bool
|
||||
// NOTE: match the real ContainerEngine signatures exactly when implementing — the
|
||||
// list above is the shape; lift precise signatures from ContainerEngine.swift.
|
||||
}
|
||||
```
|
||||
|
||||
Steps (this lands **Darwin-first** and is verified by the existing macOS test suite before
|
||||
any Windows code exists):
|
||||
1. Add the protocol; conform the Apple `ContainerEngine` (mechanical).
|
||||
2. Retype `ContainerManager`'s engine reference (and the few direct engine references in
|
||||
`ClaudeCodeBackend` / `CodexAppServerBackend` / `CodexExecBackend` / `GrokACPBackend`)
|
||||
to `any SandboxEngine`.
|
||||
3. Keep `LinuxSupport.swift` stubs compiling (Linux/nucleicd behavior unchanged — the
|
||||
stub `ContainerManager` remains the Linux story).
|
||||
4. On Windows, `ContainerManager.swift` moves **off** the exclude list and compiles against
|
||||
the protocol. Engine files (`ContainerEngine*.swift`, `ContainerizedProcessHandle.swift`,
|
||||
`MemoryBalloon.swift`) stay excluded. Audit `OCIArtifact.swift`: pure ref-parsing/version
|
||||
helpers move to a shared file if `WslcContainerEngine` wants them.
|
||||
|
||||
### 3.2 `WslcContainerEngine` mapping
|
||||
|
||||
New files in `Sources/NucleicCore/Container/Windows/` (compiled only on Windows):
|
||||
|
||||
- **`WslcBrokerClient.swift`** — spawns `nucleic-brokerd.exe` via `ChildProcess`, speaks
|
||||
NDJSON JSON-RPC over its stdio, multiplexes requests, supervises (backoff restart,
|
||||
capabilities hello, reattach → notify `ContainerManager.reconcile`).
|
||||
- **`WslcContainerEngine.swift`** — `SandboxEngine` conformance over the broker RPC.
|
||||
- **`WslcProcessHandle.swift`** — mirrors `ContainerizedProcessHandle.swift`: adapts
|
||||
`proc.stdout/stderr/exit` notifications onto the `ProcessHandle` protocol
|
||||
(`ProcessHost.swift:44-70`): `stdoutLines`/`stderrLines` as
|
||||
`AsyncThrowingStream<Data, Error>` through the shared `LineSplitter`, `writeLine`
|
||||
framing NDJSON to stdin, `wait()` → exit code, `sendSignal` → broker `proc.signal`,
|
||||
`forceCloseStreams()` on broker loss, `lastActivityNanos` bumping,
|
||||
`supportsHostCPUSampling = false`.
|
||||
|
||||
Concept mapping:
|
||||
|
||||
| Nucleic concept (macOS) | wslc equivalent |
|
||||
|---|---|
|
||||
| One microVM per container (Apple containerization) | **One wslc `Session` per channel** (`SessionSettings("nucleic-<channel>", dataDir: %LOCALAPPDATA%\Nucleic\<channel>\wslc)`, `CpuCount`/`MemoryMB` from settings) hosting **all** Nucleic containers as siblings. |
|
||||
| `ContainerSpec.name/image` | `ContainerSettings(image)` + `.Name` (keep existing naming statics incl. `channelSuffix` so dev/canary/stable coexist). |
|
||||
| `spec.mounts` (host worktree → guest path) | `ContainerVolume { hostPath: NTFS path, guestPath: mount.container, readOnly }`. |
|
||||
| `spec.cpus/memoryGiB` | Session-level `CpuCount`/`MemoryMB` (wslc resources are per-session). Per-container limits: cgroup exec inside the container if needed later. |
|
||||
| Memory balloon (`MemoryBalloon.swift`) | No per-container balloon. `reclaimMemoryNow()` → WSL autoreclaim / drop-caches exec; `MemoryMB` resize applies **after sandbox restart** — surface in Settings exactly like the existing restart-shared flow. |
|
||||
| `runAsUID: 501` | Pass through exec settings; M1 spike verifies uid semantics. If wslc runs container processes as root only, fall back to a `setpriv`/`su agent -c` wrapper argv — interceptors and nash don't care about the numeric uid. |
|
||||
| Rootfs pull + ext4 clone (`+Rootfs.swift`) | `session.PullImageAsync(PullImageOptions("ghcr.io/abkslm/naros-agent:<tag>"))` with progress → `controlDownloadProgress` plumbing. **No kernel/vminitd artifacts.** |
|
||||
| Shim seeding (`seed(_:in:)`, base64 install) | Prefer baking into the multi-arch narOS image (already the case); keep idempotent re-seed via `runCapturing` for legacy-image support. |
|
||||
| `ensureRunning` returns `hostGateway` | Broker reports the session's WSL vEthernet gateway address (see §5). |
|
||||
|
||||
### 3.3 Broker RPC surface (`windows/NucleicBroker`)
|
||||
|
||||
NDJSON JSON-RPC 2.0 over stdio; binary payloads base64. Versioned `hello` first.
|
||||
|
||||
Requests (hostd → broker):
|
||||
- `hello() → {brokerVersion, wslcVersion, capabilities: [..]}`
|
||||
- `components.missing() → {flags}` / `components.install()` (+ progress notifications;
|
||||
wraps `WslcService.GetMissingComponents` / install — feeds onboarding, §8)
|
||||
- `session.ensure({name, dataDir, cpu, memoryMB}) → {gateway}` / `session.terminate()`
|
||||
- `image.pull({ref, auth})` (+ `image.pullProgress {ref, status, current, total}`),
|
||||
`image.list` / `image.delete` / `image.inspect`
|
||||
- `container.create({name, image, volumes:[{host,guest,ro}], networkingMode, hostname, env, initArgv})`
|
||||
- `container.start` / `container.stop({name, signal, graceMs})` / `container.delete` /
|
||||
`container.list` / `container.state` / `container.stats({name})`
|
||||
- `proc.exec({container, argv, env, cwd, uid, gid, tty}) → {procId}`
|
||||
- `proc.stdin({procId, b64})` / `proc.closeStdin({procId})` / `proc.signal({procId, sig})`
|
||||
- `proc.resize({procId, cols, rows})` (tty mode, for the Terminal panel)
|
||||
- `ai.generate({prompt, schema})` (§9.2 — Phi Silica sidecar duty)
|
||||
|
||||
Notifications (broker → hostd):
|
||||
- `proc.stdout {procId, b64}` / `proc.stderr {procId, b64}` / `proc.exit {procId, code}`
|
||||
- `session.down {reason}` (from `SessionTerminationHandler`)
|
||||
- `image.pullProgress`, `components.installProgress`
|
||||
|
||||
Broker implementation notes: .NET 9, single-file publish per arch; all wslc calls behind an
|
||||
internal `IWslc` interface so unit tests run against a fake; version-pin the NuGet; map
|
||||
`ProcessOutputMode.Event` → `proc.stdout` notifications; batch/flush stdout notifications
|
||||
(agents emit high-rate NDJSON — coalesce writes, never block the WinRT event thread).
|
||||
|
||||
---
|
||||
|
||||
## 4. NucleicCore on Windows: shims & seams
|
||||
|
||||
### 4.1 `Package.swift` changes
|
||||
|
||||
- Add `onWindows` (mirror the `onLinux` detection at `Package.swift:62-64`);
|
||||
introduce `nonDarwin = onLinux || onWindows` and use it wherever `onLinux` currently
|
||||
gates `darwinOnlyTargets`, Sparkle, and product lists (`:70`, `:219`, `:266`, `:402`).
|
||||
- Widen `NucleicProtocol`'s swift-nio / swift-crypto dependency conditions from `.linux`
|
||||
to `[.linux, .windows]`.
|
||||
- NucleicCore `exclude:` for Windows = the Linux list (`:352-381`) **minus**
|
||||
`Container/ContainerManager.swift` (now protocol-typed, compiles on Windows), keeping
|
||||
excluded: `Container/ContainerEngine.swift`, `Container/ContainerEngine+Rootfs.swift`,
|
||||
`Container/ContainerizedProcessHandle.swift`, `Container/MemoryBalloon.swift`,
|
||||
all `MacVM/*Engine*`/`MacVMManager`/`MacVMExecChannel`/`MacVMAgentClient`, and the
|
||||
Network.framework Sync files. `Container/CommandInterceptor.swift`, `MacVMSpec.swift`,
|
||||
`MacVMSurface.swift` stay in (pure values).
|
||||
- Add `Sources/NucleicCore/Windows/**` and `Sources/NucleicCore/Container/Windows/**`
|
||||
(excluded on non-Windows), the `NucleicProtocolC` dynamic-library target/product, and
|
||||
the `nucleic-hostd` executable target.
|
||||
- SwiftPM remains the build system, including the DLL (`.library(type: .dynamic)`).
|
||||
If `@_cdecl` symbol auto-export proves unreliable, pass a checked-in
|
||||
`Sources/NucleicProtocolC/exports.def` via `-Xlinker /DEF:`. CMake is the documented
|
||||
fallback, not the plan.
|
||||
|
||||
### 4.2 SecretStore (Keychain replacement)
|
||||
|
||||
Introduce `protocol SecretStore` (get/set/delete/list by service+account, `Data` values)
|
||||
in NucleicCore; macOS backend wraps the existing Keychain calls; Windows backend
|
||||
`Sources/NucleicCore/Windows/CredentialStore.swift` uses **DPAPI**
|
||||
(`CryptProtectData`/`CryptUnprotectData`, user scope, `CRYPTPROTECT_UI_FORBIDDEN`)
|
||||
encrypting per-item files under `%LOCALAPPDATA%\Nucleic\secrets\<service>\<account>` with
|
||||
owner-only DACLs. DPAPI chosen over Windows Credential Manager because of Credential
|
||||
Manager's ~2.5 KB blob limit vs. Nucleic's larger OAuth token sets.
|
||||
|
||||
Retarget the ~24 `Security` call sites (~14 files) onto the protocol; macOS behavior
|
||||
unchanged. Files: `KeychainOwnedAccess.swift`, `Claude/ClaudeLoginKeychain.swift`,
|
||||
`Claude/ClaudeCredentialBroker.swift`, `Codex/CodexAuthFile.swift`,
|
||||
`Codex/CodexCredentialBroker.swift`, `Carbon/CarbonKeyCustody.swift`,
|
||||
`Carbon/CarbonCrypto.swift`, `Git/GitHubCredentials.swift`, `HeartbeatReporter.swift`,
|
||||
`ControlAuth.swift`, `SubscriptionUsage.swift`, `Sync/HostIdentityStore.swift`,
|
||||
`Sync/CredentialProvider.swift`, `Sync/RunnerCredentialVault.swift`,
|
||||
`Sync/PushRelayClient.swift`, `Sync/RelayAccess.swift`, `Sync/RunnerPoolClient.swift`
|
||||
(grep `canImport(Security)` for the authoritative list; `MacVM/MacVMEngine+MDM.swift` is
|
||||
excluded anyway). Land Darwin-first like the SandboxEngine seam.
|
||||
|
||||
### 4.3 Windows platform shims (`Sources/NucleicCore/Windows/`)
|
||||
|
||||
| File | Replaces | Implementation |
|
||||
|---|---|---|
|
||||
| `WindowsSupport.swift` | `LinuxSupport.swift` role | Stub `MacVMManager` (and anything else the Linux file stubs that Windows also lacks). Lift the `os.Logger` shim out of `LinuxSupport.swift:13-54` into a shared `PortableLogging.swift` compiled on both Linux and Windows (swift-log backend; rolling files at `%LOCALAPPDATA%\Nucleic\logs`, ETW optional later). |
|
||||
| `NIOByteConn.swift` | `NWByteConn` (Network.framework) in `MCPApprovalServer` | SwiftNIO TCP listener behind the existing `ByteConn` abstraction; `start(host:port:)` keeps its signature; AF_UNIX leg goes `#if !os(Windows)`. Also usable on Linux later. |
|
||||
| `NIOLANTransport.swift` / `NIOLANListener.swift` | `Sync/LANTransport.swift`, `LANBrowser.swift`, `PeerDialer.swift`, `MeshPathMonitor.swift` (Network.framework files, already Linux-excluded) | `FrameChannel` + listener over NIO; path monitoring degraded to periodic reachability probes (no NWPathMonitor equivalent — acceptable). |
|
||||
| `WindowsDNSSD.swift` | Bonjour advertise/browse | dnsapi.dll `DnsServiceRegister`/`DnsServiceBrowse`/`DnsServiceResolve` (WinSDK module) for `_nucleic._tcp` with the pairing TXT payload. Fallback documented: in-process NIO mDNS responder if dnsapi TXT limits bite. |
|
||||
| `ProcessTree.swift` | POSIX signal semantics in `ChildProcess.sendSignal` | Assign every host child to a **Job Object** at spawn (`CreateJobObjectW` + `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`). SIGKILL → `TerminateJobObject`; SIGTERM → `GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT)` then job-terminate after grace. (Container processes get real POSIX signals via wslc — no shim needed there.) |
|
||||
| `FilePermissions.swift` | `chmod 0600` / `posixPermissions` hardening (~10 sites: `RiskClassifier.swift`, `Carbon/CarbonKeyCustody.swift`, `Claude/{ClaudeLoginKeychain,ClaudeTokenProxy,MCPApprovalServer}.swift`, `SessionController.swift`, `Codex/{CodexCredentialBroker,CodexAuthFile}.swift`, `KeychainOwnedAccess.swift`, `CommandSummary.swift`) | `SetNamedSecurityInfoW` owner-only DACL helper; a small `SecretFile.write(_:at:)` utility both platforms call. |
|
||||
| `FileWatcher.swift` | `DispatchSource` file watches (`Claude/ClaudeTokenProxy.swift`, `Claude/MCPApprovalServer.swift`) | `ReadDirectoryChangesW` wrapper with the same callback shape. |
|
||||
| `PowerBlocker.swift` | `NucleicPowerHelper` + `SleepBlocker` | `SetThreadExecutionState(ES_CONTINUOUS \| ES_SYSTEM_REQUIRED)` held while sessions are active; released on idle. |
|
||||
| `LoginShellEnv` / `LoginShellPATH` Windows legs | POSIX login-shell probing | Read process env + registry (`HKCU\Environment`, `HKLM\...\Session Manager\Environment`); no shell probe. |
|
||||
|
||||
### 4.4 Storage & paths
|
||||
|
||||
- corelibs-foundation maps `.applicationSupportDirectory` → `%APPDATA%`. Data root:
|
||||
`%APPDATA%\Nucleic\<channel>\` (channel-suffixed so all five channels coexist —
|
||||
mirrors the container `channelSuffix` scheme). GRDB WAL `DatabasePool`
|
||||
(`Persistence/GRDBMetadataStore.swift:13` and the other Application-Support call sites:
|
||||
`KeychainOwnedAccess.swift:177`, `TailnetTransport.swift`,
|
||||
`Claude/ApprovalServerRegistry.swift`, `Cast/MeshDebugLog.swift`) get a single
|
||||
`NucleicPaths` helper rather than N inline lookups.
|
||||
- Control repos: `%USERPROFILE%\.nucleic\control\<project>\` with worktrees at
|
||||
`<repo>\.nucleic\worktrees\<slug>` (unchanged layout).
|
||||
- Approval-server socket paths (`/tmp/nucleic/<name>.sock`) become `(gatewayIP, port)`
|
||||
tuples behind the existing `ApprovalServerRegistry` API on Windows (§5).
|
||||
- Git: bundle **MinGit** (x64/arm64) in the MSIX; configure managed clones with
|
||||
`core.longpaths=true` and `core.autocrlf=false` (agents run in Linux containers — the
|
||||
repo must stay LF; `.gitattributes`-driven projects behave as configured).
|
||||
`GitRunner`/`WorktreeManager` code unchanged.
|
||||
|
||||
---
|
||||
|
||||
## 5. Control plane on Windows
|
||||
|
||||
Everything above the transport is reused: `MCPApprovalServer` routes + SSE + bearer-token
|
||||
multiplexing, `ApprovalServerRegistry`, interceptor shims, `control-bridge.js`,
|
||||
`CommandInterceptor.hookEnv`, `mcpConfigJSON` — all unchanged. Only the host↔guest byte
|
||||
path is replaced.
|
||||
|
||||
**Primary: TCP to the WSL vEthernet gateway (smallest change that works).**
|
||||
1. `MCPApprovalServer.start(host:port:)` gains the `NIOByteConn` transport (§4.3), bound
|
||||
**only** to the WSL-facing interface address (never 0.0.0.0).
|
||||
2. `WslcContainerEngine.ensureRunning` returns the gateway address the broker reports —
|
||||
this is exactly the `hostGateway` value the macOS engine already returns, so the
|
||||
contract doesn't change.
|
||||
3. `control-bridge.js` gets a small additive branch: when `NUCLEIC_CONTROL_HOST` /
|
||||
`NUCLEIC_CONTROL_PORT` env are present, forward guest-loopback `9099` to that TCP
|
||||
endpoint instead of the relayed UDS (keep the UDS branch for macOS). Same for the
|
||||
proxy bridge (`9098` → `NUCLEIC_PROXY_HOST/PORT`).
|
||||
4. Networking-mode handling: NAT (default) → gateway routing as above. Mirrored mode
|
||||
(Win11 22H2+) → broker detects and reports the reachable host address instead.
|
||||
Auto-detect; prefer NAT for determinism.
|
||||
5. Firewall: MSIX can't install rules; rely on Windows 11's Hyper-V firewall defaults
|
||||
(guest→host on the WSL vSwitch) and ship an onboarding **connectivity self-test**
|
||||
surfaced through the existing `probeControlPlane` seam, with an elevated one-shot
|
||||
`New-NetFirewallRule` remediation script if the default-deny case is hit.
|
||||
|
||||
**Spike + fallback: Hyper-V sockets (true vsock parity).** Host listens on `AF_HYPERV`
|
||||
bound to the WSL VM's VMID + a service GUID derived from the logical port; guest dials
|
||||
`AF_VSOCK` CID 2. No firewall exposure, no routing variance. Unknown: whether wslc
|
||||
containers pass vsock through their namespaces (likely — vsock isn't network-namespaced).
|
||||
M1 answers this; promote to primary if gateway-TCP shows friction in the wild
|
||||
(enterprise firewall policy, DNS tunneling interactions).
|
||||
|
||||
**Rejected:** named-pipe-via-WSL-interop (pipes aren't visible inside wslc containers
|
||||
without brittle 9P mounts).
|
||||
|
||||
Security note: the bearer token — not the transport — is the auth boundary today (the
|
||||
vsock relay was defense-in-depth). Interface-scoped binding restores equivalent posture;
|
||||
tokens remain per-session, minted in `runLoop`.
|
||||
|
||||
---
|
||||
|
||||
## 6. `NucleicProtocolC.dll` (renderer protocol client)
|
||||
|
||||
New target `Sources/NucleicProtocolC/`, depending on `NucleicProtocol` +
|
||||
`NucleicRemoteProjection` (the SPM target that already compiles
|
||||
`ios/.../Views/Transcript/{TranscriptProjection,IncrementalTranscriptProjection}.swift`
|
||||
for host testing — reuse it, don't duplicate).
|
||||
|
||||
Flat C ABI via `@_cdecl` (UTF-8 JSON strings in/out; caller frees with `np_free`;
|
||||
header at `Sources/NucleicProtocolC/include/nucleic_protocol.h`):
|
||||
|
||||
```c
|
||||
typedef void (*np_event_cb)(void* ctx, const char* host_msg_json);
|
||||
typedef void (*np_state_cb)(void* ctx, const char* state_json); // connect/disconnect/transport/errors
|
||||
|
||||
np_handle* np_client_create(const char* config_json); // identity dir, host key pins
|
||||
void np_client_set_callbacks(np_handle*, np_event_cb, np_state_cb, void* ctx);
|
||||
int np_client_connect_local(np_handle*, const char* rendezvous_json); // {host,port,psk}
|
||||
int np_client_connect_remote(np_handle*, const char* endpoint_json); // D12: LAN/relay/tailnet
|
||||
int np_client_pair(np_handle*, const char* pairing_code);
|
||||
int np_client_send_intent(np_handle*, const char* client_msg_json);
|
||||
void np_client_close(np_handle*);
|
||||
void np_free(const char*);
|
||||
|
||||
np_proj* np_projection_create(void);
|
||||
const char* np_projection_apply(np_proj*, const char* host_msg_json); // incremental render-diff JSON
|
||||
void np_projection_free(np_proj*);
|
||||
```
|
||||
|
||||
Internals: a NIO loopback/TCP `FrameChannel` implementation feeding the **unchanged**
|
||||
`SyncClient` (`NucleicProtocol/Sync/SyncClient.swift` is generic over `FrameChannel` —
|
||||
no changes). Noise runs even locally (one code path for local + remote peers; the
|
||||
rendezvous PSK auto-approves local pairing, §2.4). Callbacks fire from one dedicated DLL
|
||||
thread; C# marshals to its dispatcher.
|
||||
|
||||
**The one real work item:** JSON encode/decode for `ClientMsg`/`HostMsg` at the ABI
|
||||
boundary (they are CBOR-coded on the wire). Implement in the DLL layer only. Lock it with
|
||||
**golden fixtures**: `fixtures/protocol-abi/` holds paired CBOR↔JSON vectors for every
|
||||
message kind, tested from Swift (`Tests/NucleicProtocolCTests`) and from C# (xUnit in
|
||||
`windows/NucleicApp.Tests`) on every CI run, so the two views cannot drift. The envelope
|
||||
already carries protocol versioning — bump fixtures with it.
|
||||
|
||||
---
|
||||
|
||||
## 7. WinUI 3 renderer (`windows/NucleicApp`)
|
||||
|
||||
Stack: .NET 9, Windows App SDK 1.7+, WinUI 3, CommunityToolkit.Mvvm +
|
||||
CommunityToolkit.WinUI (SettingsControls; Markdown via Labs MarkdownTextBlock with
|
||||
RichTextBlock fallback), WebView2 (Monaco) for diff/editor, Windows Terminal control
|
||||
(`Microsoft.Terminal.Control`) for the terminal panel.
|
||||
|
||||
Layering mirrors `ios/NucleicRemote` exactly — study `Models/RemoteStore.swift` (a pure
|
||||
projection of host state; the wire types ARE the models) and `Models/HostConnection.swift`
|
||||
(owns the SyncClient, handles pairing modes and transport failover) before writing C#:
|
||||
|
||||
- `Interop/` — P/Invoke bindings for `nucleic_protocol.h`; `HostMsg`/`ClientMsg` DTOs via
|
||||
System.Text.Json **source generators** (AOT-friendly, fast).
|
||||
- `Services/HostConnection.cs` — DLL handle lifecycle, reconnect loop, rendezvous-file
|
||||
watch; **multi-host** (D12): one instance per paired host, local host auto-added.
|
||||
- `Services/RendererStore.cs` — the `RemoteStore` analog: `ObservableObject` projection
|
||||
(sessions, projects, approvals, dashboard, quotas, activity, todos, mesh state), fed
|
||||
exclusively by HostMsg events; **all mutations are intents** (`np_client_send_intent`).
|
||||
No canonical state invented in the renderer, ever.
|
||||
- `Services/HostdLauncher.cs` — mutex check, spawn hostd, health probe.
|
||||
- `Services/OnboardingService.cs` — wslc component flow (renderer → hostd intent → broker
|
||||
`components.*`), progress UI, connectivity self-test display.
|
||||
|
||||
Screens (parity targets from `Sources/NucleicApp` and `ios/.../Views`):
|
||||
|
||||
| Screen | macOS source (reference) | Notes |
|
||||
|---|---|---|
|
||||
| Shell / navigation | `RootView.swift` (1,681) | `NavigationView` split layout; host picker at top (D12). |
|
||||
| Home | `HomeView.swift`, activity grid, streaks, idea inbox (`TodoView.swift`) | Pure projections — data comes from host. |
|
||||
| Project view / Control panel | `ProjectView.swift`, `ControlPanelView.swift` | Container status/usage cards use `controlContainerStatus/Usage` wire data. |
|
||||
| Session detail + transcript | `SessionDetailView.swift` (2,407), `TranscriptRow.swift` (2,343), `HostExecCard.swift` | `ItemsRepeater` with incremental virtualization driven by `np_projection_apply` diffs; tool rows color-coded by risk; collapsible thinking. |
|
||||
| Composer | `NewChatComposer.swift`, `ChatInputField.swift`, `ComposerAttachments.swift` | Model/effort/Orchestra picker (SKU-routed backends), attachments. |
|
||||
| Approvals | approval cards in session view; `ApprovalCardView` (iOS) | Allow / deny / modify-and-allow / always-allow-in-session; first-responder-wins collapse on `approvalResolved`. |
|
||||
| Terminal panel | `Panels/TerminalPanel.swift` (SwiftTerm) | **In-container shell** (D2): hostd intent → broker `proc.exec(tty:true)` of `agentShellArgv` (nash) in the session container; bytes ride a per-terminal loopback side-channel vended by hostd; rendered in the Windows Terminal control; `proc.resize` on layout. No host ConPTY needed. |
|
||||
| Editor / diff | `Panels/EditorPanel.swift`, `SyntaxHighlighting.swift`, diff tab | Monaco in WebView2 (read-only diff-first); native later if desired. |
|
||||
| Build & Run | `BuildRunPanel.swift` | Host process spawn via hostd intent (ChildProcess + Job Objects). |
|
||||
| Settings | `SettingsView.swift` (2,995), `RemoteAccessView.swift`, `ModelCatalog.swift`, `QuotaIndicator.swift` | Sections that apply: agents/accounts (OAuth via loopback listener), sandbox (CPU/MemoryMB, restart-shared), remote access (QR pairing display, transports), updates (channel display; App Installer drives updates). |
|
||||
| Attention | `DockBounce.swift`, sound hooks (`AppStore.swift:668-681`) | `AppNotificationManager` toasts + `FlashWindowEx` + optional sound, wired to the same AppStore hook events over the wire. |
|
||||
| Accessibility/theming | 4 color-vision palettes + 5 text sizes (app-wide) | Resource dictionaries keyed off host-synced settings (`SyncedSettings.swift`); honor Windows high-contrast. |
|
||||
|
||||
---
|
||||
|
||||
## 8. Onboarding flow (first run)
|
||||
|
||||
1. Renderer launches → `HostdLauncher` starts hostd → hostd starts broker → broker
|
||||
`components.missing()`.
|
||||
2. If WSL/wslc components missing → guided install page (elevated `wsl --install` /
|
||||
component install via broker), progress streamed, reboot handling.
|
||||
3. Broker `session.ensure` → `image.pull` naros-agent (progress → existing
|
||||
`controlDownloadProgress` UI path).
|
||||
4. Control-plane connectivity self-test (`probeControlPlane`) with actionable remediation
|
||||
(firewall script) on failure.
|
||||
5. Agent CLI auth: Claude/Codex/Grok OAuth flows (loopback listener, DPAPI custody) —
|
||||
same UX as macOS Settings.
|
||||
6. Add or clone a Control project → first session.
|
||||
|
||||
---
|
||||
|
||||
## 9. Parity gap plans
|
||||
|
||||
### 9.1 Tailnet (D9)
|
||||
- Build libtailscale as `tailscale.dll` (Go `-buildmode=c-shared`) for windows/amd64 +
|
||||
windows/arm64; new `windows/scripts/build-libtailscale-windows.ps1` pinned to the same
|
||||
commit as `scripts/build-tailscalekit.sh`.
|
||||
- `Sources/NucleicTailnet` gains a Windows leg over the same libtailscale C header that
|
||||
TailscaleKit wraps (`#if canImport(TailscaleKit)` stays Apple-only; add
|
||||
`#if os(Windows)` + dlopen-or-link of the DLL). The fd-backed `FrameChannel` transport
|
||||
works over Winsock fds.
|
||||
- Absent DLL → existing `isBuiltIn == false` path (feature shows "not built in").
|
||||
|
||||
### 9.2 On-device AI (D6)
|
||||
- `Intelligence.swift` already has a provider seam with heuristic fallbacks. Add a
|
||||
`WindowsFoundryIntelligenceProvider`: Phi Silica is WinRT-only, so hostd routes requests
|
||||
to a C# sidecar — **reuse brokerd** with the `ai.generate` RPC (keeps one helper
|
||||
process). Capability-probe at startup; fall back to heuristics when unavailable
|
||||
(non-Copilot+ hardware) — identical UX to a Mac without Apple Intelligence.
|
||||
|
||||
### 9.3 Free-by-construction (verify, don't rebuild)
|
||||
LockManager, ConflictCoordinator, nvrsion, Autoship + merge queue, idea inbox, activity/
|
||||
streaks, quota gauges (`SubscriptionUsage.swift` logic), Cast/Mirror, transfer — all pure
|
||||
core + GRDB; they work once NucleicCore compiles and DPAPI custody lands. Cover via the
|
||||
existing cross-platform test suites plus the M2 e2e.
|
||||
|
||||
### 9.4 Explicitly out of scope (D4)
|
||||
`MacVM/` computer-use (mac_vm_* and GUI linux_vm_* tools stay unadvertised — they're
|
||||
per-token-registered, so simply don't register handlers), `NucleicMDM` server role,
|
||||
`NucleicPowerHelper`, Sparkle, SwiftTerm, `shell/` nash host-side use (nash still ships
|
||||
*inside* the containers and is fully used there).
|
||||
|
||||
---
|
||||
|
||||
## 10. Repo layout for new code
|
||||
|
||||
```
|
||||
windows/ # sibling of ios/ and cloud/
|
||||
Nucleic.sln
|
||||
NucleicApp/ # WinUI 3 app: Views/ Services/ Interop/ Assets/
|
||||
NucleicApp.Package/ # MSIX packaging project (per-channel manifests templated)
|
||||
NucleicBroker/ # nucleic-brokerd (Microsoft.WSL.Containers + ai.generate)
|
||||
NucleicProtocol.Interop/ # shared P/Invoke + DTO library (app + tests)
|
||||
NucleicApp.Tests/ # xUnit incl. protocol-abi golden fixtures
|
||||
NucleicBroker.Tests/ # broker contract tests against fake IWslc
|
||||
spikes/ # M1 spike programs (kept, runnable)
|
||||
appinstaller/ # nucleic-<channel>.appinstaller.template
|
||||
props/Directory.Build.props # TFM, versions, channel from VERSION/NUCLEIC_CHANNEL
|
||||
scripts/{build-hostd.ps1, package-msix.ps1, release-windows.ps1,
|
||||
build-libtailscale-windows.ps1}
|
||||
Sources/
|
||||
nucleic-hostd/ # Windows headless host (shares bring-up with nucleicd)
|
||||
NucleicProtocolC/ # C-ABI DLL target (+ include/nucleic_protocol.h, exports.def)
|
||||
NucleicCore/Container/SandboxEngine.swift
|
||||
NucleicCore/Container/Windows/{WslcContainerEngine,WslcBrokerClient,WslcProcessHandle}.swift
|
||||
NucleicCore/Windows/{WindowsSupport,CredentialStore,FilePermissions,ProcessTree,
|
||||
FileWatcher,PowerBlocker,WindowsDNSSD,NIOLANTransport,
|
||||
NIOLANListener,NIOByteConn}.swift
|
||||
NucleicCore/PortableLogging.swift # lifted from LinuxSupport.swift
|
||||
Tests/NucleicProtocolCTests/
|
||||
fixtures/protocol-abi/
|
||||
.github/workflows/windows.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Windows hostd design (`Sources/nucleic-hostd`)
|
||||
|
||||
Factor the shared headless bring-up out of `Sources/nucleicd/Nucleicd.swift` (`makeStore`,
|
||||
blank-API-key scrub, startup choreography ~lines 142-171) into an internal
|
||||
`NucleicHeadless` helper used by both nucleicd and hostd. hostd-specific duties:
|
||||
|
||||
- Single-instance mutex; rendezvous file write (owner-only DACL); `SetConsoleCtrlHandler`.
|
||||
- Start order: GRDB store → AppStore graph → broker spawn + `session.ensure` →
|
||||
`startSyncServer()` → local renderer listener (loopback TCP, port 0, Noise XXpsk0 with
|
||||
PSK auto-approval — the local analog of `meshAutoApprovePairing`, `Nucleicd.swift:101`)
|
||||
→ LAN listener + DNS-SD advertise → relay/tailnet transports per settings.
|
||||
- `PowerBlocker` held while sessions active. Windows Job Object on brokerd so it dies with
|
||||
hostd.
|
||||
- Transports env default: `NUCLEIC_SYNC_TRANSPORTS=lan,relay` (+`tailnet` when configured)
|
||||
vs nucleicd's relay-only default.
|
||||
|
||||
---
|
||||
|
||||
## 12. Work breakdown
|
||||
|
||||
Ordered; items 1–4 are **Darwin-first refactors** verifiable by the existing macOS suite
|
||||
and safe to start immediately on macOS hardware.
|
||||
|
||||
1. **Manifest & gating** — `Package.swift` only: `onWindows`/`nonDarwin`, widened platform
|
||||
conditions, Windows exclude list, new targets.
|
||||
2. **PortableLogging** — lift the Logger shim from `LinuxSupport.swift`;
|
||||
`WindowsSupport.swift` stubs.
|
||||
3. **SecretStore seam** — protocol + Keychain backend + retarget ~14 files (§4.2); DPAPI
|
||||
backend can land later with the Windows build.
|
||||
4. **SandboxEngine seam** — protocol + `ContainerEngine` conformance + retype
|
||||
`ContainerManager` and backend references (§3.1). macOS tests must stay green.
|
||||
5. **Broker** — `windows/NucleicBroker` full RPC surface (§3.3) + fake-`IWslc` tests.
|
||||
6. **Swift wslc client** — `WslcBrokerClient` / `WslcContainerEngine` /
|
||||
`WslcProcessHandle` + tests against a scripted fake broker exe (mirror the
|
||||
`fake-claude` adapter-contract pattern).
|
||||
7. **Control plane** — `NIOByteConn`, gateway plumbing through `ensureRunning`,
|
||||
`control-bridge.js` TCP branch (guest contract unchanged).
|
||||
8. **hostd** — `NucleicHeadless` factor-out; `Sources/nucleic-hostd/Hostd.swift` (§11);
|
||||
NIO LAN transport + `WindowsDNSSD`.
|
||||
9. **Windows shims** — `ProcessTree`, `FilePermissions`, `FileWatcher`,
|
||||
`LoginShellEnv/PATH` legs, `NucleicPaths`.
|
||||
10. **Protocol DLL** — target + C header + JSON bridge + golden fixtures + Swift tests.
|
||||
11. **Interop + renderer core** — `NucleicProtocol.Interop`, `HostConnection.cs`,
|
||||
`RendererStore.cs`, `HostdLauncher`, onboarding.
|
||||
12. **Screens** — Home → SessionDetail/Transcript → Composer → Approvals → Projects/
|
||||
Settings → Panels (Terminal, Diff, Build&Run, Todos) → RemoteAccess/host picker.
|
||||
13. **Remote parity** — DNS-SD advertise + QR (reuse the `mintPairingCode()` path), relay
|
||||
(portable already), tailnet DLL + `NucleicTailnet` Windows leg.
|
||||
14. **Packaging/CI/release** — `windows.yml`, MSIX projects, appinstaller templates,
|
||||
`cloud/nucleic-updates` `/win/<channel>/latest` routes, `release-windows.ps1`,
|
||||
Trusted Signing.
|
||||
15. **naros multi-arch** — extend `os/` + `.github/workflows/naros.yml` to push an
|
||||
amd64+arm64 manifest list to GHCR (D10).
|
||||
16. **Phi Silica provider** (§9.2), accessibility/theming parity, polish.
|
||||
|
||||
Packaging details (item 14): MSIX Identities `ABKSLM.Nucleic[.Dev/.Canary/.Beta/.RC]`
|
||||
mirroring the bundle-ID scheme (`Package.swift:27-35`); `runFullTrust`; payload = three
|
||||
exes + `NucleicProtocolC.dll` + Swift runtime DLLs + MinGit; per-arch MSIX with
|
||||
`.appinstaller` `<MainPackage ProcessorArchitecture>` selection; feeds
|
||||
`nucleic-<channel>.appinstaller` on R2 beside the Sparkle appcasts with in-app update
|
||||
check reading the same feed (App Installer has no delta updates); publisher CN must match
|
||||
the Trusted Signing cert.
|
||||
|
||||
---
|
||||
|
||||
## 13. Milestones & exit criteria
|
||||
|
||||
**M0 — Toolchain proof (riskiest first).**
|
||||
Swift 6.2-windows (pinned in `windows/props/swift-version.txt`, x64 + arm64 toolchains)
|
||||
builds `NucleicProtocol`; `NucleicProtocolTests` + `NucleicCarbonTests` (the designated
|
||||
cross-platform suites, `Package.swift:391-401`) green on hosted `windows-2025` (x64) and
|
||||
`windows-11-arm` runners, including GRDB/SQLite linkage.
|
||||
*Exit: `windows.yml` green on both arches; corelibs/toolchain gap list filed as issues.*
|
||||
|
||||
**M1 — De-risking spikes (parallel, checked into `windows/spikes/`).**
|
||||
(a) **wslc**: C# console — Session create → GHCR pull of naros-agent → container with an
|
||||
NTFS `ContainerVolume` → exec `git status` in the bind-mounted worktree → stdio round-trip
|
||||
→ SIGTERM; **measure** `git status` and `npm install` latency on a real repo vs. native;
|
||||
verify uid semantics and broker-crash/service-state survival.
|
||||
(b) **hvsocket**: AF_HYPERV host listener ↔ AF_VSOCK dial from *inside* a wslc container;
|
||||
also verify gateway-TCP reachability + default-firewall behavior in NAT and mirrored modes.
|
||||
(c) **DLL**: SwiftPM-built `NucleicProtocolC.dll`, C# P/Invoke connect-pair-echo round-trip
|
||||
against a Swift test host; settle symbol-export strategy.
|
||||
*Exit: written go/no-go on control-plane primary (TCP vs hvsocket); 9P perf numbers with
|
||||
mitigation decision; wslc API gaps filed upstream while preview feedback still lands.*
|
||||
|
||||
**M2 — Headless host runs an agent.**
|
||||
NucleicCore compiles on Windows (work items 1–9); hostd boots, GRDB at `%APPDATA%`,
|
||||
broker supervises the wslc session; `nucleic-smoke` (already cross-platform) pairs over
|
||||
loopback and drives a full Claude session: prompt → MCP approval round-trip →
|
||||
git-interceptor event → worktree commit on NTFS.
|
||||
*Exit: e2e session with approval + interceptor events asserted from the transcript;
|
||||
containers survive hostd kill/restart and reconcile; runs on the self-hosted x64 runner (D11).*
|
||||
|
||||
**M3 — Renderer alpha.**
|
||||
DLL interop + golden fixtures; HostConnection/RendererStore; Home, Sessions,
|
||||
SessionDetail transcript, Composer, Approvals; hostd auto-launch + rendezvous.
|
||||
*Exit: dogfood-usable real coding session end-to-end from the UI; transcript-parity harness
|
||||
(replay recorded HostMsg streams through `np_projection_apply` vs. the Swift projector,
|
||||
diff the JSON) passes on a recorded fixture session.*
|
||||
|
||||
**M4 — Remote + panels + packaging.**
|
||||
DNS-SD advertise + QR pairing (iPhone pairs to the PC over LAN), relay listener,
|
||||
remote-cockpit host picker (D12), Terminal/Diff/Build&Run/Todos/Settings panels, toasts +
|
||||
taskbar attention, MSIX per-channel + `.appinstaller` on R2 (dev + canary), Trusted
|
||||
Signing, ARM64 package.
|
||||
*Exit: iPhone full remote session against a Windows host over LAN + relay; canary
|
||||
installs and auto-updates via the App Installer feed on clean x64 + ARM64 VMs.*
|
||||
|
||||
**M5 — Parity beta.**
|
||||
Tailnet DLL + Windows leg; Phi Silica provider + fallback; accessibility/theming parity
|
||||
(4 palettes, 5 sizes); quota/OAuth flows; idle/memory policy tuning; **wslc GA migration**
|
||||
(fall 2026) absorbed behind the broker; beta channel live; winget evaluation.
|
||||
*Exit: parity checklist (every §7 screen row + every §9 gap row) green; 14-day dogfood
|
||||
with no P0s.*
|
||||
|
||||
---
|
||||
|
||||
## 14. Verification strategy
|
||||
|
||||
- **Every CI run (from M0):** `NucleicProtocolTests` + `NucleicCarbonTests` on Windows
|
||||
x64 + ARM64; the full macOS suite must stay green through the Darwin-first
|
||||
SandboxEngine/SecretStore refactors; DLL golden fixtures from Swift and C#.
|
||||
- **Broker:** C# unit tests against a fake `IWslc`; Swift `WslcBrokerClient` tests against
|
||||
a scripted fake broker exe (mirrors the `fake-claude` adapter-contract pattern).
|
||||
- **M2 gate (self-hosted runner):** `nucleic-smoke` e2e — full Claude session with
|
||||
approval + git-interceptor assertions from the transcript; hostd kill → restart →
|
||||
reconcile test; container idle-teardown test.
|
||||
- **M3 gate:** transcript-parity harness (recorded HostMsg replay, Swift vs. DLL
|
||||
projection JSON diff); renderer store unit tests over fixture event streams.
|
||||
- **M4/M5 manual matrix:** iPhone QR pairing over LAN / relay-only / tailnet; MSIX
|
||||
install → update → rollback across channels on clean x64 + ARM64 VMs with default
|
||||
firewall; accessibility spot-checks (high-contrast, palettes, text sizes); a longpath +
|
||||
LF-sensitive fixture repo through a full session + Autoship.
|
||||
|
||||
---
|
||||
|
||||
## 15. Risks & mitigations
|
||||
|
||||
| Risk | Mitigation |
|
||||
|---|---|
|
||||
| **wslc preview churn** (API breaks before GA) | All wslc calls isolated in brokerd behind our own RPC; NuGet version-pinned per release; broker `capabilities` hello lets hostd degrade; M1 files gaps early while Microsoft is taking preview feedback. |
|
||||
| **Swift-Windows toolchain gaps** (corelibs Foundation holes, DLL export quirks, NIO/GRDB linkage, `FileHandle.bytes`-class issues) | M0 front-loads discovery; the Linux build's known-gap map is the checklist; CMake fallback documented for the DLL; a `WindowsFoundationCompat.swift` for point fixes. |
|
||||
| **9P NTFS bind-mount performance** (git/npm in mounted worktrees) | M1 measures on real workloads. Mitigations in order: per-directory metadata caching; hot caches (`node_modules`, build dirs) on `ContainerNamedVolume`; repo itself stays NTFS per D8. If numbers are catastrophic, surface to the user for a decision — do not silently move repos into ext4. |
|
||||
| **hvsocket-in-container reachability unknown** | Gateway-TCP is primary precisely because it's provable in M1; hvsocket is upside, not a dependency. |
|
||||
| **Firewall / networking-mode variance** (NAT vs mirrored, DNS tunneling, enterprise policy) | Broker auto-detects and reports the reachable host address; interface-scoped bind; onboarding connectivity self-test through `probeControlPlane` with actionable remediation. |
|
||||
| **DLL↔C# JSON drift** | Golden fixtures in `fixtures/protocol-abi/` run from both languages on every CI run; DTOs reviewed against `WireMessages.swift` on protocol version bumps. |
|
||||
| **App Installer UX limits** (no delta updates, prompt fatigue) | In-app update check reads the same feed; keep MSIX payload lean (Swift runtime + MinGit are the big items); winget post-beta. |
|
||||
| **ARM64 tail risks** (Go c-shared for windows/arm64, wslc-on-ARM less trodden) | ARM64 is in CI from M0 so breakage is visible immediately; tailnet DLL is M5 scope. |
|
||||
| **CRLF/longpath git edge cases** | MinGit pinned + `core.longpaths`/`core.autocrlf` enforced on managed clones; e2e includes a longpath + LF-sensitive fixture repo. |
|
||||
|
||||
---
|
||||
|
||||
## 16. Key file index (for implementing agents)
|
||||
|
||||
| Purpose | Path |
|
||||
|---|---|
|
||||
| Manifest gating to extend | `Package.swift` (`:59-70`, `:219`, `:266`, `:352-381`, `:402`) |
|
||||
| Stub/shim template | `Sources/NucleicCore/LinuxSupport.swift` |
|
||||
| Headless bring-up to factor | `Sources/nucleicd/Nucleicd.swift` |
|
||||
| Container policy (port contract) | `Sources/NucleicCore/Container/ContainerManager.swift` |
|
||||
| Engine surface to protocolize | `Sources/NucleicCore/Container/ContainerEngine.swift` (`:513,:660,:697,:727-:924`) |
|
||||
| Process handle pattern to mirror | `Sources/NucleicCore/Container/ContainerizedProcessHandle.swift`, `Sources/NucleicCore/ProcessHost.swift` |
|
||||
| ContainerSpec + constants | `Sources/NucleicCore/Backend.swift:54` |
|
||||
| Spec construction | `Sources/NucleicCore/SessionController.swift:643`; `Sources/NucleicCore/Project.swift:77` |
|
||||
| Control plane server + ByteConn seam | `Sources/NucleicCore/Claude/MCPApprovalServer.swift`; `Sources/NucleicCore/Claude/ApprovalServerRegistry.swift` |
|
||||
| Guest bridge | `containers/nucleic-sandbox/control-bridge.js` |
|
||||
| Interceptors + hook env | `Sources/NucleicCore/Container/ContainerEngine+Rootfs.swift:394,:466`; `Sources/NucleicCore/Container/CommandInterceptor.swift` |
|
||||
| Backend run loop (container exec path) | `Sources/NucleicCore/Claude/ClaudeCodeBackend.swift:766-1087` |
|
||||
| Sync authority + bridge | `Sources/NucleicCore/Sync/SyncHost.swift`, `ConnectionHandler.swift`, `SyncHostBridge.swift`; `Sources/NucleicCore/AppStore.swift` (conforms; wires SyncHost ~`:9410`) |
|
||||
| Wire protocol + client | `Sources/NucleicProtocol/Sync/{MessageEnvelope,WireMessages,SyncClient}.swift`, `CBOR/`, `Noise/`, `FrameChannel` |
|
||||
| Renderer template | `ios/NucleicRemote/NucleicRemote/Models/{RemoteStore,HostConnection}.swift` |
|
||||
| Transcript projections (shared) | `ios/NucleicRemote/.../Views/Transcript/{TranscriptProjection,IncrementalTranscriptProjection}.swift` (SPM target `NucleicRemoteProjection`) |
|
||||
| Git layer | `Sources/NucleicCore/Git/{GitRunner,WorktreeManager,GitHubCredentials}.swift` |
|
||||
| Persistence | `Sources/NucleicCore/Persistence/GRDBMetadataStore.swift` |
|
||||
| Intelligence seam | `Sources/NucleicCore/Intelligence.swift`, `AFMRequestQueue.swift` |
|
||||
| Contracts docs | `docs/VSOCK_CONTROL_PLANE.md`, `docs/CONTAINER_ISOLATION.md`, `docs/NAROS.md`, `docs/NASH.md`, `docs/RUNTIME_ARCHITECTURE.md`, `docs/COVALENCE_RUNNER.md` |
|
||||
| Release pipeline references | `scripts/package-app.sh`, `scripts/release-macos.sh`, `cloud/nucleic-updates`, `.github/workflows/{naros,sandbox-image}.yml` |
|
||||
|
||||
External references: [WSL container overview](https://learn.microsoft.com/en-us/windows/wsl/wsl-container) ·
|
||||
[wslc API reference](https://wsl.dev/api-reference/) · [wslc samples](https://aka.ms/wslc-samples) ·
|
||||
[Swift Windows install](https://www.swift.org/install/windows/) ·
|
||||
[GRDB releases (Windows since 7.10)](https://github.com/groue/GRDB.swift/releases)
|
||||
Reference in New Issue
Block a user