Darling custom views/notebooks (#1563) - #1576
Merged
Merged
Conversation
…k-gated CRUD + catalog (#1563) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, export/import (#1563) Builds the #1563 frontend on Builder 1's loopback-gated CRUD + catalog backend: - Routes in app.js (#/views, #/view/{id}, #/view/{id}/edit, #/view/new) with an editor-route poll-clobber guard so the 60s refresh never discards an in-progress edit; a sidebar Views section + dynamic #view-list, "New view" only when can_edit. - Renderer (js/pages/views.js): tiles a view's stored panels into a responsive .panel-grid through the UNMODIFIED renderPanel, pre-checking each panel's read against the cached catalog and viz against the registry (clean strip, not a 404). - Composer (js/editor.js + pure js/derive.js): read picker grouped by category, typed param inputs from the catalog (server -> fleet dropdown; required blocks save/preview), viz + span + title, a vizcfg sub-editor SEEDED by client-side derivation from a live sample, a live debounced preview through renderPanel, and up/down panel reorder. - Export = runtime Blob download; Import = paste -> client-validate vs catalog -> POST. - can_edit gates every edit affordance (New/Edit/Delete/Save/Import); render + export stay open to all. XSS-safe (el()/textContent); series color constrained to the palette or <input type=color> (#rrggbb), never free text. - js/views-api.js (CRUD/catalog/session clients + cache + pure validateDefinition); util.js gains apiSend (shared classifier with apiGet); css/editor.css + .panel-grid. - DarlingWebAssetsTests pins the new entry files; self-containment scan auto-covers them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…validate series color server-side (#1563) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ss panels (#1563) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
erikdarlingdata
force-pushed
the
feature/1563-custom-views
branch
from
July 18, 2026 22:35
73a2e6a to
b5ccc5d
Compare
…-only Networked operation is the normal mode for the web dashboard, so custom-view editing is available to any authenticated seat (network: past the token->cookie + CIDR gate; loopback: on the host) — not restricted to loopback. Removes the IsLoopbackRemote 403 gate on POST/PUT/DELETE; /api/session can_edit = true for any request that cleared the host auth gate (false only on a failed session probe -> UI shows a reload notice). The narrow single-table viewer grant is unchanged, and CSRF/rebind stays covered by the always-on Host-header allowlist, the SameSite=Strict cookie, and the application/json requirement — NOT by a loopback restriction. IsLoopbackRemote stays (the host's tokenless-loopback auth arm still uses it). Frontend copy + the read-only notice reworded off the loopback framing; CHANGELOG entry corrected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Custom views / notebooks for the Darling web dashboard (#1562): compose your own dashboards from the read catalog — pick a read, choose a visualization, set its parameters, and save a named, shareable, versioned view that every seat can open and export. No query engine, no new SQL: a view is stored JSON (
{panels:[<renderPanel descriptor>]}) driven through the SAMErenderPanelseam the built-in pages already use.Built by two builders on one branch.
Backend (store + grant + loopback-gated CRUD + catalog)
config.custom_views(name-unique,definitionjsonb, an in-placeversioncolumn for optimistic concurrency, UTC timestamps,updated_by). A NARROWGRANT INSERT, UPDATE, DELETE ON config.custom_viewsto the least-privilege VIEWER role (mirroring the mcp role's exactly-two-table precedent — deliberately NOT an admin pool on the network-reachable web host, and the table holds no secrets so it opens no config pivot);tools/provision-roles.sqlcarries the grant for BYO stores (re-run after V31).CustomViewStorebuilt from the same VIEWER-role pool asDarlingAnalysisService: public-const SQL, bound$Nparams,definitionbound as jsonb, optimistic-concurrency update (WHERE id=$ AND version=$expected, a 0-rowcount disambiguated into NotFound vs stale-Conflict), duplicate-name (23505) -> Conflict.GET /api/views(bare-array summaries, no definition),GET /api/views/{id}(full, definition embedded as JSON),POST(201 + Location),PUT(version required),DELETE(204); plusGET /api/catalog(read names taken from the endpoint dispatch table so they can't drift, each enriched with hand-authored param metadata naming the ACTUAL wire query keys + type/required/default) andGET /api/session({can_edit}). Error bodies are always{"error": ...}, matching the read surface.ValidateDefinitionis the authority on every write: each panel's read must be on the dispatch allowlist, its viz in{table,line,stat,bandlist}, span 1 or 2, rawpath-mode rejected, 128 KB / 48-panel caps — a malformed dashboard can never be stored.403off-loopback, using the EXACT loopback determination the network auth gate uses (so the two can't drift); reads / render / export stay open to every seat. The network-reachable viewer role's writes never go on the wire; a one-line lever can open LAN editing later.Frontend (renderer + composer + export/import)
app.js:#/views(list),#/view/{id}(renderer),#/view/{id}/edit+#/view/new(composer), with an editor-route poll-clobber guard so the 60s background refresh never rebuilds an in-progress edit (the sidebar still refreshes). New sidebar Views section + dynamic#view-list; "New view" shown only whencan_edit.js/pages/views.js): tiles a view's stored panels into a responsive.panel-gridthrough the UNMODIFIEDrenderPanel(per-panel isolation is free — each panel error-strips its own fetch/throw), pre-checking every panel's read against the cached catalog and viz against the registry so a stale/removed read renders a clean "unknown read" strip instead of an opaque 404.js/editor.js+ purejs/derive.js): a read picker grouped by catalog category; typed parameter inputs from the catalog (int/double -> number, bool -> checkbox, text -> input,server-> a fleet dropdown; a required parameter blocks preview + save until filled); a viz picker; width (span 1|2); title; and a per-viz field sub-editor SEEDED by client-side derivation from a live sample fetch (infers the row set, columns/series/stats, and a format per field from the key name + sample value, reusing the util.js formatter registry) then hand-tunable — all behind a LIVE debounced preview through the realrenderPanel. Panels reorder with up/down buttons (a11y, not drag).can_edit, so a network seat sees no New / Edit / Delete / Save / Import affordance — only render + export.el()/textContent(neverinnerHTML); a series color is constrained to an<input type=color>(#rrggbb) or the chart palette — never free text — so it can't reach the chart's style-attribute sink.js/views-api.js(CRUD/catalog/session clients onapiGet, catalog+session cached, plus a pure client-sidevalidateDefinitionmirroring the server's);util.jsgainsapiSend(a POST/PUT/DELETE helper sharingapiGet's exact response classifier);css/editor.css+ a.panel-gridinapp.css. Fully air-gapped (no build step, no off-origin references — the CI self-containment scan auto-covers the new files).Tests / gates
ValidateDefinition(unknown read/viz rejected), catalogKeys == dispatch Keysparity pin, CRUD-contract + version-conflict, loopback decision table, gated live own-scoped round-trip (create -> get -> list -> update -> stale-409 -> dup-409 -> delete-404) + grant proof.DarlingWebAssetsTestspins the new entry files; the self-containment scan auto-covers them; the pure derive/validate logic verified out-of-band.node --checkon every touched/new JS file; Darling service builds clean in Release; fullDarling.Testsgreen (0 failed / 2442 passed / 140 gated-live skipped).Closes #1563
🤖 Generated with Claude Code