Ir al contenido

Panel API development

Esta página aún no está disponible en tu idioma.

cloud/api is the real backend behind the daemon’s PanelClient: the signed source of truth for the MCP / context / capability-pack catalog, minting of scoped upstream credentials, identity→teams resolution, and an append-only audit. It is built on Node built-ins — node:http, node:crypto (Ed25519), node:sqlite — with one optional runtime dependency: the pg driver, imported lazily only when DATABASE_URL selects Postgres.

Terminal window
just panel-run # or: cd cloud/api && node src/index.ts

On boot it generates (or loads PANEL_SIGNING_KEY) an Ed25519 signing key, seeds a demo org/teams/catalog, and prints the two values the daemon needs:

CHRONOS_PANEL_URL=http://127.0.0.1:8787
CHRONOS_PANEL_PUBKEY=<64-hex public key>

Wire the daemon to it:

Terminal window
export CHRONOS_PANEL_URL=http://127.0.0.1:8787
export CHRONOS_PANEL_PUBKEY=<hex from above> # the daemon pins the PUBLIC key only
export CHRONOS_IDENTITY=alice@corp
export CHRONOS_TEAMS=team-a,team-b
just run-daemon

The daemon fetches the Ed25519-signed catalog, verifies it before trusting it, resolves the cascade, and materializes only loopback MCPs — the real upstream secret stays in the broker. With CHRONOS_PANEL_URL unset the daemon stays offline (no governed MCPs), so CI never needs the panel.

One async Db interface (src/db.ts), two backends — all SQL lives behind it:

  • SQLite (default) — PANEL_DB path, panel.db if unset. Zero dependencies, single-tenant; runs with just node (22.5+).
  • Postgres / Neon — set DATABASE_URL (e.g. a Neon connection string with ?sslmode=require); it wins over PANEL_DB. The schema is created on boot and the Ed25519 signing key persists in the database, so daemons keep verifying across restarts and redeploys. Requires pnpm install locally for the pg driver.
Terminal window
just panel-test # node --test (SQLite suite; pg suite self-skips)
TEST_DATABASE_URL=postgres://… just panel-test # also runs the Postgres suite

The Postgres suite (pg.e2e.test.ts) drops the panel tables — point TEST_DATABASE_URL only at a throwaway database. Node↔Rust signature compatibility is additionally pinned by a cross-language vector test in chronos-broker (ed25519_verifies_a_node_panel_signature_vector).

The routes mirror PanelClient 1:1:

RouteAuthPurpose
GET /v1/catalog?identity&team=…optionalsigned MCP catalog (cascade-filtered)
GET /v1/context?identity&team=…optionalsigned context bundle
GET /v1/packs?identity&team=…optionalsigned capability-pack set
POST /v1/mint/:serverIdbearerscoped upstream credential (audited, rate-limited)
POST /v1/auth/tokenOIDC seam: {subject}{token, identity, teams}
GET /v1/keystrusted Ed25519 public keys
POST /v1/auditbearerdaemon session-load audit sink
GET /v1/auditaudit tail (panel dashboard)

Security invariants to preserve when changing this code: the panel signs with the private key and the daemon only ever verifies with the public key; mint is never anonymous (bearer required, rate-limited, audited — the audit records which server was minted by whom, never the token); secrets never appear in a catalog or a log. In production the signing key comes from PANEL_SIGNING_KEY (KMS/secret); the generated dev key and panel.db are gitignored.

Set PANEL_ADMIN_PASSWORD (plus optional PANEL_ADMIN_IDENTITY, default admin@corp) to create or password-reset the admin account at boot, so a fresh deploy is administrable without touching the database. PANEL_SEED_PASSWORD gives the demo accounts (alice/bob/admin) credentials in dev and tests; without it they cannot sign in.