crates/nornir-warehouse-embed/proto/warehouse.proto
// A TRIMMED copy of nornir-server's `Warehouse` generic-browser READ surface —
// the `Tables` (list) + `Scan` (one-table preview) verbs. Package `nornir.v1` so
// the generated method paths (`/nornir.v1.Warehouse/Tables` …) match the live
// `nornir-server` — a thin client READS nornir's Iceberg tables WITHOUT linking
// the `nornir` crate (a cycle) and WITHOUT opening nornir's `catalog.redb`
// (a 2nd redb write-lock → WOULDBLOCK/deadlock; the CATALOG DOCTRINE: nornir is
// the ONE live catalog, everyone else reads over gRPC). Field numbers are
// byte-identical to `nornir/proto/nornir.proto` (:931) so the wire is compatible.
//
// The `Empty` request type is the one already declared in `workspaces.proto`
// (same `nornir.v1` package) — imported here rather than re-declared.
syntax = "proto3";
package nornir.v1;
import "workspaces.proto";
// Generic warehouse browser: list tables + scan one to display rows. The server
// holds the redb lock, so clients go through here.
service Warehouse {
rpc Tables (Empty) returns (WarehouseTables);
rpc Scan (WarehouseScanRequest) returns (WarehouseScan);
// ScanVectors — the TYPED BULK leg, for the one column shape `Scan` structurally
// cannot carry. `Scan` stringifies every cell through ONE renderer whose catch-all
// is `format!("<{}>", arr.data_type())`, so a `list<float32>` column
// (`embeddings.vector`, 768 floats/row) arrives as the literal Arrow TYPE NAME —
// the same constant string on every row, carrying zero floats. Not truncated, not
// dropped: replaced by something merely shaped like a cell. That is why the dupes
// SEMANTIC tier and leafwatch's semantic leg were permanently unavailable on every
// thin client — hash tiers only, blind to "the same thing, written differently".
//
// Vectors here are raw little-endian IEEE-754 f32 bytes (4 B/float, exact), STREAMED
// in batches so a constellation-scale table never has to fit one message.
//
// UNIMPLEMENTED ⇒ this transport cannot carry vectors (pre-ScanVectors server).
// A stream that COMPLETES with zero vectors ⇒ the warehouse holds 0 embeddings.
// Those are different states and a consumer must render them differently.
rpc ScanVectors (WarehouseVectorRequest) returns (stream WarehouseVectorBatch);
}
message WarehouseTables { repeated string names = 1; }
message WarehouseScanRequest {
string table = 1;
uint32 limit = 2; // 0 = server default (~500 rows, newest-first)
}
message WarehouseRow { repeated string cells = 1; }
message WarehouseScan {
repeated string columns = 1;
repeated WarehouseRow rows = 2;
}
// Field numbers byte-identical to `edda/crates/nornir-wire/proto/nornir.proto`.
message WarehouseVectorRequest {
string table = 1; // "" = "embeddings"
string key_col = 2; // "" = "content_hash"
string vec_col = 3; // "" = "vector"
uint32 limit = 4; // 0 = every row (the semantic tier is all-pairs)
}
message WarehouseVector {
string key = 1; // content_hash
uint32 dims = 2;
bytes values = 3; // dims × little-endian f32; len MUST be dims*4
string model_profile = 4; // the embedding SPACE — cosines are not comparable across it
}
message WarehouseVectorBatch {
repeated WarehouseVector vectors = 1;
uint64 rows_scanned = 2; // cumulative SOURCE rows read
bool clipped = 3; // `limit` cut the scan short ⇒ INCOMPLETE
}