Entity model
Devices on their own are a flat list. The entity model is how CORE-M gives them structure and shares them safely: assets group and locate devices, typed relations connect everything into a navigable graph, and customers get tightly-scoped, sub-tenant access through entity views and dashboard assignments.
Everything here is tenant-scoped and stored in Aerospike namespace devices.
Assets
Section titled “Assets”An asset is a first-class entity representing a logical or physical grouping —
a site, building, production line, machine, vehicle, or meter. Assets live in
devices/assets, keyed by {tenant_id}:{asset_id}, and carry name, type,
label, tags, metadata, status, and version.
- Unique name per type per tenant. Creating a second
sitenamed “Factory 1” in a tenant that already has one is rejected withALREADY_EXISTS(and no event is published). The same name under a different type is fine. - Queryable by type and tag. You can list assets filtered by type and tag (e.g.
type=site,region=eu), paginated withnext_page_token. - Soft delete. Deleting an asset sets
status="deleted"; it is excluded from normal lists but retained for audit and retrievable withinclude_deleted=true, which returnsdeleted_atanddeleted_by.
Creating an asset publishes entity.asset.created.T1.{asset_id} and writes an audit
event.
Typed directed relations
Section titled “Typed directed relations”Relations connect entities — devices, assets, customers, dashboards, and entity
views — into a directed graph. Each relation has a type and a direction, stored
in devices/relations keyed by
{tenant_id}:{from_type}:{from_id}:{relation_type}:{to_type}:{to_id}.
| Relation type | Typical use |
|---|---|
contains | An asset contains sub-assets or devices |
manages | One entity manages another |
assigned_to | A dashboard assigned to a customer/user |
located_at | A device located at an asset |
monitors | A device monitors a target |
depends_on | A dependency edge between entities |
Creating A1 --contains--> D1 writes a relation record, builds reverse-lookup
indexes (so you can query “devices contained by A1”), and publishes
entity.relation.created.T1.A1.D1.
Cycle prevention
Section titled “Cycle prevention”For relation types where cycles are forbidden (such as a contains hierarchy),
the system rejects edges that would close a loop. If A1 contains A2, an attempt to
create A2 --contains--> A1 is rejected with FAILED_PRECONDITION and the response
identifies the cycle path, so you can see exactly which edges conflict.
Hierarchy traversal
Section titled “Hierarchy traversal”Relations are navigable in both directions and across depth. Asking for the
descendants of A1 filtered to entity_type=device — given A1 contains A2 and
A2 contains D1, D2 — returns D1 and D2, each with its relation path back to
A1. That path is what dashboards and enrichment nodes use to render and reason about
hierarchy.
flowchart TD A1["Asset: Factory 1<br/>(site)"] -->|contains| A2["Asset: Line A<br/>(line)"] A1 -->|contains| A3["Asset: Line B<br/>(line)"] A2 -->|contains| D1["Device: Boiler 1"] A2 -->|contains| D2["Device: Boiler 2"] A3 -->|contains| D3["Device: Press 1"] D1 -.located_at.-> A2
Customers and customer users
Section titled “Customers and customer users”A customer is a tenant-scoped entity that receives restricted access — think
of an end client of your tenant who should see only their own equipment, never the
whole tenant. Customers live in devices/customers keyed by
{tenant_id}:{customer_id}.
Customer users are auth users with authority customer_user and one or more
customer memberships. Their access is fundamentally narrower than a member:
Inviting a customer user creates an auth user with authority="customer_user",
assigns them to the customer, and audits the invite.
Entity views
Section titled “Entity views”An entity view is the mechanism that grants a customer access to a specific slice
of an entity’s data — and nothing more. Views live in devices/entity_views keyed by
{tenant_id}:{view_id} and carry entity_type, entity_id, customer_id,
field_mask, telemetry_key_allowlist, start_time, end_time, and version.
Three controls define the slice:
field_mask— which fields of the entity are visible. A view that masks out config and API key means a customer reading deviceD1through it sees no config, no API key, no credentials, no internal metadata, and no unrelated telemetry keys.telemetry_key_allowlist— which metrics are readable. A view allowing["temperature", "humidity"]lets the customer query only those keys for the device.start_time/end_time— the validity window.
Dashboard sharing and revocation
Section titled “Dashboard sharing and revocation”Dashboards can be assigned to customers (and users) with view or edit
permission via an assigned_to relation.
-
Assign. Assigning dashboard
DB1to customerC1withpermission="view"createsDB1 --assigned_to--> C1. Customer users forC1now seeDB1in their list but cannot edit it. -
Revoke. Revoking deletes the relation. Active WebSocket sessions for
C1immediately receive adashboard_access_revokedevent, and subsequent dashboard API reads returnPERMISSION_DENIED.
So revocation is real-time: an in-flight viewer doesn’t keep the dashboard open until they refresh — their live session is told to drop access at once.
Soft delete and audit
Section titled “Soft delete and audit”Assets, customers, relations, and entity views all support soft delete with audit
history. Deleting a customer is the broadest case: it sets status="deleted",
disables the customer’s assignments and entity views, drops customer-user access
at their next request, and writes an audit event listing the disabled
entity_view_ids and dashboard_ids. Deleted entities are retained for audit and
excluded from default list queries.
How it all connects
Section titled “How it all connects”flowchart LR cust["Customer C1"] -->|has| cu["Customer user CU1"] ev["Entity view EV1<br/>field_mask + allowlist + expiry"] -->|customer_id| cust ev -->|entity_id| dev["Device D1"] db["Dashboard DB1"] -->|assigned_to| cust cu -.reads through.-> ev cu -.sees.-> db
A customer user reaches data only along these edges: through an entity view that names their customer and a device, or through a dashboard assigned to their customer. Anything outside that path is denied.