ADR-0012: Browser Extension for Single-Word Hostname Navigation
Context and Problem Statement
joe-links uses a bare single-word hostname (go) configured via /etc/hosts. All modern
browsers — Safari, Chrome, and Firefox — treat single-word address-bar entries as search
queries before attempting DNS resolution. This means typing go/foo in any browser opens a
search for "go/foo" rather than navigating to the go-links service. How should we make
go/foo (and future keyword hosts like wtf/foo, gh/foo) resolve correctly in the browser
without requiring users to prefix every URL with http://?
Decision Drivers
- The whole point of go-links is frictionless navigation — requiring
http://defeats the UX - The solution must work on macOS for personal use (not a corporate MDM/policy environment)
- Safari, Chrome, and Firefox all need to work (or at least Chrome + Safari as primary targets)
- Future keyword hostnames (📝 ADR-0011) follow the same single-word pattern and need the same fix
- The solution should not require changing the hostname format (e.g., adding
.lan) because that breaks the canonicalgo/fooUX that go-links is known for
Considered Options
- Option A — Web Extension (Manifest V3, cross-browser) (chosen)
- Option B — Dot-suffix hostname (e.g.,
go.lan) - Option C — OS-level PAC (Proxy Auto-Config) file
- Option D — Local DNS resolver (dnsmasq) + browser intranet heuristics
- Option E — Require explicit
http://prefix
Decision Outcome
Chosen option: Option A — Web Extension, because it is the only approach that reliably intercepts the address bar before the browser sends input to the search engine, works across Chrome and Safari, and scales naturally to the multi-keyword-host feature (📝 ADR-0011) by reading the server's registered keyword list at runtime.
Consequences
- Good, because
go/fooworks exactly as intended in Chrome (sideloaded unpacked extension) and Safari (converted viaxcrun safari-web-extension-converter) - Good, because the extension can fetch
/api/v1/keywordsto dynamically discover keyword hosts, making 📝 ADR-0011'swtf/fooandgh/foowork automatically - Good, because the extension is ~30–50 lines of JS and a manifest — low maintenance surface
- Bad, because users must install the extension in each browser (one-time setup)
- Bad, because Safari requires an Xcode project and macOS app wrapper for distribution; sideloading is possible but more involved than Chrome
- Bad, because Chrome's Manifest V3 restricts
webRequestblocking; the extension must usedeclarativeNetRequestor theomniboxAPI instead of intercepting raw requests
Confirmation
Implementation is confirmed when:
- Typing
go/foo(nohttp://) in Chrome navigates to the go-links service - Typing
go/fooin Safari navigates to the go-links service (via converted extension) - Typing a non-go-link term (e.g.,
weather) still goes to the default search engine - Adding a new keyword host (
wtf) causeswtf/footo navigate correctly without reinstalling or reconfiguring the extension
Pros and Cons of the Options
Option A — Web Extension (Manifest V3, cross-browser)
A service worker registers an omnibox keyword or uses declarativeNetRequest rules combined
with webNavigation listeners to detect single-word-hostname patterns and rewrite them to
http://{host}/{path} before the browser sends the request. The extension ships a
manifest.json, one background service worker, and optionally a small options page. Chrome can
load it as an unpacked extension; Safari uses xcrun safari-web-extension-converter to wrap
it in a macOS app.
- Good, because it solves the problem at exactly the right layer (browser input → URL rewrite)
- Good, because MV3 is supported in Chrome, Edge, Firefox, and (via converter) Safari
- Good, because the extension can query the joe-links API for registered keyword hosts, enabling dynamic support for 📝 ADR-0011 keyword forwarding
- Neutral, because MV3
declarativeNetRequestis more complex than the oldwebRequestAPI - Bad, because manual install is required per browser; no auto-update without a CWS listing
- Bad, because Safari distribution requires Xcode and signing (sideload is possible but hacky)
Option B — Dot-suffix hostname (go.lan, go.local, go.test)
Add a dot to the hostname so browsers recognise it as a domain rather than a search term.
Update /etc/hosts to 127.0.0.1 go.lan, update Dex redirectURIs, update the app config.
- Good, because no extension needed — works in all browsers immediately
- Bad, because
go.lan/foois not the same UX asgo/foo;.localis mDNS-reserved - Bad, because every new keyword host also needs a dot suffix, compounding the UX regression
- Bad, because this directly contradicts the premise of go-links as a frictionless shortcut
Option C — OS-level PAC (Proxy Auto-Config) file
Configure macOS Network Settings to use a file:// PAC that intercepts certain hostnames and
proxies them to the local server.
- Good, because it's OS-wide — all browsers and apps benefit
- Bad, because PAC files are evaluated after the browser decides whether to search or navigate; a single-word entry never becomes an HTTP request for the PAC to intercept
- Bad, because PAC requires maintaining a separate JS file and configuring Network settings
Option D — Local DNS (dnsmasq) + browser intranet heuristics
Run dnsmasq locally to resolve go → 127.0.0.1 and hope browsers' intranet-detection
heuristics eventually learn to navigate rather than search.
- Good, because it's transparent to the user after initial setup
- Bad, because browser heuristics are not reliable or documented; Chrome removed the "search single-word intranet names" setting in recent versions; Safari has no such setting
- Bad, because dnsmasq adds a persistent background service and DNS configuration complexity
- Bad, because it still doesn't reliably work in Safari
Option E — Require explicit http:// prefix
Document that users must type http://go/foo. No code changes needed.
- Good, because zero implementation cost
- Bad, because this is exactly the friction go-links is designed to eliminate
- Bad, because new users will consistently make the mistake and file bugs
Architecture Diagram
More Information
- The extension interacts with 📝 ADR-0011 keyword hosts: it fetches
/api/v1/keywordsfrom the configured joe-links base URL to build its list of recognised single-word hostnames at startup and on a periodic refresh interval. - The canonical go-links hostname (
go) is always included in the extension's host list, even before any keyword hosts are configured. - For Chrome, the extension can be distributed as an unpacked load from the repo directory.
For Safari,
xcrun safari-web-extension-converter integrations/extension/produces an Xcode project that can be built and sideloaded with a free Apple Developer account. - Related: 📝 ADR-0011 (root forward keywords), 📝 ADR-0007 (routing).
- Web Extensions specification: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions