Producers
A captured producer makes a graph label fetchable on demand by calling one of the realm’s own handlers.
producers: { "incidents-by-service": { handler: "oncall.incidents", keyArgument: "serviceIds", joins: [ { targetLabel: "Incident", anchorLabel: "Service", relationship: "HAS_INCIDENT", keyField: "serviceId", recordKeyField: "serviceId" }, ], page: { argument: "cursor", maxPages: 4 }, pushdown: [ { property: "impact", argument: "impact" }, { property: "status", argument: "status" }, ], },},The handler contract. The handler receives an object holding the anchor keys as a list of
strings under keyArgument. Without page, it returns an array of record objects. Each record
carries its anchor’s key under recordKeyField, which is how the host links it back. Records may
not supply userId, worldId, workspaceId, visibleTo or any property starting with __vc;
the host owns those.
import type { IncidentsHandler } from "../.embabel/realm.d.ts";
interface IncidentPage { items: Record<string, unknown>[]; nextCursor?: string;}
export const incidents: IncidentsHandler = async (input, ctx) => { const page = (await ctx.gateway.statuspage.listIncidents({ services: input.serviceIds.join(","), impact: input.impact?.join(","), // present only when the query pinned impact status: input.status?.join(","), after: input.cursor, // absent on the first page })) as IncidentPage; return { rows: page.items, next: page.nextCursor ?? null };};Joins. 1 to 8 distinct joins, each naming the label it produces (targetLabel), the stored
label it starts from (anchorLabel), the relationship, the anchor property whose values become
keys (keyField), and the record field carrying the key back (recordKeyField).
Paging. With page, the handler returns {rows, next}. The host calls it again with
next under page.argument until next is null or maxPages (1 to 16) is reached. A repeated
cursor, a cursor over 2048 bytes, or a non-null next on the last allowed page refuses the whole
fetch; the host never returns a truncated result. The details are in the
paging profile.
Pushdown. Each rule maps a record property to a handler argument. When a query pins the
property with = or IN, the handler receives the allowed values as a list of strings under
that argument, on every page. The graph applies every filter to the returned rows again, so
pushdown changes cost and never answers. Declare a rule for every property your source can
filter on. A filter left undeclared makes the handler fetch every record the key allows, and
the answer is identical, so nothing tells you it happened. Where the host shows query
diagnostics, they name the arguments that reached the handler on each call; a filter missing
from that list is the next rule to declare. See the pushdown profile.
Names and arguments. The producer’s key is [a-z][a-z0-9._-]{0,63}. keyArgument, every
join field and every pushdown property are identifiers, [A-Za-z_][A-Za-z0-9_]{0,63}.
page.argument and every pushdown argument match [a-z][A-Za-z0-9]{0,63}, and no two of the
key, page and pushdown arguments may share a name. At most 16 pushdown rules, each listed once.
handler must be a handler the realm declares: defineRealm checks it at compile time, and synth
refuses one the realm does not declare, since the host refuses the producer. If the handler’s
input schema sets additionalProperties: false, list
the page and pushdown arguments in its properties, or the host refuses the producer at bind
time.
Reaching a producer. A query reaches the target label only by traversing one of its joins
from a bound anchor: a stored node pinned by a value or narrowed by a predicate. A bare
MATCH (i:Incident) is refused. A producer’s target may anchor another producer’s join, so
producers chain, and the engine stages each hop after the one before it. See
Virtual Cypher §2.
Limits and scope. Each fetch is at most 256 keys of up to 2048 characters, 64 KiB of arguments, 1 MiB of output and 1,024 rows, across all pages together. A refused fetch carries a reason code. Producer results are never cached. Only producers from the querying handler’s own installation run, each as a nested call that needs that handler’s grants as well as its own.

