Documentation / Local API Reference

Local API Reference

HTTP REST API specification for AI agents and local automation.

Overview

Metrix.Bar runs a local HTTP REST service bound strictly to loopback (127.0.0.1:6387). It is designed for AI coding agents, command-line scripts, and developer tools to inspect, validate, update, and manage .bar dashboards.

  • Host & Port: http://127.0.0.1:6387
  • Authentication: Bearer token via Authorization: Bearer <token> (GET /v0/health is exempt)
  • Full Schema: The complete OpenAPI specification is accessible at /openapi.json (v0 specification)

Port 6387 is an example throughout this guide. Use the address and port shown in the app’s Agent settings.

Security Model & Status Codes

The local API is hardened against unauthorized local network access and browser-based attacks:

  1. Strict Loopback & Host Header Verification: The HTTP server binds exclusively to 127.0.0.1 and verifies the Host request header, accepting only 127.0.0.1, localhost, or loopback IP variants (with optional port). Non-loopback hosts return 403 Forbidden.
  2. Browser Cross-Origin Protection: Requests containing an Origin header (including Origin: null) are rejected immediately with 403 Forbidden. CORS is disabled, preventing DNS rebinding and cross-site execution.
  3. Bearer Token Verification: Every endpoint (except /v0/health) requires the Bearer token configured in Settings → Agent; missing or invalid tokens return 401 Unauthorized.
  4. Request Body Limit: Payloads are capped at 2 MiB per request.
  5. Secret Isolation: Endpoint responses never reveal raw secret values stored in the system credential store (keychain). Secret inputs only report is_set: true | false, with value fixed to null.

Status Codes Reference

Status CodeMeaningDescription
200 OKSuccessReturns the requested resource or operation result.
201 CreatedCreatedDashboard created successfully; response headers include the new source ETag and resource Location.
202 AcceptedAcceptedAsynchronous task accepted and dispatched (refresh, event dispatch); does not wait for execution.
204 No ContentNo ContentRequest processed with no response body (inputs update, secret write, delete dashboard).
400 Bad RequestBad RequestMalformed JSON, missing required fields, or undeclared/invalid input parameters.
401 UnauthorizedUnauthorizedMissing or invalid Bearer token; includes WWW-Authenticate: Bearer realm="Metrix.Bar". The scheme is case-insensitive; the token is exact.
413 Payload Too LargeToo largeRequest body exceeds 2 MiB.
415 Unsupported Media TypeUnsupported typeMissing, malformed, or unsupported Content-Type on source endpoints.
403 ForbiddenForbiddenRequest carries an Origin header, or Host header is not a loopback address.
404 Not FoundNot FoundDashboard not found, or secret input not declared in .bar.
412 Precondition FailedPrecondition FailedIf-Match optimistic concurrency check failed; ETag mismatch; GET source again before retrying.
422 Unprocessable EntityValidation Failed.bar source parsing failed; returns structured diagnostics.
501 Not ImplementedNot ImplementedThe host integration callback is not implemented in the current runtime.

Endpoint Summary

All routes are mounted under /v0:

MethodEndpointDescription
GET/v0/healthUnauthenticated health check returning status, uptime in ms, and protocol version.
POST/v0/validateValidates .bar source text without persisting it.
POST/v0/checkPreflights an isolated Worker and validates its view tree without installing it.
GET/v0/typesReturns the Worker TypeScript declarations for editors and agents.
GET/v0/dashboardsLists all saved dashboards with status and ETag.
POST/v0/dashboardsCreates a new dashboard from raw .bar source text.
GET/v0/dashboards/:idReturns complete dashboard state snapshot and view tree.
DELETE/v0/dashboards/:idUninstalls a dashboard and its associated keychain secrets.
GET/v0/dashboards/:id/sourceRetrieves raw .bar source text with an ETag header.
PUT/v0/dashboards/:id/sourceReplaces .bar source text (supports If-Match concurrency check).
PATCH/v0/dashboards/:id/settingsUpdates entrance, default, and per-source opening-mode overrides (placements, open_mode, open_modes).
GET/v0/dashboards/:id/inputsReads input schema declarations, current values, and secret statuses.
POST/v0/dashboards/:id/inputsUpdates public input values.
PUT/v0/dashboards/:id/secrets/:nameStores a secret value in the system credential store.
POST/v0/dashboards/:id/refreshTriggers an immediate refresh cycle.
POST/v0/dashboards/:id/reloadReloads saved source after Worker preflight; preserves the existing runtime if preflight fails.
POST/v0/dashboards/:id/events/:nameDispatches a custom event to the dashboard Worker.

Detailed Endpoint Guide

Health Check

GET /v0/health

Verifies that the local server is running. Requires no authentication. Returns process uptime in milliseconds and the protocol version.

bash
curl -fsS http://127.0.0.1:6387/v0/health

Response (200 OK):

json
{
  "status": "ok",
  "uptime_ms": 9900000,
  "protocol_version": "0",
  "app_version": "0.1.0"
}

app_version is optional and only present when configured by the application.


Worker Preflight and Types

POST /v0/check accepts only application/json (optional charset) with { "source": "...", "inputs": {}, "scheduled": false, "tree": false }. Only source is required. It runs render in an isolated Worker; scheduled runs first only when scheduled: true. Both scheduled and tree default to false. Set tree: true to include the view tree. Diagnostics are returned without installation; network or storage side effects can still occur. GET /v0/types returns metrixbar.d.ts as text for editor completion.

To apply an external edit already saved on disk, call POST /v0/dashboards/:id/reload with the Bearer token and no body. A failed preflight preserves the previous runtime.

Isolated fixtures and PNG screenshots

POST /v0/check also accepts optional fixture and screenshot objects. The tree and screenshot come from one Worker render. A fixture supplies test data, is limited to 2 MiB of JSON, and rejects unknown fields. Its required now field is an RFC 3339 timestamp that freezes the UTC clock. Inputs merge in this order: declared defaults → fixture.inputs → request inputs, with normal type validation and secret input restrictions.

json
{
  "source": "<bar ...>...</bar>",
  "scheduled": true,
  "tree": true,
  "fixture": {
    "now": "2026-06-01T12:00:00Z",
    "inputs": {},
    "secrets": { "token": "fixture-token" },
    "files": { "/fixture/value": "42" },
    "icons": {
      "lucide:zap": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"m13 2-3 8H4l7 12 3-8h6Z\"/></svg>"
    },
    "http": [{ "url": "https://metrics.example.com/value", "body": "42" }]
  },
  "screenshot": { "theme": "dark", "width": 320, "height": 240, "scale": 2 }
}

http matches the exact method (default GET), full URL, and optional request_body. Status defaults to 200 and body is the raw response string. Remote modules and Image/Avatar resources also use fixture HTTP; resources load before the original Worker closes, with the same permissions and request identity headers. files matches absolute virtual paths and still requires declared permissions. secrets reads only fixture values, never system credentials. Named Icon resources (and Image.name without src/svg) require an explicit icons entry containing SVG or an image data URI, 1..256 KiB each. Missing names fail without consulting Assets, disk caches, or live Iconify services. Inline SVG and image data URIs remain supported. Sockets are disabled in fixture mode. Unmatched HTTP, files, secrets, or icons always produce diagnostics, even if the script catches the error. Never include real credentials in fixtures. Without a fixture, checks can still perform real I/O allowed by the dashboard permissions.

Omitting screenshot skips PNG generation. When supplied, theme must be light or dark; width/height use DIP and default to the panel size (320×240 when undeclared), each within 1..2048. Scale defaults to 1 and allows 0.5..4; the final image must not exceed 16 Mi pixels. Success adds screenshot: {mime_type:"image/png", width:640, height:480, data_base64:"..."} with actual PNG pixel dimensions. Rendering uses memory and waits for resources. Resource collection failures produce resources diagnostics (plus fixture for unmatched fixture I/O), and drawing failures produce screenshot diagnostics: HTTP 200, valid:false, no screenshot. Invalid parameters return 400. Loopback, Bearer, Origin, and secret isolation rules still apply.

Validating .bar Source

POST /v0/validate

Validates .bar source syntax and schema compliance without saving it to disk. The parser operates in a fail-fast model, returning at most one diagnostic.

bash
curl -fsS -X POST http://127.0.0.1:6387/v0/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @dashboard.bar

Valid Source Response (200 OK):

json
{
  "valid": true,
  "diagnostics": []
}

Invalid Source Response (200 OK):

json
{
  "valid": false,
  "diagnostics": [
    {
      "severity": "error",
      "code": "inputs/input@type",
      "message": "unknown input type \"color\"",
      "path": "inputs/input@type"
    }
  ]
}

Dashboard Management

GET /v0/dashboards

Lists all installed dashboards. The status field enum values are idle, loading, ok, or error. When present in the dashboard definition, optional metadata fields (version, icon, description, license, homepage, docs) are included in each summary.

bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards

Response (200 OK):

json
{
  "dashboards": [
    {
      "id": "github-repo",
      "title": "GitHub Repo",
      "status": "ok",
      "protocol_version": "0",
      "revision": 3,
      "etag": "0123456789abcdef",
      "version": "1.0.0",
      "description": "Track GitHub repository stars, forks, and activity."
    }
  ]
}

POST /v0/dashboards

Creates and installs a new dashboard from .bar source text. The request body accepts text/plain, application/octet-stream, or JSON {"source": "..."}. Returns the created dashboard summary (including protocol_version and any optional metadata fields declared in <bar>).

bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @dashboard.bar

Response (201 Created): The response header includes ETag: "0123456789abcdef".

json
{
  "id": "github-repo",
  "status": "loading",
  "title": "GitHub Repo",
  "protocol_version": "0",
  "etag": "0123456789abcdef"
}

If source parsing fails, returns 422 Unprocessable Entity with diagnostics.

GET /v0/dashboards/:id

Retrieves a dashboard's current state and rendered UI view tree snapshot. In view trees returned over the API, function callbacks (such as onClick) are serialized as null.

bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo

Response (200 OK):

json
{
  "id": "github-repo",
  "status": "ok",
  "updated_at": "2025-01-01T00:00:00Z",
  "revision": 3,
  "inputs": {
    "repository": "golang/go"
  },
  "docs": "Configure your GitHub repository path to start monitoring.",
  "view": {
    "tray": {
      "title": "★ 1.2k"
    },
    "panel": {
      "type": "Column",
      "props": { "padding": 16, "gap": 12 },
      "children": [{ "type": "Text", "props": { "text": "egoist/mygo", "bold": true } }]
    }
  }
}

When status is error, the response includes an error field describing the most recent run failure. docs contains plain-text setup instructions from <docs> (omitted when empty). view retains the last successfully rendered UI tree (null if no successful render has occurred yet).

GET /v0/dashboards/:id/source

Retrieves the raw .bar source text. The response header includes an RFC 9110 quoted ETag (the first 16 hex characters of the source's SHA-256 hash).

bash
curl -fsS -H "Authorization: Bearer $TOKEN" -i http://127.0.0.1:6387/v0/dashboards/github-repo/source

PUT /v0/dashboards/:id/source

Replaces the .bar source text. The request body accepts text/plain, application/octet-stream, or JSON {"source": "..."}. The id in the new source must match the :id parameter in the URL.

Supports optimistic concurrency control via the If-Match header: provide the quoted ETag returned by GET source, a comma-separated list of quoted tags, or standalone * to match any existing dashboard. Strong comparison applies: weak tags never match, bare tags and malformed lists return 400. Only an omitted header skips the tag check; concurrent file edits remain protected.

bash
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/source \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  -H 'If-Match: "0123456789abcdef"' \
  --data-binary @updated.bar

Success Response (200 OK): The response header returns the new source ETag.

json
{
  "id": "github-repo",
  "updated": true
}

ETag Mismatch Response (412 Precondition Failed):

json
{
  "error": "etag mismatch"
}

Validation Failed Response (422 Unprocessable Entity):

json
{
  "error": "validation failed",
  "diagnostics": [
    {
      "severity": "error",
      "code": "bar@id",
      "message": "source id \"other-repo\" does not match dashboard \"github-repo\"",
      "path": "bar@id"
    }
  ]
}

DELETE /v0/dashboards/:id

Uninstalls the specified dashboard, stops the running instance, and removes its stored secrets from the system credential store. The .bar source file is retained on disk.

bash
curl -fsS -X DELETE -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo

Response (204 No Content): Empty response body.


Inputs & Secrets

GET /v0/dashboards/:id/inputs

Lists input parameter declarations and current values. Only secret inputs keep value set to null and report presence via is_set: true | false. File inputs return their local path string through this authenticated API.

bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/inputs

Response (200 OK):

json
{
  "inputs": [
    {
      "name": "repository",
      "type": "string",
      "required": true,
      "label": "Repository",
      "help": "owner/repo",
      "value": "egoist/mygo",
      "is_set": true
    },
    {
      "name": "token",
      "type": "secret",
      "required": false,
      "label": "GitHub Token",
      "value": null,
      "is_set": true
    }
  ]
}

POST /v0/dashboards/:id/inputs

Updates public configuration input values. The request body must be wrapped in {"inputs": {...}}. Submitting new values triggers an asynchronous refresh cycle.

Values must match declared types (string/file as string, bool as boolean, integer as integer number, number as finite number). secret inputs cannot be updated here (use the secrets endpoint); undeclared input keys return 400 Bad Request.

bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/inputs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"inputs": {"repository": "golang/go"}}'

Response (204 No Content): Empty response body.

PUT /v0/dashboards/:id/secrets/:name

Writes a sensitive credential into the system credential store (keychain) and triggers a dashboard refresh. The target input must be declared with type="secret" in .bar, or 404 Not Found is returned. Secret values are excluded from ordinary dashboard records, snapshots, and API responses. Dashboard scripts can read them for authentication.

Request body is JSON {"value": "..."}:

bash
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/secrets/token \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "ghp_xxxxxxxxxxxx"}'

Response (204 No Content): Empty response body.


Actions & Events

POST /v0/dashboards/:id/refresh

Dispatches a refresh command to the scheduler, triggering the dashboard Worker to fetch fresh data. Returns 202 Accepted to indicate asynchronous acceptance without waiting for render completion.

bash
curl -fsS -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/refresh

Response (202 Accepted): Empty response body.

POST /v0/dashboards/:id/events/:name

Dispatches a custom event to the dashboard Worker's event(name, payload) handler. The request body accepts any valid JSON value (or empty, which defaults to null). A render is automatically scheduled when event processing finishes.

bash
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/events/notify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action": "toggle-mode"}'

Response (202 Accepted): Empty response body.

PATCH /v0/dashboards/:id/settings

The request body accepts placements, open_mode, and open_modes, with at least one field required:

  • placements: an array containing any unique combination of menubar, sidebar-left, sidebar-right, and sidebar-bottom, or null.
  • open_mode: default opening mode override (flyout, dialog, or null).
  • open_modes: per-source opening mode overrides object, or null:
    • Omitted: preserves existing per-source overrides.
    • null or {}: clears all per-source overrides (all sources revert to following the default opening mode).
    • Object: replaces the entire per-source override map (not a partial merge). Valid keys: menubar, sidebar-left, sidebar-right, sidebar-bottom, shortcut. Valid values: flyout, dialog. Unknown keys, invalid values, or wrong types return 400.

Omitted fields stay unchanged; null clears only that field's user override and restores its author default. placements: [] disables all visual entrances. Overrides are stored independently and apply equally to official and third-party dashboards.

Unknown fields, wrong types, unknown or duplicate placements, invalid modes, and an empty object return 400. The former show_in_menu_bar field is no longer accepted. A missing dashboard returns 404, an operation conflict returns 409, persistence failure returns 500, and an unavailable settings callback returns 501.

Configure entrances, default opening mode, and per-source overrides (e.g. Menu Bar and Right Sidebar entrances, default Flyout, with Keyboard Shortcut and Right Sidebar opening Dialog):

bash
curl -fsS -X PATCH http://127.0.0.1:6387/v0/dashboards/github-repo/settings \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"placements":["menubar","sidebar-right"],"open_mode":"flyout","open_modes":{"shortcut":"dialog","sidebar-right":"dialog"}}'

Response (200 OK): Returns the final effective settings. placements returns [] when disabled, never null. open_modes always returns the resolved effective modes across all five sources, never null.

json
{
  "id": "github-repo",
  "placements": ["menubar", "sidebar-right"],
  "open_mode": "flyout",
  "open_modes": {
    "menubar": "flyout",
    "sidebar-left": "flyout",
    "sidebar-right": "dialog",
    "sidebar-bottom": "flyout",
    "shortcut": "dialog"
  }
}

Other request bodies:

Change only the default opening mode:

json
{ "open_mode": "flyout" }

Disable all visual entrances:

json
{ "placements": [] }

Restore default entrances (leaves opening modes untouched):

json
{ "placements": null }

Restore default opening mode (leaves per-source overrides untouched):

json
{ "open_mode": null }

Clear all per-source overrides (all sources revert to the default opening mode):

json
{ "open_modes": null }

Restore default entrances, default opening mode, and clear all per-source overrides:

json
{ "placements": null, "open_mode": null, "open_modes": null }

Programmatic panel actions (such as $metrix.panel.open() or toggle()) follow the effective default opening mode (open_mode), and keep the {open} response.

Source endpoints (validate, create, and source replacement) require an explicit Content-Type: text/plain, application/octet-stream, or application/json with { "source": "..." }. Media type parameters such as charset are allowed. Missing or unsupported types return 415; malformed JSON returns 400; bodies over 2 MiB return 413.