Tempest Capital, S.C. · Typhoon Excel Add-in (ITEM 1) Internal analytical work product — not investment, legal, tax, or accounting advice.
This is the step-by-step to take the add-in from this repo to every analyst’s Excel at Tempest (Windows desktop, Mac desktop, and Excel on the web) via Microsoft 365 Centralized Deployment.
Do the review fixes first. This guide assumes
mainis green —npm run typecheck,npm test, andnpm run buildall pass (seePLAN.md/README.md§7).
Typhoon is two independently-deployed things. Getting firm-wide rollout right means doing both.
| Piece | What it is | How it reaches analysts |
|---|---|---|
| A · Add-in (this folder) | The ribbon tab + task-pane web app (dist/ + manifest.prod.xml) |
Preferred: M365 Centralized Deployment (§5). Firm-wide fallback when Integrated apps fails with “eligibility requirements”: Windows Trusted Catalog / network share via GPO or Intune (§5b). |
| B · Bridge (Azure Container Apps) | Entra-authenticated API that owns the Claude key and returns governed format specs | Preferred firm mode: deploy infra/typhoon-bridge/deploy.sh (§6a). Local typhoon serve is engineer-only (§6). |
The add-in UI appears once A is deployed. In the firm build, commands light up only after MSAL Nested App Authentication succeeds against B. A dead or unauthorized bridge disables governed menus and leaves workbook cells untouched.
With a current bridge build, the taskpane auto-connects via loopback GET /handoff when typhoon serve
is running — analysts do not paste the session token by hand. Intune should still auto-start typhoon serve
at logon; the token handoff is then automatic.
https://typhoon.tempestcapital.com/typhoon/, published by
.github/workflows/addin-pages.yaml. The raw shared github.io hostname is
rejected by the production build. Alternatives: Azure Static Web Apps,
Azure Blob Storage + Front Door/CDN, an internal IIS site, or a SharePoint-hosted site collection.
The origin must serve the files at the paths the production manifest expects: /taskpane.html,
/assets/*, /shortcuts.json (under that host prefix).dist/)cd addin
npm ci # clean, lockfile-exact install
npm run typecheck # tsc --noEmit (TypeScript strict)
npm test # classifier parity vectors (FMG-1) — must pass
# Firm build variables (repository variables in CI):
export TYPHOON_BRIDGE_MODE=cloud
export TYPHOON_BRIDGE_BASE=https://typhoon-api.tempestcapital.com
export TYPHOON_ENTRA_CLIENT_ID=<application-client-guid>
export TYPHOON_ENTRA_TENANT_ID=<tenant-guid>
export TYPHOON_ENTRA_SCOPE=api://$TYPHOON_ENTRA_CLIENT_ID/access_as_user
export TYPHOON_BUILD_SHA=$(git rev-parse HEAD)
npm run build:cloud # production bundle + CSP/config artifact gate
dist/ now contains taskpane.html, taskpane.js, assets/ (all 69 icons), and shortcuts.json.
This is the whole web payload — there is no server code in A.
dist/ at the firm originConfigure the Pages custom domain (for example typhoon.tempestcapital.com)
and DNS before deployment. The workflow requires TYPHOON_HOST,
TYPHOON_CUSTOM_DOMAIN, bridge URL and Entra values as repository variables;
missing values fail the release.
One-time (repo admin): GitHub → Settings → Pages → Build and deployment → Source =
GitHub Actions (not “Deploy from a branch”). The first merge of the workflow to main, or a manual
Actions → addin-pages → Run workflow, publishes:
| URL | Purpose |
|---|---|
https://typhoon.tempestcapital.com/ |
Firm landing page |
https://typhoon.tempestcapital.com/typhoon/taskpane.html |
Task pane / Shared Runtime |
https://typhoon.tempestcapital.com/typhoon/assets/icon-32.png |
Ribbon / tile icons |
https://typhoon.tempestcapital.com/typhoon/manifest.xml |
Production manifest (M365 link) |
Confirm with curl -I that taskpane.html, assets/icon-32.png, and manifest.xml return 200
before Centralized Deployment. Until Pages Source is switched to Actions, the site will keep serving the
repo README and those paths will 404.
Publish the contents of dist/ to the root of your HTTPS origin so that, for
https://typhoon.tempestcapital.com:
https://typhoon.tempestcapital.com/taskpane.html loads the pane, andhttps://typhoon.tempestcapital.com/assets/icon-32.png etc. resolve (200, not 404).Requirements: HTTPS with a trusted cert; long-lived caching is fine but cache-bust on update (see §8). No CORS config is needed for the add-in’s own assets (same origin). Example (Azure Static Web Apps):
npx @azure/static-web-apps-cli deploy ./dist --env production
# …or copy dist/* to your IIS/Blob/SharePoint site by your standard process.
The repo manifest points at https://localhost:3000 (dev). Rewrite it to the firm host — the <Id> GUID is
the stable firm-wide app identity and is left unchanged:
# Firm-owned custom host (matches addin-pages repository variable):
npm run manifest:prod -- https://typhoon.tempestcapital.com/typhoon
# → writes manifest.prod.xml
npx office-addin-manifest validate manifest.prod.xml
For a custom origin, pass that URL instead (no trailing slash). validate must report the manifest is
valid. Keep manifest.prod.xml under source control (or your release store) — it is the artifact you
upload in §5, and the same <Id> must be reused for every future update. The Pages workflow also
publishes a hosted copy at /typhoon/manifest.xml.
Bump
<Version>(e.g.1.0.0.0→1.0.1.0) whenever you change the manifest (new commands, new host). Content-only changes (JS/HTML/CSS behind the same URLs) do not need a version bump — see §8.
https://typhoon.tempestcapital.com/typhoon/manifest.xml, ormanifest.prod.xml.Typhoon.User app role; widen that group rather than using an unrelated
tenant-wide assignment.ReadWriteDocument permission (required for explicit
ribbon formatting). NAA requests only Typhoon’s access_as_user API scope,
not Graph or mailbox scopes. Accept and Deploy.That is the entire firm-wide step for A. (Excel on the web and desktop both pick it up from this one push.)
If admin.microsoft.com → Integrated apps ends with “Learn more about eligibility requirements” and Typhoon never appears in the app list, the tenant catalog write failed. That is a Microsoft 365 / Exchange eligibility problem (not the GitHub Pages host). Open a Microsoft support ticket with the correlation IDs from the pink banner, and use this path for Windows Excel desktop firm-wide in parallel.
| Piece | Where |
|---|---|
Web payload (taskpane.html, icons, JS) |
Still GitHub Pages (…/typhoon/) |
| Manifest | Copied to a UNC share readable by every analyst PC |
| Discovery in Excel | Office Trusted Catalog registry (GPO / Intune / script) |
Limits (Microsoft): Windows Excel desktop only. Not Mac, not Excel on the web.
SharePoint app catalogs also do not work here — they ignore VersionOverrides
(ribbon commands), which Typhoon requires.
\\fs01\Software\TyphoonAddin, readable by
Domain Computers / all analysts (write only for IT).cd path\to\tempest-xlsx\addin\tools
# Pilot one user:
.\install-trusted-catalog.ps1 -CatalogShare '\\fs01\Software\TyphoonAddin' -Scope CurrentUser
# Firm-wide (local admin / SYSTEM — writes HKLM Policies):
.\install-trusted-catalog.ps1 -CatalogShare '\\fs01\Software\TyphoonAddin' -Scope AllUsers
The script downloads
https://typhoon.tempestcapital.com/typhoon/manifest.xml
into the share as TempestTyphoon.xml and registers the share as a Trusted Catalog.
AllUsers script (or the equivalent registry) with Intune or
GPO to every analyst Windows PC.typhoon serve.Under
HKLM\SOFTWARE\Policies\Microsoft\Office\16.0\WEF\TrustedCatalogs\{a78def85-6545-41fb-be81-7cf950d3860d}
(or HKCU\Software\Microsoft\Office\16.0\WEF\TrustedCatalogs\… for pilot):
| Name | Type | Value |
|---|---|---|
Id |
REG_SZ | {a78def85-6545-41fb-be81-7cf950d3860d} |
Url |
REG_SZ | \\fs01\Software\TyphoonAddin (the folder, no trailing file) |
Flags |
REG_DWORD | 1 |
Trusted Catalog does not cover Mac/web. Keep pursuing Integrated apps:
# After ExchangeOnlineManagement is imported (see ACTIVATION / support notes)
Get-OrganizationConfig | Format-List AppsForOfficeEnabled, EwsEnabled
# If AppsForOfficeEnabled is False:
Set-OrganizationConfig -AppsForOfficeEnabled:$true
Exchange admin center → Roles → Organization Management → Permissions → enable Org Custom Apps. File a Microsoft 365 support ticket with the eligibility correlation IDs. When CD works, you can retire the Trusted Catalog path.
Engineer/local mode runs through typhoon serve; do not distribute this path
to firm analysts after cloud cutover:
http://127.0.0.1:8765) and mints a per-session bearer token into a
mode-0600 handoff file the task pane reads out-of-band (see README.md §4). Nothing is
hardcoded./chat
call) before broad rollout.When Azure is available, analysts should not run typhoon serve. Deploy the
cloud bridge and ship a cloud-mode add-in package:
infra/typhoon-bridge/register-entra.sh. It creates the
single-tenant NAA SPA/API, v2 access_as_user scope, group assignment and
tenant admin consent.infra/typhoon-bridge/deploy.sh, including ANTHROPIC_API_KEY; run it.
The script deploys ACR, Key Vault, managed identity/RBAC, Redis, Log
Analytics/alerts and ACA, builds the digest-pinned non-root image in ACR,
and deploys two replicas with shared abuse controls..github/workflows/addin-pages.yaml
and the Pages custom domain/DNS. The release fails if cloud values, CSP,
manifest version or bundle budget are wrong.Engineers keep §6 local daemon (local mode). See the SOL review record in
docs/research/2026-07-15_adr0105_wp7_panel_redteam.md.
Deploy to a pilot group first (§5 step 4) and confirm on a real Excel:
A1=number, A2==A1*2, A3==Sheet2!A1, A4=text → AutoColor → blue / black /
green / unchanged; Ctrl+Z reverts (proves a live write). Try Accounting, Subtotal top,
Grand-total top.es-MX display name/description render for a Spanish-locale user; the disclaimer footer is present.dist/ (§3). No admin
re-push needed — cache-bust (rename bundle or set short cache TTL) so clients pick it up.manifest.prod.xml (same <Id>, higher <Version>).deploy.sh with
TYPHOON_IMAGE_DIGEST=sha256:<known-good> to reactivate an immutable bridge
image; re-publish the previous dist/ for client rollback. Integrated apps
can remove/reassign the add-in as the final kill switch.# Build + verify
cd addin && npm ci && npm run typecheck && npm test && npm run build:cloud
# Host: merge to main (or Actions → addin-pages → Run workflow).
# One-time: Settings → Pages → Source = GitHub Actions.
# Confirm: https://typhoon.tempestcapital.com/typhoon/taskpane.html
# Production manifest (local copy; Pages also hosts /typhoon/manifest.xml)
npm run manifest:prod -- https://typhoon.tempestcapital.com/typhoon
npx office-addin-manifest validate manifest.prod.xml
# Push (preferred): admin.microsoft.com → Integrated apps → manifest URL → Deploy
# Fallback (Windows firm-wide when eligibility fails):
# .\tools\install-trusted-catalog.ps1 -CatalogShare '\\fs01\Software\TyphoonAddin' -Scope AllUsers
# → Intune/GPO that script; users Insert → Add-ins → SHARED FOLDER → Tempest Typhoon
# Engineer-local diagnostics only: `typhoon serve`