Data Model
Current schema overview for Atom's main database tables.
Atom stores security state in Postgres. All primary keys are UUIDs. Most objects have tenant_id so the same Atom instance can serve many domains or customers.
Data Model Diagram
What this means: entities prove identity with credentials. Roles and direct policies connect subjects to permission blocks. Permission blocks say where actions apply. Assignment guardrails prevent unsafe access state from being created. Audit logs record what happened.
Core Tables
| Table | Simple purpose |
|---|---|
tenants | Top boundary for a domain, customer, or workspace. |
entities | Principals such as humans, devices, services, workloads, and applications. |
credentials | Password hashes, access-token hashes, scoped-token ceilings, and issued certificate records for entities. |
sessions | Revocable login sessions referenced by JWTs. |
resources | Protected objects such as channels, rules, reports, alarms, or application resources. |
principal_groups | Who-containers for entities that receive the same roles. |
object_groups | Where-containers for entities, resources, and child object groups. |
roles | Friendly names for sets of permission blocks. |
actions | Operation names such as read, publish, manage, and authz.check. |
action_applicability | Defines which actions are valid for which protected object kinds/types. |
action_assignment_rules | Defines which entity kinds may be assigned which actions on protected object kinds/types. |
permission_blocks | The access rule: scope, effect, optional conditions, and object boundary. |
permission_block_actions | Links actions to a permission block. |
role_permission_blocks | Links permission blocks to roles. |
role_assignments | Gives a role to an entity or principal group. |
direct_policies | Gives one permission block directly to an entity or principal group. |
audit_logs | Immutable history of security-relevant events. |
certificate_crl_state | Cached CRL state for the active mounted certificate issuer. |
Aliases
Every tenants, entities, and resources row may carry an optional alias: a short, human-friendly handle used in place of the UUID. The UUID stays the canonical identity — aliases are a convenience layer for addressing and never appear as foreign keys, audit subjects, or authorization scope.
- Slug shape. An alias is a lowercase slug of 1–63 characters using
a–z,0–9, and-, with no leading or trailing dash. It is case-folded on write, may not be UUID-shaped, and is validated both in the service and by a databaseCHECK. - Uniqueness. Tenant aliases are unique across the platform. Entity and resource aliases are unique within their tenant, so the same alias (for example
watermeters) may be reused in different tenants. - Resolution.
AliasService.ResolveAlias(gRPC) resolves a tenant alias plus an object alias to the underlying UUIDs in one call. Global entities and resources use the request's explicitglobalselector. Resolution is capability-neutral; the authorization gate is the subsequentauthzcheck by UUID.
Aliases stay an alias, not a replacement: rename one and every UUID-keyed grant, session, and audit row is unaffected.
Entity And Credential State
Entities are the universal subject type. A user, device, service, workload, and application are all entities with different kind values.
Credentials belong to entities:
- password credentials store an argon2 hash;
- access-token credentials store an argon2 hash and a lookup identifier; unscoped rows are product API keys, while scoped rows carry a permission ceiling;
- certificate credentials store issued certificate metadata and no private key.
Plaintext passwords, API key/access-token secrets, and generated leaf private keys are never stored.
Disable, Delete, and Retention
Disabling and deleting are different operations with different blast radius.
Disable is a reversible status change. The row stays live and listable; the
subject is just blocked from acting (entities become inactive, profiles
disabled, tenants inactive/frozen). Re-enabling restores it. In the UI,
disabled-family states render amber as a warning.
Delete is a soft delete. The row is hidden immediately — a deleted_at
tombstone is set, child sessions and credentials are revoked — but the data is
not physically removed. Deleted rows are excluded from default (Live) listings
and surface only under the Deleted filter. In the UI, delete renders red.
Soft-deleted rows are removed permanently only by the purge background job, which deletes tombstones older than the retention window (reusing the existing foreign-key cascades). Purge is disabled by default: until it is enabled, tombstones are kept indefinitely, so a delete is recoverable.
| Setting | Env var | Default |
|---|---|---|
| Purge enabled | ATOM_PURGE_ENABLED | false |
| Retention | ATOM_PURGE_RETENTION_DAYS | 90 days |
| Sweep interval | ATOM_PURGE_INTERVAL_SECS | 86400 (daily) |
A tenant can also be purged on demand via the purgeTenant mutation, which
bypasses the retention window and is deliberately irreversible and admin-only.
Authorization State
The authorization model is split deliberately:
actionsname the operation;action_applicabilitysays where the operation is valid, but does not grant access;action_assignment_rulessay whether access may be assigned to an entity kind;permission_blocksdefine the actual rule;rolesbundle permission blocks;role_assignmentsgive roles to subjects;direct_policiesgive one permission block directly to a subject.
This keeps scope and actions in one place: the permission block.
The canonical protected object kinds are entity, resource, group,
tenant, role, policy, credential, audit_log, and signing_key.
Sub-kinds are stored with a kind prefix, such as entity:device and
resource:channel, so authorization checks, audit logs, and explain output use
the same values.
Certificate State
Atom does not store CA certificates or CA private keys in Postgres in file issuer mode. CA files are mounted into the container and loaded during startup.
Postgres stores only issued certificate lifecycle state:
- issued certificate rows in
credentials; - revocation data in certificate credential metadata;
- cached CRL bytes and CRL number in
certificate_crl_state.