dashboard

package
v1.11.1 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: Apache-2.0 Imports: 47 Imported by: 13

README

Dashboard Extension v3.0.0

Extensible micro-frontend shell for building admin dashboards with Forge. Contributors (local or remote) register pages, widgets, and settings that are merged into a unified dashboard powered by ForgeUI.

Features

  • ForgeUI Integration -- layouts, routing, theming, and HTMX-powered partial navigation
  • Contributor System -- local (in-process gomponents) and remote (HTTP fragment proxy) contributors
  • Authentication & Authorization -- page access levels (public, protected, partial), auth page provider, user menu, HTMX-aware redirects
  • Go-JS Bridge -- call Go functions from the browser via Alpine.js $go() magic helpers
  • SSE Real-Time -- server-sent events for live metric and health updates
  • Federated Search -- cross-contributor search with the SearchableContributor interface
  • Settings Aggregation -- contributor settings merged into a unified settings page
  • Data Export -- JSON, CSV, and Prometheus export formats
  • Service Discovery -- auto-discover remote contributors via the discovery extension
  • Security -- Content-Security-Policy headers, CSRF tokens, HTML sanitization
  • Theming -- auto, light, and dark modes with custom CSS injection

Quick Start

go get github.com/xraph/forge/extensions/dashboard
package main

import (
    "log"
    "time"

    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/dashboard"
)

func main() {
    app := forge.New(
        forge.WithAppName("my-app"),
        forge.WithAppVersion("1.0.0"),
    )

    if err := app.RegisterExtension(dashboard.NewExtension(
        dashboard.WithTitle("My Dashboard"),
        dashboard.WithBasePath("/dashboard"),
        dashboard.WithRealtime(true),
        dashboard.WithRefreshInterval(30 * time.Second),
        dashboard.WithExport(true),
    )); err != nil {
        log.Fatalf("failed to register dashboard: %v", err)
    }

    if err := app.Run(); err != nil {
        log.Fatalf("application error: %v", err)
    }
}

The dashboard is available at http://localhost:8080/dashboard/ui.

Architecture

Dashboard Extension
  |-- React Shell ({base}/ui) -- prebuilt SPA covering overview, health,
  |     metrics, services; reads data over the contract envelope
  |-- JSON API ({base}/api/*) -- same data (overview, health, metrics,
  |     services, ...) for direct or scripted access
  |-- ForgeUI App
  |     |-- Layouts (root -> dashboard/base/full/settings/auth)
  |     |-- Contributor pages, widgets, and settings forms
  |     +-- Auth (login, logout, register -- via AuthPageProvider)
  |-- Contributors
  |     |-- Local (in-process, gomponents)
  |     +-- Remote (HTTP fragment proxy)
  |-- Bridge (Go <-> JS function calls)
  |-- SSE Broker (real-time events)
  +-- Search / Settings / Export

The dashboard uses ForgeUI's layout and routing system. The root layout renders the HTML shell with HTMX, Alpine.js, and the sidebar. On HTMX partial requests (HX-Request header), only the page content is returned without the outer HTML shell.

Contributors provide pages, widgets, and settings via a manifest-driven system. Local contributors render gomponents nodes directly. Remote contributors serve HTML fragments over HTTP, which the dashboard proxies and embeds.

Configuration

All options use the functional options pattern:

Option Default Description
WithBasePath(path) "/dashboard" HTTP base path
WithTitle(title) "Forge Dashboard" Page title
WithRealtime(bool) true SSE real-time updates
WithExport(bool) true Data export endpoints
WithSearch(bool) true Federated search
WithSettings(bool) true Settings aggregation
WithDiscovery(bool) false Auto-discover remote contributors
WithBridge(bool) true Go-JS bridge functions
WithEnableAuth(bool) false Authentication support
WithDefaultAccess(s) "public" Default page access level
WithLoginPath(path) "/auth/login" Login page path
WithLogoutPath(path) "/auth/logout" Logout page path
WithRefreshInterval(d) 30s Data collection interval
WithHistoryDuration(d) 1h Data retention window
WithMaxDataPoints(n) 1000 Max retained data points
WithTheme(theme) "auto" Theme: auto, light, dark
WithCSP(bool) true Content-Security-Policy
WithCSRF(bool) true CSRF token protection

See full configuration reference for all options.

Authentication & Authorization

The dashboard supports optional authentication with three page access levels:

Level Behavior
public Always accessible, no login required
protected Requires authentication; redirects to login if unauthenticated
partial Always accessible but renders differently based on auth state
Enabling Auth
dashExt := dashboard.NewExtension(
    dashboard.WithEnableAuth(true),
    dashboard.WithDefaultAccess("protected"),
    dashboard.WithLoginPath("/auth/login"),
)
Providing an AuthChecker

The AuthChecker interface validates requests and returns user info. Implement it to integrate with your authentication system:

import dashauth "github.com/xraph/forge/extensions/dashboard/auth"

// Implement the AuthChecker interface
type MyChecker struct{}

func (c *MyChecker) CheckAuth(ctx context.Context, r *http.Request) (*dashauth.UserInfo, error) {
    // Validate session/token/cookie and return user info
    token := r.Header.Get("Authorization")
    if token == "" {
        return nil, nil // unauthenticated (not an error)
    }

    return &dashauth.UserInfo{
        Subject:     "user-123",
        DisplayName: "Jane Doe",
        Email:       "jane@example.com",
        Roles:       []string{"admin"},
    }, nil
}

// Wire it up after registration
typedDash := dashExt.(*dashboard.Extension)
typedDash.SetAuthChecker(&MyChecker{})
Auth Page Provider

Auth extensions provide login/register pages by implementing AuthPageProvider:

import dashauth "github.com/xraph/forge/extensions/dashboard/auth"

type MyAuthPages struct{}

func (p *MyAuthPages) AuthPages() []dashauth.AuthPageDescriptor {
    return []dashauth.AuthPageDescriptor{
        {Type: dashauth.PageLogin, Path: "/login", Title: "Sign In"},
        {Type: dashauth.PageRegister, Path: "/register", Title: "Sign Up"},
    }
}

func (p *MyAuthPages) RenderAuthPage(ctx *router.PageContext, pageType dashauth.AuthPageType) (g.Node, error) {
    // Return gomponents login/register form
}

func (p *MyAuthPages) HandleAuthAction(ctx *router.PageContext, pageType dashauth.AuthPageType) (string, g.Node, error) {
    // Handle POST: return redirect URL on success, or error node on failure
    return "/dashboard", nil, nil
}

// Register the provider
typedDash.SetAuthPageProvider(&MyAuthPages{})

Auth pages use the auth layout (centered card, no sidebar) and are always publicly accessible.

Per-Page Access Levels

Contributors can set access levels per NavItem:

Nav: []contributor.NavItem{
    {Label: "Public Stats", Path: "/stats", Icon: "bar-chart", Access: "public"},
    {Label: "Admin Panel", Path: "/admin", Icon: "shield", Access: "protected"},
    {Label: "Dashboard", Path: "/", Icon: "home", Access: "partial"},
}

Empty Access falls back to the dashboard's DefaultAccess setting.

Reading User Info in Pages

Page handlers can read the authenticated user from context:

func (c *MyContributor) RenderPage(ctx context.Context, route string, params contributor.Params) (g.Node, error) {
    user := dashauth.UserFromContext(ctx)
    if user.Authenticated() {
        // Render personalized content
    }
}
How It Works

The auth system uses two middleware layers:

  1. ForgeMiddleware runs on the forge.Router catch-all and populates UserInfo in the request context (never blocks requests)
  2. PageMiddleware runs per ForgeUI page and enforces the access level (redirects to login for protected pages)

For HTMX partial navigation, the middleware returns a 401 with an HX-Redirect header. The AuthRedirectScript (injected into the root layout) reads this header and performs a full-page redirect to the login page.

Contributor System

Extensions contribute UI to the dashboard by implementing the LocalContributor interface:

type UsersContributor struct{}

func (c *UsersContributor) Manifest() *contributor.Manifest {
    return &contributor.Manifest{
        Name:        "users",
        DisplayName: "User Management",
        Icon:        "users",
        Nav: []contributor.NavItem{
            {Label: "Users", Path: "/", Icon: "users", Group: "Identity"},
            {Label: "Roles", Path: "/roles", Icon: "shield", Group: "Identity"},
        },
        Widgets: []contributor.WidgetDescriptor{
            {ID: "active-users", Title: "Active Users", Size: "sm", RefreshSec: 60},
        },
    }
}

func (c *UsersContributor) RenderPage(ctx context.Context, route string, params contributor.Params) (g.Node, error) {
    // Return gomponents nodes
}

func (c *UsersContributor) RenderWidget(ctx context.Context, widgetID string) (g.Node, error) {
    // Return widget content
}

func (c *UsersContributor) RenderSettings(ctx context.Context, settingID string) (g.Node, error) {
    // Return settings panel
}

Register with the dashboard:

dashExt := dashExt.(*dashboard.Extension)
dashExt.RegisterContributor(&UsersContributor{})

Contributors can also implement AuthPageContributor to provide auth pages inline:

type AuthPageContributor interface {
    DashboardContributor
    RenderAuthPage(ctx context.Context, pageType string, params Params) (g.Node, error)
    HandleAuthAction(ctx context.Context, pageType string, params Params) (redirectURL string, node g.Node, err error)
}

Remote contributors are separate HTTP services that expose fragment endpoints. They can be registered manually or auto-discovered via the discovery extension.

Bridge Functions

The Go-JS bridge lets the dashboard UI call Go functions from Alpine.js:

// Register a custom bridge function
dashExt.RegisterBridgeFunction("myapp.getData", func(ctx bridge.Context, params MyParams) (*MyResult, error) {
    // Go logic here
    return result, nil
}, bridge.WithDescription("Get application data"))

From the browser:

// Alpine.js
const data = await $go('myapp.getData', { key: 'value' })

8 built-in functions are registered automatically: dashboard.getOverview, dashboard.getHealth, dashboard.getMetrics, dashboard.getServices, dashboard.getServiceDetail, dashboard.getHistory, dashboard.getMetricsReport, dashboard.refresh.

HTTP Endpoints

All routes are under the configured base path (default /dashboard):

Category Path Description
Shell /ui React shell: overview, health, metrics, services
Shell /ui/* Shell static assets and client-side routes
Auth /auth/login Login page (when auth enabled)
Auth /auth/logout Logout page (when auth enabled)
Auth /auth/register Register page (when provider supplies it)
API /api/overview Overview JSON
API /api/health Health JSON
API /api/metrics Metrics JSON
API /api/services Services JSON
API /api/service-detail?name=X Service detail JSON
API /api/history History JSON
API /api/metrics-report Metrics report JSON
Export /export/json Full JSON export
Export /export/csv CSV export
Export /export/prometheus Prometheus format
Real-time /sse SSE event stream
Bridge /bridge/call Bridge function call (POST)
Bridge /bridge/stream/ Bridge streaming (SSE)
Search /api/search?q=X Federated search
Contributor /ext/:name/pages/* Local contributor pages
Contributor /ext/:name/widgets/:id Local contributor widgets
Remote /remote/:name/pages/* Remote contributor pages
Remote /remote/:name/widgets/:id Remote contributor widgets
Settings /settings Settings index
Settings /ext/:name/settings/:id Contributor settings

/, /health, /metrics, and /services at the dashboard base path are not served. Use the shell at /ui instead; nothing redirects the old paths there.

Examples

  • basic -- Minimal dashboard with the React shell only, no contributors registered
  • contributor -- Custom local contributor with pages, widgets, and settings
  • remote -- Remote contributor registration and service discovery

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrPageNotFound is returned when a requested page does not exist.
	ErrPageNotFound = errors.New("dashboard: page not found")

	// ErrWidgetNotFound is returned when a requested widget does not exist.
	ErrWidgetNotFound = errors.New("dashboard: widget not found")

	// ErrSettingNotFound is returned when a requested setting does not exist.
	ErrSettingNotFound = errors.New("dashboard: setting not found")

	// ErrContributorExists is returned when a contributor with the same name is already registered.
	ErrContributorExists = errors.New("dashboard: contributor already registered")

	// ErrContributorNotFound is returned when a contributor is not found in the registry.
	ErrContributorNotFound = errors.New("dashboard: contributor not found")

	// ErrRemoteUnreachable is returned when a remote contributor cannot be reached.
	ErrRemoteUnreachable = errors.New("dashboard: remote contributor unreachable")

	// ErrManifestFetch is returned when fetching a remote manifest fails.
	ErrManifestFetch = errors.New("dashboard: failed to fetch remote manifest")

	// ErrDiscoveryTimeout is returned when service discovery times out.
	ErrDiscoveryTimeout = errors.New("dashboard: service discovery timed out")

	// ErrRecoveryFailed is returned when UI recovery fails.
	ErrRecoveryFailed = errors.New("dashboard: UI recovery failed")

	// ErrCollectorNotInitialized is returned when the data collector is not initialized.
	ErrCollectorNotInitialized = errors.New("dashboard: collector not initialized")
)

Functions

func NewExtension

func NewExtension(opts ...ConfigOption) forge.Extension

NewExtension creates a new dashboard extension.

func TracingMiddleware added in v1.2.0

func TracingMiddleware(store *collector.TraceStore, basePath string) forge.Middleware

TracingMiddleware creates a forge middleware that auto-captures request traces and feeds them into the given TraceStore. Only dashboard internals (static assets, SSE streams, and bridge calls) are excluded — page navigations and API calls are traced so the tracing UI has data out of the box.

Types

type BridgeAware added in v0.10.0

type BridgeAware interface {
	RegisterDashboardBridge(b *bridge.Bridge) error
}

BridgeAware is an optional interface that Forge extensions can implement to register custom Go↔JS bridge functions with the dashboard. Extensions implementing this interface have RegisterDashboardBridge called during dashboard Start() after the bridge is initialized.

Example implementation:

func (a *AuthExtension) RegisterDashboardBridge(b *bridge.Bridge) error {
    return b.Register("auth.getStats", a.handleGetStats,
        bridge.WithDescription("Get auth statistics"),
    )
}

type Config

type Config struct {
	// Server settings
	BasePath string `json:"base_path" yaml:"base_path"`
	Title    string `json:"title"     yaml:"title"`

	// RootContributor, when set, makes the dashboard root ({BasePath}) render the
	// named contributor's landing page in place. Used by embedded dashboards
	// (e.g. authsome) whose contributor owns the root. Empty leaves the root
	// unserved.
	RootContributor string `json:"root_contributor" yaml:"root_contributor"`

	// ShellSource selects where the dashboard UI at {BasePath}/ui comes from.
	// ShellEmbedded, the default, serves the prebuilt shell compiled into this
	// binary. ShellExternal mounts nothing there.
	//
	// The zero value "" is read as ShellEmbedded everywhere it is consumed, so
	// a Config assembled as a struct literal rather than through
	// DefaultConfig() still gets a dashboard.
	ShellSource ShellSource `json:"shell_source" yaml:"shell_source"`

	// Features
	EnableRealtime  bool `json:"enable_realtime"  yaml:"enable_realtime"` // SSE real-time updates
	EnableExport    bool `json:"enable_export"    yaml:"enable_export"`
	EnableSearch    bool `json:"enable_search"    yaml:"enable_search"`
	EnableSettings  bool `json:"enable_settings"  yaml:"enable_settings"`
	EnableDiscovery bool `json:"enable_discovery" yaml:"enable_discovery"` // auto-discover remote contributors
	EnableBridge    bool `json:"enable_bridge"    yaml:"enable_bridge"`    // Go↔JS bridge function system

	// Data collection
	RefreshInterval time.Duration `json:"refresh_interval" yaml:"refresh_interval"`
	HistoryDuration time.Duration `json:"history_duration" yaml:"history_duration"`
	MaxDataPoints   int           `json:"max_data_points"  yaml:"max_data_points"`

	// Tracing
	TraceMaxCount  int           `json:"trace_max_count"  yaml:"trace_max_count"`
	TraceRetention time.Duration `json:"trace_retention"  yaml:"trace_retention"`
	// TraceMaxSpansPerTrace caps how many spans one trace retains. A single
	// long-lived trace, such as a websocket, would otherwise grow unbounded.
	TraceMaxSpansPerTrace int `json:"trace_max_spans_per_trace" yaml:"trace_max_spans_per_trace"`
	// TraceIdleTTL is how long after the last dashboard request spans keep being
	// retained. A negative duration disables the gate, retaining always (the
	// pre-gate behaviour). Zero is not a reliable way to disable it: under a
	// ConfigManager, config merging (see extension_config.go) only overrides a
	// field when the source value is non-zero, so an explicit 0 here is
	// silently skipped and the default survives.
	TraceIdleTTL time.Duration `json:"trace_idle_ttl" yaml:"trace_idle_ttl"`

	// Proxy/Remote
	ProxyTimeout time.Duration `json:"proxy_timeout"  yaml:"proxy_timeout"`
	CacheMaxSize int           `json:"cache_max_size" yaml:"cache_max_size"`
	CacheTTL     time.Duration `json:"cache_ttl"      yaml:"cache_ttl"`

	// SSE
	SSEKeepAlive time.Duration `json:"sse_keep_alive" yaml:"sse_keep_alive"`

	// Security
	EnableCSP  bool `json:"enable_csp"  yaml:"enable_csp"`
	EnableCSRF bool `json:"enable_csrf" yaml:"enable_csrf"`
	// EnableContractSecurity gates CSRF validation, idempotency dedup, and
	// distributed tracing on the contract envelope endpoint. Default true;
	// set to false during a rollout window where clients have not yet
	// adopted CSRF tokens or the idempotency-key contract.
	EnableContractSecurity bool `json:"enable_contract_security" yaml:"enable_contract_security"`

	// Authentication
	EnableAuth    bool   `json:"enable_auth"    yaml:"enable_auth"`    // enable auth support
	LoginPath     string `json:"login_path"     yaml:"login_path"`     // relative auth login path (e.g. "/auth/login")
	LogoutPath    string `json:"logout_path"    yaml:"logout_path"`    // relative auth logout path (e.g. "/auth/logout")
	DefaultAccess string `json:"default_access" yaml:"default_access"` // "public", "protected", "partial"
	// RequiredRoles, when non-empty, restricts dashboard access to users
	// carrying at least one matching role. The principal endpoint returns
	// 403 PERMISSION_DENIED for users who don't qualify.
	//
	// That is the whole of it on this side. Turning the 403 into an "access
	// denied" screen is the client's job, and the client that used to do it
	// was deleted with the server-driven shell, so nothing renders it today.
	// The gate itself still holds: the endpoint answers 403 regardless.
	RequiredRoles []string `json:"required_roles" yaml:"required_roles"`

	// Theming
	Theme     string `json:"theme"      yaml:"theme"` // light, dark, auto
	CustomCSS string `json:"custom_css" yaml:"custom_css"`

	// Discovery
	DiscoveryTag          string        `json:"discovery_tag"           yaml:"discovery_tag"`
	DiscoveryPollInterval time.Duration `json:"discovery_poll_interval" yaml:"discovery_poll_interval"`

	// Export
	ExportFormats []string `json:"export_formats" yaml:"export_formats"`

	// Internal
	RequireConfig bool `json:"-" yaml:"-"`
}

Config contains dashboard extension configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default dashboard configuration.

func (Config) Validate

func (c Config) Validate() error

Validate validates the configuration.

type ConfigOption

type ConfigOption func(*Config)

ConfigOption is a functional option for Config.

func WithBasePath

func WithBasePath(path string) ConfigOption

WithBasePath sets the base URL path for the dashboard.

func WithBridge added in v0.9.12

func WithBridge(enabled bool) ConfigOption

WithBridge enables or disables the Go↔JS bridge function system.

func WithCSP added in v0.9.12

func WithCSP(enabled bool) ConfigOption

WithCSP enables or disables Content-Security-Policy headers.

func WithCSRF added in v0.9.12

func WithCSRF(enabled bool) ConfigOption

WithCSRF enables or disables CSRF token protection.

func WithCacheMaxSize added in v0.9.12

func WithCacheMaxSize(size int) ConfigOption

WithCacheMaxSize sets the maximum number of cached fragments.

func WithCacheTTL added in v0.9.12

func WithCacheTTL(ttl time.Duration) ConfigOption

WithCacheTTL sets the time-to-live for cached fragments.

func WithConfig

func WithConfig(config Config) ConfigOption

WithConfig sets the complete config.

func WithContractSecurity added in v1.6.4

func WithContractSecurity(enabled bool) ConfigOption

WithContractSecurity enables or disables the contract envelope's security stack (CSRF validation, idempotency dedup, request tracing). Defaults to true; switching off should be reserved for rollout windows where clients have not yet adopted CSRF tokens or idempotency keys.

func WithCustomCSS added in v0.9.12

func WithCustomCSS(css string) ConfigOption

WithCustomCSS sets custom CSS to inject into the dashboard.

func WithDefaultAccess added in v0.10.0

func WithDefaultAccess(access string) ConfigOption

WithDefaultAccess sets the default access level for pages ("public", "protected", "partial").

func WithDiscovery added in v0.9.12

func WithDiscovery(enabled bool) ConfigOption

WithDiscovery enables or disables automatic service discovery of remote contributors.

func WithDiscoveryPollInterval added in v0.9.12

func WithDiscoveryPollInterval(interval time.Duration) ConfigOption

WithDiscoveryPollInterval sets how often to poll for new contributors via discovery.

func WithDiscoveryTag added in v0.9.12

func WithDiscoveryTag(tag string) ConfigOption

WithDiscoveryTag sets the service discovery tag to filter dashboard contributors.

func WithEnableAuth added in v0.10.0

func WithEnableAuth(enabled bool) ConfigOption

WithEnableAuth enables or disables authentication support.

func WithExport

func WithExport(enabled bool) ConfigOption

WithExport enables or disables export functionality.

func WithExportFormats added in v0.9.12

func WithExportFormats(formats []string) ConfigOption

WithExportFormats sets the supported export formats.

func WithHistoryDuration

func WithHistoryDuration(duration time.Duration) ConfigOption

WithHistoryDuration sets the data retention duration.

func WithLoginPath added in v0.10.0

func WithLoginPath(path string) ConfigOption

WithLoginPath sets the relative login page path (e.g. "/auth/login").

func WithLogoutPath added in v0.10.0

func WithLogoutPath(path string) ConfigOption

WithLogoutPath sets the relative logout page path (e.g. "/auth/logout").

func WithMaxDataPoints

func WithMaxDataPoints(maxPoints int) ConfigOption

WithMaxDataPoints sets the maximum number of data points to retain.

func WithMemoryProfile added in v1.6.0

func WithMemoryProfile(profile MemoryProfile) ConfigOption

WithMemoryProfile auto-tunes data collection and retention settings based on the available system memory. Individual settings applied after this option will override the profile defaults.

func WithProxyTimeout added in v0.9.12

func WithProxyTimeout(timeout time.Duration) ConfigOption

WithProxyTimeout sets the timeout for proxying requests to remote contributors.

func WithRealtime

func WithRealtime(enabled bool) ConfigOption

WithRealtime enables or disables real-time SSE updates.

func WithRefreshInterval

func WithRefreshInterval(interval time.Duration) ConfigOption

WithRefreshInterval sets the data collection refresh interval.

func WithRequireConfig

func WithRequireConfig(required bool) ConfigOption

WithRequireConfig requires config from ConfigManager.

func WithRequiredRoles added in v1.6.4

func WithRequiredRoles(roles []string) ConfigOption

WithRequiredRoles restricts dashboard access to users carrying at least one of the given roles. Pass nil/empty to allow all authenticated users. Auth extensions (e.g. authsome) call this via Extension.SetRequiredRoles when their config declares a role gate; deployments can also configure it directly via this option.

func WithRootContributor added in v1.7.1

func WithRootContributor(name string) ConfigOption

WithRootContributor makes the dashboard root ({BasePath}) render the named contributor's landing page in place. Used by embedded dashboards whose own contributor owns the landing page.

Empty is the default and leaves the root unserved: it returns 404. Nothing redirects anywhere.

func WithSSEKeepAlive added in v0.9.12

func WithSSEKeepAlive(interval time.Duration) ConfigOption

WithSSEKeepAlive sets the SSE keep-alive interval.

func WithSearch added in v0.9.12

func WithSearch(enabled bool) ConfigOption

WithSearch enables or disables global search.

func WithSettings added in v0.9.12

func WithSettings(enabled bool) ConfigOption

WithSettings enables or disables aggregated settings pages.

func WithShellSource added in v1.11.1

func WithShellSource(source ShellSource) ConfigOption

WithShellSource selects where the dashboard UI at {BasePath}/ui comes from.

ShellEmbedded is the default and needs no call: the prebuilt shell is compiled into the binary, so registering the extension is enough to get a working dashboard. Pass ShellExternal when the deployment builds its own shell with its own plugins and serves it itself.

ShellExternal means the extension serves no shell, not that {BasePath}/ui is unrouted. The three shell routes are never registered, and the path then falls through to the ForgeUI catch-all the extension mounts at {BasePath}/*, which has no page for it. So it 404s today, but through a different handler, and a deployment that registers its own page there gets it.

Everything else the extension mounts is unaffected either way. An external shell still talks to the same data contract under /api/dashboard/v1.

func WithTheme

func WithTheme(theme string) ConfigOption

WithTheme sets the UI theme (light, dark, auto).

func WithTitle

func WithTitle(title string) ConfigOption

WithTitle sets the dashboard title.

func WithTraceIdleTTL added in v1.11.0

func WithTraceIdleTTL(duration time.Duration) ConfigOption

WithTraceIdleTTL sets how long after the last dashboard request traces keep being collected. Pass a negative duration to collect always, disabling the gate. Passing zero is not reliable for this: under a ConfigManager, config merging only overrides a field when the source value is non-zero, so an explicit zero here can be silently skipped in favor of the existing value.

func WithTraceMaxCount added in v1.6.0

func WithTraceMaxCount(count int) ConfigOption

WithTraceMaxCount sets the maximum number of traces kept in memory.

func WithTraceMaxSpansPerTrace added in v1.11.0

func WithTraceMaxSpansPerTrace(n int) ConfigOption

WithTraceMaxSpansPerTrace sets how many spans a single trace retains.

func WithTraceRetention added in v1.6.0

func WithTraceRetention(duration time.Duration) ConfigOption

WithTraceRetention sets the retention duration for traces.

type ContractContributorAware added in v1.6.4

type ContractContributorAware interface {
	RegisterContractContributor(
		disp *dispatcher.Dispatcher,
		reg contract.Registry,
		wreg contract.WardenRegistry,
	) error
}

ContractContributorAware is an optional interface that Forge extensions can implement to register a contract-based dashboard contributor (the slice (f)+ shape — declarative YAML manifest + typed dispatcher handlers). The dashboard auto-discovers extensions implementing this interface during Start() and calls RegisterContractContributor with the dashboard's contract registry, warden registry, and dispatcher.

Coexists with DashboardAware: an extension can implement both to register its legacy templ-based contributor AND its contract contributor during the migration window. New extensions should prefer this interface; legacy ones migrate over time per the per-slice (f, g, h) plan.

Example implementation:

func (e *StreamingExtension) RegisterContractContributor(
    disp *dispatcher.Dispatcher,
    reg contract.Registry,
    wreg contract.WardenRegistry,
) error {
    return streamingcontract.Register(disp, reg, wreg, streamingcontract.Deps{
        Manager: func() streaming.Manager { return e.manager },
        Config:  func() streaming.Config { return e.config },
    })
}

type DashboardAuthAware added in v1.2.0

type DashboardAuthAware interface {
	RegisterDashboardAuth(ext *Extension)
}

DashboardAuthAware is an optional interface that Forge extensions can implement to provide authentication for the dashboard. The dashboard auto-discovers extensions implementing this interface during Start() and calls RegisterDashboardAuth with itself, allowing the extension to set up auth pages, auth checking, and tenant resolution.

Example implementation:

func (a *AuthExtension) RegisterDashboardAuth(ext *dashboard.Extension) {
    ext.SetAuthPageProvider(myPageProvider)
    ext.SetAuthChecker(myAuthChecker)
    ext.EnableAuth()
}

Wiring an auth extension into the contract:

Auth extensions plug in by implementing both DashboardAuthAware *and* ContractContributorAware:

  • DashboardAuthAware.RegisterDashboardAuth wires the AuthChecker so /api/dashboard/v1/principal returns the current user. That endpoint distinguishes the 401 envelope (auth required) from the 200 `{authenticated:false}` envelope (auth disabled), so a client can tell "log in" apart from "auth is off".
  • ContractContributorAware.RegisterContractContributor registers the `auth.login` command intent (and optionally `auth.logout`) on the dispatcher.

Both halves are live and this is the whole server side. The client side is not: the built-in LoginScreen and the AuthGate that chose between it and a contributor's own /login page belonged to the server-driven shell, which was deleted. Nothing consumes the principal endpoint or issues `auth.login` today. Register them anyway if you own an auth extension, because the endpoint and the intent are what a future login UI builds on, but do not expect a login screen to appear because you did.

Example combined integration sketch:

func (a *AuthsomeExtension) RegisterDashboardAuth(ext *dashboard.Extension) {
    ext.SetAuthChecker(a.checker)
    ext.EnableAuth()
}
func (a *AuthsomeExtension) RegisterContractContributor(
    disp *dispatcher.Dispatcher,
    reg contract.Registry,
    wreg contract.WardenRegistry,
) error {
    return authsomecontract.Register(disp, reg, wreg, authsomecontract.Deps{
        Sessions: a.sessions,
        // Registers `auth.login` (command) and optionally ships a
        // `/login` page that overrides the built-in LoginScreen.
    })
}

type DashboardAware added in v0.10.0

type DashboardAware interface {
	DashboardContributor() contributor.LocalContributor
}

DashboardAware is an optional interface that Forge extensions can implement to signal that they bundle a dashboard contributor. The dashboard extension auto-discovers extensions implementing this interface during Start() and registers their contributors automatically — no manual registration needed.

The returned contributor can be an EmbeddedContributor (static build), SSRContributor (Node.js sidecar), or any other LocalContributor.

Example implementation in an extension:

func (a *AuthExtension) DashboardContributor() contributor.LocalContributor {
    return newDashboardContributor() // generated by forge contributor build
}

type DashboardBridge added in v0.9.12

type DashboardBridge struct {
	// contains filtered or unexported fields
}

DashboardBridge wraps a forgeui bridge.Bridge to provide Go↔JS communication for the dashboard. Bridge functions can be called from the browser via Alpine.js magic helpers ($go, $goBatch, $goStream) or the ForgeBridge JS client.

func NewDashboardBridge added in v0.9.12

func NewDashboardBridge(c *collector.DataCollector, h *collector.DataHistory) *DashboardBridge

NewDashboardBridge creates a new bridge with built-in dashboard functions registered.

func NewDashboardBridgeWithBridge added in v0.9.12

func NewDashboardBridgeWithBridge(b *bridge.Bridge, c *collector.DataCollector, h *collector.DataHistory) *DashboardBridge

NewDashboardBridgeWithBridge wraps an existing forgeui bridge instance with dashboard functions. Use this when the bridge is created by forgeui.App (via forgeui.WithBridge()).

func (*DashboardBridge) Bridge added in v0.9.12

func (db *DashboardBridge) Bridge() *bridge.Bridge

Bridge returns the underlying forgeui bridge instance.

func (*DashboardBridge) Register added in v0.9.12

func (db *DashboardBridge) Register(name string, handler any, opts ...bridge.FunctionOption) error

Register registers a custom bridge function. Extensions can use this to expose Go functions callable from the dashboard UI.

type DashboardFooterContributor added in v1.2.0

type DashboardFooterContributor interface {
	DashboardUserDropdownActions(basePath string) []shell.UserDropdownAction
}

DashboardFooterContributor is an optional interface that Forge extensions can implement to contribute custom actions to the sidebar footer user dropdown. The dashboard auto-discovers extensions implementing this interface during Start() and collects their actions for rendering in both the main dashboard sidebar and extension sidebars.

Example implementation:

func (a *AuthExtension) DashboardUserDropdownActions(basePath string) []shell.UserDropdownAction {
    return []shell.UserDropdownAction{
        {Label: "Profile", Icon: "user", Href: basePath + "/ext/authsome/pages/profile"},
    }
}

type DashboardStatus added in v1.11.1

type DashboardStatus struct {
	// Version is the extension's semver, checked against the plugin's
	// `requires` range. Empty means "do not check".
	Version string `json:"version,omitempty"`
	// Configured reports whether the extension has everything it needs to
	// serve. False makes the dashboard render the plugin's setup guide.
	Configured bool `json:"configured"`
	// Message is optional detail shown in the setup panel.
	Message string `json:"message,omitempty"`
}

DashboardStatus is what an extension reports about itself to the dashboard.

Set Configured explicitly on every value you return. The struct's zero value is Configured=false, the restrictive answer, so a bare DashboardStatus{} returned from a switch default asks the dashboard for a setup panel.

The permissive default lives elsewhere: an extension that does not implement DashboardStatusAware at all is reported with an empty Version and Configured=true, so the plugin host skips the version check rather than failing it, and renders the plugin normally. Nobody has to implement this to keep working.

type DashboardStatusAware added in v1.11.1

type DashboardStatusAware interface {
	DashboardStatus() DashboardStatus
}

DashboardStatusAware is an optional interface a Forge extension can implement to tell the dashboard its version and whether it is configured.

Without it the dashboard would have to infer setup state by calling a data method and interpreting the failure, which cannot distinguish "not configured yet" from "configured but failing" from "you lack permission". Those are three different screens.

type EmptyParams added in v0.9.12

type EmptyParams struct{}

EmptyParams is used for functions that take no input.

type Extension

type Extension struct {
	*forge.BaseExtension
	// contains filtered or unexported fields
}

Extension implements the extensible dashboard micro-frontend shell. Contributors (local or remote) register pages, widgets, and settings that are merged into a unified admin dashboard.

func (*Extension) AddRemoteContributor added in v1.6.2

func (e *Extension) AddRemoteContributor(ctx context.Context, baseURL, apiKey string) error

AddRemoteContributor fetches a remote contributor's manifest from the given base URL and registers it as a remote dashboard contributor. One-shot — returns an error on any failure. Use WatchRemoteContributor instead when the upstream may not be reachable yet at registration time (typical for dev where Portal and identity start in parallel).

baseURL must be the same prefix the upstream's contributor protocol routes are mounted under (e.g. "http://identity:7902/authsome"). apiKey is optional — pass "" when the upstream allows unauthenticated dashboard fetches.

The fetch is bounded by ProxyTimeout from the dashboard config. Subsequent calls with the same manifest name fail with ErrContributorExists; callers should treat that as idempotent.

func (*Extension) AuthChecker added in v0.10.0

func (e *Extension) AuthChecker() dashauth.AuthChecker

AuthChecker returns the configured authentication checker. Returns nil if none is set.

func (*Extension) AuthPageProvider added in v0.10.0

func (e *Extension) AuthPageProvider() dashauth.AuthPageProvider

AuthPageProvider returns the configured auth page provider. Returns nil if none is set.

func (*Extension) CSRFManager added in v0.9.12

func (e *Extension) CSRFManager() *security.CSRFManager

CSRFManager returns the CSRF token manager. Returns nil if CSRF is disabled.

func (*Extension) Collector added in v0.8.0

func (e *Extension) Collector() *collector.DataCollector

Collector returns the data collector instance.

func (*Extension) DashboardBridge added in v0.9.12

func (e *Extension) DashboardBridge() *DashboardBridge

DashboardBridge returns the dashboard bridge instance for registering custom functions. Returns nil if the bridge is not enabled.

func (*Extension) Dependencies

func (e *Extension) Dependencies() []string

Dependencies returns extension dependencies.

func (*Extension) EnableAuth added in v1.2.0

func (e *Extension) EnableAuth()

EnableAuth enables authentication support on the dashboard. This is called automatically when an auth provider (e.g. authsome) registers itself. If called after routes have already been registered (late registration), it triggers auth page registration and updates the layout/pages managers.

func (*Extension) ForgeUIApp added in v0.9.12

func (e *Extension) ForgeUIApp() *forgeui.App

ForgeUIApp returns the forgeui application instance.

func (*Extension) FragmentProxy added in v0.9.12

func (e *Extension) FragmentProxy() *proxy.FragmentProxy

FragmentProxy returns the fragment proxy for remote contributors.

func (*Extension) Health

func (e *Extension) Health(ctx context.Context) error

Health checks if the dashboard is healthy.

func (*Extension) History added in v0.8.0

func (e *Extension) History() *collector.DataHistory

History returns the data history instance.

func (*Extension) RecoveryManager added in v0.9.12

func (e *Extension) RecoveryManager() *recovery.Manager

RecoveryManager returns the recovery manager for remote contributors.

func (*Extension) Register

func (e *Extension) Register(app forge.App) error

Register registers the dashboard extension.

func (*Extension) RegisterBridgeFunction added in v0.9.12

func (e *Extension) RegisterBridgeFunction(name string, handler any, opts ...bridge.FunctionOption) error

RegisterBridgeFunction registers a custom bridge function callable from the dashboard UI. This is a convenience method — callers can also use DashboardBridge().Register() directly. Returns an error if the bridge is not enabled.

func (*Extension) RegisterContributor added in v0.9.12

func (e *Extension) RegisterContributor(c contributor.LocalContributor) error

RegisterContributor registers a local contributor with the dashboard. This is the primary API for extensions to contribute UI to the dashboard.

Dashboard-status attribution does not cover this path. It takes a LocalContributor, not an extension, so there is no DashboardStatusAware value to attach: a contract contributor registered here reports the permissive default (empty version, configured) no matter what its extension would say. discoverExtensionContributors logs a warning naming any DashboardStatusAware extension that ends up with nothing attributed, which is how this shows up. Any new registration path has the same gap unless it routes through a recordingRegistry or calls attributeContributorStatus.

func (*Extension) RegisterRemoteContractContributor added in v1.6.4

func (e *Extension) RegisterRemoteContractContributor(ctx context.Context, baseURL, apiKey string) error

RegisterRemoteContractContributor registers a contract contributor whose handlers live in another service. The dashboard fetches the upstream's manifest, validates it, records the endpoint, and installs a forwarding dispatcher (idempotently) so subsequent requests for any of the remote's intents are proxied to the upstream over HTTP.

Slice (m) added this so a single dashboard can aggregate contributors from multiple microservices, mirroring the legacy templ-path WatchRemoteContributor capability.

baseURL is the upstream service root (e.g. https://svc.internal:8443); the manifest endpoint is fetched at <baseURL>/_forge/contract/manifest and envelopes are POSTed to <baseURL>/_forge/contract/dispatch. apiKey, when non-empty, is sent as Authorization: Bearer on dashboard→service hops; end-user identity flows in parallel via X-Forwarded-Authorization and X-Forwarded-Cookie.

func (*Extension) Registry added in v0.9.12

func (e *Extension) Registry() *contributor.ContributorRegistry

Registry returns the contributor registry.

func (*Extension) SSEBroker added in v0.9.12

func (e *Extension) SSEBroker() *sse.Broker

SSEBroker returns the SSE event broker. Returns nil if real-time is disabled.

func (*Extension) Sanitizer added in v0.9.12

func (e *Extension) Sanitizer() *security.Sanitizer

Sanitizer returns the HTML sanitizer for remote fragments.

func (*Extension) Searcher added in v0.9.12

func (e *Extension) Searcher() *search.FederatedSearch

Searcher returns the federated search engine. Returns nil if search is disabled.

func (*Extension) SetAuthChecker added in v0.10.0

func (e *Extension) SetAuthChecker(checker dashauth.AuthChecker)

SetAuthChecker configures the authentication checker used to validate requests. Call this after Register() and before Start(). When auth is enabled, the checker is invoked on every request to populate the user context.

Example using the adapter for the forge auth extension:

checker := dashauth.NewAuthExtensionChecker(authRegistry, "oidc")
dashExt.SetAuthChecker(checker)

func (*Extension) SetAuthPageProvider added in v0.10.0

func (e *Extension) SetAuthPageProvider(provider dashauth.AuthPageProvider)

SetAuthPageProvider configures the provider that contributes authentication pages (login, logout, register, etc.) to the dashboard. If called after routes have already been registered and auth is enabled, auth pages are registered immediately (late registration).

func (*Extension) SetDiscoveryService added in v0.9.12

func (e *Extension) SetDiscoveryService(svc dashboarddiscovery.DiscoveryService)

SetDiscoveryService configures the discovery service used to auto-discover remote dashboard contributors. Call this before Start() if discovery is enabled.

The discovery service must implement dashboarddiscovery.DiscoveryService (ListServices + DiscoverWithTags). The forge extensions/discovery.Service type satisfies this interface.

Example:

dashExt.SetDiscoveryService(discoveryExtension.Service())

func (*Extension) SetRequiredRoles added in v1.6.4

func (e *Extension) SetRequiredRoles(roles []string)

SetRequiredRoles restricts dashboard access to authenticated users that hold at least one of the given roles. Pass nil/empty to clear the gate. Auth extensions like authsome call this from RegisterDashboardAuth when their own configuration declares a role list. The principal endpoint returns 403 PERMISSION_DENIED for users who don't qualify. Rendering that as an "access denied" screen is the client's job, and no client does it today; the 403 itself is served regardless.

func (*Extension) SetTenantResolver added in v1.2.0

func (e *Extension) SetTenantResolver(resolver dashauth.TenantResolver)

SetTenantResolver configures the tenant resolver used to populate tenant context on every request. Call this after Register() and before Start(). When configured, all dashboard pages (both standard and extension layout) can access tenant info via dashauth.TenantFromContext(ctx).

A default ScopeTenantResolver is available that reads forge.Scope from the request context:

dashExt.SetTenantResolver(dashauth.ScopeTenantResolver{})

func (*Extension) SettingsAggregator added in v0.9.12

func (e *Extension) SettingsAggregator() *settings.Aggregator

SettingsAggregator returns the settings aggregator. Returns nil if settings is disabled.

func (*Extension) Start

func (e *Extension) Start(ctx context.Context) error

Start starts the dashboard extension.

func (*Extension) Stop

func (e *Extension) Stop(ctx context.Context) error

Stop stops the dashboard extension.

func (*Extension) TenantResolver added in v1.2.0

func (e *Extension) TenantResolver() dashauth.TenantResolver

TenantResolver returns the configured tenant resolver. Returns nil if none is set.

func (*Extension) ThemeManager added in v0.9.12

func (e *Extension) ThemeManager() *dashtheme.Manager

ThemeManager returns the theme manager.

func (*Extension) TraceStore added in v1.2.0

func (e *Extension) TraceStore() *collector.TraceStore

TraceStore returns the trace store instance.

func (*Extension) UnregisterRemoteContractContributor added in v1.6.4

func (e *Extension) UnregisterRemoteContractContributor(name string)

UnregisterRemoteContractContributor removes a previously registered remote. Safe to call for unknown names; future dispatches to the contributor will fall through to CodeNotFound.

func (*Extension) WatchRemoteContributor added in v1.6.2

func (e *Extension) WatchRemoteContributor(ctx context.Context, baseURL, apiKey string, opts ...WatchRemoteOption) error

WatchRemoteContributor registers a remote dashboard contributor with retry + refresh semantics. Returns immediately after spawning a watcher goroutine; the goroutine retries with exponential backoff until the upstream becomes reachable, then re-fetches periodically to surface manifest changes (e.g. a plugin freshly loaded on the upstream).

Use this from a forge.OnBeforeRun hook when the upstream may not be ready at lifecycle-hook time. ctx should be tied to the host's lifecycle so the watcher exits cleanly on shutdown — the dashboard extension also cancels every active watcher in Stop() as a safety net.

The call is idempotent: a second WatchRemoteContributor with the same dedup key (baseURL by default) is a no-op.

type MemoryProfile added in v1.6.0

type MemoryProfile string

MemoryProfile controls memory usage tuning for the dashboard. Use "low" for 512MB systems, "medium" for 1-2GB, "high" for 4GB+.

const (
	// MemoryProfileLow tunes for 512MB systems with minimal history and trace retention.
	MemoryProfileLow MemoryProfile = "low"
	// MemoryProfileMedium is the default, suitable for 1-2GB systems.
	MemoryProfileMedium MemoryProfile = "medium"
	// MemoryProfileHigh allows maximum history and trace retention for 4GB+ systems.
	MemoryProfileHigh MemoryProfile = "high"
)

type ServiceNameParams added in v0.9.12

type ServiceNameParams struct {
	Name string `json:"name"`
}

ServiceNameParams is used for functions that take a service name.

type ShellSource added in v1.11.1

type ShellSource string

ShellSource selects where the dashboard UI comes from.

const (
	// ShellEmbedded serves the prebuilt shell compiled into this binary.
	// The default: RegisterExtension(dashboard.NewExtension()) gives you a
	// working dashboard with no frontend toolchain anywhere near you.
	ShellEmbedded ShellSource = "embedded"

	// ShellExternal serves no shell at {BasePath}/ui, for deployments that
	// build their own dashboard with their own plugins and serve it
	// themselves. See `forge dashboard new`. It means the routes are not
	// registered, not that the path is guaranteed to 404; see
	// WithShellSource.
	ShellExternal ShellSource = "external"
)

type WatchRemoteOption added in v1.6.2

type WatchRemoteOption func(*watchRemoteConfig)

WatchRemoteOption tunes a WatchRemoteContributor watcher's behaviour.

func WithInitialBackoff added in v1.6.2

func WithInitialBackoff(d time.Duration) WatchRemoteOption

WithInitialBackoff sets the first retry delay when the upstream isn't reachable. Defaults to 2 seconds.

func WithMaxBackoff added in v1.6.2

func WithMaxBackoff(d time.Duration) WatchRemoteOption

WithMaxBackoff caps exponential backoff between retries. Defaults to 30s.

func WithWatcherKey added in v1.6.2

func WithWatcherKey(key string) WatchRemoteOption

WithWatcherKey overrides the dedup key. Two WatchRemoteContributor calls with the same key collapse to a single watcher (the second is a no-op). Defaults to baseURL when not set.

func WithWatcherRefreshInterval added in v1.6.2

func WithWatcherRefreshInterval(d time.Duration) WatchRemoteOption

WithWatcherRefreshInterval sets how often the watcher re-fetches the manifest after a successful initial registration to pick up changes (e.g. a plugin loaded on the upstream). Defaults to 60s.

Directories

Path Synopsis
Package dashauth provides authentication and authorization abstractions for the dashboard extension.
Package dashauth provides authentication and authorization abstractions for the dashboard extension.
Package contract defines the declarative, single-endpoint contract for the admin dashboard: contributor manifests, request/response envelopes, the permission model, and the per-contributor version negotiation protocol.
Package contract defines the declarative, single-endpoint contract for the admin dashboard: contributor manifests, request/response envelopes, the permission model, and the per-contributor version negotiation protocol.
dispatcher
Package dispatcher implements transport.Dispatcher and transport.SubscriptionSource against a function-table of registered handlers.
Package dispatcher implements transport.Dispatcher and transport.SubscriptionSource against a function-table of registered handlers.
idempotency
Package idempotency provides command deduplication for the dashboard contract: a Store interface plus an in-memory implementation.
Package idempotency provides command deduplication for the dashboard contract: a Store interface plus an in-memory implementation.
loader
validate.go
validate.go
pilot
Package pilot ships the migrated dashboard contributor used to validate the contract end-to-end: extensions.list, services.list, services.detail, and the metrics.summary subscription, all wired against the existing collector and contributor registry.
Package pilot ships the migrated dashboard contributor used to validate the contract end-to-end: extensions.list, services.list, services.detail, and the metrics.summary subscription, all wired against the existing collector and contributor registry.
remote
Package remote implements the contract dispatcher's HTTP forwarding layer.
Package remote implements the contract dispatcher's HTTP forwarding layer.
server
Package server exposes the two HTTP endpoints a non-dashboard service needs to advertise itself as a contract contributor that other dashboards can discover + dispatch into.
Package server exposes the two HTTP endpoints a non-dashboard service needs to advertise itself as a contract contributor that other dashboards can discover + dispatch into.
transport
capabilities.go
capabilities.go
codegen
Package codegen generates Go source files from forge.contributor.yaml configuration.
Package codegen generates Go source files from forge.contributor.yaml configuration.
config
Package config defines the schema for forge.contributor.yaml configuration files that declare dashboard contributor metadata, navigation, widgets, settings, and build configuration.
Package config defines the schema for forge.contributor.yaml configuration files that declare dashboard contributor metadata, navigation, widgets, settings, and build configuration.
examples
basic command
Package main demonstrates a basic dashboard setup with default configuration and no contributors registered.
Package main demonstrates a basic dashboard setup with default configuration and no contributors registered.
contributor command
Package main demonstrates how to create a custom LocalContributor that adds pages, widgets, and settings to the dashboard.
Package main demonstrates how to create a custom LocalContributor that adds pages, widgets, and settings to the dashboard.
remote command
Package main demonstrates how to register a remote contributor with the dashboard extension.
Package main demonstrates how to register a remote contributor with the dashboard extension.
templ: version: v0.3.1001
templ: version: v0.3.1001
Package shellassets carries the built dashboard shell.
Package shellassets carries the built dashboard shell.
Package theme provides a dashboard-specific wrapper around the forgeui theme system.
Package theme provides a dashboard-specific wrapper around the forgeui theme system.
ui
templ: version: v0.3.1001
templ: version: v0.3.1001
pages
templ: version: v0.3.1001
templ: version: v0.3.1001
shell
templ: version: v0.3.1001
templ: version: v0.3.1001

Jump to

Keyboard shortcuts

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