Capability first · transport second

Portable Core packaging

arcane-os/native/portable-provider owns the portable native payload in the SDK package. It does not load Arcane OS source, choose models or compile an executable. An application's explicit native.localAI selection can include its selected upstream runtimes through the SDK local-AI installer. Without that selection, assembly does no local-AI runtime preparation. Windows, Linux and macOS hosts may compose this Node-based payload; Android needs its separate native host adaptation. The current CLI's portable platform selections include Windows, Linux and macOS. A portable artifact is not a Windows executable, Linux package or Android application.

SDK 0.97.1Runtime 0.8.12Protocol arcane/1
On this page

arcane-os/native/portable-provider owns the portable native payload in the SDK package. It does not load Arcane OS source, choose models or compile an executable. An application's explicit native.localAI selection can include its selected upstream runtimes through the SDK local-AI installer. Without that selection, assembly does no local-AI runtime preparation. Windows, Linux and macOS hosts may compose this Node-based payload; Android needs its separate native host adaptation. The current CLI's portable platform selections include Windows, Linux and macOS. A portable artifact is not a Windows executable, Linux package or Android application.

loadArcaneNativeProvider({target:'portable'}) selects the installed SDK provider without an arcaneRoot. Supplying an explicit arcaneRoot retains the existing checkout-provider route. The SDK also owns the windows-x64 executable provider described below. Other native targets remain with their actual platform providers; the loader does not substitute this payload for an unavailable host.

Provider and selected input

The default export and arcaneNativeBuilderProvider are the same provider. createPortableNativeProvider({services,packagedWeb=false,webKitDocumentLifecycle=false}) creates one with application-owned service configuration. Without that override, the provider uses the selected app descriptor's native.services, or an empty selection when omitted. An explicit services:[] overrides the descriptor with no app services. The Mac provider selects packagedWeb:true to compose the SDK's packaged-web Core service and webKitDocumentLifecycle:true for the canonical classic-client activation/replay contract. Both default to false, preserving ordinary portable and Windows composition. All implement arcane-native-builder/1:

  • describe() identifies the portable target and executable:false.
  • doctor({targetRequest, signal, onEvent}) reports missing SDK Core source files.
  • prepare({targetRequest, signal, onEvent}) returns the selected SDK version and package root. Neither operation builds or imports application services.
  • build(request) assembles one selected release and its explicit dependencies.
  • verify({artifact, targetRequest, signal, onEvent}) explicitly reads its semantic manifest and observes that the listed output files exist. It does not execute a host or establish native behavior. Build does not implicitly run verification.
  • run() reports ARCANE_NATIVE_RUN_UNSUPPORTED; portable is not an executable.

Use the existing createNativeBuildPlan() / executeNativeBuildPlan() public contract. Build receives appDescriptor, appReleaseRoot, release, readReleaseFile, dependencies, minimumCoreVersion, protectedRoots, outputRoot, targetRequest, selectedSdk, signal and onEvent. Dependencies provide their appId, releaseRoot, release and readReleaseFile. The selected release's files inventory is copied completely through its supplied reader. Direct callers may supply the release root for ordinary file reads instead.

Both the current flat signing selection and the CLI's nested signing selection are accepted by the planning boundary. Portable assembly adds no signing, receipts, hashes, content limits or security policy. The package-relative source root comes from the provider module's own location, not toolchainRoot or an Arcane OS machine bundle.

Native-only application resources

Select application service modules and their complete local source closure in the authored schema-2 descriptor's package.nativeResources, separately from its browser include/exclude selection:

json
{
  "package": {
    "nativeResources": {
      "include": ["native", "src", "package.json"],
      "exclude": ["native/drafts"]
    }
  }
}

This is a descriptor excerpt for the app-owned service example below. The projected schema-1 field is top-level nativeResources. Paths are literal and app-relative; directories include descendants. Omission or an empty include keeps previous selection and output behavior. The app supplies every required source file explicitly; packaging does not discover or execute services.

Paired buildApplication() passes the actual native target to the selected app and every explicitly bundled app. With a nonempty native include list, their input releases use dist/.native/<target>/<app-id> instead of the saved browser output, preserving existing browser/PWA artifacts. Each app's own selection is applied; complete releases and actual roots are forwarded to the provider. The provider then copies them unchanged into its artifact layout described below.

At the low-level packageApp() boundary, target defaults to 'browser' and is independent of moduleFormat. Nonbrowser selection disables generated PWA content. Native-only files bypass browser document/module/asset transformations; a file also in the ordinary browser selection retains that existing processing. An explicit low-level outputDirectory still wins. Inspect the emitted input with verifyApp({workspaceRoot,appId,outputDirectory:release.output}); neither that inventory verification nor packaging launches the service or platform host. See the complete selection contract.

Output and host contract

Each build assembles in a fresh temporary directory beneath outputRoot, then publishes the complete artifact to one stable app-specific child. Portable and Windows outputs use <outputRoot>/<app-id>/; macOS uses <outputRoot>/<app-id>.app/. The result remains {app, target, manifest}, with its actual payload directory at target.rootDir and the macOS bundle directory also returned as bundleRoot. A later successful build replaces that app's previous generated output instead of accumulating completed build directories. Windows device-name spellings such as con receive an underscore in the output folder name (_con or _con.app) on every build host; the app ID stays unchanged.

Assembly leaves the current output available. Publication uses same-filesystem renames and retains the previous output in a temporary backup until the native.payload.completed callback accepts the new final paths. A callback rejection or cancellation restores the previous output; a successful build removes its backup and temporary assembly. The completion callback runs before build acceptance, including the following cancellation check; receiving that event alone does not establish an accepted build. native.payload.started identifies the temporary assembly, while native.payload.completed identifies the final payload and, on macOS, final bundle. Manifest paths stay relative to that payload; application content and explicitly supplied configuration paths stay unchanged.

Only final replacement of the same app is reserved; independent assemblies and other apps remain concurrent. A competing publication reports the exact .arcane-publish-<app-output-name> reservation so its owner can finish before retrying. If recovery or backup cleanup fails, the error identifies the actual output, assembly and reservation paths, and whether publication committed. The reservation's owner.json records the process, acquisition time, advisory five-minute TTL/expiry and recovery locations. Expiry never permits automatic takeover or deletion. After an interrupted build, inspect those paths before restoring the previous output or removing the reservation. A running or locked app may prevent Windows replacement; close that app and retry the build after the reported failure.

Replacement applies only to the matching app's generated artifact directory. Source/release roots, unrelated outputs, host/runtime download caches and the host's separate saved profile/model directories remain outside replacement. Existing randomly named outputs from earlier SDK versions are left untouched; the new stable-output lifecycle does not sweep old directories or user data.

The selected app is copied unchanged under app/. Dependencies are copied unchanged under dependencies/<index>/; the manifest relates each directory to its actual app identifier and selected release. No HTML, payload, line ending, application import, or base URL is rewritten by this provider. Dependency navigation and service imports remain explicit host/application composition; the payload does not invent cross-app routes.

node_modules/arcane-os/ contains the SDK consumer source, runtime, schemas, commands, package metadata and legal files. Its installed published runtime dependency tree is included with licenses and actual nested dependency resolution. The generated Core entry uses the ordinary public arcane-os/core/host import. Application service modules can use those public SDK exports through Node's normal package resolution. Existing app-owned node_modules content remains authoritative where the selected app supplies it; its own dependency completeness remains part of that app's selected release. For the direct installed browser layout, the ordinary native selection also supplies selectedSdk:{packageRoot,packageSource} from the application's actual installed SDK. Assembly completes its browser-only package directory with missing files and dependencies from that selected package, preserving every selected file. It never fills an older projection with the builder's newer package metadata. An already-supplied package.json remains app-owned, without replacement or automatic completion. manifest.appSdk identifies this app package separately from the builder SDK. Low-level callers composing services with a partial direct SDK projection must supply the actual selected package source; a file inventory alone does not identify its version or Node exports. No dependency installer or package script runs. No OS model, Shell, Provisioner, catalog, prompt, or native compiler is added.

The semantic arcane-native.json names the web root, selected start URL, Node Core entry, classic client, dependencies and complete file inventory. A native host must:

  1. Mount app/ at the root of its local application URL origin, preserving the selected start path and all navigable documents.
  2. Inject runtime/arcane-api.js at document creation. It is produced by the canonical createCoreClassicSource() generator with replayRuntimeState:true. Once its transport listener is attached, each document requests the current Core and service state without starting services again.
  3. Start runtime/arcane-core.mjs with the host's supported Node runtime and connect the existing framed stdin/stdout transport.
  4. Close input and await Core's drain before terminating the child process.

The selected release must contain one shared sdk/event-manager.mjs path, possibly nested under its SDK payload directory, or the direct installed node_modules/<package-or-alias>/browser-runtime/event-manager.mjs path. The classic client imports that same owner with an origin-root URL, including on nested pages. It does not create a second event implementation. A plain file-URL launch without this host mapping is not the native serving contract. No host is launched during packaging.

App-owned runtime entry

The generated runtime/arcane-core.mjs also accepts --arcane-app-entry <app-relative-module>. This explicit mode imports the selected file from the artifact's app/ directory before ordinary launch-context or service initialization. It preserves the complete original process.argv and converts the native path to a file URL without treating filename characters as URL query or fragment syntax. Missing/empty selections and actual import failures propagate; there is no fallback to ordinary Core.

Use the existing Windows runtime/ArcaneCore.exe with that flag and module path directly. On macOS, use bundled runtime/node with runtime/arcane-core.mjs followed by the flag and module path. Portable payloads use the host's supported Node executable with that same generated entry. The macOS artifact root is its .app/Contents/Resources directory. This mode needs no new executable, SEA loader, GUI launcher, or copied runtime.

The app includes the module and its complete source closure through package.nativeResources. The imported entry starts its own top-level work, not an import.meta.main-guarded entry or an automatically invoked export. It owns any explicit launch-context read, application services, MCP tools, signals and shutdown. No ordinary Core host, shared-host bridge, manifest services, model startup or Core pipe framing is started by this branch. Static SDK definition imports remain available; the ordinary parallel service imports and host lifetime stay unchanged when the flag is absent.

For complete client command arrays, optional --arcane-launch-config handling, and MCP STDIO lifetime ownership, see launch from a native package.

Explicit application services

For the ordinary SDK build command, select native service modules in the application's authored descriptor. Include their source closure through package.nativeResources as described above:

json
{
  "native": {
    "services": [
      {"module":"native/moon-ledger.mjs", "options":{"ledgerName":"Cheese debts"}}
    ]
  }
}

Direct SDK compositions can instead pass that selection to the provider:

js
import {createPortableNativeProvider} from 'arcane-os/native/portable-provider';

const nativeBuilder = createPortableNativeProvider({
    services: [{module:'native/moon-ledger.mjs', options:{ledgerName:'Cheese debts'}}]
});

Each module default-exports a synchronous factory returning the existing Core service definition. Its first argument is the complete authored options record; its second is the shared launch context described below. Options must be JSON-serializable data. Module imports and factories run only when the generated Core entry is launched, never during packaging. Put asynchronous initialization in start(), not a top-level await or asynchronous factory, so unrelated services and dispatcher readiness remain independent. Factories own product configuration and any service dependencies; the packager does not discover, install or execute them.

js
// native/moon-ledger.mjs, owned by the application
export default function createMoonLedger({ledgerName}) {
    return {
        name: 'moon-ledger',
        methods: {
            'moon.describe': function describeLedger() {
                return {name:ledgerName, message:'The cheese debts are astronomical.'};
            }
        }
    };
}

startCoreHost({application, version, services, input, output, onError, signal}) is also available directly from arcane-os/core/host. It returns the existing stdio owner's {runtime, closed, close} without another lifecycle controller. An optional abort signal invokes that same idempotent drain. Dispatcher readiness does not wait for service/model readiness. Accepted lifetime:'service' work survives renderer cancellation and is drained before shutdown. Errors remain observable through onError and closed; no process is killed or forcibly exited. See the Core runtime contract for request and service lifetime details and the client contract for native bridge integration.

Launch-time locations

An application can select defaults for ordinary double-click launch in its descriptor, without a replacement launcher or a required command-line file:

json
{
  "native": {
    "launchContext": {
      "sharedHost": {},
      "dispatch": {"destination": "Moon Cheese Receiving Dock"}
    }
  }
}

This is the native fragment of the application's complete descriptor. The optional native.launchContext object is copied intact to the portable manifest and generated Core entry. Its presence explicitly selects native location defaults. sharedHost:{} opts into the shared Core endpoint and log; omitting sharedHost retains stdio-owned Core lifetime. Omitting native.launchContext retains the previous generated-entry behavior entirely. Windows and macOS launchers use the same packaged defaults for native state selection, so an ordinary executable launch and external MCP can select the same app-owned Core.

To expose the actual window-owned Core to an external local client while preserving that stdio lifetime, select native.launchContext:{coreListener:{endpoint:"APPLICATION-SELECTED-LOCAL-ENDPOINT"}} instead. The generated entry calls startCoreListener({runtime:host.runtime,endpoint}) after creating its ordinary stdio host. It reuses that exact service set and launch context, including the existing origin, profile and state root; no headless runtime is created. The listener endpoint has no default and its parent directory must already exist where required by the operating system. coreListener and sharedHost are alternative selections; supplying both reports TypeError.

That same coreListener opt-in registers one createModelObservationService() after startCoreHost() and before binding the listener. It receives host.runtime and the exact already-created service objects exposing localai.status, image.status and decisions.status; a configurable decision-service name remains supported. Absent owners are null. Composition creates no model or second runtime, performs no service lookup and adds no model-readiness wait. Ordinary stdio without this opt-in and the existing sharedHost branches retain their behavior. The application separately calls attachModelObservation() with its existing renderer owners and Core client; native composition cannot infer those objects. Observers select onDiagnostic to receive complete live model failure frames in their developer diagnostic surface. Observation does not start inference or establish that an existing model failure has been corrected.

Clients use connectSharedCoreHost({endpoint}) with start omitted and close only their own connection. This listener provides no host-shutdown RPC. Closing the window still drains its Core, including accepted service-owned operations, and then closes the listener. Signal-driven host shutdown remains observed while the listener is binding; a bind failure drains the newly created stdio host and reports the failure. The generated entry joins listener closure without changing the existing shared-host/headless branches. See the existing-runtime listener contract for direct composition, correlation, cancellation and complete error behavior.

resolveNativeLaunchContext({appId,context={}}) from arcane-os/core/host is the public, synchronous location owner for native applications and external MCP. It returns a new complete context and fills only omitted fields:

Field SDK default
stateRoot on Windows %LOCALAPPDATA%/Arcane/<appId>; standard home AppData/Local fallback when the environment value is absent
stateRoot on macOS ~/Library/Application Support/Arcane/<appId>
stateRoot on Linux Absolute $XDG_DATA_HOME/Arcane/<appId>, otherwise ~/.local/share/Arcane/<appId>
modelsDirectory on Windows %LOCALAPPDATA%/Arcane/Models, with the same standard home fallback
modelsDirectory on macOS ~/Library/Application Support/Arcane/Models
modelsDirectory on Linux Absolute $XDG_DATA_HOME/Arcane/Models, otherwise ~/.local/share/Arcane/Models
modelsDirectory on other adapted hosts <resolved stateRoot>/Models unless the host supplies an explicit shared location
workspaceRoot <resolved stateRoot>/Workspace
sharedHost.endpoint, only when sharedHost is selected Windows named pipe \\.\pipe\Arcane- followed by the URL-encoded complete resolved state path; Linux/macOS socket <resolved stateRoot>/core.sock
sharedHost.logFile, only when sharedHost is selected <resolved stateRoot>/Core.log

Windows/macOS roots retain the existing native host locations. Linux is the portable Node host convention; an external composing host supplies its actual state root when it selects another location. Android and other host adaptations must supply stateRoot explicitly and own native IPC/process adaptation. The resolver never reads saved preferences, creates directories, changes the environment, starts services, or migrates existing data. The generated opted-in shared entry creates its selected state directory because its socket/log need that parent; application services continue to own workspace I/O. Custom endpoint/log parent directories retain the existing shared-host requirements.

Models default to one directory per operating-system user, independent of the application ID or app-private state root. Apps selecting the same sources and shared model store reuse the SDK acquisition owner. Preferences, profiles, chat history, workspace and IPC remain app-scoped. Existing app-local models are not moved or deleted: callers can discover and open them in place.

Explicit stateRoot, modelsDirectory, workspaceRoot, endpoint, log filename and additional fields remain authored values. The resolver derives app-private paths from the absolute selected state location. Relative explicit paths retain the caller's working-directory meaning, so UI and MCP callers must select the same location. No hashing, app-local OS path composition, directory scanning, or new repository location rule is involved. resolveArcaneDataPaths() continues to own its separate ArcaneData/Repos contract. A selected native context needs a nonempty string appId and state directory; a selected sharedHost must be an object. Malformed resolver inputs report TypeError; ordinary OS endpoint requirements remain at the IPC operation.

For one app-owned Core shared by the native window and a separate MCP process, the launch context may explicitly select sharedHost:{endpoint,logFile}. The generated native child then connects through a stdio bridge, starting an independent headless Core only when the selected endpoint is absent. Its endpoint claim precedes application service imports/factories. Closing a native window disconnects its bridge; explicit shared-host shutdown drains services. Omitting sharedHost preserves the existing stdio-owned Core lifetime. See shared Core host ownership for exact startup, connection, cancellation, diagnostics, endpoint, and platform contracts. The packaged-web service exposes current() as its active {origin,url,port} or null; the shared host replays that current listener to a newly attached macOS bridge, without restarting the server or retaining historical events.

The generated Core entry accepts --arcane-launch-config <path>. The public readCoreLaunchContext({argv=process.argv.slice(2),defaults={},appId}={}) function in arcane-os/core/host reads that explicitly selected JSON object. Precedence is packaged defaults, then the native host's actual state-root argument, then every complete top-level field from the explicit JSON object. Nested records replace their default field as a whole; they are not deep-merged. An appId explicitly opts the result into resolveNativeLaunchContext; the generated entry supplies it only when native.launchContext is present. A native host can separately supply --arcane-host-state-root <directory> for the reader's stateRoot default. Every field in the explicit JSON object takes precedence unchanged, including an explicitly present stateRoot with a relative, null or other application-owned value on the ordinary non-resolving reader. With neither argument nor defaults/appId the reader still returns {}. Missing files, missing argument values and malformed JSON report their actual errors; the reader does not search for configuration, resolve or validate the state directory, rewrite the file or change the process environment. The explicitly opted-in resolver supplies its documented defaults after this merge.

The generated entry supplies an artifact-derived appRoot and then applies the complete explicit launch context. Every service factory receives that same object as its second argument, preserving its authored first argument. A launcher can supply the user's selected workspaceRoot and writable stateRoot at launch instead of embedding this machine's paths in an app package. Explicit product selections take precedence over the opted-in SDK location defaults. The SDK chooses no saved preference filename or model. JSON values and additional application-owned fields remain unchanged, including an app-selected preferencesFile. The state directory supplies no preference filename and triggers no stored-data discovery or migration. Linux and other composing hosts may pass the same separate argument; the ordinary reader chooses no platform directory until native resolution is explicitly selected.

Selected local-AI runtimes

When appDescriptor.native.localAI is present, portable assembly calls the SDK's bundleLocalAIRuntimes() owner with its runtimes selection and the requested target platform/architecture. Its reusable installation directory is <outputRoot>/local-ai-runtimes; each new payload receives the complete selected runtime trees beneath runtime/local-ai/. Runtime preparation and app copying proceed independently and both settle before assembly can clean up an incomplete output. No application service or model executes during packaging.

manifest.core.localAIRuntimes records the bundled runtime locations relative to the artifact root. Only the explicitly configured entry imports createLocalAIService from arcane-os/core/local-ai. It resolves those bundled locations against the installed artifact and supplies them in the launch context's runtimes array, alongside appRoot, before applying explicit launch configuration. It passes chat/ONNX requirements to that factory and routes selected image requirements to their separate service as described below. Applications without that selection have no generated local-AI import or service. Availability of a particular engine belongs to the selected local-AI installer and service; a generic ONNX runtime is not a speech model.

Selected onnx bundles its complete managed onnxruntime-node dependency tree. For the SDK-selected Windows x64 ONNX 1.30.0 distribution, that tree includes the corrected native libraries and its runtime record retains nativeDistributionRevision: '1.30.0-directml-reshape-1'. See ONNX runtime preparation for selection, release-asset acquisition and existing-installation behavior. Its modulePath resolves against the artifact root just as a server runtime's executable does. Library/module records need no executable. The generated Core service exposes retained worker sessions through onnx.load/run/unload/status; model files remain caller-selected application resources. See native ONNX sessions.

Selecting ONNX, image generation, or both composes one shared createModelAssetService from arcane-os/core/model-assets. Native application services obtain that same definition with await context.getService('model-assets'). They obtain the selected local-AI owner with await context.getService('local-ai') and its current native ONNX handle with getONNXRuntime(). These lookups await only their selected service startup, independently of renderer readiness. See native service ownership and cleanup.

Selected stable-diffusion.cpp additionally composes createLocalImageService from arcane-os/core/image. The image factory receives the full authored selection plus that same model-assets instance. Library, Koffi binding and variant paths resolve from the artifact root; ordinary browser imports use arcane-os/ai/core-image and arcane-os/ai/core-model-assets through the managed import map.

The runtime bundle preserves the complete selected native distribution and binding tree. It does not load or download a model. Original model URL descriptors remain available for preparation after launch; runtime-contained model paths relocate with the artifact. An external native working path reports its portability incompatibility. Supply those files through the application's native resources or the shared stored-model projection. Native use retains that projection until actual unload or shutdown completes. See local image generation for selection, complete results and CPU/Metal platform boundaries.

Injected native speech engines

Selecting whisper.cpp in native.localAI.runtimes composes the SDK's retained Whisper engine as STT in the shared speech service. Portable assembly retains the selected helper, runtime variants and their libraries, FFmpeg decoder and configured model files under the app's native runtime tree. The executable host, helper, decoder and models remain separate files; selecting speech does not embed them inside one application executable. Runtime paths relocate with the payload. The default Windows x64 preparation uses the matching SDK release's helper plus its MSVC/OpenMP redistributable closure. Other platforms require their matching helper, runtime variants and decoder; the public helper builder uses matching Whisper headers/libraries with the caller's native toolchain. See native Whisper preparation and packaging. Assembly is distinct from execution on the target host.

An application-owned service factory can return createSpeechService({stt,tts,signal}) from arcane-os/core/speech, supplying either role or both. Include that factory through the existing native.services and package.nativeResources composition above. The host constructs its selected engines; this shared service installs no engine, downloads no model and selects no browser or native speech default.

Core starts both configured role loads independently and remains responsive. speech.status and replaying service subscriptions report actual loaded-model state separately for transcription and synthesis. Complete requests/results are forwarded unchanged. Request cancellation reaches its engine independently of the service lifetime; close/drain abort and join both engines and their pending work. Engines must settle cancelled inference before Core reaches service cleanup. See the engine interface, progress and shutdown contract.

Windows executable

The ordinary application command is:

sh
npm exec -- arcane build --target windows-x64

loadArcaneNativeProvider({target:'windows-x64'}) selects the SDK's own arcane-os/native/windows-provider without an OS checkout or arcaneRoot. Its default export and arcaneNativeBuilderProvider are the same provider; createWindowsNativeProvider({services,hostDirectory}) permits explicit same-project composition. Service selection follows the portable provider's descriptor/override contract above.

The provider assembles the selected app with the precompiled host asset arcane-native-windows-x64.tar.gz from the numeric GitHub release matching the builder's installed SDK version. The asset contains the launcher, WebView2 support libraries, Node SEA Core executable and required license files. It does not select the newest host independently of that package. Downloads occur only during build(), use the caller's output directory, and are reused from <outputRoot>/.arcane-native-hosts/<sdk-version>/windows-x64. The build machine needs the standard tar command for extraction; application builds do not need a C# compiler, system Node installation for the generated executable, or host source scripts. The SDK build command itself still runs through the application's normal Node/npm toolchain.

An explicit hostDirectory selects an already-built complete host tree instead of downloading one. This is useful for SDK collaborators verifying the next host output; downstream applications use the published package and matching release asset. doctor() reports the selected prerequisites and missing files; it does not download the host or probe the installed WebView runtime.

The assembled directory contains the app-named launcher, its matching .exe.config, the unchanged app/ payload, Core runtime and semantic arcane-native.json. manifest.host.executable is the authoritative relative launcher name for shortcuts and direct launch. Assembly uses appDescriptor.displayName plus .exe, such as Moon Cheese Hotline.exe; low-level compositions without a nonempty string display name use the app ID. Windows-reserved filename characters and controls become underscores, and reserved DOS device basenames receive an underscore prefix. Supported Unicode, case and spaces remain; the original descriptor and title are unchanged. The reusable host archive/cache retains Arcane.exe and Arcane.exe.config; the internal runtime/ArcaneCore.exe also keeps its original name.

Launch the manifest-selected executable on Windows x64 with the Microsoft Edge WebView2 Runtime installed. It opens the selected app in WebView2 and owns its framed connection to the bundled Core process. verify() observes the assembled files and manifest; it does not execute the window or prove application behavior. run() reads the saved manifest and launches that executable, so existing generic-name artifacts remain usable. It observes complete diagnostics and exit. Cancelling that operation closes its input, allowing the window and accepted Core service work to drain without a forced process termination.

Initial native window size

An application can select its initial client area, resizing behavior and state in arcane-app.json, independently of its page layout:

json
{
  "native": {
    "type": "app",
    "icon": "img/icon.png",
    "order": 100,
    "bundledApps": [],
    "window": {"width": 1280, "height": 800, "resizable": true, "state": "normal"}
  }
}

This is the native portion of the existing app descriptor. Each window member is optional. width and height are positive integral client dimensions in logical pixels at 96 DPI; resizable is a boolean. Optional state is normal, maximized, or fullscreen. These example dimensions are an explicit application choice, not SDK-wide defaults. Omitting a member retains the host's existing choice, and omitting window preserves existing startup. The native registry projection preserves the record; the browser package projection gains no window controls. Portable and Windows assembly copy the record to arcane-native.json as window, without changing page or service content.

The matching Windows host applies the dimensions once on initial load, using the display DPI and fitting the outer window within the selected monitor's usable work area. This fit does not impose a persistent maximum size. Explicit resizable:false uses a fixed frame and disables the maximize control; true permits ordinary resizing and maximizing. The selected initial state is applied after this normal-window sizing, so those normal bounds remain available when returning from maximized or fullscreen. Fullscreen uses a borderless window at the selected monitor's full bounds, including the taskbar area, without setting TopMost or requesting activation. Returning to normal restores the retained normal bounds and original frame, minimize/maximize and control-box settings. Selecting maximized from fullscreen restores them before maximizing.

Later app/user resizing remains untouched. A composing launcher's Load handler runs after the initial sizing and optional state selection. Applications can read or change their own window through Arcane.window.state() and setState({state}). The application's saved Settings and their application remain application-owned; the SDK adds no saved-state or saved-bounds store and does not delay rendering for those settings. The window's profile, origin and Core lifetime are unchanged.

Direct Windows host composition uses nullable ArcaneHostOptions members InitialClientWidth, InitialClientHeight, Resizable, and optional string InitialWindowState with the same semantics. Older precompiled Windows hosts do not read these options; use the matching SDK host archive when rebuilding an application. The portable record is available to platform adapters; this addition implements its Windows host behavior and leaves window sizing/state on other platforms unchanged. The current other-platform hosts do not implement this state selection.

Application icon and window colors

appDescriptor.native.icon selects the application's existing app-relative image. Windows assembly converts PNG images, including a 512-pixel source, into a derived runtime/arcane-app.ico with 16, 32, 48 and 256-pixel images. Aspect ratio and transparency are preserved. An ICO selection keeps its authored image set. The original application asset remains unchanged.

The provider embeds the selected images in the copied app-named executable and records manifest.host.icon and manifest.host.executableIcon:true. It replaces the executable's main icon group while preserving other groups, languages, resources and file content. Conversion uses Node standard capabilities on every assembly platform; it adds no application compiler, image library or Windows-only build step. The selected host cache and Core executable remain unchanged.

JPEG/JPG, WebP and other existing descriptor formats retain ordinary assembly. They currently report native.icon.unsupported through the builder's onEvent callback, as does a selected executable whose PE layout cannot accommodate the derived icon. The event identifies the app, icon, target, code and complete message; manifest.host.executableIcon:false reports that the executable keeps its prior icon. host.icon then points to the original selected image. This is an explicit unsupported branding result, not a claim that a generic icon was replaced. Unreadable files and malformed supported image data report their ordinary filesystem or image-parser error.

The matching Windows launcher reads host.icon and loads it independently of Core and page startup. ICO and standard Windows image codecs supply the window and taskbar icon; JPEG/JPG can therefore work at runtime even while executable embedding is unsupported. WebP is outside the host's standard image codecs; runtime support is not promised. A runtime icon error preserves ordinary startup and reaches complete developer diagnostics as ARCANE_WINDOW_ICON_UNAVAILABLE. Hosts built before this option was introduced do not read the new icon field.

Arcane.window.setTheme(presentation,{signal}?) changes only the requesting native window's chrome. The shared ThemeBootstrap supplies computed application background and text colors without delaying application CSS or rendering. Direct callers may supply backgroundColor and textColor, each either {red,green,blue,alpha} or null to restore the platform default. RGB channels are finite numbers from 0 through 255 and alpha is from 0 through 1. Omitted fields remain unchanged. The complete presentation follows the existing Core request transport; the window host handles window.setTheme before forwarding other methods to Core. No system appearance setting changes.

Windows 11 build 22000 and later support titlebar background and text colors through DWM. The normal titlebar and window controls remain in place. DWM accepts opaque colors, so alpha values other than exactly 1 report that field as unsupported. RGB values round to the nearest integer, with midpoint values rounded upward. Older Windows versions report the requested fields unsupported.

The result is {platform:'windows',supported,applied,unsupported}. applied contains only fields accepted during that call, including rounded channels or null for a successful default reset. supported is true when at least one field applied; unsupported lists requested fields the platform could not apply. These results report platform acceptance rather than measured pixels. An invalid argument uses INVALID_ARGUMENT; a platform API failure uses ARCANE_WINDOW_THEME_FAILED, preserving complete native details and any earlier changes in error.details.applied and error.details.unsupported. The call is not atomic across fields. Existing request cancellation and document lifetime semantics remain in effect; an already accepted native color change is not rolled back by later cancellation. See the Core client for the facade and browser behavior.

Launch configuration

For explicit user-selected workspace/state locations, launch:

sh
"Moon Cheese Hotline.exe" --arcane-launch-config path/to/launch.json

The complete JSON object reaches the service factories as their second argument. A supplied stateRoot also owns the WebView2 profile and diagnostic directory. Otherwise the launcher uses the current user's local application data under Arcane/<app-id>. It forwards that actual host selection through --arcane-host-state-root, so ordinary double-click startup supplies stateRoot to every Core service factory. The complete explicit launch context still takes precedence without rewriting its fields. The launcher does not invent an application workspace. Relative launch-file paths resolve from the invocation's working directory. --close-on-stdin-eof is an explicit process-owner option used by the SDK runner; an ordinary double-clicked window closes through its own UI.

Windows host source and selected output

The SDK's src/core/hosts/windows/ArcaneCoreProcess.cs owns an explicitly selected executable, argument string and working directory. It reads complete framed Core messages and diagnostics, acknowledges ordered asynchronous writes, and closes stdin before awaiting accepted Core work, final output and process exit. It does not kill the child on an ordinary close.

ArcaneHost.cs composes that process with a WebView2 window through ArcaneHost.Run(options,onDiagnostic,onError) or ArcaneHostForm. Options select the app root/start path, stable virtual HTTPS origin, profile directory, title, optional IconPath, canonical classic-client source and Core process inputs. The form exposes Ready, Completion and CloseAsync(). Page navigation, transport connection, Core readiness and service readiness are separate states. Accepted work drains before the window closes; complete engineering errors go to the supplied diagnostic/error callbacks. Callbacks must not synchronously wait for the window's lifetime promises.

A WebView2 BrowserProcessExited failure retains its complete diagnostic, fails a pending Ready, and begins the existing Core drain and window close. It does not wait for a user to close a blank window or invoke document callbacks and script cleanup in the closed browser. The launcher reports the failure after that drain. Main-renderer exits and Core-only transport failures retain their existing handling. This terminal-flow correction does not identify or prevent the underlying browser-process exit, restart the application, or change its profile and saved data.

ArcaneLauncher.cs supplies the executable entry point for that composition. An explicit native.launchContext.appControl.endpoint enables the current Windows window's background document-control listener. The public client and CLI inspect, capture and act on that existing WebView without selecting a desktop window or changing its Core/profile. See native application control for launch configuration, document generations, cancellation and platform scope. tools/build-core-windows-host.mjs builds the selected SDK host output from the approved WebView2 package, existing .NET Framework compiler and selected Node SEA output. It retains complete compiler diagnostics and produces a fresh directory rather than overwriting an earlier artifact. The host requires .NET Framework 4.6.2 or later. This is SDK release tooling, not an application build requirement. Source compilation, selected host execution and real application behavior are separate evidence boundaries; an assembled app is not an installer or proof of its domain behavior.

Linux, macOS, Android dispatch and each platform's output verification retain their separate delivery boundaries. Published consumers must adopt the numeric package that includes this increment; a source checkout or a successful source push is not that public package authority.

macOS process source

src/core/hosts/macos/ArcaneCoreProcess.h and .m provide a Foundation-only adapter for macOS 11 or later, compiled with Objective-C ARC and blocks. A composing host explicitly supplies the executable URL, arguments, working directory and delegate; startup is asynchronous. Delegate callbacks deliver every raw stdout/stderr chunk and complete opaque framed JSON bodies, plus launch, error, exit and final completion observations. Each stream and accepted write retains its own order; UI dispatch belongs to the host. Callbacks must return normally and must not wait for another callback or completion.

sendJSONData:completion:error: accepts an ordered write or reports a synchronous rejection. Write completion means the frame reached stdin, not that the RPC succeeded. closeInput rejects new sends, finishes accepted writes and closes stdin, then keeps observing output and child exit without a forced termination deadline. The delegate is retained through final completion. The AppKit ArcaneHost and launcher compose that source as described below. Compilation and execution on macOS remain separate evidence from the selected Windows output.

macOS application composition

arcane-os/native/macos-provider exports createMacOSNativeProvider({services,hostDirectory}={}), its named arcaneNativeBuilderProvider, and the same provider as default. The six-method contract accepts macos-arm64 or macos-x64, platform:'macos', format:'app' and the existing unsigned local development selection. Ordinary CLI routing uses the SDK package, not an Arcane OS checkout:

sh
npm exec -- arcane build --target macos-arm64
npm exec -- arcane run --target macos-arm64

Select macos-x64 for Intel. The descriptor must declare the selected target. Source implementation does not establish that an architecture's host artifact has been built, executed or published. Consumer assembly needs the exact numeric SDK release asset arcane-native-macos-<architecture>.tar.gz; unavailable assets produce their actual download error. SDK collaborators may select an existing hostDirectory explicitly. There is no fallback to source checkout, another version, another architecture or a browser-only artifact.

The complete unchanged portable payload lives at <outputRoot>/<app-id>.app/Contents/Resources/. That directory remains target.rootDir and contains arcane-native.json, app/, dependency releases, installed SDK files, Core entry/client and runtime/node. The additive bundleRoot identifies the .app directory. Contents/MacOS/Arcane and Contents/Info.plist belong to the bundle rather than the web root; the Mac owner checks these separately from the Resources file inventory. Application identity supplies the stable bundle ID org.arcane.<app-id>. Executable permissions are preserved for the launcher and selected Node runtime.

The launcher opens its window immediately and starts one Core child. The generated Core entry starts the packaged-web service alongside application services, without waiting for models. Only core.web.ready, emitted after the actual listener is ready and the initial port is saved, supplies the application URL to WebKit. It serves the complete manifest.webRoot at / and resolves the complete manifest.start, including query and fragment, against that origin. The classic client selects current-document activation and sticky state replay; navigation does not restart the Core service or replay retired requests. The host's main-thread readonly isClosing accessor exposes its existing close intent; an unsolicited Core exit is therefore reported separately from an ordinary user-requested drain, even when the process exits successfully.

createPackagedWebService({artifactRoot}, context={}) is available from arcane-os/core/packaged-web. It uses the existing startDevServer({mode:'packaged',http:true,host:'127.0.0.1',port,releaseRoot}) owner and published node-http-server, not a second HTTP implementation. context.stateRoot takes precedence over the launcher's separate --arcane-host-state-root argument. The launcher defaults to ~/Library/Application Support/Arcane/<app-id>; an explicitly supplied launch file remains unchanged and is forwarded to Core. The shared launch reader also supplies that host default to every application service factory, with explicit launch fields taking precedence. The first successful listener stores its selected port in packaged-web-origin.json. Later launches reuse it; a port conflict or unreadable saved record is reported without changing origins or discarding stored state.

The app's persistent default WKWebsiteDataStore uses its bundle identity. stateRoot selects Core state and diagnostics, not a custom WebKit profile directory. Browser API availability, OPFS and secure-context behavior remain WebKit-owned and require actual Mac verification; the SDK bypasses no browser policy. Startup failures use core.service.state for name:'packaged-web'; unexpected listener closure or failure uses core.web.failed with the complete serialized error. Native alerts show a concise outcome, with full diagnostics outside the conversation. Core drains accepted work and the listener before the native close completes. The optional runner stdin-EOF signal uses that same path; ordinary double-click startup does not wait on stdin.

tools/build-core-macos-host.mjs is SDK host-release tooling, not an application compiler requirement. It accepts an explicit Darwin Node executable and its license, compiler path, arm64 or x64, and output directory. It uses Objective-C ARC/blocks and Apple's Foundation, AppKit and WebKit frameworks. The host source targets macOS 11; the selected Node runtime may require a newer macOS version. Its actual runtime/platform/architecture and the SDK Node engine requirement must agree. Compiler/runtime execution and redistribution belong to the separately authorized selected host output. This source increment runs none of them and claims no Mac build or execution result.