luxfi/consensus v1.36.2 has a signed hash in go.sum and in sum.golang.org that no longer matches the tag. proxy.golang.org 404s .info, .mod and .zip for it while v1.36.1 and v1.36.3 serve 200, and a direct fetch of the tag returns a go.mod hashing to m4aMFbKY..., which is byte-identical to what v1.36.3 publishes. The tag was moved forward after release; go.sum is the side that is right, so the pin moves to v1.36.3 rather than the check being suppressed. hanzoai/base v1.4.6 and v1.4.7 require github.com/hanzoai/cloud v0.1.1. That version is tagged on hanzo-inc/cloud but not on hanzoai/cloud, which is the path the module declares and the only place a fetch looks; every tag there is forge/v1.801.*. v1.4.5 is the last release whose own graph resolves. go.mod had already moved to v1.4.7 while go.sum still carried v1.4.2, so nothing in this module built at all. go vet ./... and go test ./... are clean. Co-authored-by: Hanzo Dev <[email protected]>
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).