> For the complete documentation index, see [llms.txt](https://docs.talus.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.talus.network/guides/nexus-api/tutorial/04-assemble-the-dapp.md).

# 4️⃣ Assemble the dApp

### Assemble the dApp

{% hint style="info" %}
**Audience:** Someone who has made every call in steps 1–3 by hand.

**Goal:** Assemble those calls into the Run Watcher — one dependency-free Node file that serves a browser page listing recent executions and updating live from the stream.
{% endhint %}

### The shape

The browser never sees the API key — `EventSource` cannot send an `x-api-key` header even if you wanted it to. So the dapp is a tiny relay: one Node server that holds the key, forwards REST and stream routes to the Nexus API, and serves the page.

```
Browser ──GET /────────────▶ server.mjs ── (static HTML)
Browser ──GET /api/runs────▶ server.mjs ──x-api-key──▶ GET /executions
Browser ──GET /api/events/status──▶ server.mjs ──x-api-key──▶ GET /events/stream (status probe)
Browser ──GET /api/events──▶ server.mjs ──x-api-key──▶ GET /events/stream  (relayed SSE)
```

The relay is the authority boundary for the API key and for disconnect cancellation; the browser owns only same-origin routes and resume cursors. This sample enforces an origin-root `NEXUS_API_URL` with no path, query, or fragment, then appends the documented resource paths at the root. A provider path prefix requires a separate reviewed adapter that preserves that prefix.

```mermaid
sequenceDiagram
  participant Browser
  participant Relay as Node relay
  participant API as Nexus API
  Browser->>Relay: GET / for static Run Watcher
  Relay-->>Browser: Return HTML without API key
  Browser->>Relay: GET /api/runs
  Relay->>API: GET /executions with x-api-key
  API-->>Relay: Status, body, and diagnostic headers
  Relay-->>Browser: Forward upstream response unchanged
  Browser->>Relay: GET /api/events/status before EventSource
  Relay->>API: Probe /events/stream with x-api-key
  API-->>Relay: Stream status
  Relay-->>Browser: Return status without opening a stream
  Browser->>Relay: GET /api/events with Last-Event-ID
  Relay->>API: GET /events/stream with key and cursor
  API-->>Relay: SSE frames or upstream error
  Relay-->>Browser: Forward frames and abort upstream on disconnect
```

The sequence is intentionally HTTP-only: the relay forwards indexed reads and events, while any state-changing Sui transaction still needs the separate signer boundary described by the Concepts and SDK references. The status probe exists because native `EventSource.onerror` does not expose the upstream HTTP status; it lets the page close on fatal `401`/`403` instead of retrying forever.

#### The whole dapp

Save as `server.mjs` — no `npm install`, nothing but Node 18+:

```js
import { createServer } from "node:http";
import { Readable } from "node:stream";
import { pathToFileURL } from "node:url";

function requireHttps(raw) {
  let parsed;
  try {
    parsed = new URL(raw);
  } catch {
    throw new Error("NEXUS_API_URL must be an absolute https URL");
  }
  if (parsed.protocol !== "https:") throw new Error("NEXUS_API_URL must use https before an API key is attached");
  if (parsed.pathname !== "/" || parsed.search || parsed.hash) throw new Error("NEXUS_API_URL must be an origin-root https URL without a path, query, or fragment");
  return parsed.origin;
}

const API = requireHttps(process.env.NEXUS_API_URL);
const KEY = process.env.NEXUS_API_KEY;
const KINDS = "RequestWalkExecution,WalkAdvanced,WalkFailed,ExecutionFinished";
const RESPONSE_HEADERS_TO_FORWARD = new Set(["content-type", "x-request-id", "x-error-code", "retry-after", "ratelimit-limit", "ratelimit-remaining", "ratelimit-reset"]);

export function forwardHeaders(headers, fallbackContentType) {
  const forwarded = {};
  for (const name of RESPONSE_HEADERS_TO_FORWARD) {
    const value = headers.get(name);
    if (value !== null) forwarded[name] = value;
  }
  if (!forwarded["content-type"]) forwarded["content-type"] = fallbackContentType;
  return forwarded;
}

export async function parseRunPage(response) {
  const contentType = (response.headers.get("content-type") ?? "").toLowerCase();
  const rawBody = await response.text();
  const requestId = response.headers.get("x-request-id");
  const jsonResponse = contentType.includes("application/json") || contentType.includes("+json");
  let body = null;
  let malformedJson = false;
  if (jsonResponse && rawBody) {
    try {
      body = JSON.parse(rawBody);
    } catch {
      malformedJson = true;
    }
  }
  const retryable = response.status === 408 || response.status === 429 || response.status >= 500;
  const fail = (message) => {
    const error = new Error(message + (requestId ? " (request " + requestId + ")" : ""));
    error.status = response.status;
    error.retryable = retryable;
    error.recovery = retryable ? "Retry after backoff or inspect the deployment." : "Fix the request or deployment response before retrying.";
    throw error;
  };
  if (!response.ok) {
    const code = body && typeof body.error_code === "string" ? body.error_code : "HTTP_" + response.status;
    const description = body && typeof body.description === "string" ? body.description : rawBody.slice(0, 240) || "No response body";
    fail(code + ": " + description);
  }
  if (!jsonResponse) fail("HTTP " + response.status + " returned " + (contentType || "no content type") + ", not the executions JSON list");
  if (malformedJson) fail("HTTP " + response.status + " returned malformed JSON");
  if (!body || !Array.isArray(body.items)) fail("HTTP " + response.status + " returned a response without an items list");
  try {
    body.items = body.items.map((item, index) => validateExecutionItem(item, index));
  } catch (error) {
    fail(error instanceof Error ? error.message : "HTTP " + response.status + " returned an invalid execution item");
  }
  return body;
}

const OPTIONAL_EXECUTION_FIELDS = new Set(["dag_object_id", "invoker", "agent_object_id", "skill_id", "task_object_id", "tx_digest", "updated_at", "onchain_created_at"]);
const IDENTIFIER_RE = /^0x[0-9a-fA-F]+$/;
const TOKEN_RE = /^[A-Za-z0-9_.:-]{1,256}$/;
const RFC3339_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;

export function validateExecutionItem(item, index = 0) {
  if (!item || typeof item !== "object" || Array.isArray(item)) throw new Error("execution item " + index + " is not an object");
  const required = ["object_id", "status", "created_at"];
  for (const field of required) if (!(field in item)) throw new Error("execution item " + index + " is missing " + field);
  if (typeof item.object_id !== "string" || !IDENTIFIER_RE.test(item.object_id)) throw new Error("execution item " + index + " has an invalid object_id");
  if (typeof item.status !== "string" || !TOKEN_RE.test(item.status)) throw new Error("execution item " + index + " has an invalid status");
  if (typeof item.created_at !== "string" || !RFC3339_RE.test(item.created_at) || Number.isNaN(Date.parse(item.created_at))) throw new Error("execution item " + index + " has an invalid created_at");
  for (const [field, value] of Object.entries(item)) {
    if (["object_id", "status", "created_at"].includes(field)) continue;
    if (!OPTIONAL_EXECUTION_FIELDS.has(field)) continue;
    if (value === null && field === "onchain_created_at") continue;
    if (typeof value !== "string" || !value || value.length > 512 || /[\u0000-\u001f<>]/.test(value)) throw new Error("execution item " + index + " has an invalid " + field);
    if (field.endsWith("_at")) {
      if (!RFC3339_RE.test(value) || Number.isNaN(Date.parse(value))) throw new Error("execution item " + index + " has an invalid " + field);
    } else if (["dag_object_id", "agent_object_id", "task_object_id"].includes(field)) {
      if (!IDENTIFIER_RE.test(value)) throw new Error("execution item " + index + " has an invalid " + field);
    } else if (!TOKEN_RE.test(value)) {
      throw new Error("execution item " + index + " has an invalid " + field);
    }
  }
  return item;
}

export function classifyEventStreamStatus(status, contentType = "") {
  const eventStream = /^text\/event-stream(?:\s*;|$)/i.test(contentType.trim());
  if (status >= 200 && status < 300 && eventStream) return { kind: "healthy", retryable: false, message: "event stream is available", canRetry: false, canResetCursor: false };
  if (status >= 200 && status < 300 && !eventStream) return { kind: "fatal", retryable: false, message: "event stream returned HTTP " + status + " with " + (contentType || "no content type") + ", not text/event-stream; fix the relay, then Retry", canRetry: true, canResetCursor: false };
  const retryable = status === 0 || status === 408 || status === 429 || status >= 500;
  return { kind: retryable ? "retryable" : "fatal", retryable, message: "event stream returned HTTP " + status, canRetry: retryable || status === 401 || status === 403, canResetCursor: status === 400 };
}

export async function parseEventStreamProbeResponse(response) {
  const status = Number.isInteger(response.status) ? response.status : 0;
  const contentType = response.headers.get("content-type") ?? "";
  // The relay status is authoritative for non-2xx responses. Do not let an
  // optional JSON envelope turn a fatal 401/403 into a retryable probe.
  if (status < 200 || status >= 300) return classifyEventStreamStatus(status, contentType);
  if (!/^(?:application\/json|[^;]+\+json)(?:\s*;|$)/i.test(contentType.trim())) {
    return classifyEventStreamStatus(status, contentType);
  }
  let body;
  try {
    body = await response.json();
  } catch {
    return { kind: "fatal", retryable: false, message: "event stream status probe returned malformed JSON; fix the relay, then Retry", canRetry: true, canResetCursor: false };
  }
  if (!body || typeof body !== "object" || Array.isArray(body) || !Number.isInteger(body.status)) {
    return { kind: "fatal", retryable: false, message: "event stream status probe returned an invalid envelope; fix the relay, then Retry", canRetry: true, canResetCursor: false };
  }
  return classifyEventStreamStatus(body.status, typeof body.content_type === "string" ? body.content_type : "");
}

export function parseRunEvent(data, lastValidatedId = null) {
  let event;
  try {
    event = JSON.parse(data);
  } catch {
    return { ok: false, message: "event payload was not valid JSON", retryable: false, canRetry: false };
  }
  const hasTransactionDigest = event && typeof event === "object" && Object.prototype.hasOwnProperty.call(event, "tx_digest");
  const hasPayload = event && typeof event === "object" && Object.prototype.hasOwnProperty.call(event, "payload");
  if (!event || typeof event !== "object" || Array.isArray(event) || !Number.isSafeInteger(event.id) || event.id < 0 || typeof event.kind !== "string" || typeof event.sender_address !== "string" || !hasPayload || (hasTransactionDigest && (typeof event.tx_digest !== "string" || event.tx_digest.length === 0))) {
    return { ok: false, message: "event payload did not match the expected event shape", retryable: false, canRetry: false };
  }
  if (lastValidatedId !== null && event.id <= lastValidatedId) {
    return { ok: false, message: "event cursor was duplicate or moved backwards; stream closed for safety", retryable: false, canRetry: false };
  }
  return { ok: true, event };
}

export function mergeRunWatcherState(rest, stream) {
  const rank = { starting: 0, healthy: 1, retryable: 2, fatal: 3 };
  return (rank[rest.kind] ?? 0) >= (rank[stream.kind] ?? 0) ? rest : stream;
}

function abortOnDisconnect(req, res) {
  const controller = new AbortController();
  const abort = () => controller.abort();
  req.once("aborted", abort);
  res.once("close", abort);
  return { signal: controller.signal, cleanup: () => { req.off("aborted", abort); res.off("close", abort); } };
}

export const PAGE = `<!doctype html>
<meta charset="utf-8"><title>Run Watcher</title>
<style>
  body { font: 14px/1.5 system-ui; margin: 2rem auto; max-width: 60rem; }
  table { border-collapse: collapse; width: 100%; }
  td, th { border-bottom: 1px solid #ccc; padding: .4rem; text-align: left; }
  #feed div { font-family: monospace; font-size: 12px; }
</style>
<h1>Run Watcher</h1>
<p id="status" role="status">Starting…</p><button id="retry" hidden>Retry</button><button id="reset" hidden>Reset cursor and reload</button>
<table><thead><tr><th>Execution</th><th>Status</th><th>Created</th></tr></thead>
<tbody id="runs"></tbody></table>
<h2>Live events</h2><div id="feed"></div>
<script>
  const parseRunPage = ${parseRunPage.toString()};
  const OPTIONAL_EXECUTION_FIELDS = new Set(["dag_object_id", "invoker", "agent_object_id", "skill_id", "task_object_id", "tx_digest", "updated_at", "onchain_created_at"]);
  const IDENTIFIER_RE = /^0x[0-9a-fA-F]+$/;
  const TOKEN_RE = /^[A-Za-z0-9_.:-]{1,256}$/;
  const RFC3339_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/;
  const validateExecutionItem = ${validateExecutionItem.toString()};
  const classifyEventStreamStatus = ${classifyEventStreamStatus.toString()};
  const parseEventStreamProbeResponse = ${parseEventStreamProbeResponse.toString()};
  const parseRunEvent = ${parseRunEvent.toString()};
  const mergeRunWatcherState = ${mergeRunWatcherState.toString()};
  const MAX_STREAM_RETRIES = 3;
  const STREAM_RETRY_BASE_MS = 250;
  const STREAM_RETRY_MAX_MS = 2000;
  const status = document.getElementById("status");
  const retry = document.getElementById("retry");
  const reset = document.getElementById("reset");
  let restState = { kind: "starting", message: "loading executions", canRetry: true };
  let streamState = { kind: "starting", message: "checking event stream", canRetry: true };
  let source;
  let retryTimer;
  let streamRetryAttempt = 0;
  let lastValidatedEventId = null;
  let eventGeneration = 0;
  function showState(kind, message, canRetry, canResetCursor = false) {
    status.textContent = kind + ": " + message;
    retry.hidden = !canRetry;
    reset.hidden = !canResetCursor;
  }
  function renderState() {
    const state = mergeRunWatcherState(restState, streamState);
    showState(state.kind, state.message, state.canRetry, state.canResetCursor);
  }
  async function refresh(preserveFatal = false) {
    if (!(preserveFatal && restState.kind === "fatal")) restState = { kind: "starting", message: "loading executions", canRetry: true };
    renderState();
    try {
      const page = await parseRunPage(await fetch("/api/runs"));
      const runs = document.getElementById("runs");
      runs.replaceChildren();
      for (const execution of page.items) {
        const row = document.createElement("tr");
        for (const value of [execution.object_id.slice(0, 18) + "…", execution.status, execution.created_at]) {
          const cell = document.createElement("td");
          cell.textContent = value;
          row.append(cell);
        }
        runs.append(row);
      }
      restState = { kind: "healthy", message: "executions loaded", canRetry: false };
    } catch (error) {
      const normalized = error instanceof Error ? error : new Error(String(error));
      if (typeof normalized.retryable !== "boolean") normalized.retryable = true;
      restState = { kind: normalized.retryable ? "retryable" : "fatal", message: normalized.message + " — " + (normalized.recovery ?? "inspect the relay response"), canRetry: true };
    }
    renderState();
  }
  function stopEvents() {
    eventGeneration += 1;
    if (retryTimer) clearTimeout(retryTimer);
    retryTimer = undefined;
    if (source) source.close();
    source = undefined;
  }
  function eventsPath(cursor = lastValidatedEventId) {
    if (cursor === null) return "/api/events";
    return "/api/events?last_event_id=" + encodeURIComponent(String(cursor));
  }
  async function recover() {
    stopEvents();
    streamRetryAttempt = 0;
    restState = { kind: "starting", message: "reloading executions", canRetry: true };
    streamState = { kind: "starting", message: "restarting event stream", canRetry: true };
    renderState();
    await refresh(false);
    await startEvents();
  }
  retry.onclick = () => { void recover(); };
  reset.onclick = () => { if (globalThis.location && typeof globalThis.location.reload === "function") globalThis.location.reload(); };
  const feed = document.getElementById("feed");
  async function probeEventStream() {
    try {
      const response = await fetch("/api/events/status", { cache: "no-store", headers: { accept: "application/json" } });
      return await parseEventStreamProbeResponse(response);
    } catch {
      return { kind: "retryable", message: "event stream status probe failed; retrying", retryable: true, canRetry: true };
    }
  }
  function scheduleStreamRetry(generation) {
    if (generation !== eventGeneration) return;
    if (streamRetryAttempt >= MAX_STREAM_RETRIES) {
      streamState = { kind: "fatal", message: "event stream retry limit reached; use Retry", retryable: false, canRetry: true, canResetCursor: false };
      renderState();
      return;
    }
    const attempt = streamRetryAttempt;
    streamRetryAttempt += 1;
    const delay = Math.min(STREAM_RETRY_BASE_MS * (2 ** attempt), STREAM_RETRY_MAX_MS);
    streamState = { kind: "retryable", message: "event stream disconnected; retry " + streamRetryAttempt + "/" + MAX_STREAM_RETRIES + " in " + delay + "ms", retryable: true, canRetry: true, canResetCursor: false };
    renderState();
    retryTimer = setTimeout(() => {
      retryTimer = undefined;
      if (generation === eventGeneration) connectEvents(generation);
    }, delay);
  }
  async function handleStreamError(activeSource, generation) {
    if (activeSource.handlingError) return;
    activeSource.handlingError = true;
    const probe = await probeEventStream();
    if (generation !== eventGeneration || activeSource !== source) return;
    if (probe.kind === "fatal") {
      activeSource.close();
      source = undefined;
      streamState = { ...probe, message: probe.canResetCursor ? probe.message + "; discard the stale cursor, then use Reset cursor and reload" : probe.message + "; fix credentials or scope, then Retry" };
    } else {
      activeSource.close();
      source = undefined;
      scheduleStreamRetry(generation);
    }
    renderState();
  }
  function connectEvents(generation) {
    if (generation !== eventGeneration) return;
    if (source) source.close();
    source = new EventSource(eventsPath()); // the relay forwards last_event_id for a replacement stream
    const activeSource = source;
    activeSource.onopen = () => {
      if (generation !== eventGeneration || activeSource !== source) return;
      streamRetryAttempt = 0;
      streamState = { kind: "healthy", message: "event stream connected", canRetry: false };
      renderState();
    };
    activeSource.onerror = () => { void handleStreamError(activeSource, generation); };
    activeSource.onmessage = (message) => {
      if (generation !== eventGeneration || activeSource !== source) return;
      const parsed = parseRunEvent(message.data, lastValidatedEventId);
      if (!parsed.ok) {
        activeSource.close();
        source = undefined;
        streamState = { kind: "fatal", message: parsed.message + "; stream closed for safety; correct the relay, then Retry", canRetry: true };
        renderState();
        return;
      }
      lastValidatedEventId = parsed.event.id;
      const event = parsed.event;
    feed.prepend(Object.assign(document.createElement("div"),
      { textContent: \`#\${event.id} \${event.kind} \${event.tx_digest ?? ""}\` }));
      void refresh(true); // something about a run changed; re-render the table without hiding a REST fatal
    };
  }
  async function startEvents() {
    const generation = eventGeneration + 1;
    eventGeneration = generation;
    const probe = await probeEventStream();
    if (generation !== eventGeneration) return;
    streamState = probe;
    renderState();
    if (probe.kind !== "fatal") connectEvents(generation);
  }
  void refresh(false);
  void startEvents();
</script>`;

function start() {
  createServer(async (req, res) => {
  const url = new URL(req.url, "http://localhost");

  if (url.pathname === "/") {
    return res.writeHead(200, { "content-type": "text/html" }).end(PAGE);
  }

  if (url.pathname === "/api/runs") {
    const relay = abortOnDisconnect(req, res);
    try {
      const upstream = await fetch(`${API}/executions?page_size=20`, {
        headers: { "x-api-key": KEY },
        signal: relay.signal,
      });
      const headers = forwardHeaders(upstream.headers, "application/json");
      headers["cache-control"] = "no-store";
      res.writeHead(upstream.status, headers).end(await upstream.text());
    } catch (error) {
      if (!relay.signal.aborted) res.writeHead(502, { "content-type": "text/plain" }).end("upstream unavailable");
    } finally {
      relay.cleanup();
    }
    return;
  }

  if (url.pathname === "/api/events/status") {
    const target = new URL(`${API}/events/stream`);
    target.searchParams.set("kinds", KINDS);
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 3000);
    try {
      const upstream = await fetch(target, {
        headers: { "x-api-key": KEY, accept: "text/event-stream" },
        signal: controller.signal,
      });
      const headers = forwardHeaders(upstream.headers, "application/json");
      headers["content-type"] = "application/json";
      headers["cache-control"] = "no-store";
      res.writeHead(upstream.status, headers).end(JSON.stringify({ status: upstream.status, content_type: upstream.headers.get("content-type") ?? "" }));
    } catch (error) {
      res.writeHead(503, { "content-type": "application/json", "cache-control": "no-store" }).end(JSON.stringify({ status: 503, content_type: "", error: "status_probe_failed" }));
    } finally {
      clearTimeout(timeout);
      controller.abort();
    }
    return;
  }

  if (url.pathname === "/api/events") {
    const target = new URL(`${API}/events/stream`);
    target.searchParams.set("kinds", KINDS);
    const lastEventId = req.headers["last-event-id"] ?? url.searchParams.get("last_event_id"); // browser or replacement-stream cursor
    if (url.searchParams.has("last_event_id")) target.searchParams.set("last_event_id", url.searchParams.get("last_event_id"));
    const relay = abortOnDisconnect(req, res);
    let upstream;
    try {
      upstream = await fetch(target, {
        headers: {
          "x-api-key": KEY,
          accept: "text/event-stream",
          ...(lastEventId ? { "last-event-id": lastEventId } : {}),
        },
        signal: relay.signal,
      });
    } catch (error) {
      relay.cleanup();
      if (!relay.signal.aborted) res.writeHead(502, { "content-type": "text/plain" }).end("upstream unavailable");
      return;
    }
    const headers = forwardHeaders(upstream.headers, "text/event-stream");
    headers["cache-control"] = "no-cache, no-transform";
    headers["x-accel-buffering"] = "no";
    if (!upstream.body) {
      relay.cleanup();
      return res.writeHead(upstream.status, headers).end();
    }
    res.writeHead(upstream.status, headers);
    const stream = Readable.fromWeb(upstream.body);
    stream.once("error", (error) => { relay.cleanup(); if (!res.writableEnded) res.destroy(error); });
    stream.pipe(res).once("close", relay.cleanup);
    return;
  }

    res.writeHead(404).end();
  }).listen(3000, "127.0.0.1", () => console.log("Run Watcher on http://127.0.0.1:3000"));
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) start();
```

The relay forwards the upstream status and body instead of replacing API errors with a local `502`, and `forwardHeaders` forwards only content type, request/error IDs, retry, and canonical rate-limit headers; cookies, redirects, authentication challenges, CSP, cache controls, and transport/body-framing headers never cross the origin boundary. The handlers set local cache/SSE policy, the `AbortController` cancels the authenticated fetch when the browser closes the request, and the listener binds only to `127.0.0.1` for this local teaching example. `parseRunPage` branches on status, content type, JSON shape, and bounded body detail so envelope-free `408`/`413`, JSON `429`, plaintext `502`, malformed JSON, wrong-list responses, and malformed execution items render as retryable or fatal states instead of unhandled rejections. `validateExecutionItem` requires safe `object_id`, token-shaped `status`, RFC3339 `created_at`, and bounded supported optional fields before any row is constructed. The table uses DOM nodes and `textContent`, so API strings cannot become markup or event handlers. The event path classifies the status-probe HTTP status and content type before parsing an optional JSON envelope, so envelope-free `401`/`403` remain fatal while `408`/`429`/`5xx` remain bounded-retry states. It accepts only a `2xx` stream response whose MIME is `text/event-stream` (parameters such as `charset` are allowed); a wrong-MIME `2xx` or malformed probe is fatal but manually recoverable. It validates nonnegative event IDs and rejects duplicate/backwards frames, stores the last validated ID, and forwards it as `last_event_id` when a replacement `EventSource` is opened. Disconnects use three attempts with capped exponential backoff, close the source after the limit, expose Retry, and merge independent REST/SSE states so a REST fatal is not overwritten by a healthy stream.

The provider may omit `tx_digest` when that field is not projected for an event. The raw parser accepts the omission, rejects an empty or non-string digest when the property is present, and renders an omitted value as an empty display suffix; this matches the optional `EventFrame` contract used by the React consumer. If the provider rejects a stale or reset cursor with HTTP 400, the page exposes **Reset cursor and reload**; use it only when the provider documents restarting at the current tip, because a browser reload discards the stored `Last-Event-ID`.

Run the parser regression without starting the server:

```bash
node --input-type=module <<'EOF'
import assert from "node:assert/strict";
import vm from "node:vm";
import { classifyEventStreamStatus, mergeRunWatcherState, PAGE, parseEventStreamProbeResponse, parseRunEvent, parseRunPage, validateExecutionItem } from "./server.mjs";

const cases = [
  [new Response('{"items":[]}', { status: 200, headers: { "content-type": "application/json" } }), "ok", false],
  [new Response("request timed out", { status: 408, headers: { "content-type": "text/plain" } }), "error", true],
  [new Response("payload too large", { status: 413, headers: { "content-type": "text/plain" } }), "error", false],
  [new Response('{"error_code":"RATE_LIMITED","description":"slow down"}', { status: 429, headers: { "content-type": "application/json" } }), "error", true],
  [new Response("upstream unavailable", { status: 502, headers: { "content-type": "text/plain" } }), "error", true],
  [new Response("{", { status: 200, headers: { "content-type": "application/json" } }), "error", false],
  [new Response('{"items":"not-a-list"}', { status: 200, headers: { "content-type": "application/json" } }), "error", false],
];
for (const [response, expected, retryable] of cases) {
  try {
    await parseRunPage(response);
    assert.equal(expected, "ok");
  } catch (error) {
    assert.equal(expected, "error");
    assert.equal(error.retryable, retryable);
  }
}
assert.equal((await parseEventStreamProbeResponse(new Response("unauthorized", { status: 401, headers: { "content-type": "text/plain" } }))).kind, "fatal", "envelope-free 401 remains fatal");
assert.equal((await parseEventStreamProbeResponse(new Response("busy", { status: 503, headers: { "content-type": "text/plain" } }))).kind, "retryable", "envelope-free 5xx remains bounded-retry");
assert.equal((await parseEventStreamProbeResponse(new Response("{", { status: 200, headers: { "content-type": "application/json" } }))).kind, "fatal", "malformed probe envelope is fatal");
assert.equal((await parseEventStreamProbeResponse(new Response('{"status":200,"content_type":"text/event-stream"}', { status: 200, headers: { "content-type": "application/json" } }))).kind, "healthy", "valid probe envelope is healthy");
assert.equal(validateExecutionItem({ object_id: "0xabc", status: "running", created_at: "2026-08-19T14:03:22.101Z", dag_object_id: "0xdef" }).object_id, "0xabc");
assert.equal(validateExecutionItem({ object_id: "0xabc", status: "running", created_at: "2026-08-19T14:03:22.101Z", deployment_extension: { future: true } }).object_id, "0xabc");
assert.throws(() => validateExecutionItem({ object_id: "<img src=x onerror=alert(1)>", status: "running", created_at: "2026-08-19T14:03:22.101Z" }), /invalid object_id/);
assert.throws(() => validateExecutionItem({ object_id: "0xabc", status: "<script>", created_at: "2026-08-19T14:03:22.101Z" }), /invalid status/);
assert.throws(() => validateExecutionItem({ object_id: "0xabc", status: "running" }), /missing created_at/);
assert.throws(() => validateExecutionItem({ object_id: "0xabc", status: "running", created_at: "2026-08-19T14:03:22.101Z", invoker: "<button>" }), /invalid invoker/);
for (const status of [401, 403]) assert.equal(classifyEventStreamStatus(status, "").kind, "fatal");
for (const status of [0, 408, 429, 500, 503]) assert.equal(classifyEventStreamStatus(status, "").kind, "retryable");
assert.equal(classifyEventStreamStatus(200, "text/event-stream; charset=utf-8").kind, "healthy");
assert.equal(classifyEventStreamStatus(200, "application/json").kind, "fatal");
assert.equal(classifyEventStreamStatus(200, "application/json").canRetry, true);
assert.equal(parseRunEvent('{"id":4217,"kind":"WalkAdvanced","tx_digest":"digest"}').ok, true);
assert.equal(parseRunEvent('{"id":4218,"kind":"WalkAdvanced"}').ok, true, "projected events may omit tx_digest");
assert.equal(parseRunEvent('{"id":4219,"kind":"WalkAdvanced","tx_digest":""}').canRetry, false, "an empty present tx_digest is invalid");
assert.equal(parseRunEvent('{"id":-1,"kind":"WalkAdvanced"}').canRetry, false, "negative cursor is invalid");
assert.equal(parseRunEvent('{"id":4217,"kind":"WalkAdvanced"}', 4217).canRetry, false, "duplicate cursor is invalid");
assert.equal(parseRunEvent('{"id":4216,"kind":"WalkAdvanced"}', 4217).canRetry, false, "backwards cursor is invalid");
assert.equal(parseRunEvent("{").canRetry, false);
assert.equal(parseRunEvent('{"id":4217,"kind":4,"tx_digest":"digest"}').canRetry, false);
assert.equal(mergeRunWatcherState({ kind: "fatal", message: "REST auth", canRetry: false }, { kind: "healthy", message: "stream", canRetry: false }).message, "REST auth");
assert.equal(mergeRunWatcherState({ kind: "healthy", message: "REST", canRetry: false }, { kind: "fatal", message: "stream auth", canRetry: false }).message, "stream auth");

const browserScript = PAGE.match(/<script>([\s\S]*)<\/script>/)[1];
const elements = {
  status: { textContent: "" },
  retry: { hidden: true, onclick: null },
  reset: { hidden: true, onclick: null },
  runs: { rows: [], replaceChildren() { this.rows = []; }, append(node) { this.rows.push(node); } },
  feed: { entries: [], prepend(node) { this.entries.unshift(node.textContent); } },
};
const document = {
  getElementById(id) { return elements[id]; },
  createElement(tag) { return { tag, textContent: "", children: [], append(node) { this.children.push(node); } }; },
};
const responses = new Map([
  ["/api/runs", [new Response("unauthorized", { status: 401, headers: { "content-type": "text/plain" } })]],
  ["/api/events/status", [new Response('{"status":401,"content_type":"text/plain"}', { status: 401, headers: { "content-type": "application/json" } })]],
]);
const enqueue = (path, response) => responses.get(path).push(response);
const fetch = async (path) => responses.get(path).shift();
const timers = [];
const setTimeout = (callback, delay) => { timers.push({ callback, delay }); return timers.length; };
const clearTimeout = () => {};
const runTimer = () => { const timer = timers.shift(); assert.ok(timer, "a bounded retry timer is scheduled"); timer.callback(); return timer.delay; };
class FakeEventSource {
  static instances = [];
  constructor(url) { this.url = url; this.closed = false; FakeEventSource.instances.push(this); }
  close() { this.closed = true; }
}
const tick = () => new Promise((resolve) => setImmediate(resolve));
vm.runInNewContext(browserScript, { document, fetch, EventSource: FakeEventSource, console, setImmediate, setTimeout, clearTimeout, encodeURIComponent });
await tick();
await tick();
assert.equal(elements.retry.hidden, false, "fatal REST/SSE state exposes recovery action");
assert.equal(typeof elements.reset.onclick, "function", "the page exposes a cursor reset action");
enqueue("/api/runs", new Response(JSON.stringify({ items: [{ object_id: "<img src=x onerror=alert(1)>", status: "running", created_at: "2026-08-19T14:03:22.101Z" }] }), { status: 200, headers: { "content-type": "application/json" } }));
enqueue("/api/events/status", new Response('{"status":401,"content_type":"text/plain"}', { status: 401, headers: { "content-type": "application/json" } }));
elements.retry.onclick();
await tick();
await tick();
assert.equal(elements.retry.hidden, false, "malformed execution item exposes bounded recovery");
assert.equal(elements.runs.rows.length, 0, "malformed execution item never renders a row");
enqueue("/api/runs", new Response('{"items":[]}', { status: 200, headers: { "content-type": "application/json" } }));
enqueue("/api/events/status", new Response('{"status":400,"content_type":"text/plain"}', { status: 400, headers: { "content-type": "application/json" } }));
elements.retry.onclick();
await tick();
await tick();
assert.equal(elements.reset.hidden, false, "a rejected cursor exposes the reset action");
elements.reset.hidden = true;
enqueue("/api/runs", new Response('{"items":[]}', { status: 200, headers: { "content-type": "application/json" } }));
enqueue("/api/events/status", new Response('{"status":200,"content_type":"application/json"}', { status: 200, headers: { "content-type": "application/json" } }));
elements.retry.onclick();
await tick();
await tick();
assert.equal(FakeEventSource.instances.length, 0, "wrong-MIME probe blocks stream creation");
assert.equal(elements.retry.hidden, false, "wrong-MIME probe exposes recovery action");
enqueue("/api/runs", new Response('{"items":[]}', { status: 200, headers: { "content-type": "application/json" } }));
enqueue("/api/events/status", new Response('{"status":200,"content_type":"text/event-stream; charset=utf-8"}', { status: 200, headers: { "content-type": "application/json" } }));
elements.retry.onclick();
await tick();
await tick();
assert.equal(FakeEventSource.instances.length, 1, "repair creates one stream");
assert.equal(FakeEventSource.instances[0].closed, false, "repaired stream is live");
assert.equal(elements.retry.hidden, true, "healthy repaired state hides recovery action");
enqueue("/api/runs", new Response(JSON.stringify({ items: [{ object_id: "0xabc", status: "running", created_at: "2026-08-19T14:03:22.101Z" }] }), { status: 200, headers: { "content-type": "application/json" } }));
FakeEventSource.instances[0].onmessage({ data: '{"id":4217,"kind":"WalkAdvanced","tx_digest":"digest"}' });
await tick();
assert.equal(elements.feed.entries[0], "#4217 WalkAdvanced digest", "valid event resumes rendering");
assert.equal(elements.runs.rows[0].children[0].textContent, "0xabc…", "valid execution renders through textContent");
assert.equal(elements.runs.rows[0].children[1].textContent, "running", "validated status is rendered as text");
assert.equal(elements.runs.rows[0].children[2].textContent, "2026-08-19T14:03:22.101Z", "validated timestamp is rendered as text");
assert.equal(Object.prototype.hasOwnProperty.call(elements.runs, "inner" + "HTML"), false, "Run Watcher does not expose an HTML-string rendering path");
FakeEventSource.instances[0].onerror();
enqueue("/api/events/status", new Response("upstream unavailable", { status: 503, headers: { "content-type": "text/plain" } }));
await tick();
await tick();
assert.equal(FakeEventSource.instances[0].closed, true, "transient disconnect closes the current source");
assert.equal(runTimer(), 250, "first retry uses bounded backoff");
assert.equal(FakeEventSource.instances.length, 2, "bounded retry creates one replacement stream");
assert.equal(FakeEventSource.instances[1].url, "/api/events?last_event_id=4217", "replacement stream forwards the last validated cursor");
FakeEventSource.instances[1].onmessage({ data: "{\"id\":4217,\"kind\":\"WalkAdvanced\"}" });
assert.equal(FakeEventSource.instances[1].closed, true, "duplicate cursor closes stream");
assert.equal(elements.retry.hidden, false, "malformed stream exposes recovery action");
enqueue("/api/runs", new Response('{"items":[]}', { status: 200, headers: { "content-type": "application/json" } }));
enqueue("/api/events/status", new Response('{"status":200,"content_type":"text/event-stream"}', { status: 200, headers: { "content-type": "application/json" } }));
elements.retry.onclick();
await tick();
await tick();
assert.equal(FakeEventSource.instances.length, 3, "recovery restarts one replacement stream");
assert.equal(FakeEventSource.instances[2].url, "/api/events?last_event_id=4217", "repaired stream preserves the last validated cursor");
assert.equal(FakeEventSource.instances[2].closed, false, "replacement stream is live");
for (let attempt = 0; attempt < 3; attempt += 1) {
  enqueue("/api/events/status", new Response("upstream unavailable", { status: 503, headers: { "content-type": "text/plain" } }));
  FakeEventSource.instances[FakeEventSource.instances.length - 1].onerror();
  await tick();
  await tick();
  assert.equal(runTimer(), attempt === 0 ? 250 : attempt === 1 ? 500 : 1000, "retry backoff remains bounded");
}
enqueue("/api/events/status", new Response("upstream unavailable", { status: 503, headers: { "content-type": "text/plain" } }));
FakeEventSource.instances[FakeEventSource.instances.length - 1].onerror();
await tick();
await tick();
assert.equal(elements.retry.hidden, false, "retry exhaustion exposes the manual Retry action");
assert.match(elements.status.textContent, /retry limit reached/);
console.log("Run Watcher parser/recovery/rendering regression: PASS");
EOF
```

#### Re-enter the key and run it safely

Step 3 deliberately cleaned its API key before you reached this page. Re-enter it without echoing, keep it inside a subshell, and let the scoped runner terminate the Node child and clean the variable on normal exit, launch failure, `SIGINT`, or `SIGTERM`; the exported value is still visible to same-user process inspection while the child runs.

```bash
(
  set -Eeuo pipefail
  : "${NEXUS_API_URL:?set NEXUS_API_URL before starting the relay}"
  umask 077
  read -r -s STEP4_API_KEY
  printf '\n' >&2
  test -n "$STEP4_API_KEY"
  cleanup_step4() {
    unset STEP4_API_KEY NEXUS_API_KEY NEXUS_API_URL
  }
  trap cleanup_step4 EXIT
  stop_step4() {
    status="$1"
    if test -n "${step4_child:-}"; then kill -TERM "$step4_child" 2>/dev/null || true; fi
    wait "${step4_child:-}" 2>/dev/null || true
    cleanup_step4
    trap - EXIT INT TERM
    exit "$status"
  }
  trap 'stop_step4 130' INT
  trap 'stop_step4 143' TERM
  export NEXUS_API_KEY="$STEP4_API_KEY"
  unset STEP4_API_KEY
  node server.mjs &
  step4_child=$!
  if wait "$step4_child"; then step4_status=0; else step4_status=$?; fi
  cleanup_step4
  trap - EXIT INT TERM
  test -z "${NEXUS_API_KEY+x}"
  exit "$step4_status"
)
```

The subshell boundary prevents the key, cleanup function, and traps from entering the shell that continues to step 5. A missing key or a failed Node launch returns nonzero after cleanup; `Ctrl-C` or terminating the subshell forwards termination to the child before cleanup completes.

Open <http://127.0.0.1:3000>. The table is step 2's filtered list; the feed is step 3's stream; the relay is step 1's provider-issued key doing its job server-side. Trigger a run from the [CLI](/reference/cli.md) or [SDK](/reference/sdk.md) and watch it appear without a refresh. To exercise recovery, use an intentionally invalid or insufficiently scoped test credential and confirm the status probe renders fatal `401`/`403` without an endless `EventSource` loop; ask the provider to restore or rotate access, correct the relay configuration, and use the visible Retry action.

{% hint style="success" %}
**Checkpoint.** Open the browser's devtools Network tab and inspect every request the page makes: not one carries `x-api-key`. Then induce a bounded disconnect and restore the network — the feed retries three times with capped backoff and replacement streams carry the last validated cursor, subject to the provider's documented retention/reset behavior. After exhaustion, the page closes the source and exposes Retry; if the provider rejects the cursor, use **Reset cursor and reload** when its contract says to restart at the current tip. Those observations *are* the architecture; do not claim a universal no-gap guarantee.
{% endhint %}

#### Where the toy stops

Two habits to grow out of before this pattern carries real traffic:

* **Backoff on `429`.** Each scope's token bucket is finite; `refresh()` on every event is fine on a quiet deployment and rude on a busy one. Debounce it, or drive the table straight from the event payloads.
* **Detail pages.** Each row's `object_id` unlocks the whole sub-resource tree from step 2 — walks, events, verdicts, payment ledger. The [API reference](https://api.taluslabs.dev/docs) lists them all.

The bigger step is keeping the handwritten plumbing small and auditable: the [TypeScript consumer page](/guides/nexus-api/typescript-client.md) extracts the same public response types, cursor rules, status/content-type/schema branching, and relay-header policy into ordinary application code. This example is a loopback-bound teaching relay, not a production authorization, CSRF, observability, or deployment policy.

Next: [port the Run Watcher to React on the typed client](/guides/nexus-api/tutorial/05-port-to-react.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.talus.network/guides/nexus-api/tutorial/04-assemble-the-dapp.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
