What happened
Driving the dashboard in Google Chrome stable, two font requests fail:
GET https://fonts.gstatic.com/s/inter/v20/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2
— net::ERR_FAILED
GET https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2
— net::ERR_FAILED
Source — src\ui\static\css\base.css:5:
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap');
So a dashboard that otherwise runs entirely on localhost makes an outbound request to
fonts.googleapis.com (and then fonts.gstatic.com) on every page load. Two consequences:
- Offline / air-gapped / egress-filtered environments. The requests fail and the CRT-style
typography the design depends on silently falls back to system fonts. The dashboard remains usable —
this is not a functional break — but it does not look as intended, and there is no indication why.
- An unnecessary third-party dependency for a local developer tool. Every dashboard load tells
Google that someone opened it. For a tool whose stated model is local-first and whose own docs
emphasise an auditable local trail, that is surprising.
JetBrains Mono and Inter are both SIL Open Font License, so they can be vendored without licensing
friction.
What you expected
The dashboard renders identically with no network access. Self-host the two families under
src/ui/static/ (subset woff2, @font-face with font-display: swap) and drop the remote @import,
keeping a system-font stack as the fallback.
If a remote fetch is deliberate, it should at least be degradable on purpose — a local @font-face
fallback so the intended typography survives without egress.
Steps to reproduce
$env:DOTBOT_HOME = '<dotbot checkout>'
cd <an initialised dotbot project>
& $env:DOTBOT_HOME\bin\dotbot.ps1 go
# read the port from .bot\.control\ui-port, then open http://localhost:<port>
In Chrome DevTools → Network, filter gstatic: two woff2 requests are issued on load. To see the
failure mode directly, block fonts.googleapis.com and fonts.gstatic.com (DevTools → Network request
blocking, or an offline host) and reload — the requests fail and the fonts fall back.
Scripted, with Playwright against Chrome stable:
const failed: string[] = [];
page.on("requestfailed", r => failed.push(`${r.method()} ${r.url()} — ${r.failure()?.errorText}`));
await page.goto("/", { waitUntil: "domcontentloaded" });
// -> failed contains the two fonts.gstatic.com woff2 requests
Or with no browser at all:
Select-String -Path "$env:DOTBOT_HOME\src\ui\static\css\base.css" -Pattern 'fonts.googleapis.com'
Environment
OS: Windows 11 Pro 10.0.26200 | dotbot v4.0.2 (main @ 7c95b466)
pwsh 7.6.4 | Google Chrome stable (channel: "chrome" via Playwright 1.56.1)
Harness: standalone Playwright project outside DOTBOT_HOME; 4 of 5 checks passed
Network: outbound to fonts.gstatic.com blocked in this environment
Severity
low
Logs / screenshots
The rest of the dashboard checks passed in the same run, so this is isolated to the font fetch:
✓ /api/info and /api/state return 200 with a non-empty body
✓ CSRF guard refuses a state-changing POST without X-Dotbot-Request (403 "Missing CSRF header")
✓ upload filename sanitiser resists ../ traversal, backslash traversal, CON.md, invalid chars
✓ a second browser tab attaches without errors
✗ page load reports 2 failed requests — both fonts.gstatic.com woff2
Page title: "DOTBOT // Control Panel"
No uncaught JavaScript errors; no HTTP >= 400 from the dotbot server itself.
What happened
Driving the dashboard in Google Chrome stable, two font requests fail:
Source —
src\ui\static\css\base.css:5:So a dashboard that otherwise runs entirely on localhost makes an outbound request to
fonts.googleapis.com(and thenfonts.gstatic.com) on every page load. Two consequences:typography the design depends on silently falls back to system fonts. The dashboard remains usable —
this is not a functional break — but it does not look as intended, and there is no indication why.
Google that someone opened it. For a tool whose stated model is local-first and whose own docs
emphasise an auditable local trail, that is surprising.
JetBrains MonoandInterare both SIL Open Font License, so they can be vendored without licensingfriction.
What you expected
The dashboard renders identically with no network access. Self-host the two families under
src/ui/static/(subset woff2,@font-facewithfont-display: swap) and drop the remote@import,keeping a system-font stack as the fallback.
If a remote fetch is deliberate, it should at least be degradable on purpose — a local
@font-facefallback so the intended typography survives without egress.
Steps to reproduce
In Chrome DevTools → Network, filter
gstatic: two woff2 requests are issued on load. To see thefailure mode directly, block
fonts.googleapis.comandfonts.gstatic.com(DevTools → Network requestblocking, or an offline host) and reload — the requests fail and the fonts fall back.
Scripted, with Playwright against Chrome stable:
Or with no browser at all:
Environment
Severity
low
Logs / screenshots
The rest of the dashboard checks passed in the same run, so this is isolated to the font fetch: