Summary#
- Add a knot status indicator that polls each unique knot server every 60s and shows a colored dot (amber=checking, green=online, red=offline) next to repo links and knot domain names
- Hovering the indicator shows a popover with status details and a link to the knot URL
- Clicking a repo link while its knot is offline shows a toast warning on the first click; the second click navigates normally
- Hybrid check strategy: client tries CORS first (fast path, no server load); on failure, falls back to a new appview
/knot-statusendpoint that probes server-side via thetangled.OwnerXRPC call - CORS-capable knots are remembered across poll cycles so subsequent checks skip the server entirely
Motivation#
Ephemeral knot servers (e.g. running on a laptop, https://smart-knowledge-systems.com/knot) go offline when the machine sleeps. Currently, there's no indication that a knot is down — clicking a repo link hangs. This gives users a heads-up before they click.
Standard knot servers don't send CORS headers, so browser-only fetch with mode: "cors" can't distinguish "offline" from "no CORS". The server-side fallback resolves this ambiguity while giving knot operators a migration path: add CORS headers for direct checks, or rely on the appview proxy.
Changes#
appview/state/knot_status.go— new file: TTL cache (30s),probeKnot()usingtangled.OwnerXRPC with 5s timeout,GET /knot-status?domains=...handler with SSRF prevention (only registered knots are probed), concurrent probing, JSON response withCache-Control: public, max-age=15appview/state/state.go— addknotStatusCachefield toStatestruct, initialize inMake()appview/state/router.go— registerGET /knot-statusroute (no auth middleware)appview/pages/templates/fragments/knotStatus.html— hybrid CORS-first + server fallback JS: try direct fetch, remember CORS-capable domains, batch-fallback to/knot-statusfor failuresappview/pages/templates/layouts/base.html— include the knot status script before</body>appview/pages/templates/user/fragments/repoCard.html— show indicator on repo cardsappview/pages/templates/layouts/repobase.html— show indicator in repo page headerappview/pages/templates/knots/fragments/knotListing.html— show indicator on settings/knots page
Test plan#
- Visit a profile page with repos on an ephemeral knot — verify dot appears next to repo names
- Turn off the knot server — verify dot turns red within 60s
- Hover the dot or repo link — verify popover shows status and knot URL
- Click a repo link while knot is offline — verify toast warning appears
- Click again — verify navigation proceeds
- Turn knot back on — verify dot turns green within 60s
- Visit a repo page — verify indicator in the repo header
- Visit settings/knots — verify indicator next to knot domains
- Verify polling pauses when tab is hidden, resumes when focused
- Test knot with CORS support — should resolve client-side, no
/knot-statuscall in network tab - Test knot without CORS — CORS fails silently, falls back to
/knot-status, shows correct status - Test unregistered domain in
/knot-status— returns"unknown", no outbound probe - Verify caching: second
/knot-statusrequest within 30s returns instantly without re-probing
More on motivation: https://www.smart-knowledge-systems.com/blog/the-knot-that-sleeps