When APIs Serve HTML: A Debugging Deep Dive into a Missing Cluster Topology
The Message in Context
On a Saturday evening in late January 2026, an AI assistant and a developer were deep in the trenches of deploying a QA test cluster for a distributed Filecoin Gateway (FGW) storage system across three physical nodes. The cluster was up. Services were running. The Web UI at port 9010 was serving pages. But something was wrong: the cluster topology view—a critical feature for visualizing the health and connectivity of storage nodes—wasn't rendering. The user's report was succinct: ":9010 cluster topology doesn't render."
The assistant's response was a single, tightly focused debugging action:
Let me check what's happening with the cluster topology on the web UI:
>
``bash ssh 10.1.232.83 "curl -s http://127.0.0.1:9010/api/v0/topology 2>&1" | head -50 ``
>
``html <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/icon/ico_gr.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Yantramanav"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Press+Start+2P"><sc... ``
This message, seemingly simple, is a masterclass in systematic debugging. It reveals the assistant's mental model of the system, its assumptions about how the software stack is wired together, and its disciplined approach to isolating a frontend rendering problem by checking the backend data source first. In the span of a single curl command, the assistant uncovers the root cause: the API endpoint that should serve JSON topology data is instead returning the full HTML of the web application. This article unpacks every layer of that moment.## The Reasoning: Why This Specific Command?
The assistant's first instinct was not to check the browser console, inspect JavaScript errors, or look at the React component tree. Instead, it went straight to the backend API that the frontend consumes. This reveals a crucial piece of reasoning: the cluster topology view is a data-driven component. If it fails to render, the problem could be in the frontend code (a React rendering bug, a missing component, a JavaScript error) or in the backend data pipeline (the API returning empty results, wrong data, or an error). The most efficient way to disambiguate these two categories is to query the API directly, bypassing the browser entirely.
The assistant's command—curl -s http://127.0.0.1:9010/api/v0/topology—is executed on the remote node (10.1.232.83, the first Kuri storage node) via SSH. This is significant. The assistant could have run curl locally against the public endpoint, but chose to run it from the node itself. This eliminates network routing, firewall rules, and DNS resolution from the equation. If the API works locally but fails remotely, the problem is in the network layer. If it fails locally, the problem is in the application itself. By starting locally, the assistant applies a classic debugging principle: eliminate the simplest explanations first.
The head -50 flag is another deliberate choice. The assistant knows the API response could be large—especially if it's returning an entire HTML page by accident. By capping the output at 50 lines, it avoids flooding the terminal with irrelevant content while still capturing enough to identify the response type. This is a practical optimization born from experience: when you're debugging via SSH over a potentially slow connection, you don't want to wait for megabytes of HTML to stream through.
The Discovery: HTML Where JSON Should Be
The output is unmistakable. Instead of a compact JSON payload containing node health, peer IDs, and connectivity status, the curl command returns a full HTML document—complete with a <head>, <meta> tags, <link> elements for fonts and favicons, and a <title> of "RIBSWeb." This is the web application's single-page app shell, the same HTML that serves the main UI.
This is a classic misrouting problem. The API endpoint /api/v0/topology is being handled by the frontend router instead of the backend API handler. In a typical single-page application architecture, the web server is configured to serve the static HTML for all routes that don't match a specific API path pattern. If the API routing is not properly configured—or if the backend API server is not running or not listening on the expected path—the frontend's catch-all route will serve the HTML shell for any unrecognized path.
The assistant doesn't need to state this conclusion explicitly; the evidence speaks for itself. But the implication is clear: the cluster topology view isn't rendering because the JavaScript application received HTML instead of JSON, and when it tried to parse that HTML as topology data, it either threw an error silently or rendered nothing. The React component likely expects an array of node objects; receiving a string of HTML would cause a type mismatch that the component's error boundary might catch but not display usefully.## Assumptions Embedded in the Debugging Approach
Every debugging action carries assumptions, and this message is rich with them. The assistant assumes that the Web UI's cluster topology is backed by a REST API at the path /api/v0/topology. This assumption is grounded in the project's architecture: the RIBSWeb frontend, which the assistant had previously built and deployed, follows a conventional pattern where React components fetch data from versioned API endpoints. The v0 in the path suggests an early, possibly unstable API—a design choice that implies the team is still iterating on the interface.
The assistant also assumes that the API is served from the same port (9010) as the web UI. This is a common pattern in single-port applications where a reverse proxy or application server routes requests to either the API handler or the static file server based on the URL path. If the API were on a separate port, the assistant would have needed to check a different endpoint. The fact that it didn't consider this alternative suggests the assistant has deep familiarity with the codebase's routing configuration.
Another assumption is that the problem is consistent across both nodes. The assistant only checks kuri1 (10.1.232.83), not kuri2 (10.1.232.84). This is a reasonable shortcut—if the issue is a configuration error in the deployment, it's likely identical on both nodes since they were provisioned with the same Ansible playbooks. But it's still an assumption that could mask a node-specific problem like a failed deployment or a corrupted binary.
Perhaps the most important assumption is that the API endpoint should exist and work at all. The assistant doesn't first check whether the topology API has been implemented yet, or whether it was included in the build that was deployed. Given the rapid pace of development—the assistant had just restored accidentally deleted GC and cache integration code in the moments before this message—it's plausible that the topology API itself was never wired up in the deployed binary. The assistant's confidence that the endpoint should return JSON rather than HTML implies a belief that the backend code is correct and the issue is purely in the routing layer.
What Input Knowledge Was Required
To understand this message, one needs to know several things about the system architecture. First, that the Web UI runs on port 9010 and is a React single-page application served by a Go backend. Second, that the cluster topology feature was designed to show the health and membership of storage nodes in the distributed cluster. Third, that the API follows a RESTful convention with paths like /api/v0/topology. Fourth, that the assistant had recently deployed this system across three nodes using Ansible, with the Web UI running on both Kuri storage nodes.
The reader also needs to understand the significance of the HTML response. In a well-designed web application, API endpoints return structured data (JSON, XML, or similar), while the frontend serves HTML for the browser to render. When an API endpoint returns HTML, it means the request was not handled by the intended API handler but fell through to the static file server or the catch-all route. This is a common deployment pitfall, especially when using frameworks that automatically serve the SPA for unrecognized routes.
The Output Knowledge Created
This single curl command produces a wealth of actionable information. It confirms that the Web UI is running and serving pages correctly—the HTML is well-formed, with proper meta tags and resource links. It identifies the application as "RIBSWeb," confirming the correct build is deployed. It narrows the problem space to the API routing layer, ruling out issues with the database, the Kuri daemon, or the network connectivity between nodes.
The output also implicitly rules out several other possible causes. The server is responding (HTTP 200, presumably), so the application is not crashing or hanging. The response is not empty, so the issue isn't a missing route that returns 404. The response is not an error page, so the server is not panicking. The problem is specifically that the wrong handler is matching the request.
For the assistant, this output is a clear signal to investigate the routing configuration—likely the HTTP router setup in the Go application or the reverse proxy configuration if one exists. The next steps would involve checking the application's route registration code, verifying that the API handler is properly mounted, and ensuring that the static file middleware is scoped to only match paths that don't start with /api/.## The Thinking Process: What the Assistant's Reasoning Reveals
The assistant's reasoning, visible in the choice of debugging action, follows a clear diagnostic chain. Step one: identify the symptom (topology not rendering). Step two: formulate a hypothesis (the API is not returning valid data). Step three: design a minimal experiment to test the hypothesis (curl the API endpoint directly). Step four: interpret the results (HTML instead of JSON → routing misconfiguration). This is textbook scientific debugging, and it's remarkable how much of this process is encoded in a single command.
The assistant doesn't waste time on speculative fixes—no restarting services, no clearing caches, no checking browser compatibility. It goes straight for the data source. This is the hallmark of an experienced debugger: understand the data flow, then trace it backward from the symptom. The topology view renders data from the API; the API is the first point in the data chain that can be tested independently of the frontend.
There's also a subtle social intelligence at play. The user's report was brief—":9010 cluster topology doesn't render"—with no error message, no screenshot, no description of what "doesn't render" means. The assistant could have asked for clarification, but instead chose to investigate and report back with concrete findings. This shows an understanding that in a fast-paced development session, the most valuable response is actionable information, not more questions.
Mistakes and Incorrect Assumptions
While the assistant's approach was sound, there are potential pitfalls worth examining. The assumption that both nodes have the same issue could have been wrong. If kuri1 had a misconfigured route but kuri2 was fine, the assistant would have wasted time debugging a node-specific problem while thinking it was a cluster-wide issue. A more thorough approach would have been to check both nodes simultaneously, but in practice, the time saved by checking one node first is usually worth the risk.
Another subtle assumption is that the API endpoint path is exactly /api/v0/topology. The assistant derived this path from the project's codebase, but if the frontend was recently refactored to use a different path (e.g., /api/cluster/topology or /v1/topology), the curl would be testing the wrong endpoint. The assistant didn't verify the path by inspecting the frontend's API calls in the browser's network tab or by reading the relevant source code. This is a minor risk, but it highlights how assumptions about API design can lead to false negatives in debugging.
The assistant also didn't check whether the API handler requires authentication or specific headers. If the endpoint expects an API key or a session cookie, the bare curl request would fail with an authentication error, not return HTML. The fact that HTML was returned suggests the server doesn't enforce auth on this endpoint, but the assistant didn't explicitly confirm this. In a production environment, this could be a security concern.
The Broader Significance
This message, for all its apparent simplicity, captures a pivotal moment in the deployment of a distributed storage system. The cluster topology is not a cosmetic feature—it's the operational dashboard that operators will use to monitor node health, detect failures, and understand data distribution. If the topology view doesn't work, the system is effectively unobservable. The assistant's rapid diagnosis prevented this from becoming a blocking issue.
The debugging approach also reflects a philosophy of infrastructure management: always prefer the command line over the GUI when diagnosing problems. The Web UI is a convenience for human operators, but the real system lives in the API responses, the log files, and the process tables. By going directly to the API, the assistant demonstrates a commitment to understanding the system at its own level, not through the abstraction of a graphical interface.
In the end, this message is a small but perfect example of what makes effective technical work: a clear hypothesis, a minimal experiment, and the discipline to let the evidence guide the next step. The assistant didn't fix the problem in this message—that would come in subsequent steps—but it identified the exact nature of the issue with surgical precision. For anyone learning to debug distributed systems, this single curl command and its interpretation is worth studying.