Module I — inventory & discovery
Module I is the catalog of observed entities in the estate: a passive, bus-driven inventory of agents, sessions, Claude Code instances, MCP servers, skills, tools, resources, models, providers and non-human identities that connectors have actually named. It discovers by listening, never by probing. It records relationships, identifiers and liveness — not payloads — and it is not a census of everything that exists. This page is the reference for what the catalog holds, how observation provenance and durable freshness work, and what the module deliberately does not claim.
Observation provenance and durable freshness are part of 26.10.
What it materializes
Section titled “What it materializes”Connectors emit observations, not entities. They publish normalized
edge.observed and cost.sampled
facts onto the event bus; the entities they imply are never sent. Module I
materializes the core entity each observation names from its natural
reference: an origin session/agent/identity, an MCP server, a tool, a
resource, a skill, and — from cost samples — a provider and a model
(discovered, without pricing; FinOps owns that). Inventory itself
subscribes to those two event types; live findings belong to
module II.
Core find-or-create on the natural key avoids duplicating the catalog alias
under at-least-once delivery in the current single-writer model. Two sources
can still keep distinct observations that share that alias; sharing it does
not prove they are the same physical thing, and it does not transfer owner,
workspace or grants. A new event id with the same payload is a new
receipt — whether that is a distinct activity stays unknown. The catalog’s
occurrence_count counts deliveries, including a replay of the same event
id, not unique activities.
Observation provenance
Section titled “Observation provenance”Alongside the catalog alias, the module stores an additive, tenant-scoped
projection of how an observation arrived: a receipt keyed by event id, a
member row per materialized entity (native reference and, when the
registration snapshot is valid, a stable observational identity), and a
conflict row when the same event id later carries different projected facts.
The original receipt is kept; the conflict is recorded rather than overwritten.
A missing event id gets only a storage identity and is never de-duplicated by
payload. Replay of the same id and same facts refreshes the legacy catalog
delivery count without re-resolving aliases. The console can list stored
receipts for one catalog entity at
GET /v1/m/inventory/entities/{kind}/{id}/observations with the existing
tenant-wide inventory:catalog:read permission: one item per distinct receipt
in ascending receipt-id order, pages of at most 25. Each item is a historical
registration snapshot recorded at reception (not current source registration
or health), the source-declared occurrence instant when declared, first and
last reception of the retained facts, matching-fact deliveries, and whether a
conflicting redelivery is retained. An empty page does not prove the entity
was never observed. has_more, an empty list and the catalog total are not
coverage. Catalog freshness on the entity sheet comes from a successful
current point read; it is a separate read from the history. Per-source/family
coverage and the C4 reference estate remain open.
Its contract & entities
Section titled “Its contract & entities”The module registers inventory.catalog_entry — a discovery overlay attached
to each materialized core entity. It records how a thing was found, not
what it did: signal sources, hosts when known, first- and last-seen
timestamps, an optional source-declared occurred_at (omitted when the source
declared none; distinct from last_seen, which is when this platform saw
it), an occurrence count, and a liveness status of active or stale. The
read surface is small and read-only: a summary count by kind and source, a
paginated entities listing filterable by kind and status, a
single-entity detail view, and the per-entity observation history above. Every read requires a tenant-scoped, namespaced
read permission (the lowest viewer tier suffices). Catalog and provenance
writes are high-frequency and not audited per write.
Durable freshness keeps a second overlay, inventory.freshness_sweep: at most
one lazily created row per tenant, holding the cutoff of an open cycle, the
opaque catalog cursor, and the last cutoff whose cycle finished. That last
cutoff records a finished sweep, not that any source was completely
enumerated. Full shapes live in the event bus reference
and the product’s typed interfaces.
Durable freshness
Section titled “Durable freshness”A periodic sweep marks a catalog entry stale when this platform has not seen
it since the cycle’s cutoff, and flips it back to active the moment it
reappears. Default thresholds are 30 minutes of silence and a 5-minute
cadence; they are module defaults, not an operator YAML surface in the current
binary (the composition root registers the module with empty host settings).
The module does not enumerate tenants and does not fall back to the tenants this process has happened to observe. A private composition adapter reads the durable organization directory and returns candidates only: active business tenants this instance’s residency serves — never the system partition, never a suspended org, never a tenant pinned to a region this instance does not serve. Each turn still opens the ordinary tenant-scoped store write, so residency, service withdrawal and leadership are checked again; a tenant whose state changed between the snapshot and its turn is refused there.
Without an authoritative directory the sweep fails visibly and mutates
nothing. On PostgreSQL that authority needs the dedicated NOSUPERUSER
BYPASSRLS admin read pool already used for cross-tenant directory reads
(--admin-dsn). Without that attested pool the sweep does not treat the rows
the application role can see as a complete directory. SQLite has no equivalent
pool requirement. New observations continue to persist when the data handle is
wired; a sweep that cannot run does not unsubscribe ingestion.
Each pass gives every candidate one turn. Each turn classifies at most one page of 1,000 active entries older than the cutoff and stores the cursor so a restart continues without a new event. Enumeration and each tenant turn have separate time budgets, so one slow tenant does not spend the rest of the pass. A local failure does not prevent later tenants from getting a turn in a live process. There is no cross-tenant restart cursor, no fairness under repeated restarts, and no high-availability claim.
If a turn does not complete successfully, that page is not counted as marked. Page and progress stay together; the next turn rereads whatever was last stored — the previous frontier, or one already advanced — and continues from there. The product does not reconstruct progress from memory.
What it consumes and produces
Section titled “What it consumes and produces”Module I is a consumer. It subscribes to edge.observed and
cost.sampled and writes its catalog overlay, the core entities it derives,
and the additive provenance and freshness rows above. It emits no events of
its own and exposes no actuation surface. It stores received references and selected observation facts without further
sanitizing those values. Producer-side data minimization is required before
observations are published; this module cannot certify it for every producer.
A persisted reference may still contain a query string, credentials or other
sensitive data if a producer published them. Inventory does not independently
collect complete raw activity content, and it adds no raw payload, secret,
prompt, command or SQL of its own.
Related
Section titled “Related”- Modules catalog — where module I sits and the honest Actuate split.
- Module III — the access map — the sole owner of the R/RW graph and drift.
- Event bus reference — the
edge.observed,cost.sampledandfinding.reportedevents the inventory and sessions modules consume. - Zero to graph — populating the catalog and the map on the demo estate.
- Architecture overview — the engine, the layers and the bus.