Browse all docs

Anvil Cloud / Runtime

Anvil Lens

Anvil Lens is the inspection and debugging surface for a running Cell. In alpha it is local-first: a single self-contained HTML page served by the local runtime, backed by the same /_anvil/* JSON routes the CLI uses. A hosted control plane can sit behind the same contract later.

Opening Lens

Start the dev server, then open the Lens URL it prints:

anvil cloud dev
# Anvil Local runtime  http://localhost:8787
# Anvil client         http://localhost:5173
# Anvil Lens           http://localhost:8787/_anvil/lens

Or ask the CLI for the URL of an already running server:

anvil cloud lens
anvil cloud lens --json

If no local runtime is reachable, anvil cloud lens --json returns:

{
  "ok": false,
  "errors": [
    {
      "code": "LENS_SERVER_NOT_RUNNING",
      "message": "No local runtime is reachable at http://localhost:8787. Start one with `anvil cloud dev` first."
    }
  ]
}

The client dev server proxies /_anvil/* to the runtime, so the page is also reachable at http://localhost:5173/_anvil/lens.

What Lens shows

Tab Contents
Overview Manifest summary: query, mutation, endpoint, job, workflow, and service counts, plus declared capabilities.
Logs Latest 100 NDJSON log entries with a level filter and a pausable 5-second auto-refresh.
Traces Local agent and workflow traces, including event type, duration, and redacted attributes.
Usage Local invocation totals, token totals, estimated cost, budget warnings, and top Cell/Agent consumers.
Database Local JSON database tables with row counts; click a table to view its rows.
Auth Local identity provider users, a create-user form, and per-user JWT minting with a copyable token box.
Workflows Run list with status badges, per-step detail including attempts and errors, and a run form for each declared workflow.
Services Supervised service states with start and stop actions.
Diagnostics Trust-gateway commands, runtime state, current auth user, table summary, recent errors, and raw manifest JSON.

Everything Lens renders comes from routes you can also hit with curl: GET /_anvil/inspect, GET /_anvil/logs, GET /_anvil/traces, GET /_anvil/usage, GET /_anvil/db/tables, GET /_anvil/auth/users, GET /_anvil/workflows, GET /_anvil/services, and the corresponding action routes. Lens is a viewer, not a separate source of truth.

The ControlPlaneApi contract

The @anvil-cloud/control-plane package defines the management-plane contract Lens-style tooling depends on:

interface ControlPlaneApi {
  describe(): Promise<{ cell: string; target: "local" | string; runtimeUrl?: string }>;
  manifest(): Promise<unknown>;
  inspect(): Promise<unknown>;
  logs(options?: { limit?: number; level?: string }): Promise<ControlPlaneLogEntry[]>;
  traces(): Promise<unknown[]>;
  trace(traceId: string): Promise<unknown | null>;
  dbTables(): Promise<Record<string, { rows: number }>>;
  dbDump(table: string): Promise<unknown[]>;
  authUsers(): Promise<unknown[]>;
  workflows(): Promise<unknown[]>;
  workflowRun(runId: string): Promise<unknown | null>;
  services(): Promise<unknown[]>;
  serviceAction(name: string, action: "start" | "stop"): Promise<unknown>;
}

createHttpControlPlane(baseUrl) implements this interface against a local runtime's /_anvil/* routes. The point of the abstraction is the adapter swap: a future hosted control plane implements the same interface against hosted APIs, and tooling written against ControlPlaneApi does not change. That hosted plane does not exist yet; do not plan around it.

Honest limits

  • Lens is local only. It reads the state of the dev server on your machine, nothing else.
  • The page itself has no authentication. It is only as protected as the local runtime, which binds to localhost. Do not expose the runtime port to a network you do not trust.
  • Mutating actions (create user, mint token, run workflow, start/stop service) hit the same unauthenticated local routes the CLI uses. They are development conveniences, not an admin panel.
  • Logs auto-refresh by re-reading the local NDJSON file; there is no streaming.