Plugins
What is this
Most opinionated behavior in dlboard — where data is stored, how a chart renders, who a request is
attributed to, what theme is applied — is a plugin. A plugin is any module implementing
PluginProtocol (dlboard/models/_plugin.py): a single classmethod,
def plug(cls, app: Dash) -> None: ...
dlboard.serve.app.app(plugins) calls plug(app) on every plugin in the list, in order, once at
startup. That’s the entire contract — there’s no base class to inherit and no registry to update
by hand, just a module with a plug function.
When you’d need this
You’re building a new storage backend, chart type, auth provider, or theme, and want to know where the extension points are and how a plugin gets composed into a running app. Read the page for the specific category you’re building — storage, charts, auth, themes — this page is about the plugin mechanism itself, not any one category.
Page layout (home, project, admin, experiment, account) is deliberately not a plugin category — these
modules don’t implement PluginProtocol at all, since nobody swaps out the experiment page’s
tab+accordion layout the way they might swap sqlite for postgres. dlboard.serve.app.app() calls
register(app) on each of the built-in pages directly and unconditionally — they’re never
part of the plugins list a deployment passes in, and never touch the plug(app) mechanism this
page describes. See architecture.md for where their code lives
(dlboard/serve/_pages/).
How it works
Writing one. A plugin module needs a plug(app: Dash) -> None function. What it does inside
is entirely up to the category: register a Flask route, set a data store on the app, register a
chart type, wire a callback. Look at dlboard/plugins/themes/default.py for the smallest real example
(one set_theme call, no state) before tackling a bigger category.
Registering one. Add the module to the list passed into app() — see LOCAL_DEPLOYMENT in
dlboard/plugins/__init__.py for the reference bundle dlboard serve local uses. There’s no other
registration step; a plugin not in that list is never loaded.
Identity, not state. Once plugged, a plugin is snapshotted as an InstalledPlugin
(name + first docstring line — see InstalledPlugin.describe in dlboard/models/_plugin.py)
for introspection (the admin page’s About tab lists installed plugins this way). Deliberately no
reference to the plugin module/object itself is kept, so that introspection can’t reach back into
a plugin’s internal state from an arbitrary callback thread.
Design boundary. The core app exposes minimal hooks for plugins to use (the Store
accessors described in architecture.md); any opinionated choice within a
plugin’s own category — how a chart is styled, which auth scheme resolves a user, where artifacts
land on disk — belongs in the plugin, not the core.