Files
storage/docs/architecture/decisions/041-identity-tables-in-storage.md
glm-5.1 6aa2fcc6ff Architect storage around SQLite+Honker: remove PG, add multi-tenant identity, scoping
Reorient @alkdev/storage around a single SQLite database host with Honker
for pub/sub, event streams, and task queues. PostgreSQL is removed as a
target (ADR-038), eliminating dual schema maintenance and infrastructure
complexity. Honker provides DB + pubsub + queues in one .db file (ADR-039).

Add system/tenant DB model (ADR-040): identity tables in system.db, all
graph data in tenant-{orgId}.db files. Identity tables move from the hub
into storage (ADR-041). Scoping columns (ownerId, projectId) added to
graphs table (ADR-042). Graph types get scope (system/tenant/user) to
protect infrastructure schemas (ADR-043).

Define Drizzle-Honker session adapter (ADR-044): ~100-line adapter enabling
Drizzle typed queries and Honker pubsub/queue on a single connection with
transactional consistency.

Resolve OQ-03, OQ-04, OQ-19, OQ-21, OQ-22, OQ-23, OQ-24. Add new
open questions OQ-26 through OQ-29 for Honker integration specifics.

New docs: honker-integration.md (adapter, event patterns, migration).
Scrub all PG/jsonb/libsql references from existing spec docs.
2026-05-31 15:41:41 +00:00

3.4 KiB

ADR-041: Identity Tables in Storage Package

Status

Accepted

Context

The hub currently defines identity tables (accounts, organizations, api_keys, audit_logs, organization_members) in its own src/storage/tables/ directory. The storage package provides only the 6 metagraph tables.

This separation creates problems:

  1. The system DB (ADR-040) needs identity tables, but @alkdev/storage doesn't provide them
  2. The hub has to maintain its own Drizzle table definitions separately, duplicating the common columns pattern and drizzlebox integration
  3. The scoping columns on the metagraph tables (ADR-042) logically reference identity table rows, but those tables are defined elsewhere
  4. Any consumer that wants multi-tenant graph storage needs the identity tables too — they're not hub-specific, they're infrastructure

The identity tables are NOT graph-shaped (ADR-002 established the metagraph for graph-shaped data). They are relational records with fixed schemas, indexed lookups (email uniqueness, key hash lookup), and FK constraints. But they ARE required by any deployment that uses the system/tenant DB model.

Decision

The identity tables move into @alkdev/storage/sqlite:

  • accounts — hub-local identity records
  • organizations — top-level grouping for multi-tenancy
  • organization_members — account/org membership (note: also modeled as BelongsToEdge in ACL graphs, but the SQL table provides fast indexed lookups that graph traversal cannot match)
  • api_keys — keypal-managed API key storage
  • audit_logs — append-only security event trail

These tables are in the storage package because they are database infrastructure, not hub business logic. The hub consumes them; it does not own their schema.

organization_members remains a SQL table despite BelongsToEdge existing in the ACL graph (ADR-034). The SQL table provides O(1) lookups for "list all members of org X" and FK constraints for cascading behavior. The ACL graph provides traversal-based evaluation for permission resolution. Both are needed — the SQL table is authoritative for membership state; the ACL graph edge is derived (OQ-23 resolved as "derived").

Consequences

Positive:

  • Single package provides everything needed for a multi-tenant graph database: metagraph tables + identity tables
  • The hub doesn't duplicate table definitions or common column patterns
  • createSystemDatabase(client) returns a fully-typed Drizzle instance with identity tables
  • Other consumers (spokes, tools, standalone services) get identity tables without depending on the hub
  • Referential integrity within the system DB is consistent — FK constraints work

Negative:

  • @alkdev/storage grows in scope — it's no longer just "graph storage" but also "identity infrastructure"
  • The hub loses ownership of its table schemas — changes require coordination with storage
  • organization_members existing as both a SQL table and ACL graph edges requires a dual-write contract (the SQL table is authoritative; the ACL edge is derived)
  • api_keys is tightly coupled to keypal's Storage interface — storage must not import keypal directly; the hub provides the adapter

References

  • ADR-040: System DB + tenant DB separation
  • ADR-034: ACL as metagraph
  • ADR-002: Metagraph over domain-specific tables
  • Hub identity tables: /workspace/@alkdev/hub/docs/architecture/storage/identity.md