Documentation
¶
Overview ¶
Package orbit is the pluggable admin product for the Nucleus framework.
Orbit is a separate Go module that mounts in-process into a Nucleus application via the framework's extension/module API, and serves a self-contained admin UI (Data Studio, live request/SQL feed, session viewer, RBAC, system metrics). It was extracted from the framework core per nucleus ADR-019 so it can ship, version, and evolve as its own product while the core stays lean. Mount it explicitly:
app, err := nucleus.New().
FromConfigFile("nucleus.yml").
Mount(orbit.Module(orbit.Config{Prefix: "/admin"})).
Build()
Orbit reads everything it needs from the nucleus Runtime — the model registry, the managed database handles, the session manager, the RBAC enforcer, the live event bus, storage (the accessors added in nucleus ADR-019 Slice 1/2) — so it never reaches into the framework's internals.
Index ¶
- Constants
- func Module(cfg Config) nucleus.ModuleSpec
- type ActionDownload
- type ActionField
- type ActionFieldType
- type ActionInput
- type ActionOption
- type ActionPlacement
- type ActionRequest
- type ActionResult
- type Branding
- type Cache
- type ClientCode
- type Config
- type Dashboard
- type ModelAction
- type Operator
- type Page
- type Palette
- type RecordList
- type Series
- type SeriesValue
- type StatValue
- type TableValue
- type Widget
- type WidgetItem
- type WidgetValue
Constants ¶
const ( ActionFieldText = admin.ActionFieldText ActionFieldNumber = admin.ActionFieldNumber ActionFieldBoolean = admin.ActionFieldBoolean ActionFieldSelect = admin.ActionFieldSelect ActionFieldDate = admin.ActionFieldDate )
The types an ActionField may declare.
const ( ActionOnSelection = admin.ActionOnSelection ActionOnRecord = admin.ActionOnRecord ActionOnSelectionAndRecord = admin.ActionOnSelectionAndRecord )
The places a ModelAction can be offered.
const ( // WidgetStat reads Widget.Stat: a headline figure and its change. WidgetStat = admin.WidgetKindStat // WidgetLine reads Widget.Series and draws a line chart. WidgetLine = admin.WidgetKindLine // WidgetBar reads Widget.Series and draws a bar chart. WidgetBar = admin.WidgetKindBar // WidgetTable reads Widget.Table: columns and rows of text. WidgetTable = admin.WidgetKindTable // WidgetRecords lists the newest rows of the model Widget.Records // names, read by the panel as the operator who is looking. WidgetRecords = admin.WidgetKindRecords )
The kinds of card a Widget draws (Widget.Kind). An empty Kind is the value card A6 shipped, read from Load.
const DefaultPrefix = "/admin"
DefaultPrefix is the URL path orbit mounts under when Config.Prefix is empty.
Variables ¶
This section is empty.
Functions ¶
func Module ¶
func Module(cfg Config) nucleus.ModuleSpec
Module returns orbit as a nucleus ModuleSpec, mountable on an application via the builder's Mount(...). It is self-contained: it declares its own URL prefix and acquires every framework service it needs from the Runtime in OnStart, then mounts the admin panel's own router under the prefix in Routes.
Types ¶
type ActionDownload ¶ added in v1.19.0
type ActionDownload = admin.ActionDownload
ActionDownload is a file a ModelAction answers with (EXT-03): a name, a declared media type and the bytes the action produced. The panel sends it as an attachment, refuses one over its size ceiling rather than sending it truncated, and never opens a path on the action's behalf.
type ActionField ¶ added in v1.19.0
type ActionField = admin.ActionField
ActionField is one input a ModelAction asks for before it runs (EXT-01): a name, a label, a type, whether it is required and, for a select, its options. The panel draws the fields as a form, refuses a call whose input does not match them — naming the field — before the function runs, and hands the function the values typed in ActionRequest.Input. A field the panel cannot draw (an unknown type, a select with no options) refuses to start.
type ActionFieldType ¶ added in v1.19.0
type ActionFieldType = admin.ActionFieldType
ActionFieldType is what an ActionField asks for.
type ActionInput ¶ added in v1.19.0
type ActionInput = admin.ActionInput
ActionInput is what the operator entered in an action's form, typed by its fields: text and select as string, number as float64, boolean as bool, date as time.Time.
type ActionOption ¶ added in v1.19.0
type ActionOption = admin.ActionOption
ActionOption is one choice of a select ActionField: the value posted and the label read.
type ActionPlacement ¶ added in v1.19.0
type ActionPlacement = admin.ActionPlacement
ActionPlacement says where the panel offers a ModelAction (EXT-02): over the grid's selection, on one record — its record view and its row's menu — or both. The zero value is the selection, where every action was offered before it could say. The placement is enforced by the server, not only drawn: an action offered only on a record receives exactly that record's id.
type ActionRequest ¶ added in v1.10.0
type ActionRequest = admin.ActionRequest
ActionRequest is what a ModelAction is told about the call: the model, the selected ids, the operator, the tenant in force and the database alias the grid was reading.
type ActionResult ¶ added in v1.10.0
type ActionResult = admin.ActionResult
ActionResult is what a ModelAction reports back — a message for the operator, how many rows it changed, and anything else the screen should get. Every field is optional. Besides a message, an action can answer with a page of the panel to go to (Redirect, a path relative to the panel; anything else is refused when it answers) or a file to download (Download).
type Branding ¶ added in v1.10.0
Branding is the logo, favicon and accent colour an application gives the panel (CUST-02), the theme it opens in and the palette of each theme (EXT-09, EXT-10). Every field is optional.
type Cache ¶ added in v1.10.0
Cache is what an application implements to give the panel a cache it can inspect and empty (OR-49): a name for the view, a count of entries, and a flush. It is an ALIAS of the panel's own interface, so an implementation satisfies both without the application naming an internal package.
The panel never reads or writes entries through it. Counting and emptying is the whole of what an operator does to a cache from a screen, and a contract that could also read entries would put cached values — session data, rendered fragments, whatever the application caches — behind a panel permission that was never meant to cover them.
type ClientCode ¶ added in v1.19.0
type ClientCode = admin.ClientCode
ClientCode is the application's own code for the browser side of the panel (EXT-06, EXT-07): the files it is read from, the scripts and stylesheets the panel's document loads after its own bundle, and the names of the field renderers the scripts register with window.orbit.registerFieldRenderer.
type Config ¶
type Config struct {
// Prefix is the URL path orbit mounts under (default DefaultPrefix).
Prefix string `yaml:"prefix" koanf:"prefix"`
// Title is the heading shown in the admin UI.
Title string `yaml:"title" koanf:"title"`
// Bootstrap admin user, created on first start if it does not exist. When
// BootstrapPassword is empty, bootstrapping is skipped (the operator is
// expected to provision the admin user another way).
BootstrapUsername string `yaml:"bootstrap_username" koanf:"bootstrap_username"`
BootstrapEmail string `yaml:"bootstrap_email" koanf:"bootstrap_email"`
BootstrapPassword string `yaml:"bootstrap_password" koanf:"bootstrap_password"`
// AuthDatabase optionally names a managed database alias whose handle backs
// admin authentication and the bootstrap user. Empty means use the default
// database. The panel itself (Data Studio etc.) always runs on the default
// handle; only the auth/bootstrap *sql.DB is redirected.
AuthDatabase string `yaml:"auth_database" koanf:"auth_database"`
// Multi-tenant: set these to match the host application so Data Studio is
// confined to the tenant the app resolves for each request (list, get,
// create, update, delete, bulk, exports, imports, fixtures). Only a
// superuser or a subject granted the tenant_switch RBAC action can look at
// another tenant (?tenant=<id>) or at all of them (?tenant=all), and every
// switch is audited. Leave disabled for single-tenant apps.
MultiTenantEnabled bool `yaml:"multitenant_enabled" koanf:"multitenant_enabled"`
MultiTenantDefault string `yaml:"multitenant_default" koanf:"multitenant_default"`
MultiTenantIDs []string `yaml:"multitenant_ids" koanf:"multitenant_ids"`
// Row ownership: which column of a model says WHICH OPERATOR a row
// belongs to, keyed by model name, with "*" as the default for every
// model that carries the same column. It is what makes an RBAC policy on
// admin:<Model>#own enforceable — the grant every editorial admin needs
// ("an author edits their own posts"): lists are filtered by the column,
// another operator's row is reported as not found, and a create stamps
// it. A #own grant on a model that has no entry here is refused, never
// widened to every row.
//
// RowOwnerSubject names which name of the operator the column holds:
// "username" (default) or "id".
RowOwnerFields map[string]string `yaml:"row_owner_fields" koanf:"row_owner_fields"`
RowOwnerSubject string `yaml:"row_owner_subject" koanf:"row_owner_subject"`
// FieldWidgets declares how a field is edited when its type cannot say
// it: "Model.Field" (or "Model.column") to one of json, richtext, file
// or image. A JSON document is inferred from the type and needs no
// entry; "this string is HTML" and "this string is a storage key" are
// claims about intent that no column type carries, so the application
// makes them here. A file field gets an upload route
// (POST /api/models/{model}/upload) that stores the bytes in the
// application's own storage and answers with the key the form writes.
// An entry the panel cannot apply — a widget outside those four, or a
// key that names no field of the application — refuses to start.
FieldWidgets map[string]string `yaml:"field_widgets" koanf:"field_widgets"`
// Environment is a label shown in the UI (e.g. "production"). Optional.
Environment string `yaml:"environment" koanf:"environment"`
// MigrationsPath is the directory the migrations view reads (default "migrations").
MigrationsPath string `yaml:"migrations_path" koanf:"migrations_path"`
// AuditMaxSize caps the in-memory audit log ring buffer; zero or negative
// means the default of 10000 entries. It bounds the MEMORY store only: a
// trail kept in the database is bounded by AuditRetentionDays.
AuditMaxSize int `yaml:"audit_max_size" koanf:"audit_max_size"`
// AuditStore selects where the audit trail is kept: "database" (the
// default when the application has a database) writes a table the panel
// owns and creates, so the trail survives restarts and is shared by every
// replica; "memory" keeps the process-lifetime ring instead.
AuditStore string `yaml:"audit_store" koanf:"audit_store"`
// AuditRetentionDays drops trail entries older than that many days,
// applied at mount and at most hourly afterwards. Zero keeps them until
// somebody clears the log. It is a PERIOD, which is what a compliance
// window is; AuditMaxSize is a count and answers a different question.
AuditRetentionDays int `yaml:"audit_retention_days" koanf:"audit_retention_days"`
// LiveExcludePatterns lists path patterns excluded from the live HTTP
// capture feed (e.g. health checks, the admin's own polling endpoints).
LiveExcludePatterns []string `yaml:"live_exclude_patterns" koanf:"live_exclude_patterns"`
// ClusterEnabled turns on cluster-aware live telemetry: live request/SQL
// events are relayed between nodes over Redis so the feed shows the whole
// fleet, not just the local node. Best-effort — a relay failure never blocks
// startup.
ClusterEnabled bool `yaml:"cluster_enabled" koanf:"cluster_enabled"`
// ClusterRedisURL is the Redis URL backing the live telemetry relay.
ClusterRedisURL string `yaml:"cluster_redis_url" koanf:"cluster_redis_url"`
// ClusterChannel is the Redis pub/sub channel the relay publishes on
// (default nucleus:admin:live:v1).
ClusterChannel string `yaml:"cluster_channel" koanf:"cluster_channel"`
// ClusterNodeID is an explicit node identifier for this instance in the
// relay (defaults to the runtime identity).
ClusterNodeID string `yaml:"cluster_node_id" koanf:"cluster_node_id"`
// ClusterToken is a shared secret the relay uses to reject untrusted
// (cross-tenant or spoofed) messages on the channel.
ClusterToken string `yaml:"cluster_token" koanf:"cluster_token"`
// TraceURLTemplate is an external trace-explorer URL template surfaced in
// the UI; it supports a {trace_id} placeholder.
TraceURLTemplate string `yaml:"trace_url_template" koanf:"trace_url_template"`
// Cache is the cache this application wants the panel to show and be
// able to empty. Nil — the default — means the panel says there is no
// cache here rather than offering a button that refuses.
//
// Nothing in the framework owns an application's cache (pkg/cache is a
// library an application builds with, not a service the app wires), so
// there is nothing for the panel to discover: the application is the
// only thing that can say which cache the operator is looking at.
// Go-only wiring; not bindable from YAML.
Cache Cache `yaml:"-" koanf:"-"`
// Actions are the verbs this application defines for its own models —
// "publish these three drafts", "retry these payments" — which the
// panel draws on the model's grid, on one record's view, or both (see
// ActionPlacement), and runs under the same authorization, tenant and
// row confinement as its own bulk verbs.
// Nothing here can be discovered: the panel cannot know that a column
// called status makes "publish" meaningful, so the application names
// the verb and supplies the function. Go-only wiring; not bindable
// from YAML. A declaration the panel cannot honour — an unknown model,
// a duplicate verb, one of the panel's own — refuses to start.
Actions []ModelAction `yaml:"-" koanf:"-"`
// Pages are screens of this application's own, mounted inside the
// panel: under its prefix, behind its session, gated by its RBAC and
// listed in its navigation. The application writes the handler; the
// panel supplies everything around it. Go-only wiring; not bindable
// from YAML.
Pages []Page `yaml:"-" koanf:"-"`
// Branding is how the panel wears the application's clothes: the logo
// on the sidebar and the login screen, the icon in the browser tab, the
// accent colour, the theme the panel opens in and the palette of each
// theme. Empty fields keep Orbit's own. Unlike Actions and Pages it is
// plain configuration, so it binds from nucleus.yml too.
Branding Branding `yaml:"branding" koanf:"branding"`
// Widgets are the cards this application puts on the panel's overview:
// its own numbers, which the panel cannot discover. Go-only wiring;
// each widget carries the function that reads its value, and its Kind
// says what the card draws — a value, a figure and its change, a line
// or bar chart, a table, or the newest rows of a model.
Widgets []Widget `yaml:"-" koanf:"-"`
// Dashboards are screens of cards beside the overview — the finance
// numbers, the support queue — each listed in the navigation and gated
// as a whole by its own permission (view on admin:dashboard:<id>). The
// overview is not one of them and does not change. Go-only wiring,
// like Widgets. A declaration the panel cannot draw — an unknown kind,
// a kind without its function, a dashboard with no cards — refuses to
// start, naming it.
Dashboards []Dashboard `yaml:"-" koanf:"-"`
// Client is the application's own code for the browser side of the
// panel: scripts and stylesheets the panel serves under its prefix and
// its document loads after the panel's bundle, each named with the
// digest of its bytes, so the Content-Security-Policy's script-src
// stays 'self'; and the field renderers those scripts register, which
// FieldWidgets may name to draw a field the application's way in the
// list and on the record view. Go-only wiring: it carries a file
// system. A declaration the panel cannot serve — a file that is not
// there, a path that climbs out of Files, a renderer no script was
// declared to register — refuses to start, naming it.
Client ClientCode `yaml:"-" koanf:"-"`
// Locale is the language the panel's own chrome opens in ("es",
// "pt-BR"). It covers the panel's words — navigation, buttons, empty
// states — and never the application's data: a model called Invoice is
// called Invoice in every language. Empty means English.
Locale string `yaml:"locale" koanf:"locale"`
// Messages adds to or overrides the chrome's phrases, by locale and
// key, including for a language the panel does not ship. A phrase
// nobody translated reads in English rather than as its key.
Messages map[string]map[string]string `yaml:"messages" koanf:"messages"`
// DataSource overrides the source Data Studio browses and edits (ADR-001).
// Nil means the default: a Nucleus-backed adapter over the application's
// model registry and database handles. Set it to browse another backend —
// e.g. an app that runs the Quark ORM passes a quarkdatasource adapter
// (QADR-0006, Caso 2). Go-only wiring; not bindable from YAML. When set,
// the runtime field-metadata editor is disabled (it mutates the Nucleus
// registry, which a custom source does not necessarily have).
//
// The default leaves at Orbit 2.0 with Nucleus's pkg/model
// (DEP-2026-002): the panel then reads Quark models only, and a nil
// DataSource is a configuration error. An application still on the
// default is told so at start, with one WARN.
DataSource datasource.DataSource `yaml:"-" koanf:"-"`
}
Config configures the orbit admin module. The zero value is valid (orbit mounts under DefaultPrefix); bound from the `modules.orbit.*` subtree of the application config when mounted on a config-file app.
The struct is flat, but its fields serve two different modes — do not let the cluster vocabulary scare a plain panel setup:
- Panel (what almost every app uses): Prefix, Title, Bootstrap*, AuthDatabase, MultiTenant*, Environment, MigrationsPath, AuditMaxSize, LiveExcludePatterns, TraceURLTemplate. Nothing else is required: a prefix, a title and the bootstrap fields are a complete production shape.
- Cluster live-feed relay (opt-in, off by default): the Cluster* fields. They only matter once ClusterEnabled is true; in particular, NO Redis is needed to run the panel — ClusterRedisURL is read exclusively by the relay. The standalone fleet plane (agent/, server/) is configured on its own binaries, not here.
- Go-only: DataSource (not bindable from YAML).
Grouping the Cluster* fields into a nested sub-struct would make this split structural, but the flat yaml keys (cluster_enabled, ...) are part of the frozen surface below, so that reshuffle is deliberately deferred to a hypothetical v2; within v1.x the split lives in this comment and in the configuration docs.
Config is a frozen v1.0 surface (docs/V1_GATE.md §A-3): every field keeps its name, yaml key, type, and zero-value behavior for the life of v1.x. Fields may be added; none is removed or renamed without a major. The freeze is enforced by contracts/freeze_test.go.
type Dashboard ¶ added in v1.19.0
Dashboard is a screen of cards beside the overview, listed in the navigation and gated by its own permission on admin:dashboard:<id>.
type ModelAction ¶ added in v1.10.0
type ModelAction = admin.ModelAction
ModelAction is an action an application defines for one of its own models (DS-09): the verb on the wire and in the RBAC policy, the label on the button, and the function that runs it over the rows an operator selected. It is an ALIAS of the panel's own type, so an application declares one without naming an internal package.
The ids the function receives are already confined to what that operator may touch. That is the reason the action runs through the panel instead of being an endpoint of the application's own: it inherits the row and tenant policies the panel enforces, and the audit entry, rather than being the way around them.
type Page ¶ added in v1.10.0
Page is a screen of the application's own, mounted inside the panel (CUST-04). The handler is ordinary net/http; the panel authenticates the request, authorizes it against admin:page:<id>, puts the operator on the context (OperatorFromContext) and lists the page in the navigation.
It is a link, not a frame: the panel refuses to be framed (X-Frame-Options: DENY), and its Content-Security-Policy applies to the page too — so a page's scripts come from files, not from inline <script>.
type Palette ¶ added in v1.19.0
Palette is the colours of one theme of the panel — its accent, its surface and its text — as Branding.Light and Branding.Dark declare them. Each is checked at startup against the ground it is drawn on in that theme.
type RecordList ¶ added in v1.19.0
type RecordList = admin.RecordList
RecordList names the model a "records" card lists, its columns, its order and how many rows.
type SeriesValue ¶ added in v1.19.0
type SeriesValue = admin.SeriesValue
SeriesValue is what a "line" or "bar" card draws: labels along the horizontal axis and one value per label for each Series.
type StatValue ¶ added in v1.19.0
StatValue is what a "stat" card shows: a figure, its change as the application formats it, which way it went, and whether that is good news.
type TableValue ¶ added in v1.19.0
type TableValue = admin.TableValue
TableValue is what a "table" card shows: columns and rows of text.
type Widget ¶ added in v1.10.0
Widget is one card an application puts on the panel's overview (CUST-03): a title and the function that reads the value. The panel supplies the screen, the authorization (admin:dashboard), a timeout, and a card that says it could not be read rather than a screen that drops it.
type WidgetItem ¶ added in v1.10.0
type WidgetItem = admin.WidgetItem
WidgetItem is one row of a list widget.
type WidgetValue ¶ added in v1.10.0
type WidgetValue = admin.WidgetValue
WidgetValue is what a Widget shows: a headline value, a smaller detail line, or a short list of rows.
Directories
¶
| Path | Synopsis |
|---|---|
|
agent
module
|
|
|
datasource
module
|
|
|
internal
|
|
|
admin
Package admin provides an auto-generated administration panel for Nucleus, similar to Django's contrib.admin.
|
Package admin provides an auto-generated administration panel for Nucleus, similar to Django's contrib.admin. |
|
proto
module
|
|
|
quarkbridge
module
|
|
|
quarkdatasource
module
|
|
|
server
module
|
|
|
ui
module
|