Front-end navigation map

How a request becomes a screen today, on the two hosts that matter (cleaker.me and netget.site), what the target model is, and where they differ. Written 2026-09-21 from code and from a read-only copy of the VM’s installed netget_app.conf. Nothing here was changed by writing it.

Legend: [code] read in the repository, [conf] read in the VM’s installed nginx configuration, [not verified] inferred, not observed.

1. Target model (decided, not yet implemented)

2. Today

2.1 One bundle, three roles

frontend_local builds one bundle (served from assets/main-server-ui/dist). frontendRole({host, boot}) [code: frontend_local/src/session/providerBoot.js:46] picks the role:

Condition (first match wins) Role Renders
host is local.cleaker cleaker CleakerLanding, whole page
host is local.host host HostSurface
a provider boot exists and the host is the namespace or a handle under it cleaker CleakerLanding
a provider boot exists and its monad is not a gateway monad cleaker CleakerLanding
anything else (no boot, or a gateway monad on a non-namespace host) gateway NetGetShell

The boot (window.__MONAD_NAMESPACE_PROVIDER_BOOT__) is injected by the monad when it hands out index.html.

2.2 cleaker.me (and www, and <handle>.cleaker.me)

2.3 netget.site

Route Screen
/ GatewayEntry → GatewaySetup (endpoint "", same origin)
/home Home → GatewayDashboard (/gateway-identity, /apps, polled every 5 s)
/domains Domains (REST: /domains, /add-domain, /delete-domain, /provision-cert)
/logs Logs (REST: /logs)
/terms-and-conditions, /privacy-policy static pages

2.4 Where the two disagree

  cleaker.me netget.site
Same tree path gives the same answer yes (every path → monad) no
Gateway status screen MainServerView (/netget) GatewayDashboard (/home) — a different component
Claim screen GatewaySetup at /netget GatewaySetup at /
Session / keychain yes none in this shell
.me launcher context resolved fixed local.cleaker
Monad link target — /monads/<name> (old internal alias; public form is /apps/<name>) [code: Home.jsx:26]

3. Paths netget.site answers before the namespace can [conf]

These exist as nginx locations on the netget.site server and therefore shadow a tree path of the same name there, while cleaker.me would pass the same path to the monad:

Consequence for the target model: the gateway API has to move under /.gateway/* (or otherwise stop sharing names with tree paths) before netget.site/x can equal cleaker.me/x. /domains and /logs are today both an SPA page and an API route on the same path (nginx tells them apart by Sec-Fetch-Mode: navigate, which a browser sets only on real navigations, and rewrites those to index.html) — the clearest case of the API and the screen sharing a name.

4. GatewayDashboard (netget.site /home) — review

Files: gui/src/compounds/GatewayDashboard/GatewayDashboard.tsx, gui/src/molecules/GatewayCard/GatewayCard.tsx, gui/src/compounds/MonadMesh/MonadMesh.tsx. Same criterion as the MainServerView fix: an absent fact must not be shown as a negative fact, and different names must not share a field.

# Where What it does Why it misleads
1 GatewayDashboard.tsx:49 adminCount: data.adminCount ?? 0 A response without the count shows “Admins 0”. This is the original adminCount: 0 symptom from the Lua handler.
2 GatewayDashboard.tsx:48 bootstrapped: !!data.bootstrapped A missing field reads as “unclaimed”.
3 GatewayDashboard.tsx:46 gatewayId: data.gatewayId ?? 'unknown' Presents “unknown” as if it were an id.
4 GatewayCard.tsx:10–11,49 Title is gatewayId, documented as “hostname / node ID” Two different facts in one field. The contract now carries hostname separately; the card ignores it.
5 GatewayCard.tsx:64 Owner shown only as a hash (HashLabel) The contract carries ownerUsername; MainServerView shows it, this card does not — the same gateway reads differently on the two hosts.
6 GatewayCard.tsx:71 “Listening on scheme://ip:port” ip is the machine’s local IPv4 from the contract, not what nginx was verified to listen on. The label claims more than the field says.
7 GatewayCard.tsx:21,103 updatedAt typed as ISO string, labelled “snapshot” The contract sends epoch milliseconds (still renders); “snapshot” describes the cache, while the authority is the monad’s canonical branch.
8 GatewayCard.tsx:2 Header says it reads gateway-claims.json Out of date: that file is a cache of the canonical branch.
9 MonadMesh.tsx:47 Array.isArray(data.apps) ? data.apps : [] A 200 without apps shows “No live monads registered” (a non-2xx or non-JSON reply does show its error).
10 MonadMesh.tsx:102–103 trust ?? 'guest', exposure ?? 'loopback' A missing value is shown as a definite one; “loopback” states the monad is not exposed.
11 MonadMesh.tsx:83 Hint says to call netget.registerApp() Stale instruction.
12 Home.jsx:26 Monad click → window.location.href = /monads/<name> Leaves the SPA through the old alias; the public form is /apps/<name>.

Not a defect, worth knowing: fetch failures do show the real message (“404 Not Found”), which is the honest behaviour MainServerView needed for /entrypoints.

Findings 1–3 are fixed locally: missing gateway IDs, claim status, and admin counts now show explicit unavailable states. An explicit false still means unclaimed and an explicit 0 still shows zero. GatewayDashboard rejects malformed values for these three fields. The MissingIdentityFields story covers a successful but incomplete response. Two neighbours of the same class are closed as well: a missing owner shows “Owner unavailable” (an explicit null still shows “not set”) and a missing scopes shows “Not available” (an explicit [] still shows 0). The mapping lives in gui/src/compounds/GatewayDashboard/identityView.ts and is covered by tests/gateway-identity-view.test.ts (part of npm test). Findings 4–12 remain pending; this does not imply deployment.

5. What is still unknown

6. Order of work implied (nothing started)

  1. Inventory consumers of the section 3 paths (GUI, CLI, monads’ registration, tests, docs).
  2. Introduce /.gateway/* as the control API next to the existing paths; move consumers; then retire the old names on the netget.site server.
  3. Declare the administrative screens in the GUI document as routes with their own components.
  4. Make netget.site resolve tree routes through the same document as cleaker.me, with admin as the initial screen; drop the fixed local.cleaker launcher in favour of the resolved context and transport.
  5. Apply the section 4 corrections (independent of 1–4).