Documentation
¶
Overview ¶
Package domain is the TWINOS domain pack: the only fabriq package with TWINOS-specific knowledge. It seeds three entities that exercise every fabric capability — site (plain aggregate), asset (graph edges), tag (telemetry metadata whose readings bypass per-row events).
Index ¶
Constants ¶
const ReadingsSeries = "tag_readings"
ReadingsSeries is the Timescale hypertable telemetry readings land in. Readings are written through Timeseries().BulkWrite — the event-bypass path: no per-row outbox events; the worker publishes conflated deltas.
Variables ¶
This section is empty.
Functions ¶
func PagesDDL ¶ added in v0.0.3
func PagesDDL() []string
PagesDDL returns the DDL that creates the demo "pages" materialization target (table + tenant index + tenant-isolation RLS). It is deliberately NOT part of fabriq's shipped migration chain: materialization targets are application entities, and a library must never create a generically-named "pages" table in a host database (it would collide with the app's own table). Examples and the document-plane integration tests apply this as the table owner before provisioning the app role. The statements mirror fabriq's standard tenant-isolation pattern (ENABLE+FORCE RLS, USING/WITH CHECK on app.tenant_id).
func RegisterAll ¶
RegisterAll registers the TWINOS domain pack. Call it once at startup; follow with reg.Validate() (fabriq.New does both).
Types ¶
type Asset ¶
type Asset struct {
grove.BaseModel `grove:"table:assets"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenant_id"`
Version int64 `grove:"version,notnull" json:"version"`
Name string `grove:"name,notnull" json:"name"`
Kind string `grove:"kind" json:"kind"` // pump, valve, motor, ...
Serial string `grove:"serial" json:"serial"`
SiteID string `grove:"site_id" json:"site_id"`
ParentID string `grove:"parent_id" json:"parent_id"`
}
Asset is a piece of industrial equipment. It carries both graph edges: LOCATED_AT -> Site and CHILD_OF -> Asset (equipment hierarchy).
type BlobObject ¶
type BlobObject struct {
grove.BaseModel `grove:"table:blob_objects"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
ScopeID string `grove:"scope_id" json:"scopeId"`
Version int64 `grove:"version,notnull" json:"version"`
Hash string `grove:"hash,notnull" json:"hash"`
Size int64 `grove:"size,notnull" json:"size"`
ContentType string `grove:"content_type" json:"contentType"`
}
BlobObject is the catalog row for a stored blob. Many BlobObjects may share a Hash (content dedup); the bytes live in the object store, the ref-count in blob_cas.
type BlobSource ¶
type BlobSource struct {
grove.BaseModel `grove:"table:blob_sources"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
ScopeID string `grove:"scope_id" json:"scopeId"`
Version int64 `grove:"version,notnull" json:"version"`
ProjectID string `grove:"project_id" json:"projectId"`
Name string `grove:"name,notnull" json:"name"`
Provider string `grove:"provider" json:"provider"`
Endpoint string `grove:"endpoint" json:"endpoint"`
BasePath string `grove:"base_path" json:"basePath"`
AuthEnc []byte `grove:"auth_enc" json:"-"`
WatchConfig map[string]any `grove:"watch_config" json:"watchConfig"`
FileFilter map[string]any `grove:"file_filter" json:"fileFilter"`
Tags map[string]string `grove:"tags" json:"tags"`
Enabled bool `grove:"enabled" json:"enabled"`
}
BlobSource is a connection record for an external storage provider. The auth credentials are stored ONLY as ciphertext (auth_enc); the plaintext map never touches a column.
type DigestNode ¶
type DigestNode struct {
grove.BaseModel `grove:"table:digest_nodes"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
Version int64 `grove:"version,notnull" json:"version"`
Level int `grove:"level,notnull" json:"level"`
Kind string `grove:"kind,notnull" json:"kind"`
ScopeName string `grove:"scope_name" json:"scopeName"`
ScopeID string `grove:"scope_id" json:"scopeId"`
SourceID string `grove:"source_id" json:"sourceId"`
SourceKind string `grove:"source_kind" json:"sourceKind"`
SummaryHash string `grove:"summary_hash" json:"summaryHash"`
ContentHash string `grove:"content_hash" json:"contentHash"`
SemHash string `grove:"sem_hash" json:"semHash"` // 16-hex
ChildIDs []string `grove:"child_ids" json:"childIds"` // JSONB
ParentIDs []string `grove:"parent_ids" json:"parentIds"` // JSONB
UpdatedAt int64 `grove:"updated_at" json:"updatedAt"` // unix nanos
Tokens int64 `grove:"tokens" json:"tokens"` // cached summary token count
}
DigestNode is one node of a tenant's context-distillation Merkle tree. Levels: 0 = entity (per source row), 1 = scope/cluster backbone, 2 = tenant root. The summary text is content-addressed in the file-plane CAS (SummaryHash); ContentHash is the Merkle freshness key; SemHash is the SimHash fingerprint (16-hex). The vector lives in the vector plane (entity "digest_node"), not here.
type FsBookmark ¶
type FsBookmark struct {
grove.BaseModel `grove:"table:fs_bookmarks"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
ScopeID string `grove:"scope_id" json:"scopeId"`
Version int64 `grove:"version,notnull" json:"version"`
UserID string `grove:"user_id,notnull" json:"userId"`
NodeID string `grove:"node_id,notnull" json:"nodeId"`
SortOrder int `grove:"sort_order" json:"sortOrder"`
CreatedAt time.Time `grove:"created_at" json:"createdAt"`
}
FsBookmark is a user's bookmark of an fs_node (a favourite), with a per-user sort order. Unique per (tenant, user, node).
type FsNode ¶
type FsNode struct {
grove.BaseModel `grove:"table:fs_nodes"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
ScopeID string `grove:"scope_id" json:"scopeId"`
Version int64 `grove:"version,notnull" json:"version"`
ParentID string `grove:"parent_id" json:"parentId"` // ""=root
Name string `grove:"name,notnull" json:"name"`
Path string `grove:"path" json:"path"`
NodeType string `grove:"node_type,notnull" json:"nodeType"` // "folder" | "file"
BlobID string `grove:"blob_id" json:"blobId"`
Size int64 `grove:"size" json:"size"`
ContentType string `grove:"content_type" json:"contentType"`
Checksum string `grove:"checksum" json:"checksum"`
IsLocked bool `grove:"is_locked" json:"isLocked"`
LockedBy string `grove:"locked_by" json:"lockedBy"`
Metadata map[string]any `grove:"metadata" json:"metadata"`
MountConfig map[string]any `grove:"mount_config" json:"mountConfig"`
DeletedAt *time.Time `grove:"deleted_at" json:"deletedAt"`
DeletedBy string `grove:"deleted_by" json:"deletedBy"`
CreatedAt time.Time `grove:"created_at" json:"createdAt"`
UpdatedAt time.Time `grove:"updated_at" json:"updatedAt"`
}
FsNode is a filesystem catalog node (folder or file) in the tree over the blob plane. parent_id is the adjacency truth; path is a materialized index maintained transactionally on move/rename. File nodes reference a blob_object (blob_id) with denormalized facets.
Metadata and MountConfig are JSONB NOT NULL columns. Nil maps must be normalised before any INSERT or UPDATE to avoid a NOT NULL constraint violation. The hooks below fire because the command plane reaches Postgres through pgdriver.PgTx.NewInsert / NewUpdate — grove's model-mutation API — which calls hook.RunModelBeforeInsert / RunModelBeforeUpdate on the model prior to executing the statement (adapters/postgres/tx.go ApplyChange).
func (*FsNode) BeforeInsert ¶
BeforeInsert implements grove/hook.BeforeInsertHook. It fires via pgdriver.PgTx.NewInsert (called by adapters/postgres/tx.go ApplyChange) before the INSERT executes, normalising nil JSONB maps so the mount_config/metadata NOT NULL constraint is never violated.
func (*FsNode) BeforeUpdate ¶
BeforeUpdate implements grove/hook.BeforeUpdateHook for the same reason: pgdriver.PgTx.NewUpdate (also in ApplyChange) fires this before the UPDATE.
type FsPermission ¶
type FsPermission struct {
grove.BaseModel `grove:"table:fs_permissions"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenantId"`
ScopeID string `grove:"scope_id" json:"scopeId"`
Version int64 `grove:"version,notnull" json:"version"`
NodeID string `grove:"node_id,notnull" json:"nodeId"`
PrincipalType string `grove:"principal_type" json:"principalType"` // user|role|team
PrincipalID string `grove:"principal_id" json:"principalId"`
Permission string `grove:"permission" json:"permission"` // read|write|delete|admin
GrantedBy string `grove:"granted_by" json:"grantedBy"`
CreatedAt time.Time `grove:"created_at" json:"createdAt"`
}
FsPermission is an ACL grant on an fs_node. Append-only: grant = insert, revoke = delete. Enforcement (effective-permission evaluation) lives in the consuming seam, not in fabriq.
type FsShare ¶
type FsShare struct {
}
FsShare is a share-link record for an fs_node. fabriq stores it verbatim; the seam generates the token, hashes the password, and enforces expiry/cap/password.
type Link ¶
type Link struct {
grove.BaseModel `grove:"table:links"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenant_id"`
Version int64 `grove:"version,notnull" json:"version"`
Kind string `grove:"kind,notnull" json:"kind"` // relationship type
SourceID string `grove:"source_id,notnull" json:"source_id"`
TargetID string `grove:"target_id,notnull" json:"target_id"`
Note string `grove:"note" json:"note"`
}
Link is a generic reified relationship between two assets, used to exercise fabriq's reified-edge projection with the demo domain's own vocabulary.
type Page ¶
type Page struct {
grove.BaseModel `grove:"table:pages"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenant_id"`
Version int64 `grove:"version,notnull" json:"version"`
Title string `grove:"title" json:"title"`
Body string `grove:"body" json:"body"`
}
Page is the KindDocument demo entity: a collaborative page-builder document. Its row is written ONLY by document-plane materialization (the command plane rejects document kinds); after the quiet window the merged CRDT state lands here with version++ and one ordinary page.updated event, so projections, search and audit see pages as normal entities.
type Site ¶
type Site struct {
grove.BaseModel `grove:"table:sites"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenant_id"`
Version int64 `grove:"version,notnull" json:"version"`
Name string `grove:"name,notnull" json:"name"`
Code string `grove:"code" json:"code"`
Region string `grove:"region" json:"region"`
}
Site is a physical location (plant, facility) assets live at.
type Tag ¶
type Tag struct {
grove.BaseModel `grove:"table:tags"`
ID string `grove:"id,pk" json:"id"`
TenantID string `grove:"tenant_id,notnull" json:"tenant_id"`
Version int64 `grove:"version,notnull" json:"version"`
Name string `grove:"name,notnull" json:"name"`
Unit string `grove:"unit" json:"unit"` // °C, bar, rpm
Datatype string `grove:"datatype" json:"datatype"` // float, bool, int
AssetID string `grove:"asset_id" json:"asset_id"`
}
Tag is telemetry metadata: one measured point on an asset. The tag row itself is an ordinary aggregate (created/updated/deleted events); its readings are not.