Files
nucleic/Package.swift
T

621 lines
37 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// swift-tools-version: 6.2
import Foundation
import PackageDescription
// Build channel, selected at manifest-evaluation time via the NUCLEIC_CHANNEL
// environment variable (dev | canary | beta | rc | stable); defaults to `dev`. It drives two
// things in lockstep:
// • the app executable's PRODUCT name — which becomes the on-disk binary and
// therefore the OS process name (Activity Monitor / `ps`):
// dev -> nucleic-local canary -> nucleic-canary beta -> nucleic-beta
// rc -> nucleic-rc stable -> nucleic
// • a compile-time define (`NUCLEIC_STABLE/RC/BETA/CANARY/DEV`) read by NucleicApp for its build
// identity + warning banner (red local / yellow canary / blue beta / gold release-candidate /
// none stable), and by NucleicCore's `ContainerManager` to suffix non-release container names.
// Branch mapping (see BUILD.md): dev builds from `dev`, canary from `canary`, beta from `staging`,
// rc from `rc`, stable from `main`.
// TailscaleKit (the embedded tsnet node behind the Tailnet sync transport) has no SwiftPM
// distribution — it's an Xcode framework built from a pinned libtailscale commit by
// scripts/build-tailscalekit.sh into third_party/TailscaleKit/ (untracked, ~100 MB). The
// binary target is declared only when the artifact exists so a fresh clone builds without
// Go/Xcode legwork; NucleicTailnet compiles either way (`#if canImport(TailscaleKit)`) and
// reports "not built in" at runtime when the framework is absent.
let tailscaleKitAvailable = FileManager.default.fileExists(
atPath: Context.packageDirectory + "/third_party/TailscaleKit/TailscaleKit.xcframework")
let nucleicChannel = (Context.environment["NUCLEIC_CHANNEL"] ?? "dev").lowercased()
let (appProductName, channelDefine): (String, String) = {
switch nucleicChannel {
case "stable", "prod", "release": return ("nucleic", "NUCLEIC_STABLE")
case "rc", "candidate": return ("nucleic-rc", "NUCLEIC_RC")
case "beta": return ("nucleic-beta", "NUCLEIC_BETA")
case "canary": return ("nucleic-canary", "NUCLEIC_CANARY")
default: return ("nucleic-local", "NUCLEIC_DEV")
}
}()
// Shipped channels drop the Swift 6 dynamic actor-isolation checks (SE-0423) from the app
// target. These are diagnostic-only traps the compiler inserts where a `@MainActor` closure
// crosses a nonisolated boundary (every SwiftUI `@ViewBuilder` closure); they change no
// behavior when they pass. Canary build 889 crashed *inside* one of these checks
// (RootView's NavigationSplitView sidebar closure → swift_task_isCurrentExecutorWithFlags →
// swift_getObjectType, a pointer-auth trap on a corrupt executor ref handed over by the
// macOS 27 beta runtime) — the check itself was the only casualty, so shipped builds skip
// it rather than crash the user. Dev builds keep the checks: there they catch real
// isolation bugs at the source instead of as heisencrashes later.
let appDisableIsolationChecks: [SwiftSetting] =
channelDefine == "NUCLEIC_DEV" ? [] : [.unsafeFlags(["-disable-dynamic-actor-isolation"])]
// The Linux build (docs/COVALENCE_RUNNER.md §9, docs/CLOUD_RUNTIME.md Phase 2): the headless
// `nucleicd` runner host compiles for Linux with the Apple-only surface gated out — no SwiftUI
// app or spikes, no Apple containerization (the cloud container IS the sandbox;
// `RunSpec.container` stays nil), CryptoKit backed by swift-crypto inside NucleicProtocol.
// `#if os(Linux)` here runs at manifest-evaluation time on the build machine. Most Apple-only
// *package dependencies* stay declared on Linux so dependency resolution (and Package.resolved)
// is identical on every platform; their products are simply never linked there. The exception is
// Sparkle: SwiftPM clones EVERY declared package during resolution regardless of platform
// conditions on its products, and Sparkle is a large repo the Linux runner-container build can
// never use (its only consumer, NucleicApp, isn't built here). So it's dropped from the Linux
// dependency list below (`onLinux ? [] : [...]`) to keep the `nucleicd` cross-build lean —
// Package.resolved parity is worth less than not fetching a heavy, unusable dependency.
#if os(Linux)
let onLinux = true
#else
let onLinux = false
#endif
// The Windows port (docs/WINDOWS_PORT.md): the same headless shape as the Linux leg — a
// `nucleic-hostd` host process owns NucleicCore while a WinUI 3 renderer speaks the sync
// protocol over loopback — so `nonDarwin` gates everything the two non-Apple platforms share:
// no SwiftUI app / spikes / Sparkle, and NucleicProtocol's swift-crypto + NIO legs. Windows
// differs from Linux in one place: it COMPILES the container-policy layer
// (`Container/ContainerManager.swift`, retargeted onto the `SandboxEngine` protocol) because
// the wslc broker supplies a real sandbox mechanism there, where Linux keeps the
// LinuxSupport.swift stub (the cloud container IS the sandbox).
#if os(Windows)
let onWindows = true
#else
let onWindows = false
#endif
let nonDarwin = onLinux || onWindows
// M0 CI leg (docs/WINDOWS_PORT.md §13): `NUCLEIC_WINDOWS_PROTOCOL_ONLY=1` narrows the
// Windows manifest to the wire layer — NucleicProtocol + its tests — because `swift test`
// builds every declared target, and NucleicCore doesn't compile on Windows until the §12
// item 79 shims land. The flag is manifest-eval-time env, like NUCLEIC_CHANNEL, and is set
// only by `windows/build.ps1 -Target protocol` (the core leg runs WITHOUT it, so
// the full-core gap list stays visible in CI as the expected-failing leg).
let windowsProtocolOnly =
onWindows && Context.environment["NUCLEIC_WINDOWS_PROTOCOL_ONLY"] == "1"
// The iPhone app's transcript projection (pure Foundation + NucleicProtocol model
// folding), compiled here as a host-testable library. The iOS app target compiles these
// same sources directly via its file-system-synchronized group; this target exists so the
// incremental projector's prefix-equivalence suite (a required safety net —
// docs/TRANSCRIPT_INCREMENTAL_PROJECTION.md) runs under plain `swift test`, where the
// Xcode project has no test target. `sources` is an explicit allowlist: the folder's
// other files are SwiftUI views this library must not build. Declared out-of-line because
// three platform-gated lists include it: Darwin host testing, the Windows renderer DLL, and
// Linux's DLL/projection CI contract (docs/WINDOWS_PORT.md §6).
let projectionTarget: Target = .target(
name: "NucleicRemoteProjection",
dependencies: ["NucleicProtocol"],
path: "ios/NucleicRemote/NucleicRemote/Views/Transcript",
// The folder's other files are SwiftUI views compiled only by the Xcode iOS app
// target; excluding them keeps this host-testable library building just the two
// pure-projection sources and silences SwiftPM's "unhandled files" warning. Add
// any new sibling view here (or it re-triggers the warning).
exclude: [
"AskUserQuestionResultCard.swift",
"ExitPlanModeCard.swift",
"GitCommitCard.swift",
"HostCommandSummary.swift",
"HostExecCard.swift",
"MarkdownText.swift",
"SandboxToolDisplay.swift",
"ToolGroupRow.swift",
],
sources: [
"TranscriptProjection.swift",
"IncrementalTranscriptProjection.swift",
"TranscriptProjectionABI.swift",
])
// The macOS cockpit + its build plugin and the de-risking spikes do not compile (or make sense)
// on Linux. This list also owns the Darwin declaration of the iOS projection library; Linux
// declares that pure target separately for the DLL contract tests below.
let darwinOnlyTargets: [Target] = [
.executableTarget(
name: "NucleicApp",
dependencies: [
"NucleicCore",
"NucleicPowerProtocol",
"NucleicExceptionGuard",
.product(name: "SwiftTerm", package: "SwiftTerm"),
// Auto-update for direct (non-App-Store) distribution. macOS-only.
.product(name: "Sparkle", package: "Sparkle"),
],
// Keep the precompiled Core ML directory opaque. `.process` descends into Core ML
// artifacts and tries to synthesize model bindings; the runtime classifier deliberately
// loads the checked-in `.mlmodelc` lazily on its own actor instead.
resources: [.copy("Resources/PurposeClassifier")],
swiftSettings: [.define(channelDefine)] + appDisableIsolationChecks,
// FoundationModels ships in macOS 26, but its Swift ABI is still shifting between the
// 26/27 beta seeds: a symbol present in the SDK we build against (e.g.
// `Generable.promptRepresentation`, emitted by the `@Generable` guided-generation code
// in AppleIntelligence.swift) can be ABSENT from an older seed's framework. Strong-linked
// (the Swift autolink default), that makes dyld abort BEFORE main() on any Mac whose seed
// lacks the symbol — "Symbol not found … FoundationModels", EXC_CRASH / Namespace DYLD —
// even though every use is already gated behind `#available` + `model.isAvailable`, since
// those runtime guards never get to run. Weak-linking binds a missing symbol to NULL at
// load instead of aborting, so the app always launches and the guards do their job (the
// soft-AI features fall back to their heuristics). Verify the flag took with
// `otool -l <binary> | grep -A3 FoundationModels` → expect LC_LOAD_WEAK_DYLIB.
linkerSettings: [
.unsafeFlags(["-Xlinker", "-weak_framework", "-Xlinker", "FoundationModels"]),
],
plugins: ["EmbedGitCommit"]),
.target(
name: "NucleicPowerProtocol",
swiftSettings: [.define(channelDefine)]),
// One-function ObjC shim: run a block under @try/@catch and hand any NSException back to
// Swift, which cannot catch them itself. Exists for GuardedPopover (NucleicApp), which
// survives a macOS 27 beta ViewBridge assertion by calling `-[NSPopover show…]` through it.
.target(name: "NucleicExceptionGuard"),
.executableTarget(
name: "NucleicPowerHelper",
dependencies: ["NucleicPowerProtocol"],
exclude: ["Info.plist"],
linkerSettings: [
.unsafeFlags([
"-Xlinker", "-sectcreate",
"-Xlinker", "__TEXT",
"-Xlinker", "__info_plist",
"-Xlinker", Context.packageDirectory + "/Sources/NucleicPowerHelper/Info.plist",
]),
]),
// Prebuild plugin: stamps the built-from commit into a generated `GitCommit.swift`
// at build time, so the hash is baked into the binary (correct even on a
// packaged beta / rc / stable archive, where git isn't present at runtime).
.plugin(name: "EmbedGitCommit", capability: .buildTool()),
// Adapter-contract test stubs (fed to NucleicCoreTests). Darwin-only: they lean on
// Foundation surfaces corelibs doesn't ship (`FileHandle.bytes`).
.executableTarget(name: "fake-claude", dependencies: ["NucleicCore"]),
.executableTarget(name: "fake-grok", dependencies: ["NucleicCore"]),
.testTarget(
name: "NucleicCoreTests",
dependencies: ["NucleicCore", "NucleicTailnet"],
resources: [.copy("Fixtures")],
// The TailscaleKit binary framework is staged at the build-products root, which
// executables reach via their default rpath but .xctest bundles do not (their
// rpaths point at PackageFrameworks + the bundle's own Frameworks dir). Walk the
// three levels from Contents/MacOS/<binary> back up to the products dir.
linkerSettings: [.unsafeFlags(["-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../.."])]
),
.testTarget(
name: "NucleicAppTests",
dependencies: ["NucleicApp", "NucleicCore"],
resources: [.copy("Fixtures")],
// NucleicApp links Sparkle. Test bundles need to walk back from
// Contents/MacOS/<test> to SwiftPM's products directory to find the framework.
linkerSettings: [.unsafeFlags(["-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../.."])]),
.executableTarget(name: "nucleic-spike", dependencies: ["NucleicCore"]),
// Standalone de-risking spike for the container rework: drives the real `ContainerEngine`
// end-to-end — pulls an image from a registry, boots the VM, execs a process, and streams
// its stdout through `ContainerizedProcessHandle` (the agent's path). Run signed with
// signing/spike.entitlements; not shipped.
.executableTarget(
name: "container-spike",
dependencies: [
"NucleicCore",
.product(name: "Containerization", package: "containerization"),
.product(name: "ContainerizationOCI", package: "containerization"),
]),
// Standalone de-risking spike for the macOS-VM rework: drives the real `MacVMEngine`
// end-to-end on Apple's `Virtualization` framework — clone the golden base, boot the guest,
// `ssh` a command in, and stream stdout (the `mac_vm_exec` agent path); `NUCLEIC_MACVM_BUILD_BASE=1`
// runs the one-time macOS install instead. Run signed with signing/spike.entitlements; not shipped.
.executableTarget(name: "macvm-spike", dependencies: ["NucleicCore"]),
// Standalone de-risking + ops harness for the MDM plane (docs/MACOS_VM_MDM.md §7 P0): stands
// up the real `NucleicMDMServer` with a fresh local CA, emits the enrollment profile + CA cert
// + `mdm-enroll.sh` to a directory, queues the notification-fix profiles, and logs every
// protocol exchange — used to validate the load-bearing assumptions (no-APNs drain, UAMDM,
// SCEP interop) against one throwaway guest. Not shipped.
.executableTarget(name: "mdm-spike", dependencies: ["NucleicMDM"]),
projectionTarget,
.testTarget(
name: "NucleicRemoteProjectionTests",
dependencies: ["NucleicRemoteProjection", "NucleicProtocol"]),
]
// The Windows shipping surface (docs/WINDOWS_PORT.md §10): the headless hostd host and the
// renderer's C-ABI protocol DLL. The DLL target is also declared on Linux below as a CI compile
// contract, but its dynamic product and hostd remain Windows-only.
let protocolCTarget: Target = .target(
name: "NucleicProtocolC",
dependencies: ["NucleicProtocol", "NucleicRemoteProjection"],
// The C header consumed by the C# P/Invoke layer, not compiled by SwiftPM.
exclude: ["include"],
swiftSettings: [.define(channelDefine)])
let windowsOnlyTargets: [Target] = [
.executableTarget(
name: "nucleic-hostd",
dependencies: ["NucleicCore"],
swiftSettings: [.define(channelDefine)]),
protocolCTarget,
projectionTarget,
]
// Compile the renderer ABI on Linux as a CI contract even though the dynamic product ships only
// on Windows. This catches Swift/C boundary and projection regressions on every ordinary Linux
// test run instead of waiting for the scarcer Windows runner.
let linuxProtocolCTargets: [Target] = onLinux ? [
projectionTarget,
protocolCTarget,
.testTarget(
name: "NucleicProtocolCTests",
dependencies: [
"NucleicProtocolC",
"NucleicProtocol",
"NucleicRemoteProjection",
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
]),
.testTarget(
name: "NucleicRemoteProjectionTests",
dependencies: ["NucleicRemoteProjection", "NucleicProtocol"]),
] : []
// The platform-gated list concatenations below are hoisted OUT of the `Package(...)` call:
// inlined, they push the initializer expression past the manifest type-checker's budget
// ("unable to type-check this expression in reasonable time" — hit first on the Linux
// toolchain), so each list gets its own explicitly-typed `let` and the call site only
// concatenates typed arrays.
let coreProducts: [Product] = [
.library(name: "NucleicProtocol", targets: ["NucleicProtocol"]),
// The Tailnet transport (embedded tsnet node + fd-backed FrameChannel). A separate
// product because the iPhone client links it directly (NucleicCore is host-only).
.library(name: "NucleicTailnet", targets: ["NucleicTailnet"]),
.library(name: "NucleicCore", targets: ["NucleicCore"]),
// Headless mesh client for runner smoke tests (docs/COVALENCE_RUNNER.md §0.2 item 2):
// pairs from a pairing code, dials the relay, starts a chat, answers approvals over the
// wire. NucleicProtocol-only, so it builds everywhere — incl. the static musl target.
.executable(name: "nucleic-smoke", targets: ["nucleic-smoke"]),
// NAT-traversal test harness (Covalence direct, SYNC §3.3): drives the real STUN +
// punch core (`NucleicProtocol/Sync/Direct`) so `scripts/nat-sim` can exercise it
// across simulated NATs (netns + nftables masquerade). NucleicProtocol-only, so it
// builds everywhere the punch core does — including inside the sim's network namespaces.
.executable(name: "nucleic-punch-harness", targets: ["nucleic-punch-harness"]),
]
// `nucleicd` is the Covalence *runner* host — a Mac or a Linux runner container. Windows has
// its own headless host (`nucleic-hostd`), and nucleicd's control endpoint is a BSD-socket
// listener with a `DispatchSource` signal handler, neither of which exists there. Excluding it
// from the Windows manifest is what keeps `swift test` (which builds every declared target) from
// having to compile a daemon that platform will never run.
let nonWindowsProducts: [Product] = [
// The headless Covalence runner host (docs/COVALENCE_RUNNER.md §9): the same NucleicCore
// runtime as the app, no UI. Runs on a spare Mac (self-host v0) AND on Linux — the
// runner-container daemon.
.executable(name: "nucleicd", targets: ["nucleicd"]),
]
let nonWindowsTargets: [Target] = [
.executableTarget(name: "nucleicd", dependencies: ["NucleicCore"]),
]
let windowsOnlyProducts: [Product] = [
// The Windows headless host (docs/WINDOWS_PORT.md §11): hostd owns AppStore + SyncHost;
// the WinUI 3 renderer is a client over loopback.
.executable(name: "nucleic-hostd", targets: ["nucleic-hostd"]),
// The renderer's protocol client (docs/WINDOWS_PORT.md §6): NucleicProtocol + the
// transcript projection behind a flat C ABI, P/Invoked by the WinUI app.
.library(name: "NucleicProtocolC", type: .dynamic, targets: ["NucleicProtocolC"]),
]
let darwinOnlyProducts: [Product] = [
// Product (binary / process) name varies by channel — see `appProductName`.
.executable(name: appProductName, targets: ["NucleicApp"]),
.executable(name: "nucleic-power-helper", targets: ["NucleicPowerHelper"]),
// Adapter-contract test stubs (fed to NucleicCoreTests). Darwin-only: they lean on
// Foundation surfaces corelibs doesn't ship (`FileHandle.bytes`), and the core test
// suite they feed runs on Darwin.
.executable(name: "fake-claude", targets: ["fake-claude"]),
.executable(name: "fake-grok", targets: ["fake-grok"]),
.executable(name: "nucleic-spike", targets: ["nucleic-spike"]),
.executable(name: "container-spike", targets: ["container-spike"]),
.executable(name: "macvm-spike", targets: ["macvm-spike"]),
.executable(name: "mdm-spike", targets: ["mdm-spike"]),
]
let packageDependencies: [Package.Dependency] = [
.package(url: "https://github.com/groue/GRDB.swift.git", from: "7.0.0"),
// The Noise/CBOR crypto on Linux (docs/COVALENCE_RUNNER.md §9): swift-crypto is the
// cross-platform CryptoKit — NucleicProtocol imports it only where CryptoKit is absent
// (`#if canImport(CryptoKit)`), and the NucleicProtocolTests Noise/enrollment vectors
// pin the two backends byte-for-byte. Already in the graph transitively (containerization
// → grpc-swift), so this adds no new resolution.
.package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
// The relay WebSocket on Linux (docs/COVALENCE_RUNNER.md §9): corelibs-foundation's
// URLSessionWebSocketTask rides libcurl's experimental WS support, absent from distro
// builds — so the runner's socket is SwiftNIO end to end (TLS via NIOSSL, HTTP/1.1
// upgrade, RFC 6455 client framing). Darwin/iOS keep URLSessionWebSocketTask; these
// products are linked into NucleicProtocol on Linux only. Both packages are already in
// the graph transitively (containerization → grpc-swift), so this adds no resolution.
// Floor is 2.101.3 for WINDOWS, not for any API we call: NIOPosix didn't compile on
// Windows before it (apple/swift-nio#3433). 2.101.2 fails three ways — `HANDLE` is an
// opaque pointer whose `Sendable` conformance is unavailable, so `ThreadOpsWindows`
// and `NIOThread.handle` are both rejected, and `CInt(IPPROTO_UDP)` doesn't compile
// now that WinSDK imports IPPROTO as an enum (2.101.3 uses `.rawValue`).
.package(url: "https://github.com/apple/swift-nio.git", from: "2.101.3"),
// TEMPORARY FORK — revert to `.package(url: "https://github.com/apple/swift-nio-ssl.git",
// from: "2.27.0")` the moment apple/swift-nio-ssl#567 lands.
//
// NIOSSL's Swift layer has no Windows support upstream, not even on main as of 2.37.2:
// every file's platform #if handles only Darwin/Musl/Glibc/Android and then hits
// `#error("unsupported os")`. Only the C layer was fixed (#585), which is why
// CNIOBoringSSL compiles and then hundreds of Swift errors follow. #567 ("Add support
// for Windows", by the same contributor whose Winsock fixes shipped in swift-nio
// 2.101.3) ports the Swift layer; it is mergeable and actively moving.
//
// Pinned by REVISION, not branch, so the build stays reproducible and a force-push to
// the fork can't silently change what CI compiles. To move to a newer PR commit, bump
// the revision here AND in Package.resolved. (The commit lives only on the fork —
// apple/swift-nio-ssl doesn't expose PR refs to a default clone, so the upstream URL
// can't be kept while pointing at this revision.)
//
// EXPECTED NOISE while this pin is in place: four "Conflicting identity for
// swift-nio-ssl" warnings, because third_party/containerization, async-http-client,
// swift-nio-extras and grpc-swift-nio-transport all reach the upstream URL. The root
// package wins, so the build is correct — but SwiftPM says it "will be escalated to an
// error in future versions", which is a second reason not to sit on this fork.
.package(
url: "https://github.com/Joannis/swift-nio-ssl.git",
revision: "bc3bd90098701c6c1d7aa6275b6911860fe9d439"),
// The host-side MDM plane (docs/MACOS_VM_MDM.md): a local CA that issues the guest's
// enrollment identity + the server's TLS cert, and the SCEP/CMS ASN.1 the enrollment
// handshake speaks. swift-certificates (X.509 issuance, CSR parse, CMS SignedData) +
// swift-asn1 (the DER layer we hand-roll CMS EnvelopedData on, which swift-certificates
// does not provide). Both are already in the graph transitively (swift-certificates →
// swift-asn1; both reachable via grpc-swift/containerization), so this adds no resolution.
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.19.0"),
.package(url: "https://github.com/apple/swift-asn1.git", from: "1.7.0"),
.package(url: "https://github.com/migueldeicaza/SwiftTerm.git", from: "1.2.0"),
// Apple's containerization framework — the in-process runtime the `container` CLI is built
// on. **Vendored** (third_party/containerization) at upstream commit 6b7b42ca rather than
// pulled from github, because we carry a small patch: `LinuxContainer.Configuration` forwards
// `vmExtensions` into `VMConfiguration.extensions` so we can attach a memory-balloon device
// (see third_party/containerization/PATCHES.md). The package is pre-1.0 (source stability
// holds only within a minor version); to bump, re-vendor the new commit and re-apply the
// patch. All framework calls are centralized in `ContainerEngine`/`MemoryBalloon`.
.package(path: "third_party/containerization"),
] + (nonDarwin ? [] : [
// Sparkle — in-app auto-update for the directly-distributed (non-App-Store) macOS
// builds. macOS-only and used ONLY by the NucleicApp executable target, so the iOS
// resolution of the package graph never compiles it. Updates are delivered as signed,
// notarized DMGs described by a per-channel appcast (see BUILD.md "Auto-update").
// Excluded from the Linux graph (see the manifest note above): NucleicApp isn't built for
// the runner container, and leaving it declared makes `swift build --product nucleicd`
// clone Sparkle's large repo during resolution for a target that can never use it.
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.6.0"),
])
// Pure, platform-agnostic: identifiers, the AgentEvent model, the sync wire
// protocol (ClientMsg/HostMsg), CBOR framing, and the Noise SecureChannel.
// No GRDB / Process / Network — compiles for macOS, iOS, Linux, and Windows alike
// (CryptoKit on Darwin, swift-crypto elsewhere; see the swift-crypto dependency note).
// Out-of-line because the `windowsProtocolOnly` M0 CI leg declares JUST this pair.
let protocolTarget: Target = .target(
name: "NucleicProtocol",
dependencies: [
.product(
name: "Crypto", package: "swift-crypto",
condition: .when(platforms: [.linux, .windows])),
// The non-Apple relay WebSocket (RelayTransport.swift's `#else` leg) — see the
// swift-nio dependency note above. Windows rides the same NIO leg as Linux
// (docs/WINDOWS_PORT.md §4.1).
.product(
name: "NIOCore", package: "swift-nio",
condition: .when(platforms: [.linux, .windows])),
.product(
name: "NIOPosix", package: "swift-nio",
condition: .when(platforms: [.linux, .windows])),
.product(
name: "NIOHTTP1", package: "swift-nio",
condition: .when(platforms: [.linux, .windows])),
.product(
name: "NIOWebSocket", package: "swift-nio",
condition: .when(platforms: [.linux, .windows])),
.product(
name: "NIOFoundationCompat", package: "swift-nio",
condition: .when(platforms: [.linux, .windows])),
.product(
name: "NIOSSL", package: "swift-nio-ssl",
condition: .when(platforms: [.linux, .windows])),
],
linkerSettings: [
// Sync/Direct is hand-rolled BSD sockets (DirectSocketShim.swift). On Windows
// that means Winsock, and interface enumeration has no getifaddrs equivalent —
// it goes through GetAdaptersAddresses, which lives in the IP Helper API.
.linkedLibrary("ws2_32", .when(platforms: [.windows])),
.linkedLibrary("iphlpapi", .when(platforms: [.windows])),
])
let protocolTestsTarget: Target = .testTarget(
name: "NucleicProtocolTests",
dependencies: ["NucleicProtocol"])
let crossPlatformTargets: [Target] = [
protocolTarget,
// The Tailnet sync transport (SYNC_PROTOCOL §3): an embedded tsnet node
// (TailscaleKit) plus the fd-backed FrameChannel both sides speak over it. Compiles
// for macOS (host listener) and iOS (phone dialer) alike; when the TailscaleKit
// artifact is absent the module still builds and `TailnetSupport.isBuiltIn` is false.
.target(
name: "NucleicTailnet",
dependencies: ["NucleicProtocol"] + (tailscaleKitAvailable ? ["TailscaleKit"] : [])),
// The host-side MDM plane (docs/MACOS_VM_MDM.md), factored out of NucleicCore so it
// builds and tests in **isolation** — pure Swift + crypto/ASN.1, no Virtualization or
// containerization — which is what makes `swift test --filter NucleicMDMTests` fast. The
// one Darwin-only surface (the `NWListener`+TLS HTTPS server, `NucleicMDMServer`) is
// guarded with `#if canImport(Network)`, so this target still compiles on Linux (server
// omitted) alongside the rest of NucleicCore. NucleicCore depends on it for the
// provisioning-time (Mode A) and live-clone (Mode B) glue.
.target(
name: "NucleicMDM",
dependencies: [
.product(name: "X509", package: "swift-certificates"),
.product(name: "SwiftASN1", package: "swift-asn1"),
.product(name: "Crypto", package: "swift-crypto"),
.product(name: "_CryptoExtras", package: "swift-crypto"),
]),
.testTarget(
name: "NucleicMDMTests",
dependencies: ["NucleicMDM"]),
.target(
name: "NucleicCore",
dependencies: [
"NucleicProtocol",
"NucleicTailnet",
"NucleicMDM",
.product(name: "GRDB", package: "GRDB.swift"),
// In-process container runtime (host-only, macOS-conditioned: on Linux the cloud
// container IS the sandbox — `RunSpec.container` stays nil and the Container/*
// sources compile to empty via `#if canImport(Containerization)`). EXT4 unpacking
// + vmnet networking are reached transitively through `Containerization`;
// `ContainerizationOCI` provides the `User`/`Platform` types used directly by
// `ContainerEngine`.
.product(
name: "Containerization", package: "containerization",
condition: .when(platforms: [.macOS])),
.product(
name: "ContainerizationOCI", package: "containerization",
condition: .when(platforms: [.macOS])),
// For `AddressAllocator` — named in the `VZInstanceExtension.configureVZ` signature
// our `MemoryBalloon` conforms to (it lives here, not in `Containerization`).
.product(
name: "ContainerizationExtras", package: "containerization",
condition: .when(platforms: [.macOS])),
// The Windows control-plane TCP listener (MCPApprovalServer's NIO leg,
// docs/WINDOWS_PORT.md §4.3) — Winsock SOCKETs aren't POSIX fds, so the
// Linux BSD-socket mirror can't serve there. Already in the graph via
// NucleicProtocol's non-Apple legs; this just links it into NucleicCore.
.product(
name: "NIOCore", package: "swift-nio",
condition: .when(platforms: [.windows])),
.product(
name: "NIOPosix", package: "swift-nio",
condition: .when(platforms: [.windows])),
],
// Non-Darwin platforms compile NucleicCore without the Apple-framework surfaces: the
// containerization sandbox engine + macOS-VM engine (stub actors in
// LinuxSupport.swift keep the shared signatures compiling; the pure value/spec
// files — MacVMSpec/MacVMSurface/MacVMPackage/CommandInterceptor — stay) and the
// Network.framework LAN transport (a runner listens relay/tailnet-first;
// docs/COVALENCE_RUNNER.md §9). Linux additionally drops the container-policy layer
// (`Container/ContainerManager.swift` — the LinuxSupport.swift stub is the Linux
// story), which Windows KEEPS: there the manager compiles against the platform-
// neutral `SandboxEngine` protocol and the wslc broker supplies the mechanism
// (docs/WINDOWS_PORT.md §3.1). The `Windows/` shim directories are compiled only on
// Windows and excluded everywhere else.
exclude: computeNucleicCoreExcludes(),
// The build channel reaches the container-naming code here: `ContainerManager` appends a
// per-channel suffix (e.g. `-beta`, `-local`) to non-release container names so a beta /
// local-dev build's containers don't collide with a release's on the shared on-disk
// container store (ContainerEngine.defaultStorageRoot). Same define NucleicApp's banner uses.
swiftSettings: [.define(channelDefine)]),
.executableTarget(name: "nucleic-smoke", dependencies: ["NucleicProtocol"]),
.executableTarget(name: "nucleic-punch-harness", dependencies: ["NucleicProtocol"]),
protocolTestsTarget,
// The Carbon data layer's suites (docs/CARBON_SHARDING.md §16), split out of the
// Darwin-only NucleicCoreTests because phase acceptance requires them green on macOS
// AND Linux (the frozen vectors pin CryptoKit and swift-crypto byte-for-byte).
.testTarget(
name: "NucleicCarbonTests",
dependencies: ["NucleicCore"]
),
]
let package = Package(
name: "Nucleic",
// NucleicProtocol (the shared sync wire layer) must build for iOS too — that's what the
// NucleicRemote iPhone client links. The host-only targets (NucleicCore/App/spike) use
// macOS-only APIs and are simply never built for iOS.
//
// macOS floor is 27: the app depends on macOS 27-only features. (The container sandbox is also
// built directly on Apple's `containerization` framework, whose in-process vmnet networking
// requires macOS 26 + Apple silicon — comfortably below our 27 floor.) `containerization` is a
// dependency of NucleicCore ONLY (never of the iOS-linked NucleicProtocol), so the iOS build
// resolves the package graph but never compiles it.
//
// iOS floor is 18.1: TailscaleKit ships with an 18.1 minimum (its `state()` methods use
// the Failure-typed `any AsyncSequence` existential, an iOS 18 Swift-runtime feature), and
// SwiftPM has no per-target floors, so the iOS-linked targets rise with it. Every device
// that runs iOS 17 also runs 18.1, so this drops no hardware — only stale installs.
platforms: [.macOS("27.0"), .iOS("18.1")],
products: windowsProtocolOnly
? [.library(name: "NucleicProtocol", targets: ["NucleicProtocol"])]
: coreProducts
+ (onWindows ? windowsOnlyProducts : nonWindowsProducts)
+ (nonDarwin ? [] : darwinOnlyProducts),
dependencies: packageDependencies,
targets: windowsProtocolOnly
? [protocolTarget, protocolTestsTarget]
: crossPlatformTargets
+ (nonDarwin ? [] : darwinOnlyTargets)
+ linuxProtocolCTargets
+ (onWindows ? windowsOnlyTargets : nonWindowsTargets)
+ (tailscaleKitAvailable
? [.binaryTarget(
name: "TailscaleKit",
path: "third_party/TailscaleKit/TailscaleKit.xcframework")]
: [])
)
// NucleicCore's per-platform `exclude` list (hoisted out of the target declaration for the
// same type-checker reason as the products/targets lists above; a function declaration is
// visible to the earlier `crossPlatformTargets` literal, and by the time that literal is
// evaluated the `onLinux`/`onWindows` lets it reads are initialized).
func computeNucleicCoreExcludes() -> [String] {
let appleOnlyEngineFiles = [
"Container/ContainerEngine.swift",
"Container/ContainerEngine+Rootfs.swift",
"Container/OCIArtifact.swift",
"Container/ContainerizedProcessHandle.swift",
"Container/MemoryBalloon.swift",
"MacVM/MacVMAgentClient.swift",
"MacVM/MacVMEngine.swift",
"MacVM/MacVMEngine+Base.swift",
"MacVM/MacVMEngine+Computer.swift",
"MacVM/MacVMEngine+ComputerAgent.swift",
"MacVM/MacVMEngine+ConsentGrant.swift",
"MacVM/MacVMEngine+HostPaths.swift",
"MacVM/MacVMEngine+InstallApps.swift",
"MacVM/MacVMEngine+InstallPackages.swift",
"MacVM/MacVMEngine+MDM.swift",
"MacVM/MacVMEngine+MDMDiagnostics.swift",
"MacVM/MacVMEngine+MDMPolicyPass.swift",
"MacVM/MacVMEngine+PolicyGrants.swift",
"MacVM/MacVMEngine+Readiness.swift",
"MacVM/MacVMEngine+LinuxArtifactCache.swift",
"MacVM/MacVMEngine+LinuxBase.swift",
"MacVM/MacVMEngine+LinuxConfig.swift",
"MacVM/MacVMEngine+LinuxProvision.swift",
"MacVM/MacVMEngine+Provision.swift",
"MacVM/MacVMEngine+Provision27.swift",
"MacVM/MacVMEngine+Reprovision.swift",
"MacVM/MacVMEngine+ShellDrain.swift",
"MacVM/MacVMExecChannel.swift",
"MacVM/MacVMManager.swift",
"Sync/LANTransport.swift", "Sync/LANBrowser.swift",
"Sync/LANAddress.swift", "Sync/MeshPathMonitor.swift",
]
let windowsOnlyDirs = ["Windows", "Container/Windows"]
if onWindows { return appleOnlyEngineFiles }
if onLinux {
return appleOnlyEngineFiles + ["Container/ContainerManager.swift"] + windowsOnlyDirs
}
return windowsOnlyDirs
}