hermes-gateway-web-ui
Skill0 starsChanged 20 days agoMITView on GitHub
Use when connecting a web UI to the Hermes gateway.
- Reads credentials
In this repository86 other files it gives its agents.
- ai-assistant-editor-setupSkill
- api-and-interface-designSkill
- banner-designSkill
- brainstormingSkill
- caveman-exploreSkill
- caveman-reviewSkill
- chat-silence-diagnosisSkill
- ci-cd-and-automationSkill
- code-auditSkill
- codebase-designSkill
- koordinator-team-dispatchSkill
- constraint-driven-developmentSkill
- context-engineeringSkill
- cpanel-shared-hosting-deploySkill
- cross-profile-cron-opsSkill
- deprecation-and-migrationSkill
- documentation-and-adrsSkill
- domain-modelingSkill
- doubt-driven-developmentSkill
- finishing-a-development-branchSkill
- frontend-designSkill
- git-workflow-and-versioningSkill
- graphic-design-laneSkill
- hermes-profile-hygieneSkill
- hermes-profile-provisioningSkill
- hermes-service-persistenceSkill
- hermes-stream-stale-diagnosisSkill
- hermes-team-dashboardSkill
- idea-refineSkill
- incremental-implementationSkill
- indonesian-official-lettersSkill
- internal-commsSkill
- interview-meSkill
- investigate-firstSkill
- kanban-rate-limit-resilienceSkill
- karpathy-guidelinesSkill
- large-project-gated-deliverySkill
- lean-buildSkill
- live-agent-office-operationsSkill
- live-react-runtime-qaSkill
- live-web-release-verificationSkill
- markdown-pdf-pipelineSkill
- marketing-strategySkill
- meta-social-publishingSkill
- meta-threads-oauth-lifecycleSkill
- migrationSkill
- multi-profile-project-orchestrationSkill
- network-operationsSkill
- observability-and-instrumentationSkill
- office-skill-distributionSkill
- organization-site-menu-researchSkill
- performance-optimizationSkill
- php-upload-size-limitsSkill
- planning-and-task-breakdownSkill
- ponytail-auditSkill
- ponytail-debtSkill
- ponytail-reviewSkill
- ponytailSkill
- portable-app-handoffSkill
- receiving-code-reviewSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 49
Correlate the user-visible failure first: Read DevTools Network/Console for the failed shell request's status/body/time.
- Very specific: line 58
Correlate logs at the screenshot timestamp: Check app log and journalctl --user -u <unit> --since ....
- Very specific: line 61
Check systemctl --user list-timers | grep <name> for watchdog activity.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
When to Use
Use this when installing or debugging a self-hosted web UI (Hermes3D, a custom dashboard, a gateway-connected frontend) that must talk to a Hermes gateway: when the UI reports "not connected", "no gateway", a 403, a WebSocket timeout, or a full-page "Hermes is restarting… / shell could not load cleanly" notice, and you must determine whether the backend or the browser is at fault.
Connecting a self-hosted web UI to the Hermes gateway
Always-on rules
- Prove the backend before touching the browser, then test the exact failing path. A UI status label describes the browser's view, not the server's state. Probe the backend health endpoint first, but treat health as transport/liveness evidence only: it does not prove authenticated APIs, session retrieval, SSE, or WebSocket flows are healthy. Use DevTools Network/Console on the affected tab and correlate the failed request's URL, status/body, and timestamp with server logs before attributing cause.
- Never stop or reconfigure the existing gateway process to make room. Run a second isolated instance instead (step 2).
- Do not report migration success until the new UI renders and is verified end-to-end. Old services stay running until the new one is proven — then and only then remove them.
- Never print tokens/keys into chat, screenshots, or report text; mask them (
***, len only).
Procedure
- Install the app into a project dir (
git clone ... && npm install). - Start a backend on a free port, isolated from the main gateway:
hermes serve --isolated --host 127.0.0.1 --port <free> --skip-build(The default dashboard port is usually already taken by the running gateway — that is expected, not an error.) - Create a dedicated token for the app in
~/.hermes/.env(dedupe to ONE entry), plus a copy under the profile scratch dir withchmod 600. - Point the app's env at the backend (
ws://localhost:<port>+ token + adapter type). Check whether the app re-reads.envat runtime or needs a restart. - If you run the app as a production build (
npm run build+npm start), set the outbound allowlist env first (UPSTREAM_ALLOWLIST=localhost,127.0.0.1, plusCUSTOM_RUNTIME_ALLOWLISTif present) — production mode refuses any upstream host not listed (see Pitfalls). - Verify the WebSocket handshake: token must go in the query string —
?token=→ HTTP 101; Authorization/X-Session-Token headers → 403. Seereferences/gateway-ws-protocol.mdfor the exact curl. - Verify the protocol with a Node probe (
node ws_probe.js) and expecthello-okplus a profile list. This isolates server vs. browser in one shot. - Isolate the browser: test the WS from a non-app page on the same origin — navigate the browser to a plain endpoint (an API route that renders JSON), then from the console run
new WebSocket('ws://<host>/api/gateway/ws')and wait foronopen. If it opens in milliseconds there but never opens inside the app, the network path is proven healthy and the fault is the app's own JS on that page. - Localize the fault from the failing request. If health fails, debug server availability, logs, and port. If health succeeds but an API fails, inspect its actual auth/session response and correlate server logs; if APIs succeed but streaming fails, test the stream/WebSocket handshake and gateway run state. Do not conclude the browser/UI is at fault solely because health succeeds, and do not recommend a restart without evidence it will address the failing layer.
- Verify visually (screenshot of the rendered UI) before declaring done, then remove the old services.
- Remote access should prefer a private overlay network over public exposure. For Tailscale, verify both client and server are in the same tailnet; install/authenticate Tailscale on the server, then expose a loopback-only WebUI with
tailscale serve(not Funnel) or a localhost SSH tunnel. Confirm the exact Serve syntax withtailscale serve --help, test from a tailnet peer, and keep authentication enabled. A WebUI bound only to127.0.0.1is not reachable through the server's Tailscale IP unless a local reverse proxy/Serve forwards it. Decision table and the full install/approval/verification recipe:references/private-remote-access-mesh-vpn.md. - Public-IP publishing is a separate gated phase — see
references/public-reverse-proxy-exposure.md: loopback first, first-run password BEFORE exposure (onboarding wizards can auto-complete with auth still off), negative auth probes, then the proxy vhost.
Diagnosing a "restarting / shell could not load" page
The page is an error fallback (often HTTP 503 from the shell route's exception handler), not proof the process is dead. Diagnose before explaining or restarting:
- Correlate the user-visible failure first. Read DevTools Network/Console
for the failed shell request's status/body/time. Probe
/login,/health?deep=1, and one authenticated API (401 unauthenticated is normal). A healthy later probe proves recovery now, not what caused the earlier page; label the old event transient only when logs support that. - Map listener to service.
ss -ltnp | grep <port>identifies the live PID; compare it withsystemctl --user status <unit>Main PID. One FATAL saying another server owns the port usually belongs to the newcomer, while the original process survives. Do not call it a crash loop from that line. - Correlate logs at the screenshot timestamp. Check app log and
journalctl --user -u <unit> --since ...; distinguish failed second start, shell-route exception, health-watchdog restart, and browser-side stale page. Checksystemctl --user list-timers | grep <name>for watchdog activity. - Prove restart frequency. Count actual start/stop events since incident; repeated starts mean loop, debug cause before claiming recovery. If current probes pass and no repeat starts appear, tell user to refresh; do not restart healthy service without evidence.
Pitfalls
- Reading a FATAL crash line as "the server is dead" — a log line like
Another server is already responding on <port>means the CRASHING pid is the newcomer losing a port race; the original listener survived and is still serving. Identify which PID owns the listening socket before diagnosing; the user's page was a hit during the restart window, and the fix is a refresh, not a restart. - Send the WS token as
?token=, not a header — the gateway's WS auth only reads the query, so headers fail with 403 and look like an auth bug when the token is fine. - Use
{type:'req', method:'connect', id, params}for the gateway connect frame — a bare{type:'connect'}(ormethod-only) frame is rejected with close1008 connect required, which reads as "client never connected". - Fill React-controlled inputs with the native value setter (
Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set+ dispatchinput/change) — a plain.value =assignment leaves React state empty, so the form looks filled but submits nothing. - Don't patch
window.WebSocketto sniff frames on a page you are trying to connect — wrapping the constructor can break the handshake itself and send you chasing a bug you created; record passively or use CDP instead. - A UI label can be misleading while the backend is healthy (e.g. "No local gateway found" after a successful server handshake) — trust the probe result over the label, and check the server's own logs for the browser's attempt.
- Confirm which URL the browser actually dials — apps often route through a local proxy path (
ws://localhost:3000/api/gateway/ws) rather than the backend directly; verify at that hop. - Production builds gate outbound upstream hosts behind an allowlist — with
UPSTREAM_ALLOWLISTunset, connect fails browser-side with a generic1011 connect failed/studio.gateway_url_blockedwhile the server log saysrefusing upstream connection: UPSTREAM_ALLOWLIST is empty in production. Set it in the app's.envbefore testing production mode; dev mode doesn't enforce it, so a flow that worked in dev silently "breaks" in production for this reason alone. - A flood of
Maximum update depth exceedederrors during connect starves the browser main thread —onopennever fires, the handshake times out, and the UI reports a gateway failure that doesn't exist. Treat the gateway as healthy until the plain-page probe (step 7) proves otherwise, and never patch gateway code for a render-storm artifact observed only inside the app page.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

