Fabriq is the single data fabric for your application and its agents. Application code never opens a connection, writes SQL, or calls Redis directly — it holds one facade and reaches every store through typed capability ports. AI agents reach the same fabric as a brain: fused semantic, lexical, and graph recall, guarded memory formation, and a live watch stream — in-process or over MCP. Both ride one small set of invariants that are otherwise impossible to hold across many engines.
The three invariants01
Every command runs in a Postgres transaction that appends exactly one versioned event to a transactional outbox. A leader-elected relay publishes it to Redis Streams. Delivery-on-commit: the event can never outrun the data.
Tenant rides on
<code>
context.Context
</code><code>
SET LOCAL
</code>The knowledge graph and the search index are
<em>
derived
</em>Architecture02
Postgres is the source of truth. Redis is the event fan-out. FalkorDB and
Elasticsearch are derived projections. The kernel in core/ is
domain-agnostic and engine-agnostic; engine dialects live exclusively under
adapters/.
Capability ports03
There is deliberately no unified query language. Each storage capability has an explicit, engine-typed port. Relational work speaks SQL through grove, graph work speaks openCypher, search speaks queries over declared fields. No engine types appear in any signature, so adapters stay swappable.
| Port | Engine | Purpose |
| Relational | Postgres (grove) | The source of truth: aggregate rows, raw SQL escape hatch. |
| Graph | FalkorDB | Knowledge-graph projection, openCypher traversal + batched hydration. |
| Search | Elasticsearch | Full-text projection over declared fields. |
| Timeseries | TimescaleDB | Bulk telemetry ingest and windowed reads (event-bypass). |
| Vector | pgvector | Embedding upsert and nearest-neighbour search. |
| Spatial | PostGIS | Geometry upsert/delete and GiST-accelerated radius search. WKT+SRID, engine-neutral. SRID 4326 → true metres (geography); other SRIDs → planar metres. |
| Document | Postgres + grove CRDT | Collaborative CRDT documents that materialize into ordinary events. |
| Blob | Object store (via Trove) | External byte storage as f.Blob(): put/get/head/delete/list/copy, with presign, multipart, and range detected per driver, plus content-addressable dedup. Bytes never touch Postgres. |
Reads over opted-in entities are transparently cached — a two-level (per-id rows + query result-sets), write-invalidated read-through cache behind these ports, with an optional per-node in-process L1. Off by default; a registration flag, not a code change.
Files and blobs04
Beyond structured records, fabriq has a full file plane.
The catalog — files, folders, permissions, shares, and bookmarks — lives in
Postgres as ordinary tenant-scoped entities, so it keeps every fabriq invariant:
versioned events, RLS, graph and search projections, and live queries. The raw
bytes live in an external object store reached through the f.Blob() port and
never touch Postgres — content-addressed, reference-counted, and reclaimed by a
reconciler-driven garbage collector. It is shipped dark: the blob port is nil
until a storage driver is configured.
ADR 0008 amends ADR 0007 with one bounded exception — referenced external blobs — fenced by three disciplines: opaque (no catalog authority), checksum-verifiable, and reconciled rather than rebuilt. See the File Plane overview.
Cross-tenant analytics05
Catalog mode physically isolates each tenant in its own database — which makes
fleet-wide reporting impossible with an ordinary query. The optional
analytics sink closes that gap: a third projection,
peer to graph and search, that consumes the same shared event stream and
materializes a denormalized, cross-tenant read model in one Postgres database
keyed by tenant_id. Operators run fleet-wide SQL without ever touching a
per-tenant database, in single, sharded, and catalog modes alike.
It is the one place fabriq deliberately co-locates data from many tenants, so it
is fenced as a trust boundary: deny-by-default (an aggregate is analyticized
only when explicitly marked), field-level redaction (only allow-listed
columns cross), a separate database and credential (rejected at boot if it
collides with a tenant DSN), and tenant_id NOT NULL on every row. Off until
Config.Analytics is set.
The analytics store is operator-only: it has no RLS and holds many tenants' data side by side. Never expose it to tenant-facing surfaces, and mark only the fields that genuinely need fleet-wide reporting. See the Analytics sink trust-boundary section.
A first taste06
reg := registry.New()
_ = domain.RegisterAll(reg) // or your own entity pack
f, stores, err := fabriq.Open(ctx, reg, fabriq.Config{
Postgres: fabriq.PostgresConfig{DSN: dsn},
Redis: fabriq.RedisConfig{Addr: redisAddr},
})
// Writes: the only path, one versioned event per command.
res, err := f.Exec(tenantCtx, command.Command{
Entity: "asset", Op: command.OpCreate,
Payload: &domain.Asset{Name: "Pump 7", SiteID: siteID},
})
// Reads: capability ports.
var a domain.Asset
err = f.Relational().Get(tenantCtx, "asset", res.AggID, &a)
// Live deltas: server-resolved channel, conflated, resumable.
deltas, err := f.Subscribe(tenantCtx, query.SubscribeScope{
Entity: "asset", Scope: "site", ID: siteID,
})
// Live query: a maintained, ordered, filtered window with
// enter/leave/move/update deltas and exact top-N.
snap, live, cancel, err := f.LiveQuery(tenantCtx, livequery.LiveQuery{
Entity: "asset",
Where: query.Where{query.Eq("kind", "pump")},
Sort: []livequery.SortKey{{Column: "name"}},
Limit: 50,
})Every call requires a tenant-stamped context (tenant.WithTenant), set only
by auth middleware from validated claims. An unstamped context is rejected
before it reaches a store.
Embed or serve07
By default a service embeds fabriq and owns its datastore pools. An optional
remote topology lets backend services instead talk to a central,
connection-owning fabriq over gRPC — through the same query.Fabric interface,
so call sites are identical.
The remote protocol is experimental / in development: write, read,
subscribe, live-query, blob, and the graph/search/vector retrieval channels (so
an agent's recall works remotely) are wired and tested over real gRPC + mTLS.
Interactive transactions are a non-goal (ExecBatch is the transaction). See
Remote protocol.
An agent's brain08
An AI agent can use fabriq as its brain through the
agent toolkit: one recall(query, budget) that fuses
semantic, lexical, and graph retrieval into a token-budgeted context pack;
guarded memory formation with auto-embedding on write; and a live watch stream —
exposed in-process to Go agents and over MCP to any agent.
tk, _ := agent.NewToolkit(f, f.Registry(), embedder, agent.Config{})
pack, _ := tk.Recall(tenantCtx, agent.RecallRequest{
Query: "overheating pumps at the north site", Budget: 8000,
Entities: []string{"asset", "note"},
})The admin console09
Fabriq ships a mountable, plugin-based web admin console over a running instance — one interactive surface per subsystem: browse and edit entities, author schemas, run text / semantic / hybrid search, traverse the graph, manage files and CRDT documents, inspect the outbox, and run read-only SQL — all tenant-scoped.

It reaches an instance through the adminapi HTTP surface, so any client can connect the
same way: with a portable connection string —
fabriq://<key>@host/<tenant> — consumed by connect() in TypeScript and a full
adminapi-mirror client package in Go. Both are verified by an opt-in
authentication layer: per-tenant API keys for machine clients,
and a username/password dashboard login for humans that mints a short-lived session
token validated by the same key middleware.
Where to go next10
A web dashboard over every subsystem, a portable connection string, and opt-in API-key + dashboard-login authentication.
Install fabriq, wire the facade, and run your first command, query, and subscription.
The facade, the registry, tenancy layers, the command/outbox plane, projections, and the delta plane.
Per-port guides: relational, graph, search, timeseries, vector, spatial, and the CRDT document plane.
Files, folders, shares, and blobs: a Postgres catalog over an external object store, with content-addressable dedup and reconciler-driven GC.
Use fabriq as an agent's brain: fused recall, guarded memory, a live watch stream, and an MCP interface for any agent.
Deploy the worker, run migrations, rebuild and reconcile projections, and read the metrics and runbooks.
Fleet-wide reporting over db-per-tenant deployments: an opt-in, field-redacted read model fed by the shared event stream, co-located behind a documented trust boundary.
Fabriq is built on the Forge ecosystem: storage on grove, binaries on forge (apps) and forge/cli.