Skip to content

fix(dash0): show the region browser capability the API already advertises - #265

Merged
fclairamb merged 13 commits into
mainfrom
batch/2026-08-26
Aug 26, 2026
Merged

fclairamb merged 13 commits into
mainfrom
batch/2026-08-26

Conversation

@fclairamb

Copy link
Copy Markdown
Owner

Regions have advertised a browser capability since spec 2026-08-19-03 — whether a
worker has a Chrome to drive — with the same three-state yes / no / unknown
semantics as IPv6, aggregated per region and already on the wire in the public
regions response. The dashboard rendered none of it, so anyone creating a browser
check picked regions blind.

No backend changes: reporting, aggregation and the wire format already existed. This
is purely the missing UI.

The icon

BrowserCapabilityIcon + a browserCapability() helper, mirroring
ipv6-capability.tsx. Deliberately icon-only — a lucide AppWindow whose color
carries the state and whose tooltip carries the explanation. A second text badge
beside the IPv6 one would have crowded the region picker, which is the whole reason
this renders as an icon.

Three states, and unknown is never collapsed into no. That distinction is the
point: a region with no live worker, or one running an older agent, may well support
browser checks — telling the user "no" would be wrong. The helper returns unknown
for an absent map, a missing key, and an unrecognised value, so it never guesses.

hideUnknown keeps dense surfaces quiet, with one guarantee that makes it safe:
no always renders, so a missing icon can never be misread as a negative.

Where it shows

  • Region picker (check-form.tsx) — always for a browser check; for other
    types it only speaks up when it has a definite yes/no.
  • Private locations page — beside the existing IPv6 badge.
  • Design reference — all three states with the import line, per the repo's
    frontend convention.

Hint, never a gate

The advertised value does not hide, filter, or disable a region — the runtime worker
remains the authority. The region sort order is untouched (sorting browser-capable
regions first was raised in the spec and explicitly left out of scope).

Tests

11 unit tests covering each state, the three paths to unknown, and both halves of
hideUnknown — including that no still renders under it. Note this is strictly
more coverage than the component it mirrors: the IPv6 badge has no unit tests at all.
An e2e spec mirroring check-region-ipv6-capability.spec.ts is included; it was
authored but not executed locally (no test-mode server in that session), so CI is its
first real run.

No locale keys were added, deliberately: ipv6-capability.tsx carries no i18n and
there are zero ipv6 keys across all four locale trees. Adding keys only here would
have made the new icon translated while the badge sitting next to it stayed
English-only.


Note on the diff: this branch also carries a pre-existing commit that is not part
of the spec work — 828272e8b feat(openapi): point the explorer at its own origin and list the cloud server. It is bundled here because it was already on the batch branch.

🤖 Generated with Claude Code

fclairamb and others added 13 commits August 26, 2026 09:55
…d server

The Scalar explorer is served by the instance it documents, so override the
spec's static servers list with window.location.origin at load time; the
spec itself now lists https://solidping.io ahead of localhost for everyone
reading it outside the explorer. Drop the hand-written docs api
_category_.json — the generated API reference manages its own sidebar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors ipv6-capability.tsx's three-state (yes/no/unknown) contract for the
browser capability already on the wire in the regions API
(server/internal/regions/capabilities.go). Icon-only (no label text) so it
stays quiet next to the IPv6 text badge; unknown never collapses to no.
Adds the icon next to the IPv6 badge in check-form.tsx's region list. Always
meaningful for a browser check (all three states shown); for other check
types it stays quiet via hideUnknown, only speaking up on a definite yes/no.
Renders next to the existing IPv6 badge in the regions table.
Per the repo's frontend convention: every shipped component belongs in the
design reference alongside its import line.
Mirrors agent-version.test.tsx's structure: helper state transitions plus
component rendering, with explicit negative assertions that unknown never
collapses into no and that hideUnknown still lets a definite no/yes through.
…on picker

Mirrors check-region-ipv6-capability.spec.ts: mocks all three states
deterministically and asserts unknown never renders as no, nothing is
hidden/disabled, and hideUnknown keeps a non-browser check type quiet on
unknown while still showing a definite no.

Authored but not run locally — no SP_RUNMODE=test devloop is up on :4000 in
this session (a shared working tree, avoided starting a side-car server to
not disturb concurrent batch work).
/openapi.yaml is consumed directly by code generators, Postman and agents,
none of which have the client-side hook the Scalar explorer uses. They were
handed a hardcoded https://solidping.io, so a spec fetched from a self-hosted
instance, a custom domain or localhost pointed every generated client at the
wrong host.

The servers block is now rebuilt per request from the origin the request
actually arrived on (reusing requestOrigin, so X-Forwarded-Proto is honoured
behind a proxy), with the cloud kept as a second entry and never duplicated
when the origin already IS the cloud. The embedded file is untouched: the docs
site generates its reference from it at build time and should keep advertising
the cloud.

The Host header is client-controlled, so anything that could not appear in a
real deployment falls back to the embedded list rather than being spliced in —
Go's server already rejects a malformed Host with 400, this is the second line.

Also sets an explicit Content-Type: serveFile passes a whole path to
mime.TypeByExtension, which takes an extension, so the spec was going out
untyped.
828272e removed web/docs/docs/api/_category_.json, which was the
generated-index that created the /docs/api page. Two links in the migration
guide still pointed there, so the Docusaurus build failed on broken links and
took the whole CI gate down with it.

Rather than restore the hand-written category — the point of removing it was to
let the generated reference own its own sidebar — the links now target the
reference's own index page, /docs/api/solidping-api (id solidping-api, the page
every generated operation already references as info_path).
serveFile passed a whole embed path to mime.TypeByExtension, which takes an
EXTENSION. It therefore returned "" for every file, and the result was written
through as the header value — so /openapi served its HTML with an empty
Content-Type. Setting the header empty is worse than leaving it off, because an
explicitly-set Content-Type suppresses net/http's own sniffing: the response
went out genuinely untyped rather than sniffed.

Extraction into contentTypeForFile, which takes the extension, and which the
caller only writes when it resolves to something. YAML is pinned explicitly
(RFC 9512) because Go's table is seeded from the OS mime database and misses it
on macOS while resolving it on a typical Linux image — precisely the difference
that makes a header appear in CI and vanish locally.

serveOpenAPISpec now shares the helper instead of hardcoding the YAML type.
writeDocsFile (the /docs and /llms.txt path) already did this correctly, with a
DetectContentType fallback, and is untouched.

Verified live: /openapi is text/html, /openapi.yaml application/yaml, both
previously empty. The regression test fails against the original one-liner.
The previous commit consulted Go's mime table first and used the pin only as a
fallback. Go seeds that table from the host, so .yaml answers
"application/yaml" on a typical Linux image and nothing at all on macOS: the
served Content-Type therefore depended on the build machine, which is the exact
variance the helper was added to remove. CI caught it, the local run could not.

The pin is now consulted first, so the answer is identical everywhere.

The regression test no longer depends on the host it runs on either — it calls
mime.AddExtensionType to reproduce the Linux entry on any machine, then asserts
the pin still wins. Against the previous ordering it fails on macOS with the
same "application/yaml" vs "application/yaml; charset=utf-8" mismatch CI
reported.

The map became a switch to satisfy gochecknoglobals.
@fclairamb
fclairamb merged commit 22d968a into main Aug 26, 2026
11 checks passed
@fclairamb
fclairamb deleted the batch/2026-08-26 branch August 26, 2026 10:08
fclairamb added a commit that referenced this pull request Aug 26, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.18.3](v0.18.2...v0.18.3)
(2026-08-26)


### Bug Fixes

* **dash0:** show the region browser capability the API already
advertises ([#265](#265))
([22d968a](22d968a))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Sep 8, 2026
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