Documentation
¶
Overview ¶
Package adminapi exposes a read-only admin HTTP surface over the fabriq query facade. It is designed for consumption by the fabriq-admin SPA and any operator tooling that needs a stable, auth-agnostic read API over the data fabric.
The extension is auth-agnostic: the host MUST attach authentication and tenant-injection middleware via WithRouteOptions. Fabriq adminapi adds no auth of its own so that the host controls the security boundary entirely.
Phase 1 endpoints:
GET {BasePath}/meta → service metadata + capabilities
GET {BasePath}/entities → paginated entity list (requires ?type=)
GET {BasePath}/entities/{id} → single entity by type and id (requires ?type=)
GET {BasePath}/capabilities → fabric subsystem capabilities (instance-level,
or per-type with ?type=)
Index ¶
- Constants
- func PluginRemoteSpec() registry.EntitySpec
- type Authorizer
- type AuthorizerFunc
- type Extension
- func (e *Extension) Dependencies() []string
- func (e *Extension) Description() string
- func (e *Extension) Name() string
- func (e *Extension) Register(app forge.App) error
- func (e *Extension) Start(ctx context.Context) error
- func (e *Extension) Stop(_ context.Context) error
- func (e *Extension) Version() string
- type IssuedKey
- type KeyRecord
- type KeySpec
- type KeyStore
- type Option
- func WithAdminLogin(username, password string) Option
- func WithAnalyticsAdmin() Option
- func WithAnalyticsRead() Option
- func WithAuth(store KeyStore) Option
- func WithAuthDisabled() Option
- func WithAuthorizer(a Authorizer) Option
- func WithBasePath(p string) Option
- func WithConnectionsRead() Option
- func WithEmbedder(e agent.Embedder) Option
- func WithRouteOptions(opts ...forge.RouteOption) Option
- func WithSchemaAdmin() Option
- func WithTenantsAdmin() Option
- func WithWritePolicy(p agent.WritePolicy) Option
Constants ¶
const APIVersion = 1
APIVersion is the current major version of the adminapi wire contract. Clients may advertise the version they were built against via the X-Fabriq-Api-Version request header; the auth middleware rejects requests whose advertised major differs from this value (HTTP 426). Responses always carry the current APIVersion in the same header.
const PluginRemoteEntityType = "admin_plugin_remote"
PluginRemoteEntityType is the well-known dynamic entity type name for remote plugin specs. The host application must register a matching EntitySpec (with a DynamicSchema) before using these endpoints; see PluginRemoteSpec.
const Version = forgeext.Version
Version is the adminapi extension version.
Variables ¶
This section is empty.
Functions ¶
func PluginRemoteSpec ¶
func PluginRemoteSpec() registry.EntitySpec
PluginRemoteSpec returns the canonical EntitySpec for admin_plugin_remote. The host application calls reg.Register(adminapi.PluginRemoteSpec()) during startup so the entity is available to the command plane and the relational querier. In unit tests, register it via buildPluginWorld or equivalent.
Types ¶
type Authorizer ¶ added in v0.0.6
type Authorizer interface {
Authorize(ctx context.Context, capability string) (allowed bool, err error)
}
Authorizer decides whether the current request may exercise a named capability. ctx is the request context — it carries the resolved tenant and API-key id, plus any principal a host authz middleware (e.g. warden) stamped before the handler ran. A resource-free (ctx, capability) shape is the minimal common denominator: the tenant is already in ctx, so a per-tenant authz system reads it from there.
type AuthorizerFunc ¶ added in v0.0.6
AuthorizerFunc adapts a plain function to Authorizer.
type Extension ¶
type Extension struct {
forge.BaseExtension
// contains filtered or unexported fields
}
Extension exposes the fabriq data fabric as a read-only admin HTTP surface. It depends on the "fabriq" extension and resolves its query facade at Start.
func NewAdminAPI ¶
NewAdminAPI builds the adminapi extension wired to a started fabriq Extension. The endpoint is auth-agnostic: the host MUST attach authentication and tenant-injection middleware via WithRouteOptions.
func (*Extension) Dependencies ¶
Dependencies implements forge.Extension.
func (*Extension) Description ¶
Description implements forge.Extension.
func (*Extension) Register ¶
Register registers the admin controller routes; the fabric resolves lazily in Start.
type IssuedKey ¶
IssuedKey is returned once, at issue time. Key is the plaintext bearer token and is never persisted or recoverable afterwards — only its hash is stored.
type KeyRecord ¶
type KeyRecord struct {
ID string
Prefix string
TenantID string
Label string
CanManageKeys bool
CreatedAt time.Time
RevokedAt *time.Time
// ExpiresAt is nil for keys that never expire. Session tokens (issued via
// IssueSession) always set it; the authn middleware denies a resolved key
// once ExpiresAt is in the past, mirroring how RevokedAt is enforced.
ExpiresAt *time.Time
}
KeyRecord is a stored key as seen on the read paths. It deliberately carries NO hash or plaintext field: redaction is structural, so List can never leak a verifiable secret. TenantID == "" means the key is multi-tenant (NULL in the database).
type KeySpec ¶
KeySpec describes a key to be issued. TenantID == "" issues a multi-tenant key (stored as NULL tenant_id, callable against any tenant); a non-empty TenantID scopes the key to that single tenant.
type KeyStore ¶
type KeyStore interface {
// Issue mints a new key, persists its hash, and returns the plaintext once.
Issue(ctx context.Context, spec KeySpec) (IssuedKey, error)
// IssueSession mints a multi-tenant, manage-keys session token that
// expires after ttl. It is the foundation for dashboard login: a session
// is just an expiring KeyStore row, validated by the same middleware path
// as any other API key.
IssueSession(ctx context.Context, ttl time.Duration) (IssuedKey, error)
// Lookup resolves a key by its sha256 hex hash with NO tenant scoping.
// Revoked keys still return (found=true): revocation is enforced by the
// middleware, not by hiding the row.
Lookup(ctx context.Context, keyHash string) (rec KeyRecord, found bool, err error)
// List returns every key as a redacted KeyRecord (no hash/plaintext).
List(ctx context.Context) ([]KeyRecord, error)
// Revoke stamps revoked_at on the key. The row stays visible to Lookup.
Revoke(ctx context.Context, id string) error
}
KeyStore is the persistence port for hosted-fabriq API keys. It operates on the instance-global fabriq_api_key table (created by migration 0027), which is deliberately NOT under RLS: keys are resolved by hash before any tenant context exists, mirroring the outbox's tenant-less rationale.
func NewKeyStore ¶
NewKeyStore builds a KeyStore over a grove.DB. The grove MUST be backed by the pg driver (grove + pgdriver), which is fabriq's only supported backend; this mirrors postgres.OpenWithGrove's driver assertion.
type Option ¶
type Option func(*config)
Option configures the adminapi extension.
func WithAdminLogin ¶
WithAdminLogin enables the dashboard-login surface (POST {BasePath}/login + /logout): username is compared verbatim at login time and password is bcrypt-hashed immediately, at option-application time — the plaintext password is never retained. WithAdminLogin REQUIRES WithAuth: login mints a session token via KeyStore.IssueSession, so Extension.Start fails fast with an error if AdminLoginUser is set but no KeyStore was configured.
func WithAnalyticsAdmin ¶ added in v0.0.6
func WithAnalyticsAdmin() Option
WithAnalyticsAdmin enables the cross-tenant analytics sink endpoints — synchronous backfill (single-tenant or fleet) plus a status check (the HTTP twin of `fabriq analytics backfill`). Leave OFF unless the host guards the admin API: backfill replays tenant snapshots into the analytics sink and is an instance-global operation.
func WithAnalyticsRead ¶ added in v0.0.6
func WithAnalyticsRead() Option
WithAnalyticsRead enables the read-only analytics status/freshness endpoint (GET /analytics/status) without the mutating operations — a least-privilege gate for a freshness dashboard. WithAnalyticsAdmin implies this.
func WithAuth ¶
WithAuth sets the instance-global API key store, enabling the /admin/keys issue/list/revoke management routes (registered only when the store is non-nil). It does NOT install the request-verifying middleware — that is a separate, explicit step: attach authMiddleware(store, basePath) via WithRouteOptions to actually gate admin requests on a valid key.
func WithAuthDisabled ¶
func WithAuthDisabled() Option
WithAuthDisabled turns OFF secure-by-default auth: no KeyStore is auto-provisioned and the admin API is served without authentication. Use for trusted internal deployments or tests that intentionally run keyless.
func WithAuthorizer ¶ added in v0.0.6
func WithAuthorizer(a Authorizer) Option
WithAuthorizer overrides the default flag-based capability gating with a host-supplied Authorizer — the seam for per-user RBAC (e.g. bridging warden). fabriq itself imports no authz system; the host wires the adapter.
func WithBasePath ¶
WithBasePath sets the admin API base path (default "/admin").
func WithConnectionsRead ¶ added in v0.0.6
func WithConnectionsRead() Option
WithConnectionsRead enables the connection/topology info endpoints — GET /connections (the tier's stores + cluster topology) and GET /tenants/:id/connection (a tenant's dedicated database). All connection info is REDACTED server-side: passwords and other secrets are never serialized. It is a read-only gate, deliberately separate from the mutating WithTenantsAdmin so a topology viewer can be granted without provisioning rights; leave OFF unless the host guards the admin API.
func WithEmbedder ¶
WithEmbedder supplies the embedding model used by the text-mode vector search endpoint (POST {BasePath}/search/vector with a {query} body). Fabriq stays model-agnostic: the host provides the implementation (Anthropic, OpenAI, a local model). When unset, text-mode vector search returns 501 and callers must use the similar-to-entity mode ({id} body) instead.
func WithRouteOptions ¶
func WithRouteOptions(opts ...forge.RouteOption) Option
WithRouteOptions forwards forge route options (auth middleware, OpenAPI decorators) to all admin routes — the extension stays auth-agnostic.
func WithSchemaAdmin ¶
func WithSchemaAdmin() Option
WithSchemaAdmin enables the privileged schema-ops endpoints — running/rolling back static migrations and executing ad-hoc DDL. These are instance-global, schema-owner operations; leave OFF unless the host guards the admin API.
func WithTenantsAdmin ¶ added in v0.0.6
func WithTenantsAdmin() Option
WithTenantsAdmin enables the tenant-management endpoints — list/get plus the suspend/resume lifecycle (the HTTP twin of the `fabriq tenant` CLI). These are instance-global, catalog-mode-only operations; leave OFF unless the host guards the admin API and runs db-per-tenant.
func WithWritePolicy ¶
func WithWritePolicy(p agent.WritePolicy) Option
WithWritePolicy sets the agent-toolkit write allowlist backing the guarded write endpoint (POST {BasePath}/agent/remember). Deny-by-default: without this option Remember permits no writes. Example: allow product creates/updates via agent.WritePolicy{Allow: map[string][]command.Op{"product": {command.OpCreate, command.OpUpdate}}}.
Source Files
¶
- agent_write.go
- analytics.go
- analytics_jobs.go
- analytics_query.go
- authn.go
- authn_middleware.go
- authz.go
- cache_admin.go
- capabilities.go
- commands.go
- connections.go
- controller.go
- crdt.go
- distill.go
- drift.go
- entities_write.go
- errors.go
- events.go
- extension.go
- files.go
- graph.go
- keys.go
- live.go
- login.go
- migrations.go
- plugins.go
- projections.go
- query.go
- recall.go
- schema.go
- schema_ddl.go
- schema_write.go
- search.go
- spatial.go
- tenants.go
- timeseries.go
- vector_admin.go
- version.go