Customer Dashboard — Project Briefing

Published

Project article; see sources and editorial standards.

Version 1.0.569 · Chrome MV3 · macOS · Internal Tool

This briefing is written for several audiences, and each section header marks its primary one. Leadership-facing sections use plain language; developer- and reviewer-facing sections use precise technical terms. Paths and version numbers come from the project’s repository, which is internal and not published, so paths such as docs/SECURITY.md are references, not links. Figures are as of version 1.0.569 (June 2026) and later changes are not reflected; the Granola integration, for example, was removed in September 2026. “The operator” means the person running the extension.


1. Executive Summary (leadership)

The Customer Dashboard is a Chrome browser extension that I built, as a Technical Account Manager (TAM), to bring the customer account context a TAM or support engineer needs into one AI-assisted workspace. To prepare for a customer call or escalation, a TAM would otherwise switch between Hub (the support-case portal), Salesforce, monday.com, Jira, Aha!, Google Drive, Granola, Glean and MongoDB Atlas. The extension instead aggregates and indexes that data locally, generates LLM-powered reports on demand, and surfaces case status notifications automatically.

It is a local-first tool. The corpus lives in the operator’s browser storage and in a backend running on the same machine. Data leaves the machine mainly in three ways: prompts to the LLM providers the operator already has access to (Claude, Gemini, Glean or GitHub Copilot, using credentials already configured for day-to-day work), writes to monday.com boards, and the optional Atlas mirror, which is written only when the operator configures it.

This is an internal tool in active daily use, not a prototype. It has 707 automated tests across four suites, a documented security architecture, a three-workflow CI pipeline, and a documentation suite of 78 markdown files. It has been through dozens of feature iterations.


2. Key Features (all)


3. Problems Solved (leadership + team)

Problem What the extension does
Context fragmentation — preparing for a customer call means opening 6–10 tabs across Hub, Salesforce, Slack, monday.com, Jira, Aha!, Drive, and Glean Aggregates all sources into a single indexed corpus; generates a pre-call context report in under a minute
Account context lives in two systems — the technical story is in Hub, but ownership, annual recurring revenue (ARR), and roadmap status live in Salesforce and Aha! Content scripts extract Salesforce Account fields and Aha! item status directly into the same corpus, so a brief reflects both
Case status blind spots — severity escalations are only visible if you happen to be looking at Hub Polls open cases in the background and surfaces Chrome notifications on severity or status changes
monday.com maintenance overhead — board items go stale; reconciling them against Hub cases is manual Automated monday.com reconciliation: creates new items, archives resolved cases, updates rows via LLM-assisted diffing
Meeting context loss — notes from Granola, Plaud recordings, and Slack threads are siloed Ingests meeting transcripts and recordings through native host bridges and content scripts, and indexes them into the local corpus
Report generation time — writing a pre-call summary or case analysis by hand takes 30–60 minutes LLM report generation with configurable prompts produces structured reports from the indexed corpus in under a minute
Atlas diagnostics access — diagnostics snapshots require navigating to each project separately Per-cluster diagnostics snapshot refresh and storage directly from the extension dashboard
No unified to-do surface — action items from calls, Slack, and cases live in different places Floating always-on-top to-do window with keyboard shortcut
Slow onboarding — new TAMs rebuild account history from scratch A new TAM’s own dashboard rebuilds account history from the sources automatically instead of starting from scratch
Slow escalation response — priority changes surface only if someone is watching Case-status notifications can arrive before the customer sends a follow-up

The time figures in this table are my own rough numbers, not benchmarks.


4. Scope of Work (leadership + reviewers)

I designed this project and built it myself, with AI coding assistants, as an internal productivity tool rather than a vendor product.

Component Approx. lines Tests
Chrome extension (background, content, UI) ~102,000 294 (Vitest)
Local backend server (server/) ~18,000 346 (Vitest)
Live Hub Toolkit (live-hub-toolkit/, which generates customer hub documents) ~3,900 30 (node --test)
Native host bridges (native-host/) ~6,900 Python AST + integration
Account Context MCP server ~800 37 (node --test)
Documentation suite 78 markdown files —
Total ~131,600 707

Line counts are raw file lines (including comments and blank lines) from wc -l, intended as scope indicators rather than SLOC. Test counts are exact pass counts from running each suite at v1.0.569. The 707 total does not include the native-host Python tests, which CI gates separately.

Engineering quality markers:


5. Security Posture (reviewers + leadership)

Summary for reviewers: The corpus is stored, unencrypted, on the operator’s machine. Once the operator sets up the vault, the secrets it covers are encrypted at rest with AES-GCM. The local backend requires a bearer token and checks the origin of state-changing requests. On the live recommender path, case text, transcripts and Slack messages are wrapped in explicit injection-guard tags. Customer data mainly leaves through LLM providers the operator already uses, writes to monday.com, the optional Atlas mirror, and backups or exports to Google Drive. docs/external-calls.md in the repository audits every outbound call, including optional Sentry error reporting.

Secret storage

Once the operator sets up the vault, the Glean token, the Atlas private key, the monday.com token, the backend token and OAuth refresh tokens are stored in a passphrase-encrypted AES-GCM vault in chrome.storage.local (src/common/secret-vault.js). Without a vault they are not encrypted, and the Anthropic, Gemini and Copilot API keys are not vault-encrypted either.

Network and backend security

Control Implementation
Local backend isolation Server binds to 127.0.0.1:8787 by default, so it is not reachable from outside the machine
Origin allow-list All state-changing backend requests checked against an explicit origin allow-list in server/src/middleware/origin-check.js; cross-origin requests are rejected
Timing-safe token verification Backend bearer token comparison uses crypto.timingSafeEqual; not === (server/src/middleware/auth.js)
No query-string tokens Auth sent as Authorization: Bearer — never embedded in URLs

Prompt injection defense

On the live recommender path, case text, transcripts and Slack messages are:

  1. Wrapped in named <untrusted_*> XML tags.
  2. HTML-entity escaped before insertion.
  3. Referenced by a system-prompt rule instructing the model never to execute content from those tags.

The live recommender (the backend component that recommends actions on material Slack and case events) has exactly one tool. That tool only produces text, with no API calls or code execution, which limits the blast radius of any indirect injection.

Content script isolation

What this tool does not defend against, and known gaps


6. Architecture Overview (reviewers + team)

The workspace has five independently installed components: the Chrome extension, the native hosts, the local backend (server/), the Live Hub Toolkit and the Account Context MCP server. Only the Chrome extension is strictly required; the others unlock additional capabilities. The diagram below omits the MCP server, which gives AI clients access to the corpus.

C4 Level 1 — System context

Operator
  │
  ├─ Chrome MV3 extension (repo root)
  │    ├─ service worker + extension pages + content scripts
  │    ├─ chrome.storage.* + IndexedDB
  │    └─ offscreen document
  │
  ├─ native messaging
  │    └─ native-host/*.py + launcher shells + optional Swift speech host
  │
  ├─ HTTP loopback
  │    └─ server/ (Express + local mongod + optional Atlas mirror)
  │
  └─ filesystem handoff
       └─ live-hub-toolkit/ generated artifacts + config

C4 Level 2 — Chrome-side containers

Container Path Owns Talks via
Service worker src/background/service-worker.js message routing, alarms, sync engines, corpus writes chrome.runtime.sendMessage, chrome.alarms, IndexedDB, native messaging, loopback HTTP
Options page src/options/ settings and credentials UX runtime messages + chrome.storage.*
Popup src/popup/ quick actions and dashboard launch runtime messages + chrome.storage.*
Dashboard pages src/dashboard/ main operator workspace, overlays, to-do tooling runtime messages + chrome.storage.*
Offscreen document src/offscreen/ SSE client, LLM streaming (kept alive by silent <audio>) runtime messages
Content scripts src/content/ Hub extraction, Slack relay/export, Plaud hook, Drive scraper, Salesforce/Aha!/Atlas scrapers chrome.runtime.sendMessage → service worker

Storage surfaces

Surface Use for
chrome.storage.local Settings, accounts, OAuth refresh tokens, vault envelope
chrome.storage.session Vault DEK cache, OAuth access tokens, sync locks (memory-only)
IndexedDB (src/background/db.js) Full corpus (cases, Slack, meetings, reports) — primary store
Local mongod, plus Atlas when configured, via server/ Dual-write mirror; durable backend for SSE pipeline

Data flow

Content scripts extract from web pages → Service worker indexes in IndexedDB → Backend mirrors to the local MongoDB and, when configured, Atlas → Reports generated by LLM on demand → Dashboard renders results. Live updates flow the reverse direction via SSE from the server’s /api/live endpoint.


7. Installation Prerequisites (new users)

Required

Optional (unlocks additional features)

The step-by-step install is in the project’s installation guide, which this post does not reproduce.