Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/target
.cursor/
__pycache__/

# Dev stack runtime state created by scripts/up.sh
.run/
config.toml
certs/
3 changes: 3 additions & 0 deletions dashboard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
test-results/
traces-*.json
57 changes: 57 additions & 0 deletions dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Agent Gateway Dashboard

A standalone web server that visualizes an agent gateway deployment in real time. It ingests OpenTelemetry log records from the gateway and surrounding services, and queries the authority registry directly for snapshots of registered humans, devices, and agents.

The dashboard is owned by this repo. The backend it visualizes (gateway, platform, registry, agents) lives elsewhere and is the contract surface described below.

## What it does

- Serves the dashboard UI (`public/index.html`, `public/topology.js`, `registry-panel.js`).
- Accepts OTLP/HTTP+JSON log records on `POST /v1/logs` and pushes them to connected browsers over a WebSocket. Recent events are kept in an in-memory ring buffer (`MAX_EVENTS = 1000`).
- Proxies `GET /api/registry` and `GET /api/devices` to an upstream authority registry (`REGISTRY_URL`). The browser uses these for the registry panel and the topology's user-device discovery.

## Backend contract

The dashboard expects the backend to provide:

| Surface | What the dashboard does with it |
|---------|---------------------------------|
| OTLP push to `POST /v1/logs` | Live event feed: gateway decisions (`request.allowed` / `request.denied` / `auth.allowed` / `tunnel.error`), platform lifecycle (`agent.created` / `agent.terminated`), user-device actions (`agent.requested` / `prompt.sent` / `permission.change_requested` / `termination.requested`). |
| `GET /snapshot` on the registry (proxied as `/api/registry`) | Topology + registry panel: humans, agents, permission grants. |
| `GET /users` on the registry (proxied as `/api/users`) | User nodes in the topology. |

The OTLP shape is plain HTTP/JSON (no gRPC). `translate.js` parses the standard OTLP `resourceLogs` envelope and lifts each `LogRecord`'s attributes into a flat event the browser consumes.

## Configuration

| Env var | Default | Purpose |
|---------|---------|---------|
| `REGISTRY_URL` | `http://authority-registry:8080` | Upstream registry. The two `/api/...` endpoints proxy to this host. |

The server binds `0.0.0.0:3000`.

## Running

```bash
npm install
npm start # node server.js
npm test # node --test
```

Open `http://localhost:3000`. With no events flowing in and no upstream registry reachable, the topology will show only the static infrastructure nodes and the registry panel will display an error.

## Layout

```
.
├── server.js # HTTP + WebSocket server, OTLP ingest, registry proxy
├── translate.js # OTLP -> flat-event translation
├── translate.test.js
├── registry-panel.js # Browser-side renderer for the registry panel
├── registry-panel.test.js
├── theme.css # Shared color tokens
├── public/
│ ├── index.html # Dashboard shell
│ └── topology.js # Topology canvas, edge highlighting, node placement
└── package.json
```
37 changes: 37 additions & 0 deletions dashboard/otel-collector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# OpenTelemetry Collector config used by the dashboard's docker-compose
# service. It bridges the gateway (which emits OTLP gRPC) and the dashboard
# (which accepts OTLP HTTP/JSON on /v1/traces).

receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317

processors:
batch:
# Small flush window so the dashboard sees events within ~1s of the
# gateway emitting them.
timeout: 1s
send_batch_size: 50

exporters:
otlphttp/dashboard:
# The collector runs in docker; the dashboard runs on the host. Reach
# it via the dockerized host alias. The exporter appends /v1/traces
# automatically, so this matches POST http://<host>:3000/v1/traces.
endpoint: http://host.docker.internal:3000
encoding: json
tls:
insecure: true

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/dashboard]

telemetry:
logs:
level: info
183 changes: 183 additions & 0 deletions dashboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "dashboard",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node server.js",
"test": "node --test"
},
"dependencies": {
"pg": "^8.20.0",
"ws": "^8.19.0"
}
}
Loading