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.
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):
apps/installer/) — a single NSIS-built
GUI executable, ExcelRewind-Setup.exe, that end users double-click. No PowerShell
window is ever shown to a user (a deliberate constraint — see §7). It installs two independent
components (Excel add-in sideload registration, and the Companion tray helper) and writes one
unified uninstaller.package.json deploy
script + scripts/*.mts) — generates the production add-in manifest and customer
install page from .env, builds every Vite app, pushes each to its own Cloudflare
Pages project via wrangler, and runs a post-deploy smoke test against the live
origins.
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.
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
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)"]
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"]
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
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
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
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"
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
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 system | Protocol / mechanism | Purpose | Where 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 |
| File | Function / export | What it does | Why 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) |
.exe; the only PowerShell that runs is invisible, hidden, non-interactive, and
internal (telemetry POST, Companion /shutdown). This is why
install-rewind.ps1 was deleted and why make-manifest.mts explicitly
documents "we deliberately do NOT emit any PowerShell (.ps1)" — hosting a script on the public
website would let attackers fetch and study it.connect-src http(s)://127.0.0.1:*) is scoped to loopback only, granting no remote
origin anything..xlsx files.HKCU\Software\ExcelRewind\Edition and installs binaries; the
TIER_ADDIN/TIER_COMPANION defines in the .nsi are
documentation-only hooks, not enforcement, consistent with the "single source of truth is the
tiers table" rule from CLAUDE.md.build-installers.ps1 throws if the staged manifest lacks
addin.excelrewind.com or contains localhost; smoke.mts
separately re-verifies the deployed manifest has the prod GUID prefix and no
localhost:3000 after deploy.Flags=1 registers a
catalog but hides it from Insert > Add-ins > Shared Folder — a real field bug. Every
installer run forces existing matching catalogs back to Flags=3, which is also why
"just re-run the installer" is the official repair procedure (the Troubleshooter component was
removed 2026-07-16; its logic was folded directly into the main install Section).net share ... /DELETE /Y before recreating, ignoring the delete's
exit code (share may not have existed)./GRANT on net share. The default
Everyone/Read grant is locale-proof and also fixes an older bug where the share was granted to
the elevated user's name instead of the actually-logged-in user.%LOCALAPPDATA%\Microsoft\Office\16.0\Wef\{AddinInfo,AppCommands}; both install and
uninstall clear these directories (other add-ins simply re-cache on next Excel launch), which is
what makes "run the installer again" an effective fix.net share), but the Companion must NOT run
elevated — an elevated Companion cannot see a normal-integrity Excel via
GetActiveObject. Fixed by launching through explorer.exe, which
re-parents the new process to the user's normal-integrity shell.--standalone produces
a whole directory tree (exe + DLLs + .pyd files + a PIL/ subdir); the
.nsi uses File /r to recurse the whole staged
dist\companion\*.* tree, and the uninstaller does RMDir /r on the
dedicated COMP_DIR (never a parent/shared directory — verified safe by scoping to
%LOCALAPPDATA%\Programs\ExcelRewindCompanion specifically).%COMPUTERNAME%/%USERNAME% reads — both are worked around: telemetry
POSTs shell out to hidden PowerShell Invoke-RestMethod; host/user are read via
ReadEnvStr in .onInit/un.onInit./shutdown's CSRF guard was added after this module's contract was
fixed. companion-win/server.py _origin_host_ok() (v3.19.13,
CHANGELOG M-3) rejects any POST whose Origin header is present but not
allow-listed — closing a hole where a malicious web page could hit the loopback port and force
a shutdown. It deliberately does not require the bearer token, because the installer's
PowerShell Invoke-RestMethod call sends no Origin header at all and
would otherwise 401 into a skipped COM release → force-kill → ghost EXCEL.EXE, the
exact failure this endpoint exists to prevent.${GetTime} ordering. Returns day/month/year in a fixed register
order; the install-date marker is written as YYYY-MM-DD — flagged in the README as
unverified against locale (display-only today, so low risk, but noted for whoever wires it into
logic later).connect-src http(s)://127.0.0.1:*, which made the
browser block the add-in's Companion /status probe — every install silently read
as "Companion not installed." Any future _headers edit must re-verify this
allowance survives, on the deployed origin (dev builds have no _headers
file at all, so this can only be checked after a real Pages deploy).TELEMETRY_URL points
at a real, standard Supabase Edge Function URL that is not yet deployed
(supabase/functions/uninstall-feedback exists in the repo but nobody has run
supabase functions deploy). Every POST is wrapped in the PowerShell command's own
try {} catch {}, so the 404 is silent and never blocks install/uninstall. Setting
TELEMETRY_URL to the REPLACE-ME sentinel hard-disables all network
calls from the installer.npm run deploy copies ExcelRewind-Setup.exe into
apps/website/ — per OPS.md §4b this is a manual
copy apps\installer\dist\ExcelRewind-Setup.exe apps\website\ExcelRewind-Setup.exe
followed by a manual npm run manifest + wrangler pages deploy apps/website.
Forgetting this step means the installer build succeeds but customers keep downloading the old
binary.