Installer & Deploy Pipeline

apps/installer (Windows NSIS installer + build script) and the root deploy/manifest/smoke scripts (scripts/make-manifest.mts, scripts/smoke.mts, scripts/dev-headers.mts, root package.json deploy pipeline, Cloudflare Pages _headers CSP files). Module: installer-deploy.

Contents 1. Overview 2. Architecture 3. Data flow 4. Sequences 5. Interconnections 6. Key files & functions 7. Invariants & gotchas

1Overview

This module is not part of the running product — it is the machinery that gets the product onto a customer's machine and the web apps onto the public internet. It has two independent halves that share one thing (the add-in manifest):

Both halves run on the developer/owner's machine (or CI, currently nobody's CI — this is manual per OPS.md), never inside the product itself. The installer is built by apps/installer/build-installers.ps1 which shells out to makensis; the resulting .exe is then manually copied into apps/website/ and deployed as a static asset alongside the marketing site (see §4b in OPS.md — this hop is not scripted, it's a manual copy + redeploy). The web pipeline runs via npm run deploy at the repo root, which chains manifest generation → build → four wrangler pages deploy calls (addin, viewer, website, and — not wired into the top-level deploy script — admin/team have their own deploy:admin / deploy:team commands) → npm run smoke.

A second, parallel "dev" pipeline (deploy:addin:dev, deploy:team:dev, deploy:admin:dev) builds against .env.dev/dev Supabase and rewrites the built _headers CSP to point at the dev Supabase project via scripts/dev-headers.mts, so a tester can sideload a distinct "ExcelRewind (DEV)" add-in (separate GUID) alongside the production one without collision.

2Architecture diagram

flowchart TB
  subgraph WIN["Windows installer build (apps/installer/)"]
    BPS["build-installers.ps1"]
    NSI["ExcelRewind-Setup.nsi\n(NSIS/MUI2 script)"]
    MAKENSIS["makensis.exe\n(NSIS compiler, external tool)"]
    SETUPEXE["dist/ExcelRewind-Setup.exe\n(~10-23 MB GUI installer)"]
    BPS -->|"stages payload, reads companion version"| NSI
    BPS -->|"invokes /DDIST /DCOMPANION_VERSION /DEDITION"| MAKENSIS
    NSI --> MAKENSIS
    MAKENSIS --> SETUPEXE
  end

  subgraph PAYLOAD["Staged payload (apps/installer/dist/)"]
    MANIFESTPROD["manifest.prod.xml\n(copied from apps/addin-excel/public/)"]
    COMPFOLDER["companion/\n(Nuitka standalone folder\nor PyInstaller onefile exe)"]
  end
  BPS -->|"copies"| MANIFESTPROD
  BPS -->|"builds if missing via\nbuild-nuitka.ps1 / build.ps1"| COMPFOLDER

  subgraph NSISLOGIC["ExcelRewind-Setup.nsi internals"]
    SECADDIN["Section 'ExcelRewind Add-in'\n(SEC_ADDIN)"]
    SECCOMP["Section 'ExcelRewind Companion'\n(SEC_COMPANION)"]
    REGCAT["Function RegisterCatalog"]
    STOPCOMP["Function StopCompanion / un.StopCompanion"]
    SENDTEL["Function SendTelemetry / un.SendTelemetry"]
    JSONSAN["Function JsonSanitize / un.JsonSanitize"]
    FINALIZE["Section '-Finalize'\n(writes uninstall.exe + ARP entry)"]
    OPTPAGE["OptionsPageCreate/Leave\n(Start-Menu + autostart checkboxes)"]
    SURVEYPAGE["un.SurveyPageCreate/Leave\n(uninstall reason survey)"]
    UNINSTALL["Section 'Uninstall'"]
    SECADDIN --> REGCAT
    SECCOMP --> STOPCOMP
    SECCOMP --> SENDTEL
    SENDTEL --> JSONSAN
    UNINSTALL --> STOPCOMP
    UNINSTALL --> SENDTEL
    UNINSTALL --> SURVEYPAGE
  end

  subgraph LEGACY["apps/installer/troubleshoot/ (retired, kept for reference)"]
    REPAIRPS1["repair-excelrewind.ps1"]
    REPAIRBAT["Repair ExcelRewind.bat"]
  end

  subgraph WEBDEPLOY["Root deploy pipeline (npm run deploy)"]
    MKMAN["scripts/make-manifest.mts"]
    BUILD["npm run build\n(vite build, all workspaces)"]
    DEPLOYADDIN["wrangler pages deploy\napps/addin-excel/dist\n(project: excelrewind-addin)"]
    DEPLOYVIEWER["wrangler pages deploy\napps/viewer/dist\n(project: excelrewind-viewer)"]
    DEPLOYWWW["wrangler pages deploy\napps/website\n(project: excelrewind-website)"]
    SMOKE["scripts/smoke.mts"]
    MKMAN --> BUILD --> DEPLOYADDIN --> DEPLOYVIEWER --> DEPLOYWWW --> SMOKE
  end

  subgraph DEVDEPLOY["Dev-surface deploy (deploy:*:dev)"]
    DEVHEADERS["scripts/dev-headers.mts\n(rewrites dist/_headers CSP\nprod Supabase -> dev Supabase)"]
    DEVDEPLOYADDIN["wrangler pages deploy ...-dev"]
    DEVHEADERS --> DEVDEPLOYADDIN
  end

  subgraph HEADERS["CSP source (apps/*/public/_headers, per app)"]
    HADDIN["addin-excel/public/_headers"]
    HVIEWER["viewer/public/_headers"]
    HADMIN["admin/public/_headers"]
    HTEAM["team/public/_headers"]
  end
  HADDIN -.->|"copied verbatim by vite build into dist/_headers"| DEPLOYADDIN
  HADDIN -.-> DEVHEADERS

  MANIFESTPUB["apps/addin-excel/public/manifest.prod.xml\n(source, written by make-manifest.mts)"]
  MKMAN -->|"writes"| MANIFESTPUB
  MANIFESTPUB -.->|"Copy-Item, staged into the\ninstaller payload by build-installers.ps1"| MANIFESTPROD
  MKMAN -->|"writes"| INSTALLHTML["apps/addin-excel/public/install.html"]

  SETUPEXE -.->|"manual copy step (OPS.md #4b, not scripted):\napps/installer/dist/ExcelRewind-Setup.exe ->\napps/website/ExcelRewind-Setup.exe"| WEBSITEEXE["apps/website/ExcelRewind-Setup.exe"]
  WEBSITEEXE --> DEPLOYWWW

3Data-flow diagrams

3a. Windows installer — what gets written where

flowchart LR
  DEV["Developer machine\n(build-installers.ps1)"] -->|"stages files"| DISTDIR["apps/installer/dist/\n(manifest.prod.xml, companion/*)"]
  DISTDIR -->|"File / File /r\n(NSIS payload embed)"| EXE["ExcelRewind-Setup.exe"]

  EXE -->|"double-click, runs elevated"| USERPC["End-user Windows PC"]

  subgraph USERPC_INTERNAL["Writes on the end-user PC"]
    ADDINDIR["%LOCALAPPDATA%\\RewindAddin\\\nrewind-manifest.xml"]
    SMBSHARE["SMB share \\\\HOST\\RewindAddin\n(net share, Everyone/Read)"]
    CATALOGREG["HKCU\\...\\Office\\16.0\\WEF\\\nTrustedCatalogs\\{guid}\nId / Url / Flags=3"]
    WEFCACHE["%LOCALAPPDATA%\\Microsoft\\Office\\16.0\\Wef\\\nAddinInfo + AppCommands\n(cleared, not written)"]
    COMPDIR["%LOCALAPPDATA%\\Programs\\\nExcelRewindCompanion\\\n(Nuitka standalone folder)"]
    RUNKEY["HKCU\\...\\Run\\ExcelRewindCompanion\n(optional autostart)"]
    ARPKEYS["HKCU\\...\\Uninstall\\ExcelRewind\nComp_Addin / Comp_Companion flags"]
    EDITIONKEY["HKCU\\Software\\ExcelRewind\nEdition / InstallDate"]
  end
  USERPC --> ADDINDIR
  USERPC --> SMBSHARE
  USERPC --> CATALOGREG
  USERPC --> WEFCACHE
  USERPC --> COMPDIR
  USERPC --> RUNKEY
  USERPC --> ARPKEYS
  USERPC --> EDITIONKEY

  SMBSHARE -.->|"UNC \\\\host\\RewindAddin\\rewind-manifest.xml\nExcel fetches this XML"| EXCELCLIENT["Excel Desktop\n(Insert > Add-ins > Shared Folder)"]
  CATALOGREG -.->|"tells Excel which UNC\nto trust as a catalog"| EXCELCLIENT

  COMPDIR -->|"launched via explorer.exe\n(non-elevated re-parent)"| COMPANIONPROC["ExcelRewindCompanion.exe\n(tray process)"]
  COMPANIONPROC -.->|"HTTP loopback, JSON,\nports 38743-38747"| ADDINPANE["Add-in task pane\n(companion.ts)"]

  EXE -->|"install/uninstall event, JSON POST\n(hidden PowerShell Invoke-RestMethod)"| TELEMETRY["Supabase Edge Function\nuninstall-feedback\n(NOT YET DEPLOYED - 404s harmlessly)"]

3b. Web deploy pipeline — manifest/build/deploy artifact flow

flowchart LR
  ENVFILE[".env (real secrets)\nor .env.example (fallback origins)"] --> MKMAN["make-manifest.mts"]
  ADDINXML["apps/addin-excel/manifest.xml\n(source template, localhost GUID)"] --> MKMAN
  PKGJSON["package.json version"] --> MKMAN
  MKMAN -->|"GUID swap, DisplayName,\nVersion, AppDomain rewrite"| MANIFESTOUT["apps/addin-excel/public/manifest.prod.xml\n(or manifest.dev.xml with --dev)"]
  TEMPLATEHTML["scripts/templates/install.html"] --> MKMAN
  MKMAN -->|"__ADDIN_ORIGIN__ / __VIEWER_ORIGIN__\ntoken substitution"| INSTALLHTMLOUT["apps/addin-excel/public/install.html"]

  MANIFESTOUT -->|"vite build bundles into dist/"| VITEBUILD["npm run build\n(all workspaces)"]
  INSTALLHTMLOUT --> VITEBUILD
  HEADERSSRC["apps/*/public/_headers"] -->|"copied verbatim"| VITEBUILD
  VITEBUILD --> DISTDIRS["apps/*/dist/"]

  DISTDIRS -->|"wrangler pages deploy\n--branch master"| CFPAGES["Cloudflare Pages\n(4-5 separate projects)"]
  CFPAGES --> LIVEORIGINS["addin.excelrewind.com\nviewer.excelrewind.com\nexcelrewind.com (website)\nadmin.excelrewind.com\nteam.excelrewind.com"]

  LIVEORIGINS -->|"plain fetch() over HTTPS"| SMOKETEST["smoke.mts"]
  SMOKETEST -->|"checks: 200 status, BUILD_TAG present,\nmanifest GUID/origin correct,\nsample.rewind ZIP magic"| RESULT["exit code = failure count"]

4Process / sequence diagrams

4a. Building & shipping the Windows installer end-to-end

sequenceDiagram
  participant Dev as Developer
  participant BPS as "build-installers.ps1"
  participant Manifest as "make-manifest.mts"
  participant Nuitka as "build-nuitka.ps1"
  participant NSIS as "makensis.exe"
  participant Website as "apps/website/ (manual copy)"
  participant CF as "Cloudflare Pages"

  Dev->>BPS: .\build-installers.ps1 -Edition Free
  BPS->>BPS: locate makensis (PATH or Program Files)
  alt manifest.prod.xml missing
    BPS->>Manifest: npm run manifest (or env vars from .env.example)
    Manifest-->>BPS: apps/addin-excel/public/manifest.prod.xml
  end
  BPS->>BPS: verify staged manifest contains addin.excelrewind.com,\nNOT localhost (hard-fail otherwise)
  alt Nuitka companion build missing
    BPS->>Nuitka: build-nuitka.ps1 (~16 min, native compile)
    Nuitka-->>BPS: dist-nuitka/run_companion.dist/ExcelRewindCompanion.exe
  end
  BPS->>BPS: stage companion/ folder + read version.py
  BPS->>NSIS: makensis /DDIST /DCOMPANION_VERSION /DEDITION ExcelRewind-Setup.nsi
  NSIS-->>BPS: dist/ExcelRewind-Setup.exe
  Dev->>Website: copy apps\installer\dist\ExcelRewind-Setup.exe apps\website\
  Dev->>Manifest: npm run manifest (regenerate install.html)
  Dev->>CF: wrangler pages deploy apps/website --branch master
  Dev->>CF: wrangler pages deploy apps/addin-excel/dist --branch master
  Note over Dev,CF: manual, undocumented-in-code hop (OPS.md #4b) —\nnot part of npm run deploy

4b. Installer runtime — Add-in section (trusted-catalog sideload)

sequenceDiagram
  participant User as End user
  participant Setup as "ExcelRewind-Setup.exe (elevated)"
  participant FS as "%LOCALAPPDATA%\\RewindAddin"
  participant SMB as "Windows SMB (net share)"
  participant Reg as "HKCU TrustedCatalogs"
  participant Excel as "Excel Desktop"

  User->>Setup: double-click, click through wizard
  Setup->>FS: File /oname=rewind-manifest.xml manifest.prod.xml
  Setup->>SMB: net share RewindAddin /DELETE /Y (idempotent cleanup)
  Setup->>SMB: net share RewindAddin="%LOCALAPPDATA%\RewindAddin"
  alt share creation fails (code != 0)
    Setup-->>User: MessageBox: offer manual install page\n(#manual-install, LanmanServer hint)
  end
  Setup->>Reg: RegisterCatalog — scan existing catalogs for matching Url
  alt existing catalog found
    Setup->>Reg: WriteRegDWORD Flags=3 (enabled + Show-in-Menu)
  else no match
    Setup->>Setup: CoCreateGuid (ole32, no PowerShell)
    Setup->>Reg: write Id/Url/Flags=3 under new {guid} subkey
  end
  Setup->>SMB: IfFileExists \\host\RewindAddin\rewind-manifest.xml
  alt UNC not reachable
    Setup-->>User: MessageBox: offer manual install page
  end
  Setup->>FS: RMDir /r Wef\AddinInfo + Wef\AppCommands (cache clear)
  Setup->>Setup: WriteRegDWORD Comp_Addin=1 (drives uninstaller)
  Note over User,Excel: User closes/reopens Excel
  Excel->>SMB: fetch \\host\RewindAddin\rewind-manifest.xml
  Excel-->>User: "ExcelRewind Recorder" under Home > Add-ins > Shared Folder

4c. Installer runtime — Companion section, upgrade-safe stop/start

sequenceDiagram
  participant Setup as "ExcelRewind-Setup.exe"
  participant OldComp as "Running Companion (if any)"
  participant CompDir as "%LOCALAPPDATA%\\Programs\\ExcelRewindCompanion"
  participant Explorer as "explorer.exe"
  participant NewComp as "New Companion process"
  participant Tele as "uninstall-feedback Edge Function"

  Setup->>Setup: Call StopCompanion (before overwrite — avoids locked-file failure)
  loop ports 38743..38747
    Setup->>OldComp: hidden PowerShell POST http://127.0.0.1:PORT/shutdown
  end
  Setup->>Setup: Sleep 1500ms
  Setup->>OldComp: taskkill /IM ExcelRewindCompanion.exe (WM_CLOSE)
  Setup->>Setup: Sleep 1500ms
  Setup->>OldComp: taskkill /F /IM ExcelRewindCompanion.exe (force fallback)
  Setup->>CompDir: File /r dist\companion\*.* (Nuitka standalone tree)
  opt Start-Menu shortcut checked
    Setup->>CompDir: CreateShortcut
  end
  opt autostart checked
    Setup->>Setup: WriteRegStr HKCU Run\ExcelRewindCompanion
  end
  Setup->>Setup: WriteRegDWORD Comp_Companion=1
  Setup->>Tele: SendTelemetry("install", "", "", "") — best-effort JSON POST
  Note right of Tele: TELEMETRY_URL not yet deployed -> 404,\nwrapped in try/catch, silently ignored
  Setup->>Explorer: Exec explorer.exe "CompDir\ExcelRewindCompanion.exe"
  Explorer->>NewComp: re-parented to non-elevated user shell
  Note over NewComp: launched non-elevated deliberately —\nan elevated Companion cannot COM-attach\nto a normal-integrity Excel process

4d. Uninstall — survey, telemetry, reverse only what was installed

sequenceDiagram
  participant User as User
  participant Uninst as "uninstall.exe"
  participant Survey as "un.SurveyPageCreate/Leave"
  participant Comp as "Running Companion"
  participant Tele as "uninstall-feedback Edge Function"
  participant Reg as "Registry / filesystem"

  User->>Uninst: run uninstall.exe (from ARP or install dir)
  Uninst->>Reg: ReadRegDWORD Comp_Companion
  alt Companion was installed
    Uninst->>Survey: show survey page (radio reason + free text + optional contact)
    User->>Survey: fill in / skip, click Next
  else Companion not installed
    Uninst->>Uninst: Abort survey page (skip straight to confirm)
  end
  Uninst->>Uninst: MUI_UNPAGE_CONFIRM, MUI_UNPAGE_INSTFILES
  alt Comp_Companion == 1
    Uninst->>Comp: un.StopCompanion (same /shutdown -> taskkill ladder as 4c)
    Uninst->>Tele: un.SendTelemetry("uninstall", reason, detail, contact)
    Uninst->>Reg: delete shortcut, Run key, RMDir /r CompDir, delete ARP subkey
  end
  alt Comp_Addin == 1
    Uninst->>Reg: delete matching TrustedCatalogs entry, net share /DELETE,\ndelete manifest file + folder, clear Wef cache
  end
  Uninst->>Reg: delete legacy Troubleshooter dir + shortcut (older installs)
  Uninst->>Reg: delete uninstall.exe, ARP key, Edition key, install root
  Uninst-->>User: MessageBox "ExcelRewind has been removed"

4e. Post-deploy smoke test (last step of npm run deploy)

sequenceDiagram
  participant NPM as "npm run deploy"
  participant Smoke as "smoke.mts"
  participant Addin as "addin.excelrewind.com"
  participant Viewer as "viewer.excelrewind.com"
  participant WWW as "excelrewind.com"

  NPM->>Smoke: tsx scripts/smoke.mts (after all wrangler deploys)
  Smoke->>Smoke: load .env, require ADDIN_ORIGIN + VIEWER_ORIGIN
  Smoke->>Addin: GET / — expect 200 + HTML content
  Smoke->>Viewer: GET / — expect 200 + HTML content
  Smoke->>Addin: chase script-tag src attributes to find BUILD_TAG (may be in hashed JS bundle)
  Smoke->>Viewer: same BUILD_TAG chase
  Smoke->>Addin: GET /install.html — expect manifest.prod.xml link present
  Smoke->>Addin: GET /manifest.prod.xml — parse as XML, verify prod GUID (a4c8e6f0...),\nverify origin present + no localhost:3000
  Smoke->>Viewer: GET /sample-session.rewind — expect ZIP magic bytes "PK"
  opt EXCELREWIND_WWW_ORIGIN not explicitly empty
    Smoke->>WWW: GET / — expect disclaimer text + install.html link
    Smoke->>WWW: GET /privacy — expect 200
  end
  Smoke->>Smoke: validate local manifest.prod.xml via office-addin-manifest package
  Smoke-->>NPM: exit code = count of failed checks

5Interconnections

flowchart LR
  INSTALLER["Windows installer\n(ExcelRewind-Setup.exe)"]
  DEPLOYSCRIPTS["Deploy scripts\n(make-manifest / smoke / dev-headers)"]

  INSTALLER -->|"HTTPS POST JSON,\nvia hidden PowerShell Invoke-RestMethod"| EDGEFN["Supabase Edge Function\nuninstall-feedback\n(NOT deployed yet)"]
  INSTALLER -->|"net share, HKCU registry writes"| WINOS["Windows OS\n(SMB, WEF TrustedCatalogs, Run key, ARP)"]
  INSTALLER -->|"HTTP loopback POST /shutdown\nports 38743-38747"| COMPANION["ExcelRewindCompanion.exe\n(companion-win)"]
  INSTALLER -->|"launches via explorer.exe"| COMPANION

  DEPLOYSCRIPTS -->|"reads .env / .env.example"| ENVCONFIG["VITE_EXCELREWIND_ADDIN_ORIGIN\nVITE_EXCELREWIND_VIEWER_ORIGIN\nVITE_SUPABASE_URL"]
  DEPLOYSCRIPTS -->|"wrangler pages deploy\n(Cloudflare API, authenticated CLI)"| CFPAGES["Cloudflare Pages\n(5 projects: addin/viewer/website/admin/team)"]
  DEPLOYSCRIPTS -->|"plain fetch() HTTPS GET"| LIVEADDIN["addin.excelrewind.com"]
  DEPLOYSCRIPTS -->|"plain fetch() HTTPS GET"| LIVEVIEWER["viewer.excelrewind.com"]
  DEPLOYSCRIPTS -->|"plain fetch() HTTPS GET"| LIVEWWW["excelrewind.com"]
  DEPLOYSCRIPTS -->|"npm workspace import\noffice-addin-manifest"| VALIDATOR["office-addin-manifest\n(remote validation service)"]

  MANIFESTOUT["manifest.prod.xml\n(deployed to addin origin)"] -->|"AppDomain entries:\nADDIN_ORIGIN, SUPABASE_ORIGIN"| SUPABASE["Supabase project\ntohvepsjfeecjycspocg.supabase.co"]
  MANIFESTOUT -.->|"viewer origin deliberately\nNOT an AppDomain (system-browser handoff)"| VIEWERORIGIN["viewer.excelrewind.com"]
External systemProtocol / mechanismPurposeWhere in code
Supabase Edge Function uninstall-feedback HTTPS POST JSON, via hidden powershell -WindowStyle Hidden Invoke-RestMethod (best-effort, try/catch, 5s timeout) Anonymous install/uninstall telemetry + uninstall survey (reason, free text, optional contact) ExcelRewind-Setup.nsi SendTelemetry/un.SendTelemetry; URL = TELEMETRY_URL define. Function written but not deployed — POST 404s harmlessly today.
Companion (companion-win) HTTP loopback, JSON, ports 38743-38747, no bearer-token auth on /shutdown — but an Origin/Host CSRF guard (_origin_host_ok, added v3.19.13) rejects any request carrying a non-allow-listed Origin; the installer's headerless PowerShell POST sends no Origin header, so it still passes POST /shutdown to release the Companion's COM reference to Excel before an upgrade overwrites its files, or before uninstall ExcelRewind-Setup.nsi StopCompanion/un.StopCompanion
Windows OS — SMB (net share) Shell-out (nsExec::ExecToLog), local Windows service (LanmanServer) Serve the add-in manifest over UNC so Excel's Trusted Add-in Catalog can fetch it ExcelRewind-Setup.nsi Section "ExcelRewind Add-in"
Windows OS — registry (HKCU) Native NSIS registry ops Trusted-catalog registration (WEF\TrustedCatalogs), autostart (...\Run), Add/Remove Programs entries, edition marker ExcelRewind-Setup.nsi throughout
ole32.dll (CoCreateGuid) System::Call (NSIS System plugin, in-process COM call) Fresh GUID for a new trusted-catalog registry subkey, without shelling to PowerShell ExcelRewind-Setup.nsi macro NewGuid
Cloudflare Pages (5 projects: excelrewind-addin, excelrewind-viewer, excelrewind-website, excelrewind-admin, excelrewind-team — plus *-dev variants) wrangler pages deploy <dist> --project-name ... --branch master (Cloudflare API via authenticated CLI) Static hosting for every web surface; --branch master is required — the custom domain silently serves stale content if omitted (see memory cloudflare-pages-branch) root package.json deploy:* scripts
Live production origins (addin.excelrewind.com, viewer.excelrewind.com, excelrewind.com) Plain fetch() HTTPS GET, no auth Post-deploy smoke verification: 200 status, current BUILD_TAG, manifest GUID/ origin correctness, sample .rewind ZIP magic bytes present scripts/smoke.mts
office-addin-manifest npm package Node import, calls a remote Microsoft manifest-validation service Validates the locally staged manifest.prod.xml is well-formed per Office schema scripts/smoke.mts
Supabase project (tohvepsjfeecjycspocg.supabase.co prod / ragrgchidtdpgkyjkznu.supabase.co dev) Not called directly by this module — but its origin is baked into the generated manifest's AppDomain and every app's _headers CSP connect-src Lets the add-in/viewer/admin/team apps reach Supabase auth + RPCs from inside the CSP; dev builds get the origin swapped by dev-headers.mts scripts/make-manifest.mts, scripts/dev-headers.mts, apps/*/public/_headers
appsforoffice.microsoft.com (Office.js CDN) Baked into _headers CSP script-src/connect-src (not fetched by this module, but this module ships the CSP that allows it) Office.js runtime that the add-in loads inside the Excel task-pane iframe apps/addin-excel/public/_headers
makensis.exe (NSIS compiler, external tool, not npm-managed) Local process invocation (& $makensis /DDIST=... ExcelRewind-Setup.nsi) Compiles the .nsi script + staged payload into the single ExcelRewind-Setup.exe apps/installer/build-installers.ps1
Nuitka / PyInstaller build scripts (apps/companion-win/build-nuitka.ps1, build.ps1) Local process invocation (auto-triggered if the companion binary is missing) Produces the Companion binary staged into the installer apps/installer/build-installers.ps1

6Key functions & files

FileFunction / exportWhat it doesWhy it matters
apps/installer/ExcelRewind-Setup.nsi Section "ExcelRewind Add-in" (SEC_ADDIN) Copies the manifest, deletes+recreates the SMB share (delete-first fixes a field bug where a stale share with the right name but wrong path passed the old exists-check), registers the trusted catalog, verifies the UNC is actually reachable, clears the Wef cache The whole add-in registration path; every "add-in won't add" support case traces through here
apps/installer/ExcelRewind-Setup.nsi RegisterCatalog Scans existing TrustedCatalogs entries for a matching UNC Url; if found forces Flags=3, else creates a fresh-GUID subkey with Flags=3 Flags=1 registers but hides the add-in from the Shared Folder menu — this function exists specifically to correct that field bug on every run, making re-installation a repair
apps/installer/ExcelRewind-Setup.nsi Section "ExcelRewind Companion" (SEC_COMPANION) Stops any running Companion first (upgrade safety), stages the Nuitka standalone folder, writes optional Start-Menu/autostart, writes its own ARP entry, fires an install telemetry event, launches it via explorer.exe The explorer.exe launch is load-bearing: an elevated-launched Companion cannot COM-attach to a normal-integrity Excel process ("Excel not detected")
apps/installer/ExcelRewind-Setup.nsi StopCompanion / un.StopCompanion (macro DEF_StopCompanion) Three-stage shutdown: (a) hidden-PowerShell POST /shutdown to each of ports 38743-38747, (b) 1.5s wait then taskkill /IM (WM_CLOSE), (c) 1.5s wait then taskkill /F /IM Defined once via macro, instantiated for both installer and uninstaller contexts — prevents a locked-file File /r failure on upgrade and a ghost EXCEL.EXE on uninstall
apps/installer/ExcelRewind-Setup.nsi SendTelemetry / un.SendTelemetry (macro DEF_SendTelemetry) Builds a minimal JSON payload and POSTs it via a hidden, non-interactive PowerShell Invoke-RestMethod call; no-ops entirely if TELEMETRY_URL is still the REPLACE-ME sentinel Only network egress point in the installer besides the SMB share itself; wrapped in try/catch so a 404 (function not yet deployed) never blocks install/uninstall
apps/installer/ExcelRewind-Setup.nsi JsonSanitize / un.JsonSanitize Strips \, ", CR, LF from free-text survey fields via WordReplace Ponytail-flagged (crude but sufficient) — no full JSON encoder; only reason to trust the hand-built JSON string literal doesn't break
apps/installer/ExcelRewind-Setup.nsi Section "-Finalize" Always-run hidden section that writes the unified uninstall.exe and the main ARP entry, regardless of which components were selected Standard NSIS idiom for "always execute"; this is what makes one uninstaller cover both optional components
apps/installer/ExcelRewind-Setup.nsi Section "Uninstall" Reads Comp_Addin/Comp_Companion registry flags and reverses only what was actually installed; also unconditionally cleans up legacy Troubleshooter files from older installs Single uninstaller for a two-component product without a component-selection UI on uninstall
apps/installer/build-installers.ps1 top-level script Locates makensis, stages the manifest (hard-fails if it can't produce a non-localhost prod manifest), stages the Companion binary (builds it if missing, Nuitka by default), reads the Companion version, invokes makensis The two hard-fail guards (manifest must contain addin.excelrewind.com and must not contain localhost) are the only thing standing between a normal build and shipping a broken installer to customers
scripts/make-manifest.mts top-level script Loads .env (or .env.dev with --dev), swaps the placeholder GUID for a fixed prod/dev GUID, rewrites DisplayName, Version, and AppDomain entries, writes manifest.prod.xml/manifest.dev.xml and (prod only) install.html from a template Single source of truth for the three fixed GUIDs (SOURCE_GUID local sideload, PROD_GUID, DEV_GUID) — never change once testers have installed; deliberately emits no PowerShell so nothing scriptable is hosted on the public website
scripts/make-manifest.mts AppDomain rewrite Adds the add-in origin and Supabase origin as <AppDomain> entries, but explicitly does not add the viewer origin Deliberate: window.open to the viewer must leave the Office webview for the system browser, because Google blocks OAuth inside webviews
scripts/smoke.mts hasBuildTag Checks the served HTML for BUILD_TAG; if absent, chases every <script src> to check the hashed JS bundle too Catches a stale Cloudflare Pages deploy that served old HTML/JS — the whole reason BUILD_TAG exists in packages/branding
scripts/smoke.mts top-level checks Verifies addin/viewer 200+HTML, BUILD_TAG present, install.html links the manifest, manifest parses as XML with the prod GUID prefix and no localhost:3000, viewer serves a ZIP-magic sample-session.rewind, website serves its disclaimer + /privacy, local manifest validates via office-addin-manifest Last gate in npm run deploy; exit code equals the failure count so CI/manual runs can tell success from failure at a glance
scripts/dev-headers.mts top-level script Rewrites a built app's dist/_headers, replacing the prod Supabase origin string with the dev Supabase origin string — throws if the expected prod string isn't found (CSP shape drift guard) Never touches public/_headers (the source template); only mutates the post-build output for dev deploys, so prod builds are provably unaffected
apps/addin-excel/public/_headers CSP directives Defines the deployed CSP for the add-in origin: no frame-ancestors (Office hosts it in an iframe), connect-src allows Office.js CDN, Supabase, and the Companion loopback (http(s)://127.0.0.1:*), media-src 'self' blob: The loopback connect-src entry is the fix for a real regression (v3.20.0) where its absence made every Companion install read as "not installed" because the browser blocked the /status probe as a CSP violation
apps/viewer/public/_headers, admin/public/_headers, team/public/_headers CSP directives Each subdomain gets its own CSP scoped to what it actually needs (Supabase origin, frame-ancestors 'none' since these apps are never framed) plus X-Robots-Tag: noindex App subdomains are deliberately kept out of search; viewer additionally allows media-src blob: for replayed audio/video
root package.json deploy script npm run manifest && npm run build && deploy:addin && deploy:viewer && deploy:website && npm run smoke The canonical production deploy sequence; note deploy:admin and deploy:team are separate scripts not chained into this one — they must be run explicitly
root package.json deploy:* (addin/viewer/website/admin/team) Each is a direct wrangler pages deploy <dist> --project-name ... --commit-dirty=true --branch master invocation --branch master is required on every call — omitting it deploys a preview branch and the custom domain keeps serving the previous build (see memory cloudflare-pages-branch)

7Invariants & gotchas

Product-level constraints enforced here

Platform quirks this code specifically works around