Atom

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

TableSimple purpose
tenantsTop boundary for a domain, customer, or workspace.
entitiesPrincipals such as humans, devices, services, workloads, and applications.
credentialsPassword hashes, access-token hashes, scoped-token ceilings, and issued certificate records for entities.
sessionsRevocable login sessions referenced by JWTs.
resourcesProtected objects such as channels, rules, reports, alarms, or application resources.
principal_groupsWho-containers for entities that receive the same roles.
object_groupsWhere-containers for entities, resources, and child object groups.
rolesFriendly names for sets of permission blocks.
actionsOperation names such as read, publish, manage, and authz.check.
action_applicabilityDefines which actions are valid for which protected object kinds/types.
action_assignment_rulesDefines which entity kinds may be assigned which actions on protected object kinds/types.
permission_blocksThe access rule: scope, effect, optional conditions, and object boundary.
permission_block_actionsLinks actions to a permission block.
role_permission_blocksLinks permission blocks to roles.
role_assignmentsGives a role to an entity or principal group.
direct_policiesGives one permission block directly to an entity or principal group.
audit_logsImmutable history of security-relevant events.
pki_authoritiesManaged roots, platform intermediates, platform leaf issuers, and tenant intermediates.
certificate_profilesReusable leaf issuance and renewal policy.
certificate_revocationsImmutable revocation evidence for exact certificate credentials.
certificate_crl_stateCached CRL state for each managed 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 database CHECK.
  • 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 explicit global selector. Resolution is capability-neutral; the authorization gate is the subsequent authz check 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.

SettingEnv varDefault
Purge enabledATOM_PURGE_ENABLEDfalse
RetentionATOM_PURGE_RETENTION_DAYS90 days
Sweep intervalATOM_PURGE_INTERVAL_SECS86400 (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:

  • actions name the operation;
  • action_applicability says where the operation is valid, but does not grant access;
  • action_assignment_rules say whether access may be assigned to an entity kind;
  • permission_blocks define the actual rule;
  • roles bundle permission blocks;
  • role_assignments give roles to subjects;
  • direct_policies give 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, api_endpoint, 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.

Exact-object scopes store a UUID, so every UUID-addressable protected object shares one database-enforced namespace. The registry covers tenants, entities, resources, principal and object groups, roles, credentials, direct policies, role assignments, and custom API endpoints. Soft-deleted objects retain their reservation until physical purge. This prevents an exact grant for one kind from matching a different kind that happened to reuse the same UUID. Profiles and individual audit-log rows are not exact-object targets; signing keys use a textual kid and stay platform-scoped.

Certificate State

Atom stores every managed authority certificate and its lifecycle state in Postgres. Managed CA private keys are envelope-encrypted in Postgres by default; an authority can instead store an opaque reference to a non-exportable PKCS#11 key. The production root private key remains offline and is never accepted by Atom.

Postgres stores certificate lifecycle state including:

  • authority rows in pki_authorities;
  • issued certificate rows in credentials;
  • immutable revocation rows in certificate_revocations;
  • cached per-issuer CRL bytes and CRL numbers in certificate_crl_state.

On this page