orbit

package module
v1.9.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

README

Orbit

The pluggable admin product for the Nucleus framework.

Go Reference Go

Orbit is a self-contained admin panel — Data Studio, a live request/SQL feed, a session viewer, RBAC management, and system metrics — that mounts in-process into any Nucleus application through the framework's extension/module API. It is a separate Go module with its own release cadence, extracted from the framework core per nucleus ADR-019 so the core stays lean and the admin can evolve as its own product.

You add one dependency and one Mount(...) call; orbit reads everything it needs from the running app's Runtime and serves its embedded React SPA — no separate asset deployment, no out-of-process sidecar, no database of its own.


Framework floor. Orbit builds against Nucleus and moves in lockstep with the certified suite set. The exact version it requires is in go.mod — that file is the source of truth, so this note cannot go stale.

Features

Module What it does
Data Studio Browse, create, edit, and delete records for every model in the app's registry — tenant-aware, with import/export.
Live runtime inspector Real-time feed of incoming HTTP requests and executed SQL across the whole app (sourced from the framework's observability event bus), with optional cross-node aggregation.
Session viewer List and revoke active server-side sessions.
Access control (RBAC) Inspect and manage the Casbin policies and roles backing the app's authorizer.
System metrics Runtime and resource consumption — CPU, memory, goroutines, database pool.
Audit log An in-memory ring of admin actions.
Overview & Health Dashboard and health at a glance.

The UI ships embedded in the binary (go:embed), version-pinned to the orbit module — a consumer who mounts orbit gets the full admin offline, in a single binary.

Install

go get github.com/jcsvwinston/orbit@latest

The current tagged release is v1.9.5; pin that tag for reproducible builds.

v1.0 promise: the public surfaces (the root module and datasource) are frozen for the life of v1.x — enforced by contracts/freeze_test.go (see docs/V1_GATE.md).

Requires Go 1.26+ and a Nucleus application to mount into.

Quick start

Mount orbit on the application builder:

import (
    "os"

    "github.com/jcsvwinston/nucleus/pkg/nucleus"
    "github.com/jcsvwinston/orbit"
)

func main() {
    app, err := nucleus.New().
        FromConfigFile("nucleus.yml").
        Mount(orbit.Module(orbit.Config{
            Prefix:            "/admin",
            Title:             "Acme Admin",
            BootstrapUsername: "admin",
            BootstrapEmail:    "admin@acme.test",
            // When BootstrapPassword is empty, bootstrapping is skipped —
            // provision the admin user another way (e.g. nucleus createuser).
            BootstrapPassword: os.Getenv("ADMIN_BOOTSTRAP_PASSWORD"),
        })).
        Build()
    if err != nil {
        panic(err)
    }
    _ = nucleus.Run(app)
}

Start the app, open /admin, and sign in with the bootstrap user. Orbit self-registers its prefix with the framework's default-deny RBAC and enforces its own session-based auth below that prefix.

The zero value is valid too — orbit.Module(orbit.Config{}) mounts under /admin with sensible defaults.

Examples

Two runnable examples, both built by CI:

  • examples/minimal — the in-process admin panel on a Nucleus app (this Quick start, as a program you can go run).
  • agent/examples/fleet-app — the cluster leg: a host app wired with the Orbit agent, shipping observability to a standalone admin server you run alongside it.

Signing in against your directory

If the host application declares an authentication chain, the admin panel uses it:

auth_backends: [ldap, local]

Orbit ships no LDAP client. It asks the framework's chain, so whatever the operator configured for the application applies to the panel too.

Authentication is delegated; authorization is not. The chain answers who the credentials belong to; this panel's own admin table still decides whether that person may enter. A directory account that is not an administrator here is refused — otherwise connecting a corporate directory would quietly make every employee in the company an administrator of your admin panel.

That also means a local admin row is not a bypass: with a chain configured, the chain still has to accept the password, so a revoked directory account cannot get in through a stale row.

Without auth_backends, nothing changes: the panel validates against its own table exactly as before.

Configuration

orbit.Config is bound from the modules.orbit.* subtree of your nucleus.yml (or set it directly in Go). All fields are optional.

Precedence: the YAML subtree is overlaid on whatever you passed to orbit.Module(...), key by key — so Go supplies the base and nucleus.yml wins for the keys it sets. The one exception is prefix: the framework mounts the module before it binds the subtree, so modules.orbit.prefix cannot move the panel. Setting it to something other than the mount point fails at startup, naming both values, rather than serving a panel whose own links point elsewhere.

Key (modules.orbit.*) Type Default Description
prefix string /admin URL path orbit mounts under. Must agree with the prefix passed to orbit.Module(...); see the precedence note above.
title string Heading shown in the UI.
environment string Label shown in the UI (e.g. production).
bootstrap_username string Admin user created on first boot.
bootstrap_email string Email for the bootstrap user.
bootstrap_password string Password for the bootstrap user; empty → bootstrapping is skipped (provision the admin user another way, e.g. nucleus createuser).
auth_database string app default DB alias whose handle backs admin auth + the bootstrap user (use a dedicated DB for the admin user store).
migrations_path string migrations Directory the migrations view reads.
audit_max_size int 10000 In-memory audit-log ring size.
multitenant_enabled bool false Confine Data Studio (list, get, create, update, delete, bulk, CSV export, exports and their jobs, imports, fixtures) to the tenant the host app resolves for the request; a request with no resolved tenant and no default is a 403. ?tenant=<id> / ?tenant=all are accepted only from a superuser or a subject granted the tenant_switch RBAC action, and audited as tenant.override. The host's resolution must not be client-controlled (a header the client sets) for the confinement to hold.
multitenant_default string Tenant applied when the host resolves none; without it such a request is refused unless the operator may switch tenants.
multitenant_ids []string Known tenant IDs for the selector UI.
live_exclude_patterns []string Path patterns excluded from the live HTTP feed.
trace_url_template string External trace-explorer URL template (supports {trace_id}).
cluster_enabled bool false Aggregate the live feed across nodes via a Redis relay.
cluster_redis_url string Redis URL for the live-telemetry relay.
cluster_channel string nucleus:admin:live:v1 Pub/sub channel for the relay.
cluster_node_id string runtime id Explicit node identifier in the relay.
cluster_token string Shared secret to reject untrusted relay messages.
# nucleus.yml
modules:
  orbit:
    prefix: /admin
    title: Acme Admin
    environment: production
    bootstrap_username: admin
    bootstrap_email: admin@acme.test

How it works

Orbit is a nucleus.ModuleSpec. On startup it captures the app's Runtime and builds its panel from the framework's public accessors — the model registry, all managed database handles, the session manager, the RBAC enforcer, the live event bus, and storage. It never reaches into framework internals:

  • In-process — it sees live runtime state (sessions, SQL, model registry, metrics) that an out-of-process sidecar could not, without any IPC surface.
  • Self-contained auth — it owns a session-based login (DatabaseAdminAuth) against the nucleus_admin_users table and self-registers its prefix with the framework's default-deny RBAC, so the framework middleware never double-gates it.
  • Embedded SPA — the React UI is built into the module and served under the mount prefix.

Cluster observability (optional)

For fleet-wide live telemetry, orbit ships sibling modules that are releasable independently:

Module Role
orbit/proto Connect-RPC contract + generated stubs.
orbit/agent In-process agent (agent.NewExtension) that ships events to an admin server.
orbit/server Standalone admin-server binary that receives them.

Most applications only need the root orbit module for the in-process panel.

Relationship to Nucleus

Orbit is a "dogfooding" consumer of Nucleus: mounting a real, deep admin exercises and hardens the framework's extension/Runtime surface. The admin used to live in the framework core as pkg/admin; ADR-019 extracted it here as a clean break. Nucleus itself no longer ships any admin code.

Requirements

  • Go 1.26+
  • A Nucleus application to mount into.
  • (Optional) Redis — only for the cross-node live-telemetry relay.

Development

go work sync          # Go workspace: ./ ./agent ./proto ./quarkbridge ./quarkdatasource ./server
make build            # go build ./... in every module
make test             # go test ./... in every module

That is the quick inner loop. CI additionally builds and tests each of the six modules standalone (GOWORK=off, the way a consumer's toolchain resolves them), verifies the protobuf stubs and the two web UIs, and runs the repository guards. CONTRIBUTING.md walks through all of it — read it before opening a PR. Security reports go through SECURITY.md, never a public issue.

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

View Source
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 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"`

	// 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.
	AuditMaxSize int `yaml:"audit_max_size" koanf:"audit_max_size"`

	// 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"`

	// 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).
	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; the four-field examples/minimal is 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.

Directories

Path Synopsis
agent module
Package datasource is Orbit's neutral, backend-agnostic contract for Data Studio (ADR-001).
Package datasource is Orbit's neutral, backend-agnostic contract for Data Studio (ADR-001).
examples
minimal command
Command minimal is the smallest runnable Orbit example: a Nucleus app with the in-process admin panel (orbit.Module) mounted at /admin.
Command minimal is the smallest runnable Orbit example: a Nucleus app with the in-process admin panel (orbit.Module) mounted at /admin.
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.
datasource/nucleus
Package nucleus is the Nucleus-backed implementation of Orbit's neutral datasource contract (ADR-001).
Package nucleus is the Nucleus-backed implementation of Orbit's neutral datasource contract (ADR-001).
proto module
quarkbridge module
server module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL