Skip to content

Domain model

The API exposes five core aggregates — Space, Unit, Resource, Action, Certificate — plus three role aggregates (UnitRoleAssignment, UnitRoleInvitation, SpaceAdminAssignment) covered in the Roles section. All resources are scoped to a Space: every request targets one via the X-Space-Id header, and no data ever crosses that boundary.

Tenant boundary and root of data isolation. Every other resource belongs to exactly one Space.

FieldTypeNotes
idUUID
namestring
statusSpaceStatusactive or deleted. Subscription gating is independent — see Permissions → Subscription gating
ownerUserIdstringIdP user identifier of the Space owner

The Space lifecycle (creation, deletion) is managed by the platform, not by regular users. Access to a Space depends on the user’s membership and the subscription status.

A tag or logical grouping inside a Space — for example an office, a team, or a site. Units have no hierarchy.

FieldType
idUUID
namestring

Relations:

  • many-to-many with Resource (a Unit groups Resources)
  • many-to-many with Action (a Unit requires a set of Actions)

A person, piece of equipment, environment, or substance tracked inside a Space.

FieldTypeNotes
idUUID
namestring
typeResourceTypeperson, equipment, environment, or substance. Immutable after creation

Relations:

  • belongs to one Space
  • tagged with zero or more Units
  • has zero or more Certificates

A Resource can exist without being assigned to any Unit — in that case it carries no compliance obligations.

A compliance requirement defined at Space level — for example a training course, a medical exam, or an equipment check.

FieldTypeNotes
idUUID
namestring
typeActionTypetraining, health, check, ppe, or maintenance
targetResourceTypeResourceTypeThe Resource type this Action applies to. Immutable after creation
validityPeriod{ value: integer, unit: "year" | "month" | "day" }How long a Certificate for this Action stays valid

Relations:

  • belongs to one Space
  • associated with zero or more Units
  • has zero or more Certificates

An Action applies to Resources in the Units it is associated with, but only when resource.type === action.targetResourceType (see Type matching).

Proof of fulfillment linking a Resource to an Action.

FieldType
idUUID
resourceIdUUID
actionIdUUID
namestring
issuedAtISO 8601 timestamp
expiresAtISO 8601 timestamp
attachmentsarray of Attachment

Invariants:

  • resource and action must belong to the same Space
  • action.targetResourceType === resource.type
  • issuedAt <= expiresAt
  • An expired Certificate no longer counts as compliance coverage

Attachments are embedded in the Certificate. The URL is opaque — it can point to any external store (Google Drive, Dropbox, SharePoint, etc.).

FieldType
idUUID
urlstring, non-empty
labelstring, non-empty
createdAtISO 8601 timestamp

Removing a Certificate cascades to its attachments.

Compliance is computed on the fly — never persisted. For every (Resource, Action) pair, the API derives one of:

  • compliant — there is a valid Certificate with expiresAt beyond the configured threshold
  • expiring — there is a valid Certificate but it will expire within the threshold (default 30 days)
  • non_compliant — there is no valid Certificate for an existing obligation

A (Resource, Action) pair is an obligation when:

  1. the Resource is assigned to at least one Unit, and
  2. the Action is associated with the same Unit, and
  3. resource.type === action.targetResourceType.

Pairs that do not match on type simply do not produce obligations — there is no not_applicable state.

Access control is driven by role assignments on Units and on the Space itself. See Permissions & roles for the full matrix.

AggregatePurpose
UnitRoleAssignment(userId, unitId) with a UnitRole — one role per user per Unit
UnitRoleInvitationPlaceholder for role assignments to users who have not signed up yet ((unitId, email) unique)
SpaceAdminAssignmentGrants the Admin role at Space level. Only the Owner can add or remove Admins

Every entity belongs to exactly one Space. No operation crosses Space boundaries. The Space is the boundary for security, data isolation, and billing.

All relations between entities must stay within the same Space:

  • Resource.units[*] must belong to the same Space as the Resource
  • Action.units[*] must belong to the same Space as the Action
  • A Certificate’s Resource and Action must share the same Space

An Action only applies to Resources whose type equals the Action’s targetResourceType. This constraint is enforced both when computing compliance and when issuing a Certificate — the API rejects mismatched pairs with 422 Unprocessable Entity (TypeMismatchException).

Eriga does not ship a predefined catalog of trainings, medical exams, or checks. Every Space defines its own Actions.

erDiagram
SPACE ||--o{ UNIT : contains
SPACE ||--o{ RESOURCE : contains
SPACE ||--o{ ACTION : contains
RESOURCE }o--o{ UNIT : "tagged with"
ACTION }o--o{ UNIT : "applies to"
RESOURCE ||--o{ CERTIFICATE : has
ACTION ||--o{ CERTIFICATE : has
SPACE {
UUID id PK
string name
SpaceStatus status
}
UNIT {
UUID id PK
string name
}
RESOURCE {
UUID id PK
string name
ResourceType type
}
ACTION {
UUID id PK
string name
ActionType type
ResourceType targetResourceType
ValidityPeriod validityPeriod
}
CERTIFICATE {
UUID id PK
UUID resourceId FK
UUID actionId FK
timestamp issuedAt
timestamp expiresAt
jsonb attachments
}