Thing Access Model

This document describes how things are enrolled, provisioned, and made accessible to different accounts on the FlexGalaxy.AI platform. It is the conceptual model behind the repository-visibility ADRs — see ADR-0001, ADR-0002, and ADR-0003.

Core concepts

Thing

A device, product, or piece of equipment registered on the platform. Things go through a lifecycle: ENROLLED PROVISIONED ACTIVE.

Registrar

A manufacturing context owned by an account. Things are enrolled into a registrar. An account can have multiple registrars (e.g., different factory lines or product categories).

Access Code

A per-thing credential generated at provisioning. Used by other accounts to gain access to the thing. Reusable (multiple accounts can use the same code) until explicitly retired by the registrar owner.

Thing Access

A grant that allows an account to see and interact with a specific thing. Each access grant records which access code was used. Access is valid only while the access code matches the thing’s current active code.

Enrollment vs Access

ThingMake (manufacturer console) shows enrolled things — things the account’s registrar enrolled. The registrar owner has inherent access to all things in their registrar without needing an access code.

ThingHub (device management) shows accessible things — things the account has been granted access to, regardless of how. This includes:

  • Things in the account’s own registrar (inherent access)

  • Things accessed via access code

  • Things auto-granted access (a platform-driven grant with a null access code)

Lifecycle

Enrollment

  1. Manufacturer creates a registrar (if they don’t have one)

  2. Manufacturer enrolls a thing into the registrar

  3. Thing is created with status=ENROLLED, no thing_id, no access_code

  4. The registrar owner has inherent access (no ThingAccess record needed inline — the registrar/repository system inserts one; see ADR-0001)

Provisioning

  1. Manufacturer provisions the thing

  2. thing_id is assigned (NanoID 16 characters)

  3. access_code is auto-generated

  4. status changes to ACTIVE

  5. The thing is now ready to be shared with others

Sharing access

  1. Manufacturer shares the access code with another party (e.g., distributor, partner, customer)

  2. The other party enters the access code in ThingHub

  3. Platform looks up the thing by access code

  4. A ThingAccess record is created: (thing_id, access_code, account_id)

  5. The thing now appears in the other party’s ThingHub “Things” list

Auto-granted access

The platform can create a ThingAccess record automatically using the thing’s current access code (or a null access code for platform-driven grants). This lets a platform-approved account view a thing without the manufacturer manually sharing a code. Auto-granted access carries the same revocation semantics as any other grant: retiring the current access code invalidates it.

Access validation

On every access check, the platform validates:

  1. A ThingAccess record exists for (thing_id, account_id)

  2. The access_code in the record matches the thing’s current active code (a null access_code counts as an auto-granted match)

  3. If the code has been retired (no longer matches), access is denied

This means retiring an access code instantly revokes all access grants that were made with that code.

Access code management

Action

Effect

Provision thing

First access code auto-generated

Share code

Others enter the code to gain access (reusable, multiple accounts)

Retire + regenerate

Current code retired, new code generated. All existing access grants tied to the old code become invalid. New code can be shared with new parties.

Auto-grant

Access created using current code (or null); same revocation semantics

Registrar access

  • Registrar owners have inherent access to all things in their registrar. This access does NOT go through the access code model.

  • Other accounts CANNOT access a registrar. They can only access individual things within it (via access code or auto-grant).

  • Registrar information (name, owner, etc.) is hidden from non-owner accounts in ThingHub.

Data model

Thing entity

things (
    id          UUID PRIMARY KEY,
    thing_id    VARCHAR(16),          -- assigned at provisioning (NanoID)
    type        VARCHAR(50) NOT NULL,
    status      VARCHAR(20) NOT NULL,  -- ENROLLED, ACTIVE, SUSPENDED, DEREGISTERED
    registrar_id UUID NOT NULL,        -- FK to registrars
    access_code VARCHAR(20),           -- current active code (null before provisioning)
    model_id    VARCHAR(100),
    registered_at TIMESTAMP,
    updated_at  TIMESTAMP
)

ThingAccess entity

thing_accesses (
    id          UUID PRIMARY KEY,
    thing_id    UUID NOT NULL,         -- FK to things.id (internal ID)
    account_id  VARCHAR(100) NOT NULL,
    access_code VARCHAR(20) NOT NULL,  -- the code used to gain access
    granted_at  TIMESTAMP NOT NULL,
    UNIQUE(thing_id, account_id, access_code)
)

Access check: SELECT 1 FROM thing_accesses ta JOIN things t ON ta.thing_id = t.id WHERE ta.thing_id = ? AND ta.account_id = ? AND ta.access_code = t.access_code

Registrar entity

registrars (
    id          UUID PRIMARY KEY,
    name        VARCHAR(255) NOT NULL,
    account_id  VARCHAR(100) NOT NULL, -- owner account
    status      VARCHAR(20) NOT NULL,
    created_at  TIMESTAMP,
    updated_at  TIMESTAMP
)

API endpoints

ThingHub (accessible things)

GET  /api/v1/accesses                     -- list accessible things
POST /api/v1/things/access                -- grant access by access code
                                             body: { "access_code": "..." }

ThingMake (enrolled things)

GET  /api/v1/registrars                   -- list own registrars
POST /api/v1/registrars                   -- create a registrar
GET  /api/v1/registrars/{id}/things       -- list enrolled things
POST /api/v1/things                       -- enroll a thing
POST /api/v1/things/{id}/provision        -- provision (assigns thing_id + access_code)
POST /api/v1/things/{id}/regenerate-code  -- retire current code, generate new one

Access code flows

-- Share: manufacturer gives code to partner
POST /api/v1/things/access
{ "access_code": "B002-X002-Y002" }
→ 201: { "thing_id": "BROIT-CLN-002", "access_code": "B002-X002-Y002", ... }

-- Retire + regenerate: manufacturer rotates code
POST /api/v1/things/{id}/regenerate-code
→ 200: { "access_code": "NEW-CODE-HERE", ... }
-- All previous access grants using old code are now invalid

Examples

A manufacturer (RUBY-Dalian) enrolls a cleaning robot:

1. POST /registrars         → creates "RUBY Dalian Manufacturing"
2. POST /things             → enrolls robot (status=ENROLLED)
3. POST /things/{id}/provision → thing_id=BROIT-CLN-002, access_code=B002-X002-Y002
4. RUBY sees the robot in ThingMake (enrolled) and ThingHub (accessible)

A distributor (GREEN) gets access via code, then loses it on rotation:

1. RUBY shares access code B002-X002-Y002 with GREEN
2. GREEN enters code in ThingHub → ThingAccess created
3. GREEN sees BROIT-CLN-002 in ThingHub
4. RUBY retires code → GREEN loses access
5. RUBY generates new code, shares with BLUE → only BLUE has access

Access is additive and managed entirely through grants: there is no owner-transfer or claim concept. Revoking is deleting the ThingAccess row (or rotating the code).