Architecture
What is this
dlboard is a single Dash web app assembled at startup from a flat list of plugins — storage,
charts, auth, theming, backend routes. The core app just gives plugins a few places to hook into a
running Dash instance. Page layout (home, project, admin, experiment, account) is the one deliberate
exception: it’s opinionated, non-optional dlboard behavior, not a plugin category — see
plugins/overview.md for why.
When you’d need this
Read this before touching dlboard/serve/app.py, adding a new kind of plugin category (not a new
plugin — a new category, which is rare), or trying to understand how a request from a training
script ends up as a row in the database and eventually a chart in the browser.
How it works
Composing the app. dlboard.serve.app.app(plugins: list[PluginProtocol]) -> Dash
(dlboard/serve/app.py) builds a Dash instance, calls plug(app) on every plugin in the caller’s
list, then calls register(app) directly on each of the built-in pages (always, unconditionally
— they aren’t PluginProtocol and are never part of the plugins argument at all), and wires the
shared AppShell layout (header, collapsible navbar, page container) plus a few global callbacks:
breadcrumbs, navbar collapse state (persisted to localStorage), and the navbar’s project/experiment
listing. dlboard serve local is the reference composition — it passes dlboard.plugins.LOCAL_DEPLOYMENT
(sqlite storage, filesystem artifacts, anonymous auth, the REST backend, the four built-in chart types)
plus a theme.
Where plugins reach shared state. A plugin’s plug(app) registers whatever it needs — routes,
a chart renderer, the data store — directly on the Dash app instance. Anything another
plugin needs to read back (the data store, the artifact store, the auth provider, the current
user) goes through small accessor functions in dlboard/serve/_backend/ (e.g.
get_data_store(), get_auth_provider(), get_current_user()) rather than plugins reaching into
each other directly. See plugins/overview.md for the plugin contract itself.
Who’s asking, and what they may do. Every route sits behind one request gate, which resolves
the caller to a User before the route runs: by API token, session, or the auth plugin. Within a
request, get_data_store()/get_artifact_store() return wrappers bound to that user, which
enforce project-level access on every call. A storage backend never knows who’s asking, and
background work no user is behind uses get_system_data_store() instead. See
plugins/auth.md.
Request flow, client to server.
DLBoardLogger(dlboard/client/dlboard_logger.py) creates a run viaBasicDlboardAPI(dlboard/plugins/backend/basic_rest_backend.py) and spawns two daemon subprocesses that batch metrics/artifacts off multiprocessing queues and flush them to the REST API on an interval, so training never blocks on network I/O.- The server-side half of
basic_rest_backendexposes the ingest routes those batches land on. - Storage is split into a
DataStore(structured metadata — projects, experiments, runs, metrics, hparams) and anArtifactStore(blob storage), both defined as generic protocols indlboard/models/_data_store.py.SQLStoreBase(dlboard/serve/_backend/_sql_store_base.py) implementsDataStoreon SQLAlchemy Core, with tables generated from the pydantic models; thesqlite/filesystemplugins underdlboard/plugins/data_stores/supply the concrete backendsdlboard serve localuses. - Browser-side, routed pages under
dlboard/serve/_pages/(Dash’s file-baseduse_pagesrouting) read back through the sameDataStore/ArtifactStoreaccessors to render what got logged.
Shared models. dlboard/models/ holds the Pydantic models both client and server import
(Experiment, Run, Project, HyperParams, LoggedMetrics, Artifact, and the chart/view
models Page/ChartType). Most have a New* variant for creation payloads versus the
persisted/read model. Dash component-id constants that are genuinely shared across pages and plugins live in
dlboard/serve/_constants.py — a plugin-local id has no business there.