AIDE Packaging SOP (offline, Windows)

SkillFiles & storage

Packaging SOP for the AIDE offline IDE, ship the workbench with its 3 GGUF models, fully offline, on Windows. Use whenever creating installers, deciding where model files live, verifying model integrity, or planning release/update mechanics.

Use AIDE Packaging SOP (offline, Windows) in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add AIDE Packaging SOP (offline, Windows) and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use the AIDE Packaging SOP (offline, Windows) skill

Details

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

AIDE Packaging SOP (offline, Windows)Start free

What this skill tells your AI

The instructions your AI receives, as published by anonymousnomad/covert-coder in skills/packs/aide-packaging-offline/SKILL.md and read by Ahel’s review.

Target: E:\aide-sovereign-workbench — browser frontend + Node daemon (port 4777) + 3 GGUF models, packaged as a Tauri v2 Windows desktop app that works with ZERO internet. Installers are verified with honest smoke tests (silent install → daemon API probes → uninstall). No browser is used in smoke tests (Edge headless networking is broken on this machine — daemon API probes only).

CURRENT VERIFIED STATE (2026-09-10)

  • desktop/prepare.mjs stages the UI in desktop/frontend and loose runtime/server resources in desktop/resources.
  • The loose resource tree contains the TS arch server, legacy daemon, facade, workbenches/, shared common/, stack launcher, Node runtime, and the four runtime package trees required by the staged server.
  • desktop/src/main.rs launches stack-launcher.mjs; the launcher starts TS on 4778, legacy on 4779, and the facade on 4777, with writable per-user workspace/model directories.
  • desktop/tauri.conf.json uses resources: {"resources/": ""}, targets: "nsis", and WebView2 offlineInstaller.
  • npm run desktop:verify with AIDE_REQUIRE_MODEL_RUNTIME=1 and AIDE_LLAMA_SERVER_BINARY=E:\llama-cpp\llama-server.exe passes. npm run desktop:staged-smoke passes through the facade on ephemeral ports.
  • Native Tauri compilation, installer install/uninstall, offline install, and model-pack/chat verification remain open because Rust is not installed on this machine.
  • Engine staging fact (verified this week): desktop/prepare.mjs:31 stages the engine directory from $env:AIDE_ENGINE_SOURCE (default E:\llama-cpp); if the source is absent it prints "skipping engine staging" and continues — a packaged app can then silently ship with NO model engine. Always set AIDE_ENGINE_SOURCE to a real llama.cpp build dir when preparing, and verify the staged copy exists before claiming a model-capable build (see desktop/verify-prepare.mjs).
  • Thin-exe fact (verified this week): E:\llama-cpp\llama-server.exe is a 9216-byte THIN LAUNCHER whose real payload lives in 29 sibling DLLs in the same directory. Any packaging step that copies only llama-server.exe will silently break model spawns — copy the whole engine directory (exe + DLLs) or preserve the sibling layout. Verify with desktop/verify-prepare.mjs + scripts/desktop-battery.mjs (12/12) before claiming a staged stack works.

1. Research base (verified from primary sources, Aug 2026)

TopicVerified factSource
bundle.resourcesFiles listed there are copied as loose files into $RESOURCE (on Windows NSIS/MSI = the install dir), preserving relative structure; directory entries copy recursively; object-map form controls target layout ("resources/": "" puts files at $RESOURCE root). frontendDist files are embedded into the exe — anything big in frontend/ bloats the binary and cannot be spawned as a file on disk.https://v2.tauri.app/develop/resources/
WebView2 install modesdownloadBootstrapper (default, needs internet, +0MB), embedBootstrapper (+~1.8MB, still needs internet), offlineInstaller (no internet, +~127MB), fixedVersion (+~180MB), skip (app breaks without runtime). Win10 (Apr 2018+)/Win11 ship the runtime in the OS; offlineInstaller still required for Win10 LTSC/Server.https://v2.tauri.app/distribute/windows-installer/
NSIS vs MSINSIS: multi-language single exe, installMode default currentUser → installs to %LOCALAPPDATA% with no admin/UAC, silent flag /S. MSI (WiX v3): only buildable on Windows, needs the VBSCRIPT optional feature, silent via msiexec /qn. Both have a ~2GB limit (NSIS: per-file and total packed data; WiX: single files <2GB). Our largest payload-in-installer file is node.exe (98.4MB) — no risk today; any future >2GB single file forces MSI-out or a companion pack.https://v2.tauri.app/distribute/windows-installer/ ; https://github.com/tauri-apps/tauri/issues/7372 ; https://nsis.sourceforge.io/Failure_when_compiling_a_big_installer
Sidecarsbundle.externalBin bundles an executable named name-<target-triple>[.exe] into the bundle, invoked via app.shell().sidecar(). For Node, either compile with @yao-pkg/pkg OR embed node.exe + JS as resources and spawn with std::process::Command (what desktop/src/main.rs does).https://v2.tauri.app/develop/sidecar/ ; https://v2.tauri.app/learn/sidecar-nodejs/
Model weights doctrineWeights live BESIDE the app (%LOCALAPPDATA%\<app>\models, Local NOT Roaming), never inside the signed installer if >~200MB. Ship SHA-256 in the manifest; verify on first run; refuse on mismatch with an exact message. Update = atomic swap: download .partial → hash while writing → rename (atomic on one FS) → smoke prompt → switch manifest → delete old dir.Established doctrine (see prior skill research)
Silent install/verify on Windows CINSIS /S, MSI msiexec /i <pkg> /qn /norestart /L*v <log>; find install via HKCU/HKLM\...\Uninstall\* DisplayName; verify the app by probing its localhost API, not by UI. Tauri MSI exit codes 0/3010 = success.https://v2.tauri.app/distribute/windows-installer/ ; observed in repo scripts/desktop-lifecycle-smoke.ps1

2. Packaging architecture decision (LOCKED)

  • Shell: Tauri v2 (@tauri-apps/cli ^2.11.4, Rust project at desktop/). desktop/src/main.rs spawns the embedded Node runtime and resources/stack-launcher.mjs; the launcher starts the TS arch server, legacy daemon, and facade on internal 4778/4779 plus user-facing 4777, with writable AIDE_WORKSPACE and AIDE_MODEL_DIR, and the shell tree-kills it on exit.
  • Daemon transport: Node daemon as an embedded runtime + resources (current design), NOT a pkg-compiled sidecar (avoid extra toolchain; main.rs already works).
  • Models: OUTSIDE the installer. Installed model home = %LOCALAPPDATA%\AIDE\models\. The installer NEVER contains .gguf files (CI builds without AIDE_INCLUDE_MODEL_WEIGHTS, enforced by desktop/verify-prepare.mjs which fails if a .gguf is staged).
  • WebView2: offlineInstaller (mandatory for the "zero internet" guarantee).
  • Installer kind: NSIS is the canonical Windows target (per-user, no UAC, silent /S — reliable in CI). MSI (WiX) stays as a secondary artifact; it is NOT used for CI smoke because per-machine elevation/MSI discovery was flaky in the journal.
  • Repo facts to respect:
    • models/manifest.json packs[] carry file + sha256: smollm2-360m-instruct-q8_0.gguf = 48ab3034…0201 (368.5MB), qwen2.5-coder-0.5b-instruct-q4_k_m.gguf = 1d961463…ff32 (468.6MB), qwen2.5-coder-1.5b-instruct-q4_k_m.gguf = cc324af0…b046 (1065.6MB). Total ~1.9GB. Daemon endpoint per model: smollm2 8082, qwen-0.5b 8083, qwen-1.5b 8087 (start_model_servers.ps1, models/manifest.json models[]).
    • Daemon env resolution (daemon/server.mjs:41-45): AIDE_HOME = daemon's parent dir; MODEL_DIR = $env:AIDE_MODEL_DIR | <AIDE_HOME>/models; MANIFEST = <AIDE_HOME>/models/manifest.json. AIDE_MODEL_DIR is the documented override — the packaging layer MUST pass it, otherwise the installed app looks for models inside the (read-only-ish) install dir.
    • Daemon API (all on 127.0.0.1:4777): GET /health → 200, GET /api/models → manifest models, GET /api/models/status → {models:[{id,status,artifact_available,…}]}, POST /api/chat {modelId,messages}. These are the smoke probes. No browser ever.

3. Build SOP (Windows)

3.1 Prereqs on the build machine

Rust MSVC toolchain + rustup target add x86_64-pc-windows-msvc, Node ≥20, and for MSI only: VBSCRIPT optional feature (Settings → Apps → Optional features). NSIS is fetched by the Tauri CLI automatically.

3.2 Historical audit (superseded by CURRENT VERIFIED STATE)

Current desktop/tauri.conf.json:

{
  "$schema": "https://schema.tauri.app/config/2",
  "productName": "AIDE Sovereign Workbench",
  "version": "0.1.0",
  "identifier": "org.ferrellsyntheticintelligence.aide",
  "build": { "frontendDist": "frontend", "devUrl": "http://127.0.0.1:4173" },
  "app": { "windows": [{"title": "AIDE Sovereign Workbench", "width": 1440, "height": 900, "minWidth": 900, "minHeight": 600}],
    "security": {"csp": "default-src 'self' http://127.0.0.1:4173 http://127.0.0.1:4777 http://127.0.0.1:*; connect-src 'self' http://127.0.0.1:*"} },
  "bundle": {"active": true, "targets": "all", "icon": ["icons/icon.png", "icons/icon.ico"], "resources": []}
}

Problems found in inspection:

  1. resources: [] is EMPTY — node.exe (98.4MB), daemon/, and the model manifest are currently staged under frontendDist → embedded into the exe, so resource_dir/runtime/node.exe does NOT exist as a real file in the built app and main.rs silently skips spawning the daemon. Fix: move runtime + daemon + manifest into bundle.resources (map form, to keep the exact paths main.rs expects):
"bundle": {
  "active": true,
  "targets": "nsis",             /* canonical; add "msi" later, never "all" on CI */
  "icon": ["icons/icon.png", "icons/icon.ico"],
  "resources": {
    "frontend/runtime/": "runtime/",
    "frontend/daemon/": "daemon/",
    "frontend/models/manifest.json": "models/manifest.json"
  },
  "windows": { "webviewInstallMode": { "type": "offlineInstaller" } }
}

(Paths in tauri.conf.json are relative to desktop/, so frontend/… = desktop/frontend/….) 2. desktop/prepare.mjs copies EVERY non-.gguf file from models/ — including the 401MB qwen2.5-coder-0.5b-instruct-q4_k_m.gguf.corrupt leftover. It was already staged into desktop/frontend/models/ (measured: staged frontend = 499.9MB, of which ~401MB is that corrupt file). [TODO] Delete models/qwen2.5-coder-0.5b-instruct-q4_k_m.gguf.corrupt (and from desktop/frontend/models/), or make prepare.mjs whitelist only *.gguf|*.json|*.md. 3. main.rs must pass the model home: .env("AIDE_MODEL_DIR", <model_dir>) where <model_dir> = %LOCALAPPDATA%\AIDE\models (app.path().app_local_data_dir()), created on first run. [TODO — currently only sets AIDE_WORKSPACE].

3.3 Commands

# from E:\aide-sovereign-workbench
npm ci
npm run desktop:verify          # prepare + verify-prepare + staged TS/legacy/facade smoke
npm run desktop:staged-smoke    # prepare + staged TS/legacy/facade smoke only
npm run desktop:build           # = desktop:prepare && tauri build --config desktop/tauri.conf.json
npm run desktop:smoke           # scripts/desktop-artifact-smoke.mjs: bundle exists, installers non-empty

Output lands in desktop/target/release/bundle/nsis/ (and …/msi/ if enabled). The NSIS installer embeds the ~127MB WebView2 offline installer → expect ~150–250MB installer; exe payload stays <200MB because models are excluded.

4. Models first-run SOP (fail-to-safe)

  1. Location: models live in %LOCALAPPDATA%\AIDE\models\ (Local, never Roaming, never Program Files). The shell passes this as AIDE_MODEL_DIR to the daemon (3.2.3); daemon resolves per daemon/server.mjs:44.
  2. Shipping: the release includes a companion archive aide-models-0.1.0.zip (3 × GGUF, ~1.9GB) BESIDE the installer (same download page). First run shows "Import model pack" → user points at the unpacked folder → provisioner copies into %LOCALAPPDATA%\AIDE\models\. Fully-offline: the archive is the payload, the installer is the app.
  3. Verify: on first run AND at every daemon start, for each packs[].file with a sha256 present in models/manifest.json: Get-FileHash -Algorithm SHA256 compare. Log MODEL_VERIFY: <file> OK|FAIL. Reference implementation exists as CLI: scripts/verify-model-bundle.mjs. [TODO] port it into the daemon (model-manager.mjs currently does NO hash verification — only existence checks, artifact_available).
  4. Fail-to-safe: any model whose hash mismatches or whose file is missing is reported status: unavailable with the EXACT message MODEL_VERIFY: <file> FAIL — hash mismatch (expected <sha256>, got <sha256>). The app still launches; only that model is disabled. Never half-load a model.
  5. Update/atomic swap (future model updates): download weights.gguf.partial into the new version dir → hash while writing → Rename-Item (atomic on one FS) → smoke prompt (fixed prompt, expected-shaped response) → switch manifest.json to the new version dir → delete old version dir only after the new one ran successfully. Rollback = one-line manifest change.
  6. Never re-sign the app to change models — the manifest rides in %LOCALAPPDATA%, not in the signed payload.

5. Installer smoke SOP (headless, no browser)

scripts/desktop-lifecycle-smoke.ps1 EXISTS and already does: locate bundle → pick installer (MSI first, NSIS fallback) → silent install → locate installed exe via Uninstall registry entries → launch → poll GET http://127.0.0.1:4777/health (200) → CloseMainWindow → same-build reinstall probe → uninstall → assert exe gone and daemon unreachable. Honest gaps:

  • [TODO] Model probe step: after /health 200, assert GET /api/models/status returns HTTP 200 with JSON {models:[…]} containing all 3 model ids, and POST /api/chat {modelId:"qwen-coder-1.5b-q4", messages:[{role:"user",content:"ping"}]} returns 200 with generated text (CPU load takes 30–90s; poll up to 5min like start_model_servers.ps1). No browser, no WebDriver — Edge headless is broken on this machine.
  • [TODO] scripts/desktop-installer-smoke.ps1 does NOT exist — create it as the CI-cheap variant: silent install → /health 200 → /api/models/status 200 with 3 models (pre-seeded model dir, or skip if none) → uninstall → /health unreachable. Runs in ~2min vs lifecycle's full reinstall dance.
  • Flags used (verified in script): NSIS /S; MSI msiexec /i <pkg> /qn /norestart /L*v <log> and /x <ProductCode> /qn /norestart. Exit codes 0/3010 = success; kill after 180s timeout.
  • Installer discovery (the historically flaky part): script scans 4 bundle roots (desktop/target/release/bundle, target/release/bundle, ± ..\) and prefers MSI. Mitigation: build "targets": "nsis" for CI (single artifact, deterministic discovery); check HKCU + HKLM + WOW6432Node uninstall keys and %LOCALAPPDATA%, %LOCALAPPDATA%\Programs, Program Files(x86).

6. Offline guarantee checklist (run on a network-disabled machine or with firewall blocking all egress)

  • Fresh install succeeds with NO internet (WebView2 installed from embedded offlineInstaller).
  • App launches; GET /health 200 with no network.
  • GET /api/models/status shows all 3 models; a POST /api/chat produces text on each.
  • MODEL_VERIFY lines all OK; deliberately corrupting one model file → that model disabled with exact message, app still runs.
  • netstat -ano | findstr 4777 shows only 127.0.0.1:4777 listener — daemon binds loopback only (HOST = '127.0.0.1').
  • No outbound connections at boot: with firewall egress blocked, the app boots and works identically (no telemetry, no update check, no CDN calls).
  • No .gguf present anywhere inside the installer payload (msiexec /a <pkg> /qn TARGETDIR=<dir> or 7-Zip on NSIS, then search).

7. CI note

.github/workflows/desktop.yml (exists): 3-OS matrix (ubuntu-22.04, macos-14, windows-2022) → npm ci → desktop:verify → desktop:build (weights intentionally excluded) → desktop:smoke → Windows: scripts/desktop-lifecycle-smoke.ps1 → upload desktop/target/release/bundle/**. Adjustments [TODO]:

  • Pin "targets": "nsis" on Windows CI to kill the MSI discovery flakiness; run the full lifecycle smoke locally on this machine (GTX 1060 laptop) per release and log evidence.
  • Windows CI cannot verify models (none shipped) — it verifies installer mechanics only; model verification is the local Windows run (sections 4–5).
  • desktop:build must run with AIDE_INCLUDE_MODEL_WEIGHTS unset — desktop/verify-prepare.mjs enforces this (fails if .gguf staged).

8. Audit checklist (before calling a release done)

  • desktop/tauri.conf.json: resources non-empty and contains runtime/, daemon/, models/manifest.json; webviewInstallMode: offlineInstaller; targets: nsis.
  • desktop/frontend/models/ contains NO .gguf and NO *.corrupt files; models/ tree has no junk (the 401MB .corrupt file is gone).
  • desktop/prepare.mjs + desktop/verify-prepare.mjs agree on what may be staged.
  • main.rs spawns daemon with AIDE_MODEL_DIR=%LOCALAPPDATA%\AIDE\models; daemon hash-verifies on start (MODEL_VERIFY lines).
  • scripts/desktop-installer-smoke.ps1 exists and passes: install → /health 200 → /api/models/status 200 with 3 models → chat returns text → uninstall → daemon gone.
  • Offline checklist (section 6) passes on a network-off machine.
  • Installer + model pack hashes recorded; smoke output logged (installer path, sizes, hashes, API results).

Sources

Signals

GitHub stars
43
Forks
14
Last commit
Oct 2026
Advanced
Item type
skill
Key
aide-packaging-offline
Source
github.com/anonymousnomad/covert-coder