Filtering and Lookup
Beyond HEAD snapshots, WarmHub provides targeted query functions for specific lookups, filtered searches, history, and batch operations.
Get by Wref
Section titled “Get by Wref”Fetch a single thing by its exact wref. Use this when you know the reference and want its current state or a pinned version.
wh thing view Location/cavewh thing view Location/cave --version 3{ "name": "warmhub_thing_get", "arguments": { "wref": "Location/cave", "version": 3 }}The HTTP API does not currently mount a single-wref thing lookup route. Use the CLI, SDK, or MCP surfaces for direct wref lookups.
Each result includes the thing’s core identity fields (name, wref, shapeName, kind, version, active), its data payload, and a metadata envelope containing durableId, createdOn, and revisedOn. Assertions also include an aboutWref field. The committerWref field is present when the originating write recorded a committer identity. The createdBy field, when present, carries the immutable creator attribution recorded at the time the thing was first written. The revisedBy field, when present, identifies the author of the version that was actually returned — which is the current HEAD version on an unversioned read, or the pinned version when a specific version was requested (e.g. --version 3).
Within data, fields typed as wref rehydrate on read: same-repo refs return their local form (e.g. Loc/cave@v1); cross-repo refs return their canonical form (e.g. wh:org/repo/Loc/cave@v1). When a cross-repo wref points at a thing in a soft-deleted source repo, the field is returned as null — the hidden repo’s identity is suppressed rather than leaked through the read surface. The same suppression applies to assertion aboutWref and other ref fields surfaced by /head, /about, and SDK reads.
Query by Filters
Section titled “Query by Filters”Query a repo by combinable filters such as shape, kind, about target, and wref glob. Use this when you don’t have an exact wref. Filters are optional and they stack.
# By shapewh thing query --shape Location
# By kindwh thing query --kind assertion
# By about target (assertions about a specific thing)wh thing query --about Location/cave
# Combined filterswh thing query --shape Observation --about Location/cave --limit 20
# Resolve through collections — include assertions about collections containing the targetwh thing query --about Location/cave --resolve-collectionswh thing query --shape Observation --about Location/cave --resolve-collections
# With glob pattern (match applies to full wrefs: Shape/name)wh thing query --shape Location --match "Location/dungeon/*"
# Count matching items (no pagination, returns { count: N })wh thing query --shape Location --countwh thing query --kind assertion --about Location/cave --countMCP note: The examples below assume a repo-scoped
/mcp/<org>/<repo>endpoint. If you are connected to the global/mcpendpoint, you must also passorgNameandrepoNamein theargumentsobject.
{ "name": "warmhub_thing_query", "arguments": { "shape": "Observation", "about": "Location/cave", "kind": "assertion", "match": "Observation/dungeon/*", "includeRetracted": false, "limit": 50 }}Count via MCP:
{ "name": "warmhub_thing_query", "arguments": { "shape": "Observation", "about": "Location/cave", "count": true }}The HTTP example below uses the public warmhub-data/us.congress.trades repo so you can copy-paste it directly; the CLI and MCP blocks above use generic Location/Observation names that you’d swap for your repo’s own shapes.
GET /api/repos/warmhub-data/us.congress.trades/query?shape=CongressTrade&kind=thing&match=CongressTrade%2F20034954%2F*&limit=25Anonymous callers reading public repos are capped at limit=25 per page; authenticated callers can request up to limit=500. See Anonymous Pagination Caps.
All filter parameters are optional. Combine them to narrow results. The HTTP API does not currently mount a count-only route; use CLI/MCP count surfaces for count-only reads.
Field-Value Predicates
Section titled “Field-Value Predicates”Shape, kind, about target, and wref glob narrow by a record’s identity. To filter on the values of fields inside a thing’s data — state, severity, a nested address.county — add where predicates.
where is supported across the typed read surfaces: the CLI (wh thing query, wh thing list), the SDK (client.thing.query, thing.head, thing.about, thing.count), and MCP (warmhub_thing_query, warmhub_thing_head, warmhub_thing_about). No HTTP read route parses where, and thing.search does not accept it — use thing.query for structured field filtering.
# Equalitywh thing query --shape Observation --where "status=active"
# Numeric comparison (also >, <, <=, !=)wh thing query --shape Observation --where "severity>=3"
# Prefix match on a string field (trailing * is optional)wh thing query --shape Location --where "region~north"
# Set membership — value is one of the listwh thing query --shape Location --where "biome in:[forest,desert,tundra]"
# Field existencewh thing query --shape Observation --where "resolvedAt?"
# Multiple predicates are ANDed (up to 8 per query)wh thing query --shape Observation --where "status=active" --where "severity>=3"Each --where flag is one predicate, written as a field path, an operator, and (except for exists) a value:
| CLI form | Operator | Matches |
|---|---|---|
field=value | eq | equal |
field!=value | ne | not equal |
field>value / field>=value | gt / gte | greater than (or equal to) |
field<value / field<=value | lt / lte | less than (or equal to) |
field~prefix | prefix | string or wref field starts with prefix |
field in:[a,b,c] | in | value is one of the list (1–1000 values) |
field? | exists | field is present |
The SDK and MCP take the same predicates as structured objects — { fieldPath, op, rhs }, where rhs is a scalar for the scalar operators, an array for in, and omitted for exists:
// SDKconst result = await client.thing.query('acme', 'world', { shape: 'Observation', where: [ { fieldPath: 'status', op: 'eq', rhs: 'active' }, { fieldPath: 'severity', op: 'gte', rhs: 3 }, ],})// MCP equivalent{ "name": "warmhub_thing_query", "arguments": { "shape": "Observation", "where": [ { "fieldPath": "status", "op": "eq", "rhs": "active" }, { "fieldPath": "severity", "op": "gte", "rhs": 3 } ] }}Notes:
whererequires a shape and can’t be combined with a globmatchorincludeRetracted. Each predicate resolves against a shape’s typed fields, so the query must set a shape (--shape/shape). Combiningwherewith a--match/matchglob pattern or with--include-retracted/includeRetractedreturns a field-index error rather than running.- Predicates combine as AND, up to 8 per call. Field paths are dotted for nested fields (
address.county). - You can filter on scalar fields a shape declares —
string,number,boolean, and reference (wref) fields. Fields that hold objects or arrays aren’t filterable, and a predicate on a field that isn’t available for filtering fails with an error naming the field, rather than silently returning no matches. - Value typing follows the field’s declared type. The CLI keeps numeric-looking strings (
"42") and ISO date strings as strings and lets the field’s type classify them, but parses baretrue/falseas booleans — quote them (field="true") to match literal text. In the SDK and MCP, pass the scalar as the type you want (3versus"3"). - Execution budget: typed
wherereads are execution-bounded. If a broad predicate or a deep page exhausts the query budget, the call fails withQUERY_TOO_EXPENSIVE. When you see that error, narrow the predicate (add more filters or tighten the value range) or request a shallower page. The same budget applies to count-only reads (--count/count: true).
Assertion-domain field filtering
Section titled “Assertion-domain field filtering”wh assertion list also accepts an equality shorthand for field filtering: --field data.path=value. This uses the same indexed field filtering path as wh thing query, and works for unscoped lists, target-scoped lists (--about), and --count. Only equality (=) is supported — for other operators, use wh thing query --shape <YourAssertionShape> --kind assertion --where.
# Equality filter on an assertion data fieldwh assertion list --field data.status=active
# Combined with a target scopewh assertion list --about Location/cave --field data.severity=3
# Count onlywh assertion list --field data.status=active --countField paths must use the data. prefix. The filter is equality-only on this surface.
About Queries
Section titled “About Queries”List the assertions that target a specific thing. Use this when you have a thing’s wref and want the assertions made about it:
wh thing about Location/cavewh thing about Location/cave --shape Observationwh thing about Location/cave --match "Observation/*"
# Include assertions about collections containing the targetwh thing about Location/cave --resolve-collectionswh assertion list --about Location/cave remains available when you are already working in the assertion domain.
Collection Resolution
Section titled “Collection Resolution”By default, --about only returns assertions that directly target the specified thing. To also include assertions about collections (Arc, Bond, Set, List) that contain the thing, add --resolve-collections:
wh thing about Location/cave --resolve-collectionswh assertion list --about Location/cave --resolve-collectionswh thing query --about Location/cave --resolve-collectionswh thing history --about Location/cave --resolve-collectionswh thing search "safe" --about Location/cave --resolve-collectionsCollection resolution looks up current (HEAD) collection memberships. It is not supported with --mode vector or --mode hybrid search.
Search pagination caveat: when combining search with --about or --resolve-collections, pages may be sparse — a page may contain fewer items than limit, or even zero items, while nextCursor is still non-null. Keep paginating until nextCursor is absent to collect all results.
The --depth flag retrieves child assertions about the returned assertions:
wh thing about Location/cave --depth 2{ "name": "warmhub_thing_about", "arguments": { "wref": "Location/cave", "shape": "Observation", "match": "Observation/*", "includeRetracted": false, "depth": 2 }}The response includes a target object (the thing being queried) and an assertions array. Both the target object and each entry in assertions include a metadata envelope with durableId, createdOn, and revisedOn. With depth > 1, each assertion may include a children array.
Foreign assertions on target-side reads
Section titled “Foreign assertions on target-side reads”When the queried target belongs to the addressed repo, thing.about (CLI wh thing about, SDK client.thing.about, and MCP warmhub_thing_about) also aggregates readable foreign assertions: assertions stored in other repos that the caller has repo:read access to and that target the same thing. These foreign assertions appear inline in the assertions array with their canonical wh:org/repo/... wrefs, so you can distinguish them from assertions stored in the addressed repo.
Version History
Section titled “Version History”See every version of a thing, or filter history by shape or about target. Use this when you need how an item changed over time, not just its current state:
wh thing history Location/cavewh thing history Location/cave --limit 10Query history by shape or about filters (without a specific wref):
wh thing history --shape Observation --limit 20wh thing history --about Location/cave --limit 5
# Include collection assertions in history filteringwh thing history --about Location/cave --resolve-collections{ "name": "warmhub_thing_history", "arguments": { "wref": "Location/cave", "limit": 10 }}At least one of wref, shape, or about is required. Each version entry includes: version, operation (add/revise/retract), active, createdAt, committerWref — the committer’s local or canonical wref when the originating write recorded one, omitted otherwise — and metadata.durableId and metadata.createdOn.
Cross-repo visibility
Section titled “Cross-repo visibility”Cross-repo wref lookups require effective repo:read permission on the target repo. Public repos are readable by anyone. For private repos, callers without that access see an error — except cross-repo search and batch lookup, which fold unreadable results into { items: [] } or missing[] entries to keep search and batch streaming-friendly.
See Getting Access for the precise rules.
Wref Resolution
Section titled “Wref Resolution”Resolve a shape or shaped-thing wref to its canonical identity. Use this when you have a local or version-relative reference and need its stable canonical form — CLI and SDK also return the resolved version’s data:
wh thing resolve Location/cave{ "name": "warmhub_wref_resolve", "arguments": { "wref": "Location/cave" }}- CLI
wh thing resolveand SDKclient.thing.resolve(...)return the same payload asthing.get. The default CLI render shows identity columns only; pass--jsonfor the full payload. - MCP
warmhub_wref_resolvereturnsname,kind,active,version,shapeName, and themetadataidentity/timestamp envelope. Pair withwarmhub_thing_getto fetch data.
Batch Lookup
Section titled “Batch Lookup”Fetch shapes or shaped things by wref in a single call. On the CLI, wh thing view is variadic — pass multiple wrefs or use --file to trigger batch mode. Also available via the TypeScript SDK (client.thing.getMany) and MCP.
The CLI enforces a 500-wref cap per call, applied after deduplication — so repeated wrefs across positional args, --file, and stdin are collapsed before the cap is checked. The MCP enforces a 500-wref cap on the raw wrefs array length as supplied, before any deduplication. The SDK accepts any number of wrefs and automatically chunks requests above the 500-wref backend transport cap, so you do not need to manually split large lists when using the SDK.
The CLI examples below use a couple of wref conventions: Shape/name reads HEAD, Shape/name@v3 pins to version 3, and --file=- is the standard Unix marker for “read newline-delimited input from stdin.”
# CLI — wrefs come from positional args, --file <path>, or piped stdin (any combination)wh thing view Location/cave Location/forest Player/alicecat wrefs.txt | wh thing view # one wref per line, piped stdinwh thing view --file wrefs.txt --version 1 # newline-delimited file, pin all to @v1wh thing view --file=- < wrefs.txt --format jsonl # one JSON line per *deduped* requested wref// SDK — accepts any number of wrefs; auto-chunks above 500const result = await client.thing.getMany( 'acme', 'world', ['Location/cave', 'Location/forest', 'Player/alice'], 1, // optional fallback version for unpinned wrefs { includeRetracted: true }, // optional — return retract versions when reading a historical version (otherwise retract versions are excluded))// result: { requested, items, missing }// MCP equivalent{ "name": "warmhub_thing_get_many", "arguments": { "orgName": "acme", "repoName": "world", "wrefs": ["Location/cave", "Location/forest", "Player/alice"], "version": 1 }}Shared across CLI, SDK, and MCP:
- All three surfaces return
{ requested, items, missing }:requestedis a number — the count of input wrefs the call processed. The CLI unions positional +--file+ stdin inputs and dedupes them before the round-trip, so a wref supplied more than once counts only once toward bothrequestedand the 500-wref cap. The SDK and MCP forward thewrefsarray as-is, so duplicates produce duplicateitems/missingentries; on MCP, each entry in the supplied array counts toward the 500-wref cap.items[]carries the resolved entities — same fields as a single-thing read, including themetadataenvelope withdurableId,createdOn, andrevisedOn, and the optionalcreatedByandrevisedByattribution fields.missing[]isstring[]— wrefs that don’t exist or that the caller can’t read, returned instead of throwing.
- Missing entries are version-qualified when a top-level
version/--versionwas supplied and the wref didn’t already carry a version modifier (@vNor@HEAD). Per-wref pins always survive intact (no double-pinning). - The CLI enforces a 500-wref cap after dedupe; MCP enforces a 500-wref cap on the raw supplied array. The SDK auto-chunks above that cap.
CLI-only:
-
--versionimplies--include-retracted, so retract versions can be retrieved by their pinned id (mirrorswh thing view --version). Pass--include-retractedexplicitly when you want retract versions without a fallback--version. -
--format jsonlemits one row per deduped requested wref, in input order. (Inputs from positionals +--file+ stdin are unioned and deduped before the round-trip, so a wref supplied twice produces one row.) Use it for shell pipelines where you want to filter or fan out the result. Use--jsoninstead when you want the full{requested, items, missing}envelope as a single object.Each row is
{requested, found, wref, ...}.requestedis the input wref preserved verbatim (so canonical/cross-repo inputs are still identifiable on the consumer side),wrefis the local form for hits or the version-qualified form for misses, andfoundis the boolean. -
--liveis rejected (batch reads are one-shot — usewh thing view <wref> --livefor per-thing polling).
Search
Section titled “Search”Search things by text content. Three modes are available:
- text (default) — full-text search against thing names, shape names, and data fields. Supports pagination.
- vector — semantic similarity search using embeddings. No pagination; does not support
--aboutfilter. - hybrid — runs text and vector searches in parallel, merges results with reciprocal rank fusion. No pagination.
# Full-text search (default mode)wh thing search "safe location" --shape Observation
# Semantic similarity searchwh thing search "places that are dangerous" --mode vector
# Hybrid search — combines text and vector resultswh thing search "policy" --mode hybrid --limit 10
# Filter by about target (text mode only)wh thing search "safe" --about Location/cave
# Resolve through collections (text mode only)wh thing search "safe" --about Location/cave --resolve-collections
# Paginate through text results# NOTE: when using --about or --resolve-collections, pages may be sparse —# a page may return fewer items than --limit (or even zero) while nextCursor# is still present. Paginate until nextCursor is absent to collect all results.wh thing search "policy" --limit 50 --cursor <token>
# Fetch all pages automatically (text mode only)wh thing search "policy" --allMCP note: The example below assumes a repo-scoped
/mcp/<org>/<repo>endpoint. If you are connected to the global/mcpendpoint, you must also passorgNameandrepoNamein theargumentsobject.
{ "name": "warmhub_thing_search", "arguments": { "query": "safe location", "shape": "Observation", "mode": "hybrid", "limit": 10 }}Search is available via the CLI, SDK (client.thing.search()), and MCP (warmhub_thing_search) but is not currently exposed as an HTTP endpoint.
Anonymous search narrowing: anonymous callers (unauthenticated requests to public repos) are subject to narrowed paging on all thing.search calls: limit is capped at 25 and an omitted limit defaults to 25. Text-mode paging additionally stops after two pages — once that boundary is crossed, the backend returns UNAUTHENTICATED (Sign in to keep paging). Reducing limit will not resolve that error; you need to authenticate to continue paginating.
Reactive Mode
Section titled “Reactive Mode”Most CLI queries support --live for real-time updates:
wh thing query --shape Location --livewh thing history Location/cave --livewh thing about Location/cave --liveThis re-runs the query periodically and re-renders whenever the underlying data changes.