Skip to content

feat(proxy): per-proxy status hosts + HTTPS interstitial permit-gate - #4050

Merged
0pcom merged 1 commit into
skycoin:developfrom
0pcom:feat/proxy-status-and-interstitial-https
Aug 21, 2026
Merged

feat(proxy): per-proxy status hosts + HTTPS interstitial permit-gate#4050
0pcom merged 1 commit into
skycoin:developfrom
0pcom:feat/proxy-status-and-interstitial-https

Conversation

@0pcom

@0pcom 0pcom commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Two related features on the embedded-web-proxy surface (dmsg_web, skynet_web, and the skysocks-client tunnel they chain to). Design + first working increment; read-only MVP with explicit seams for route control. See docs/proxy-status-and-interstitial.md.

1. Interstitial over HTTPS

The branded "building a route over the mesh…" interstitial (pkg/proxyinterstitial) rides a locally-terminated TLS session (TLS MITM) for HTTPS targets, since a raw-TLS tunnel can't carry an HTML page. TLS termination uses a name-constrained local CA (pkg/skynetca) that can only mint leaves for .skynet/.dmsg.

The live bug: the HTTPS-interstitial path attempted a mint for every TLS-port dial failure — including clearnet HTTPS forwarded to the upstream skysocks-client, which the CA can never cover — producing a host does not match permitted suffix error on every such request.

Fix: skynetca.Permits(minter, host) — a non-breaking optional interface (HostPermitter, implemented by CachedMinter) that reports whether the CA can cover a host without minting. Both resolving proxies now gate the HTTPS-interstitial mint on it:

  • .skynet/.dmsg HTTPS → interstitial renders over TLS as intended;
  • clearnet HTTPS (un-MITM-able by a name-constrained CA) → falls through cleanly to the real error, no per-request log spam.

The interstitial page also gained a footer deep-link to the surface's status host.

2. Per-proxy status hosts

Each proxy serves a read-only diagnostic page at a reserved, well-known host through itself, mirroring the existing in-process home.<suffix> host:

host surface app
http://status.dmsg/ dmsg dmsgweb
http://status.skynet/ skynet skynetweb
http://status.skysocks/ skysocks skysocks-client

New pkg/proxystatus owns the surface taxonomy, host matcher (Match), the in-process HTTP responder (ServeConn, same net.Pipe trick as serveHomeInProcess), the read-only Snapshot/Provider contract, and the HTML renderer. Both resolving proxies' Dial callbacks check Match(host) before suffix resolution / upstream forwarding, so any of the three hosts is reachable through either proxy.

The visor implements proxystatus.Provider (pkg/visor/embedded_proxystatus.go) entirely on existing read APIs:

  • loggingLogsSince(app) tails the app's log store;
  • mux viewRouteGroupMuxInfo(app) gives the same per-leg bandwidth/RTT/retransmit telemetry cli proxy mux plot renders, drawn as a per-leg bandwidth-share table (meta-refreshes to stay live);
  • runningprocManager.ProcByName(app).

Implemented vs. scaffolded

  • status.skysocks — full MVP: logs + live per-leg mux view of the route group where multiplexing actually happens.
  • status.dmsg / status.skynet — share the identical page; mux section is empty until their route group is tagged for RouteGroupMuxInfo.
  • route/transport events — empty section today; the collection buffer is the scaffolded extension point.

Extension seam: route control

MVP is deliberately read-only. The page renders a disabled "route control" section, and Snapshot/Provider are shaped so control lands additively: add a mutating method to proxystatus.Provider, implement it on the existing visor mux-reshape API (AddMuxRoute/RemoveMuxRoute/SetMuxMode) + dmsg relay selection. No wire reshape, no new proxy plumbing. Interception in skysocks-client for the browser-points-directly-at-:1080 case is a noted future seam.

Scope / testing

Contained to the embedded-web-proxy packages + pkg/skynetca (the permit helper) + a small visor status provider; pkg/router/policy untouched. go build ., go vet, make format clean. New unit tests for pkg/proxystatus (Match/Render/ServeConn), skynetca.Permits, and the interstitial status footer.

Two related features on the embedded-web-proxy surface.

1. Interstitial over HTTPS. The branded route interstitial's TLS-MITM
   path attempted a leaf mint for every TLS-port dial failure, including
   clearnet HTTPS a name-constrained CA can never cover — producing a
   "host does not match permitted suffix" error on every such request.
   Add skynetca.Permits (non-breaking optional HostPermitter interface)
   and gate the HTTPS-interstitial mint on it, so .skynet/.dmsg targets
   reliably render the interstitial over TLS while clearnet HTTPS falls
   through cleanly to the real error. The page gains a footer deep-link
   to the surface's status host.

2. Per-proxy status hosts. Each proxy serves a read-only diagnostic page
   at a reserved host through itself — status.dmsg (dmsg_web),
   status.skynet (skynet_web), status.skysocks (skysocks-client) —
   mirroring the in-process home.<suffix> host. New pkg/proxystatus owns
   the surface taxonomy, host matcher, in-process HTTP responder, the
   read-only Snapshot/Provider contract and HTML renderer; the visor
   implements Provider on existing read APIs (LogsSince for logging,
   RouteGroupMuxInfo for the per-leg mux view, ProcByName for running).
   Both resolving proxies intercept the status hosts before suffix
   resolution / upstream forwarding, so any surface is reachable through
   either proxy. status.skysocks is the full MVP (logs + live mux view);
   the others share the page. Route/transport events and route CONTROL
   are explicit, marked extension seams (disabled control section,
   additive Provider/Snapshot shape).
@0pcom
0pcom merged commit e658c42 into skycoin:develop Aug 21, 2026
12 of 16 checks passed
0pcom added a commit that referenced this pull request Aug 21, 2026
…ntrast (#4053)

Two follow-ups to the per-proxy status pages (#4050).

HTTPS without a cert warning. proxystatus was HTTP-only (in-process SOCKS
splice) at bare hosts, so https://status.skysocks/ needed the self-signed
skynetca CA installed. Serve the same pages through the browse-origin
listener (pkg/visor/meshproxy.go), which already terminates TLS with the
deployment's real wildcard cert under BrowseOrigin.Suffix — reached at a
single-label host (status-<surface>.<suffix>, e.g.
status-skysocks.haltingstate.net) that a single-level wildcard covers, so
https:// loads clean with no CA install. meshStatusHandler intercepts these
hosts on the browse-origin mux (subdomain and port modes) before the reverse
proxy; every other host falls through. The plain-HTTP SOCKS path remains the
fallback when browse-origin is disabled/unconfigured.

Contrast. The dark status page's muted grey (#7a80a8) and the light block's
greys were near/below WCAG AA. Raise --muted (dark #a2a8cc ~8:1, light
#4a4f63) and darken the light-mode status colors (--ok/--warn/--standby, whose
dark brights are illegible on white) plus the near-white h2/surface, so every
text token clears 4.5:1 in both schemes. Accent gradient / identity unchanged.
Locked by a WCAG contrast unit test.
@0pcom
0pcom deleted the feat/proxy-status-and-interstitial-https branch August 24, 2026 23:45
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