The whole surface now lives in hanzoai/observability on msgType 203, ordinals preserved at +200, implementation in its dash_*.go files. A dashboard is a saved query over the traces/observations/scores that service owns, and both halves were blocked on the same ClickHouse shim; apart, it had to be written twice. History is kept: the ordinals, wire layout and per-category permission bits this repo established are still the live contract. Co-authored-by: Hanzo Dev <[email protected]>
7.1 KiB
dashboards — FOLDED INTO hanzoai/observability
This service no longer ships as its own binary. Its whole surface — the five tRPC routers below — now lives in
hanzoai/observability, served on the same ZAP router (msgType 203, port 9992) as the traces / observations / scores backbone, with the method ordinals preserved at +200 (allDashboards was @0, is now @200). Implementation is thedash_*.gofile set there; the schema is merged intoproto/observability.zap.Why: a dashboard is a saved QUERY over exactly the traces, observations and scores that service already owns, and both halves were blocked on the same ClickHouse analytics shim. Kept apart, that shim had to be written twice against two copies of one OLTP model, and
chart/scoreHistogramcould not reach the scores they aggregate without a network hop. One service, one shim, one capability gate.Do not build or deploy this repo. Open PRs against
hanzoai/observability. The history here is kept because the ordinals, the wire layout and the per-category permission bits it established are still the live contract.
Original README (historical)
A Hanzo Base-native Go service binary in the console tRPC→ZAP migration. It
replaces five in-process console tRPC routers — dashboardRouter,
dashboardWidgetRouter, tableRouter, TableViewPresetsRouter,
monitorsRouter — with one typed ZAP capability-RPC interface at
msgType 206.
Pattern: Go binary built on Hanzo Base (embedded encrypted SQLite
- plugins) exposing a typed ZAP capability-RPC interface. No Prisma, no Postgres-as-source-of-truth, no Mongo, no Cap'n Proto, no tRPC in the backend.
.zap schema (source of truth) ──zapgen──▶ gen/ (Go views)
│ │
└──zapgen --target=ts──▶ console TS ▼
server/ ─ ZAP RPC handler (cap-gated)
main.go ─ base.New() + ZAP router :9995
Base HTTP (health/metrics) :8090
vault → per-org encrypted SQLite
Run
make build
./dashboards serve --http=127.0.0.1:8090 --zap=127.0.0.1:9995
Optional per-org encrypted SQLite (vault plugin): add --vaultDir=/data/vaults.
The five backing collections (dashboards, dashboard_widgets,
table_batch_jobs, table_view_presets, monitors) are provisioned on first
boot (idempotent, see server/collections.go).
Smoke test
In-process suite (per-method CRUD + per-category permission gate + pipelining proof + analytics/queue 501 contracts):
make test
Out-of-process probe against a live binary (dashboard CRUD + pipelined dashboard→widget create):
./dashboards serve --zap=127.0.0.1:9995 &
go run ./cmd/probe --addr 127.0.0.1:9995 --peer dashboards
The interface
30 methods on a CapKindIAMSession capability, each gated on exactly one
DashPermissions bit (server/wire.go). The bits are the orthogonal product of
the five resource categories × {Read, Write} plus an analytics-read bit, mapping
1:1 to the console RBAC scopes:
| Category | Methods | DashPermissions bit ⇄ RBAC scope |
|---|---|---|
| Dashboard CRUD | allDashboards, getDashboard, createDashboard, updateDashboardMetadata, updateDashboardDefinition, updateDashboardFilters, cloneDashboard, deleteDashboard | dashboards:read / dashboards:CUD |
| Widget CRUD | allWidgets, getWidget, createWidget, updateWidget, copyWidgetToProject, deleteWidget | (shares Dashboard bits) |
| Dashboard analytics | chart, scoreHistogram, executeQuery | PermAnalyticsRead — 501, ClickHouse shim pending |
| Table batch-action | isBatchActionInProgress | PermDashboardRead — 501, BullMQ shim pending |
| TableViewPreset CRUD | getPresetsByTableName, getPresetById, createPreset, updatePreset, updatePresetName, deletePreset, generatePermalink | TableViewPresets:read / TableViewPresets:CUD |
| Monitor CRUD | allMonitors, getMonitor, createMonitor, updateMonitor, deleteMonitor | monitors:read / monitors:CUD |
Capability auth is the single chokepoint Server.authorize: it Wraps the opaque
capability buffer, enforces Kind == CapKindIAMSession and the method's
DashPermissions bit (via permissionFor), and (when an issuer registry is
wired) verifies the signature. The signature step is stubbed in bootstrap (TODO
in server.go) — Kind + Permissions are always enforced. Every data query is
scoped to the projectId in the request — the multi-tenant boundary.
Pipelining
createDashboard/createWidget support Cap'n Proto-style promise pipelining:
the widget call Targets the dashboard call's PromiseID, so the server resolves
the dashboard's project scope and dispatches the promised widget create against
it — eliding a client round trip. See Client.PipelineCreateDashboardThenWidget
and TestPipelineDashboardThenWidget.
Not-yet-wired (TODO, return 501)
The analytics reads (chart/scoreHistogram/executeQuery) hit ClickHouse
(OLAP), and the table batch-action read hits a BullMQ/Valkey queue — neither
is Base. Both return 501 until their shims land; they never fabricate data:
server/analytics.go— ClickHouse reader portinggetScoreAggregate,getNumericScoreHistogram,getObservation*ByTime, and thefeatures/queryexecuteQuery builder from@hanzo/shared.server/batchqueue.go— Valkey/BullMQ reader portinggenerateBatchActionIdgetJobStatefromconsole/web/src/features/table/server.
Wiring the console to this service (handoff)
The console keeps its existing TS client (same .zap contract, compiled to TS by
zapgen --target=ts instead of Go). Its ZAP bridge substitutes the in-process
routers for a ZAP client to this service. The wire envelope is (method:u32, promiseID:u32, target:u32, cap:bytes, payload:bytes) at ZAP msgType 206;
responses are (status:u32, promiseID:u32, body:bytes). Method ordinals match
the schema (allDashboards=0 … deleteMonitor=29).
This repo does not edit console.
Layout
| Path | What |
|---|---|
proto/dashboards.zap |
Canonical schema (zap-spec dialect → Go + TS). |
gen/ |
zapgen output (make zap-gen). Generated; do not edit. |
server/wire.go |
Transport envelope codec + msgType/method/status/permission consts. |
server/server.go |
ZAP RPC handler: cap auth, project scoping, dispatch, promise pipelining. |
server/handlers.go |
The 30 per-router method bodies (Base CRUD + analytics/queue TODOs). |
server/encode.go |
Base record → generated wire-struct projection (one encoder per model). |
server/collections.go |
The five Base collection schemas. |
server/client.go |
Typed client (one method per RPC) + pipelining + SyntheticCap. |
main.go |
Binary: base.New() + vault + ZAP router :9995. |
cmd/probe/ |
Out-of-process smoke probe. |
Container registry: ghcr.io/hanzoai/dashboards (CI-built, multi-arch).