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 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 WithAuth(store KeyStore) Option
- func WithAuthDisabled() Option
- func WithBasePath(p string) Option
- func WithEmbedder(e agent.Embedder) Option
- func WithRouteOptions(opts ...forge.RouteOption) Option
- func WithSchemaAdmin() 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 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 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 WithBasePath ¶
WithBasePath sets the admin API base path (default "/admin").
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 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
- authn.go
- authn_middleware.go
- cache_admin.go
- capabilities.go
- commands.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
- timeseries.go
- vector_admin.go
- version.go