Skip to content

refactor: make TokenTracker local-only#72

Merged
pitimon merged 7 commits into
mainfrom
refactor/local-only-remove-cloud
Jul 21, 2026
Merged

refactor: make TokenTracker local-only#72
pitimon merged 7 commits into
mainfrom
refactor/local-only-remove-cloud

Conversation

@pitimon

@pitimon pitimon commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Makes TokenTracker local-only. Removes its own cloud upload, device pairing, InsForge auth, the hosted dashboard, and the Leaderboard, Login, Device, Marketing landing and Share features.

Scope was decided explicitly: delete the cloud rather than make it opt-in. Three judgment calls that were genuinely product decisions, not mechanical ones, were settled up front — the marketing landing goes and / serves the dashboard; Share goes (it built links as /u/{userId}, a cloud path that would 404); the leaderboard-social UI (LikeButton, MacAppBanner) goes.

What moved

CLIsync no longer uploads; device-login is gone. local-api.js drops the /api/auth-bridge/verifier endpoint, the /api/auth/* reverse proxy to InsForge, and the on-disk cookie relay. The local localAuthToken stays: it guards local mutations and is not a cloud credential. browser-auth.js became browser-open.js — only openInBrowser survived, so the old name described nothing. runtime-config loses baseUrl / dashboardUrl / deviceToken; doctor loses the cloud reachability probe and two checks that could only ever report green; upload-throttle.js is deleted.

Dashboard — 65 files deleted. The real work was not the deletions: lib/auth-token was imported by six data hooks, so the dashboard had been gating its local API fetches on a cloud access token. Removing the token meant unpicking that gate in each one, or they would wait forever for a token that can never arrive.

Docs — README, PRODUCT, CLAUDE and the openwiki tables no longer describe a hosted product. The openwiki extractor's hardcoded route table matched /u/:userId by substring against /u, so the fact check was demanding a route that exists nowhere.

Migration behavior worth knowing

init now scrubs deviceToken, baseUrl and dashboardUrl from an existing config and unlinks relay-cookies.json, upload.throttle.json and auto.retry.json. This matters: previously init spread the whole prior config forward, and this same change removed the status / diagnostics lines that used to report the token — so a user told "local-only, nothing is uploaded" would have kept a valid InsForge bearer token on disk with nothing left to reveal or rotate it. relay-cookies.json was worse: the only code that ever deleted it was clearRelayCookies(), which went with the auth proxy.

--link-code now fails loudly instead of being ignored — it was advertised in cli.js and silently discarded, so a user pairing a device saw "Setup complete!" and believed it worked. --no-auth, --email, --password and --device-name are accepted and ignored so existing hooks and scripts do not start failing with "Unknown option".

status --json narrowed: base_url, device_token_set, queue.pending_bytes, queue.offset, last_upload, next_upload_after, backoff_until, last_upload_error, auto_retry are gone. In-tree consumers verified safe — both menu-bar apps talk to the local HTTP server, not to status --json. Third-party scripts reading those fields will get undefined.

Test plan

  • npm test763 pass
  • npm --prefix dashboard test233 pass
  • npm run ci:local — exit 0
  • npm run dashboard:build — exit 0, dist/index.html contains no leaderboard, cloud-sync, InsForge or unsubstituted-placeholder text
  • docs:openwiki:check — 0 findings
  • Smoke tested by actually running it: serve against a synthetic queue — / returns 200 with the app root, the JS bundle serves, and usage-summary / usage-daily / user-status all return 200
  • Credential scrub verified on a populated fixture: deviceToken / baseUrl / dashboardUrl removed, deviceId preserved, stale files unlinked
  • Reviewer: load the dashboard in a real browser and click through Usage, Limits, Skills and Settings. Automated checks cover rendering the shell, not the views.
  • Version lockstep (package.json, both project.yml MARKETING_VERSION, TokenTrackerWin.csproj) + DMG workflow — deliberately not bumped, pending the release decision, same as the PRs merged before this.

Review

Three independent reviews ran against this branch — security, silent-failure, and quality. No CRITICAL, and no over-deletion: every removed symbol was grepped for surviving callers. What they found was under-deletion and things that lied, and those are fixed in the last two commits:

  • the shipped index.html still told readers "cloud sync is optional and used only for the public leaderboard" in eight places that survived into dist/, directly contradicting README
  • dist/index.html was shipping a literal __TOKENTRACKER_OG_URL__ in a canonical link
  • doctor reported auto_retry_no_spawn for a spawn path deleted in the first commit
  • init stopped telling the user where to find the dashboard once the cloud default URL was removed
  • --password=VALUE fell through to Unknown option, printed as a stack trace, putting the secret on stderr
  • @vercel/analytics still bundled: gated on non-loopback, so LAN-IP access beaconed out, against README's "nothing leaves your machine"

Two tests I had written as inversions were judged too weak and were strengthened. dashboard-missing-jwt-guard asserted only the absence of a token gate — an empty file would have satisfied it, and it is the designated guard for that reversal. runtime-config never pinned the env rung its comment claimed; deleting env from the pick chain left it green.

Tests were inverted rather than deleted wherever behavior reversed instead of disappearing, so the removals stay pinned: no auth proxy or cookie relay can return to local-api, the cloud doctor checks stay gone, /leaderboard and /login stay unrouted, and the hooks must keep fetching without a token. Tests whose subject genuinely no longer exists were deleted.

Deferred

Everything still cloud-facing that does not ship in the npm packageedge-patches/, CI credentials, the native apps' OAuth flow, vercel.json, and the dormant dev-server proxy — is tracked in #71 rather than grown into this PR. The real remediation there is rotating the InsForge service-role key at the source, not editing YAML.

Refs #71

itarun.p added 6 commits July 21, 2026 06:11
… CLI

First step of making this fork local-only. Two things go away here: the
queue upload path and the browser-pairing command that existed to obtain
the credential it needed.

`sync` no longer resolves a runtime cloud config, no longer calls
drainQueueToCloud, and no longer POSTs to /functions/tokentracker-ingest.
Parsing is untouched — the local queue still fills exactly as before, it
simply has nowhere to be sent.

Removing the upload call alone would have left a trap: the queue offset
only ever advanced on a successful upload, so `pendingBytes` would have
been permanently positive and the auto-retry scheduler would have rebooked
itself forever with nothing to retry. The whole retry chain goes with it —
scheduleAutoRetry, clearAutoRetry, spawnAutoRetryProcess,
buildAutoRetryScript, coerceRetryMs, the auto.retry.json artifact and the
upload throttle state.

`device-login` is deleted outright, along with its CLI registration, help
text and test. Its only purpose was pairing a headless session with a
browser sign-in to mint a device token.

Help text now says what is true: sync parses into the local queue and
nothing is uploaded.

Zero new test failures — 778/782, the same 4 pre-existing
parseKiroCliIncremental failures that are red on main (#65). The count
drops from 795 because device-login.test.js is gone with its subject.

Refs #67 (local-only direction)
Second phase of the local-only change. Deletes the Leaderboard, Login, Device
pairing, Marketing landing and Share features from the dashboard, along with
the InsForge auth context and every helper that existed to serve them.

The file count hid the real work. `lib/auth-token` was imported by six data
hooks (usage, trend, model-breakdown, heatmap, project-summary, pulse), so the
dashboard had been gating its *local* API fetches on a *cloud* access token.
Removing the token meant unpicking that gate in each one; leaving it would have
left the hooks waiting for a token that can never arrive, and the dashboard
permanently empty.

Two tests were INVERTED rather than deleted, because their subjects did not
disappear — the intended behavior reversed:
- `app-route-pathname-guard` asserted /leaderboard and /login exist; it now
  asserts they stay gone, along with the pages behind them.
- `dashboard-missing-jwt-guard` asserted the hooks refuse to fetch without a
  token; it now asserts they do not gate on one. Deleting it would have left
  the reversal unprotected.
Test files whose subject was genuinely removed were deleted outright.

Also rewires the HTML meta. `index.html` reads its title and Open Graph tags
from copy keys, and those keys lived under `landing.meta.*`. Dropping them with
the landing page left the dashboard shipping an empty `<title>` — silently,
because a missing copy key only warns and the build still exits 0. They are now
`app.meta.*` with local-appropriate values, and `share.html` is removed from the
Vite inputs and the Vercel rewrites rather than being built as a dead entry.

Verified: `npm test` 789 pass, `npm --prefix dashboard test` 233 pass,
`npm run dashboard:build` exit 0 with no missing-key warnings, `<title>` renders
as TokenTracker, and `grep -rlEi "insforge|deviceToken|tokentracker\.cc|leaderboard"
dashboard/src` returns nothing.

The CLI side (local-api auth proxy, runtime-config, doctor, diagnostics,
status, init) is still to do.
Third and final phase. The CLI no longer proxies auth, holds a device token,
points at a remote endpoint, or reports upload state.

- `local-api.js`: the `/api/auth-bridge/verifier` endpoint and the `/api/auth/*`
  reverse proxy to InsForge are gone, along with the on-disk cookie relay that
  existed to share a login session between the browser and the WKWebView. The
  local `localAuthToken` stays — that one guards local mutations and is not a
  cloud credential.
- `browser-auth.js` → `browser-open.js`: the sign-in flow and its local callback
  server are removed. Only `openInBrowser` survives, which `serve.js` uses, so
  the old name would have described nothing.
- `runtime-config.js`: `baseUrl`, `dashboardUrl` and `deviceToken` removed.
  `doctor` loses the cloud reachability probe and the base_url / device_token
  checks; its `upload.last_error` check went too, since `diagnostics.upload` no
  longer exists and it could only ever report a permanent green.
- `diagnostics.js` / `status.js`: upload-throttle and auto-retry reporting
  removed, and `upload-throttle.js` deleted once nothing imported it.
- `init.js`: cloud setup, the `--base-url` / `--dashboard-url` flags and the
  written endpoint config are gone. `--no-auth` and friends are accepted and
  ignored rather than removed, so existing hooks and scripts do not start
  failing with "Unknown option".
- Docs: README, PRODUCT and CLAUDE no longer describe a hosted product, a
  leaderboard, or an opt-in upload. The openwiki route and command tables lost
  their removed entries, and the extractor's hardcoded route table lost them too
  — it matched `/u/:userId` by substring against `/u`, so the fact check was
  reporting a route that no longer exists anywhere.
- `index.html` drops `og:image`, `og:url` and `twitter:image`: a local-only app
  has no canonical URL or hosted preview, and empty tags are worse than absent.

Tests were inverted rather than deleted wherever the behavior reversed instead
of disappearing — `local-api-security` now pins that no auth proxy or cookie
relay can return, `doctor` pins that the cloud checks stay gone, and
`runtime-config` keeps its precedence and env-isolation guarantees by testing
them on a field that still exists. Tests whose subject was genuinely removed
were deleted.

The UI hardcode baseline was regenerated: 19 of its 53 files no longer exist.
The three "new" tokens it then reported are heuristic false positives — a code
fragment, a test label, and a reworded comment — not user-facing copy.

Verified: `npm test` 762 pass, `npm --prefix dashboard test` 233 pass,
`npm run ci:local` exit 0, `docs:openwiki:check` 0 findings, `tracker status`
and `tracker doctor` both exit 0, and no shipped file under src/, bin/ or
dashboard/src/ mentions insforge, deviceToken, tokentracker.cc or leaderboard.
Three independent reviews (security, silent-failure, quality) found no
over-deletion — every removed symbol was checked for surviving callers — but a
number of things that now lied or leaked.

The two that mattered, both flagged independently by two reviewers:

- **A live cloud credential survived the upgrade, invisibly.** `init` rebuilt
  config with `{...existingPlainConfig}`, carrying `deviceToken` forward, and
  this same release removed the `status` / `diagnostics` lines that used to
  report it. A user told "local-only, nothing is uploaded" had a valid InsForge
  bearer token on disk with nothing left to reveal or rotate it. `init` now
  scrubs `deviceToken` / `baseUrl` / `dashboardUrl` and unlinks
  `relay-cookies.json`, `upload.throttle.json` and `auto.retry.json` — the last
  of which nothing could remove any more, because `clearRelayCookies()` went
  with the auth proxy. Verified on a populated fixture.

- **The shipped dashboard still marketed the cloud.** `index.html` told readers
  "cloud sync is optional and used only for the public leaderboard" in eight
  places that survived into `dist/`, directly contradicting README's "there is
  no upload path at all". Fixed, along with `sitemap.xml` and `llms.txt`.
  `dist/index.html` was also shipping a literal `__TOKENTRACKER_OG_URL__` in a
  canonical link, because the placeholder was dropped from the substitution map
  but not from the HTML.

Also:
- `--link-code` was advertised in `cli.js` and silently discarded, so a user
  pairing a device saw "Setup complete!" and believed it worked. It now fails
  loudly. `doctor --base-url` left the help text too.
- `doctor` reported `runtime.auto_retry_no_spawn :: auto retry spawn enabled`
  for a spawn path deleted in the first commit. Removed end to end.
- `init` stopped telling the user where to find the dashboard once the cloud
  default URL was gone; it now prints the local address.
- `--password=VALUE` fell through to `Unknown option`, which is printed as a
  stack trace — putting the secret on stderr. Swallowed like its space form.
- Dead code and dependencies removed: `normalizeRemoteHttpBaseUrl`,
  `buildProxyHeaders`, `leaderboard-ui.js`, `host-mode.ts`, `@insforge/sdk`,
  `html-to-image`, `ogl`, `three`, the four `@dnd-kit` packages, and
  `verify:share-clipboard`.

Two of my own inverted tests were judged too weak and were strengthened:
`dashboard-missing-jwt-guard` asserted only the ABSENCE of a token gate, which
an empty file would satisfy — it now asserts the hook still issues a request
first. `runtime-config` never pinned the env rung it claimed to; deleting env
from the pick chain left it green.

`init preserves existing config fields and custom URLs` was inverted to
`init scrubs cloud credentials and endpoints while preserving local config`,
since that preservation is precisely the defect.

Verified: `npm test` 763 pass, dashboard 233 pass, `ci:local` exit 0,
`dashboard:build` exit 0, and the built `dist/index.html` contains no
leaderboard, cloud-sync, InsForge or unsubstituted-placeholder text.
Both reach npm users, unlike the remaining cloud surface:

- root `package.json` published `"leaderboard"` as a keyword for a feature this
  release removes.
- `@vercel/analytics` and `@vercel/speed-insights` were imported in `App.jsx`
  and bundled into `dashboard/dist/`. `shouldEnableVercelInsights()` gated them
  on non-loopback, so a normal install never fired them — but reaching the
  dashboard over a LAN IP beaconed to Vercel, contradicting README's "nothing
  leaves your machine". With no hosted deploy left, that gate could only ever
  be true in a case the product no longer supports.

Caught by actually running the app rather than trusting the build: `vite` does
not resolve undefined JSX identifiers, so a partial removal left
`enableVercelInsights` referenced at three sites with a green build. Three
dashboard tests failed; the smoke test would have caught it either way.

Smoke tested end to end (`serve --port 7791` against a synthetic queue):
`/` returns 200 with the app root and no cloud text, the JS bundle serves,
and `usage-summary`, `usage-daily` and `user-status` all return 200.

Noted, not changed: removed endpoints such as `/api/auth/refresh` return 200
with the SPA shell rather than 404, because `serve.js:141` has always had an
unconditional SPA fallback for unmatched paths. Pre-existing, and no caller for
those routes remains.
Found by clicking through the running dashboard, not by grep or build.
The earlier cloud sweep missed it because the grep pattern was
case-sensitive and the host is "tokentracker.statuspage.io" — it matches
neither "insforge" nor "leaderboard".

- dashboard/src/lib/config.ts: drop STATUSPAGE_URL
- MenuBarSection.jsx: NativeAppFooter now returns null when the native app
  is absent. The status link was the only thing it rendered in that case,
  so the alternative was an empty flex row.
- copy.csv + 4 i18n locales: remove the orphaned settings.footer.statusPage
  key. validate:copy does not catch i18n orphans in this direction.

Verified: dashboard:build clean, tsc --noEmit clean, ci:local green
(763 + 4, fail 0), dashboard vitest 233/233, and all six routes
re-smoked in a real browser with zero console errors.
@pitimon

pitimon commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

Reviewer summary — clean, self-contained deletion, merge-ready

Independent audit of this PR (not just CI-green). Verified on head 07175d2 (branch now current with main, gated on Linux by the new dashboard-test step from #74).

The two questions a deletion PR must answer:

1. Does anything still reference the removed surface? — No.

  • All 10 spot-checked deleted modules (InsforgeAuthContext, auth-token, cloud-sync, LeaderboardPage, upload-throttle, device-login, ShareModal, MarketingLanding, …) have 0 runtime import references in src/ / dashboard/src/.
  • External-host enumeration of the whole post-deletion tree (git grep -hoE "https?://…") shows no leftover cloud host — no tokentracker.cc/io, no *.statuspage.io, no InsForge host. Every remaining https:// is a legitimate AI-provider / GitHub / local / vendor API. getBackendBaseUrl() now returns "" (relative path to the CLI-embedded API).
  • i18n / copy: 0 orphaned removed-surface keys (235 keyword hits all live features or substring false-positives; marketing locale files deleted wholesale).

2. Does the remaining app still work? — Yes.

  • tsc --noEmit clean, dashboard:build clean, ci:local green on ubuntu-latest, dashboard vitest 233/233.
  • Browser smoke test: all 6 routes (/, /limits, /skills, /settings, /widgets, /ip-check) render real content with 0 console errors; nav has no Login / Leaderboard / Device / Marketing entries.

Deletion breakdown (~18.6k lines)

Subsystem ~Lines Self-contained
Leaderboard (pages, modal, columns, avatars) ~3,300
Marketing / landing ~2,750
Share-card ~2,220
Auth / login (InsForge) ~2,220
i18n / copy keys ~1,970 ✅ pruned in-sync
Cloud-sync / upload ~770
CLI / local-api / runtime-config ~700 ✅ inverted guard tests added
Device pairing ~450
Telemetry (Vercel) + Docs

Two notes — neither blocking

  1. [LOW — intentional, documented] Server-side InsForge code retained. dashboard/edge-patches/ still holds 18 Edge functions (device-flow, tokentracker-leaderboard*, tokentracker-ingest, account-*). Not imported by the app, not in the npm package — so local-only operation is unaffected. Deferred to Decommission the InsForge cloud backend #71 per the PR description. The one conscious reviewer decision: accept this scope boundary (a "remove cloud" PR that leaves the InsForge server code, whose real remediation is the service-role-key rotation tracked in Decommission the InsForge cloud backend #71) or require Decommission the InsForge cloud backend #71 be linked/blocking. Guard tests still correctly cover the retained files.
  2. [TRIVIAL] Dead vi.mock("@vercel/…") calls in App.navigation-preload.test.jsx and App.preload.test.jsx — packages removed, mock on a never-imported module is a harmless no-op (tests green). Cosmetic cleanup only.

Bottom line: no dangling cloud reference in shipped runtime code; the deletion is internally consistent. The only substantive decision is the edge-patches/#71 deferral.

@pitimon
pitimon merged commit f36dae1 into main Jul 21, 2026
1 check passed
@pitimon
pitimon deleted the refactor/local-only-remove-cloud branch July 21, 2026 21:41
pitimon added a commit that referenced this pull request Jul 21, 2026
Part of #71. Removes the repo/CI/web-dev cloud surface. None of this ships
in the npm package, so deleting it undeploys nothing — the live InsForge
backend is retired separately by rotating its service-role key (a user
action on the InsForge dashboard; nothing in this repo held that secret).

Scope kept to web/CI/repo per decision; native-app OAuth (Swift/C#) is a
separate follow-up on #71.

Removed:
- dashboard/edge-patches/ — all 18 InsForge Edge functions (ingest,
  leaderboard*, device-*, account-*, badge/embed svg, public-visibility,
  profile-likes) and the 3 tests that only guarded them (edge-patch-
  pricing-sync, badge-svg-render, and two leaderboard-refresh byte-equality
  cases in model-breakdown.test.js; the live buildFleetData/pricing tests stay).
- InsForge creds from all 4 workflows (ci, npm-publish, release-dmg,
  release-windows) + the stale comments claiming their absence breaks the
  build or "disables cloud OAuth (isCloudInsforgeConfigured())" — that
  function was deleted in #72 and nothing reads VITE_INSFORGE_* anymore.
- dashboard/vercel.json (deploy that no longer exists), dashboard/.mcp.json
  (InsForge MCP dev tooling), the /api/auth→InsForge proxy and the
  insforgeBaseUrl passthrough in dashboard/vite.config.js.
- InsForge/service-role/device rows from .env.example; rewrote
  dashboard/.env.example to the one local (mock) var.
- Stale references in SECURITY.md (insforge/ path), CLAUDE.md, the
  curated-overrides pricing note, the guardrails ignore list, and the
  leaderboard-absence item in the publish checklist.

Verified: dashboard:build succeeds with NO VITE_INSFORGE_* env (proving the
"must be present or the build fails" comments were stale), bundle carries no
insforge/statuspage host, and ci:local is green end-to-end — dashboard
233/233, root 752 + 4, all validators pass. No secret was ever in the repo
(edge functions read INSFORGE_SERVICE_ROLE_KEY from Deno env; CI injected
only the public ik_ anon key, now removed too).

Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant