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/healthis 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:
- Strict Loopback & Host Header Verification: The HTTP server binds exclusively to
127.0.0.1and verifies theHostrequest header, accepting only127.0.0.1,localhost, or loopback IP variants (with optional port). Non-loopback hosts return403 Forbidden. - Browser Cross-Origin Protection: Requests containing an
Originheader (includingOrigin: null) are rejected immediately with403 Forbidden. CORS is disabled, preventing DNS rebinding and cross-site execution. - Bearer Token Verification: Every endpoint (except
/v0/health) requires the Bearer token configured in Settings → Agent; missing or invalid tokens return401 Unauthorized. - Request Body Limit: Payloads are capped at 2 MiB per request.
- Secret Isolation: Endpoint responses never reveal raw secret values stored in the system credential store (keychain). Secret inputs only report
is_set: true | false, withvaluefixed tonull.
Status Codes Reference
| Status Code | Meaning | Description |
|---|---|---|
200 OK | Success | Returns the requested resource or operation result. |
201 Created | Created | Dashboard created successfully; response headers include the new source ETag and resource Location. |
202 Accepted | Accepted | Asynchronous task accepted and dispatched (refresh, event dispatch); does not wait for execution. |
204 No Content | No Content | Request processed with no response body (inputs update, secret write, delete dashboard). |
400 Bad Request | Bad Request | Malformed JSON, missing required fields, or undeclared/invalid input parameters. |
401 Unauthorized | Unauthorized | Missing or invalid Bearer token; includes WWW-Authenticate: Bearer realm="Metrix.Bar". The scheme is case-insensitive; the token is exact. |
413 Payload Too Large | Too large | Request body exceeds 2 MiB. |
415 Unsupported Media Type | Unsupported type | Missing, malformed, or unsupported Content-Type on source endpoints. |
403 Forbidden | Forbidden | Request carries an Origin header, or Host header is not a loopback address. |
404 Not Found | Not Found | Dashboard not found, or secret input not declared in .bar. |
412 Precondition Failed | Precondition Failed | If-Match optimistic concurrency check failed; ETag mismatch; GET source again before retrying. |
422 Unprocessable Entity | Validation Failed | .bar source parsing failed; returns structured diagnostics. |
501 Not Implemented | Not Implemented | The host integration callback is not implemented in the current runtime. |
Endpoint Summary
All routes are mounted under /v0:
| Method | Endpoint | Description |
|---|---|---|
GET | /v0/health | Unauthenticated health check returning status, uptime in ms, and protocol version. |
POST | /v0/validate | Validates .bar source text without persisting it. |
POST | /v0/check | Preflights an isolated Worker and validates its view tree without installing it. |
GET | /v0/types | Returns the Worker TypeScript declarations for editors and agents. |
GET | /v0/dashboards | Lists all saved dashboards with status and ETag. |
POST | /v0/dashboards | Creates a new dashboard from raw .bar source text. |
GET | /v0/dashboards/:id | Returns complete dashboard state snapshot and view tree. |
DELETE | /v0/dashboards/:id | Uninstalls a dashboard and its associated keychain secrets. |
GET | /v0/dashboards/:id/source | Retrieves raw .bar source text with an ETag header. |
PUT | /v0/dashboards/:id/source | Replaces .bar source text (supports If-Match concurrency check). |
PATCH | /v0/dashboards/:id/settings | Updates entrance, default, and per-source opening-mode overrides (placements, open_mode, open_modes). |
GET | /v0/dashboards/:id/inputs | Reads input schema declarations, current values, and secret statuses. |
POST | /v0/dashboards/:id/inputs | Updates public input values. |
PUT | /v0/dashboards/:id/secrets/:name | Stores a secret value in the system credential store. |
POST | /v0/dashboards/:id/refresh | Triggers an immediate refresh cycle. |
POST | /v0/dashboards/:id/reload | Reloads saved source after Worker preflight; preserves the existing runtime if preflight fails. |
POST | /v0/dashboards/:id/events/:name | Dispatches 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.
curl -fsS http://127.0.0.1:6387/v0/health
Response (200 OK):
{
"status": "ok",
"uptime_ms": 9900000,
"protocol_version": "0",
"app_version": "0.1.0"
}
app_versionis 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.
{
"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.
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):
{
"valid": true,
"diagnostics": []
}Invalid Source Response (200 OK):
{
"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.
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards
Response (200 OK):
{
"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>).
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".
{
"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.
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
Response (200 OK):
{
"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
statusiserror, the response includes anerrorfield describing the most recent run failure.docscontains plain-text setup instructions from<docs>(omitted when empty).viewretains the last successfully rendered UI tree (nullif 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).
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.
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.
{
"id": "github-repo",
"updated": true
}ETag Mismatch Response (412 Precondition Failed):
{
"error": "etag mismatch"
}Validation Failed Response (422 Unprocessable Entity):
{
"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.
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.
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/inputs
Response (200 OK):
{
"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.
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": "..."}:
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.
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.
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 ofmenubar,sidebar-left,sidebar-right, andsidebar-bottom, ornull.open_mode: default opening mode override (flyout,dialog, ornull).open_modes: per-source opening mode overrides object, ornull:- Omitted: preserves existing per-source overrides.
nullor{}: 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):
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.
{
"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:
{ "open_mode": "flyout" }Disable all visual entrances:
{ "placements": [] }Restore default entrances (leaves opening modes untouched):
{ "placements": null }Restore default opening mode (leaves per-source overrides untouched):
{ "open_mode": null }Clear all per-source overrides (all sources revert to the default opening mode):
{ "open_modes": null }Restore default entrances, default opening mode, and clear all per-source overrides:
{ "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.