Files
storage/docs/architecture/README.md
glm-5.1 ae242f33b9 Restructure identity tables: separate credential types, add peer_credentials, specify FK cascades and indexes
Identity tables were derived from hub's PostgreSQL schema but simplified
without documenting what was removed or why. This restructures them for the
current auth landscape (API key + wraith SSH/cert-authority):

- ADR-049: Separate api_keys and peer_credentials tables (different lookup
  patterns, columns, lifecycles), remove Gitea columns, map hub data→metadata
- ADR-050: Extract SHA-256 vs KDF decision from inline spec text
- Add peer_credentials table for SSH key and cert-authority auth
- Specify all FK cascade behaviors within system DB (RESTRICT, CASCADE, SET NULL)
- Complete index specifications for all identity tables
- Add scope boundary section (storage owns schemas, not auth/authorization)
- Update audit_logs with credentialId+credentialType polymorphic reference
- Add 3 new open questions (OQ-33/34/35) for credential type expansion
2026-06-02 12:33:20 +00:00

119 lines
8.9 KiB
Markdown

---
status: draft
last_updated: 2026-06-02
---
# @alkdev/storage Architecture
Typed graph storage with SQLite via Honker. Deno-first, published via JSR.
## Current State
Storage has Phase 1-3 of the metagraph implementation complete: Metagraph Module,
bridge functions, reference graph type Modules (CallGraph, SecretGraph), and
crypto utility. Phase 4 (graphology bridge) is pending. The package is
transitioning to SQLite-only with Honker integration (ADR-038, ADR-039) and
adding identity tables for multi-tenant support (ADR-041). The repository/CRUD
layer, Drizzle-Honker adapter, identity tables, and additional graph types
remain to be implemented.
## Architecture Documents
| Document | Content | Status |
|----------|---------|--------|
| [overview.md](overview.md) | Package purpose, exports, dependencies, database model, ecosystem integration | draft |
| [metagraph-module.md](metagraph-module.md) | TypeBox Module type system, bridge functions, implementation path | reviewed |
| [sqlite-host.md](sqlite-host.md) | SQLite tables (metagraph + identity), client factories, Honker adapter | draft |
| [honker-integration.md](honker-integration.md) | Drizzle-Honker adapter, event patterns, system/tenant DB coordination | draft |
| [schema-evolution.md](schema-evolution.md) | Value.Diff/Cast/Patch for schema migration, version strategy | reviewed |
| [encrypted-data.md](encrypted-data.md) | Crypto utility, encrypted node type, key management | reviewed |
| [forward-look.md](forward-look.md) | Pointers, dbtype, ujsx IR (conceptual, post-v1) | draft |
| [acl.md](acl.md) | Access control graph: principal/agent framework, scoping, operations integration | draft |
### Design Decisions
| ADR | Decision | Status |
|-----|----------|--------|
| [001](decisions/001-deno-first-jsr-publishes.md) | Deno-first, JSR publishes, npm comes free | Accepted |
| [002](decisions/002-metagraph-over-domain-tables.md) | Metagraph pattern over domain-specific tables | Accepted |
| [003](decisions/003-typebox-module-as-api-surface.md) | TypeBox Module as the graph type definition API surface | Accepted |
| [004](decisions/004-injectable-clients-no-side-effects.md) | Injectable clients, no module-level side effects | Accepted |
| [005](decisions/005-drizzle-plus-typebox-via-drizzlebox.md) | Drizzle + TypeBox via drizzlebox | Accepted |
| [006](decisions/006-enum-pattern-as-const-objects.md) | `as const` objects, not TypeScript enums | Accepted |
| [007](decisions/007-no-comments-in-code.md) | No comments in code | Accepted |
| [008](decisions/008-common-columns-pattern.md) | Common columns pattern | Accepted |
| [009](decisions/009-typebox-module-replaces-schemabuilder.md) | TypeBox Module replaces the SchemaBuilder | Accepted |
| [010](decisions/010-metagraph-import-for-same-package.md) | Metagraph.Import() for same-package Modules | Accepted |
| [011](decisions/011-config-as-module-entry-with-literal-values.md) | Config as Module entry with Literal values | Accepted |
| [012](decisions/012-node-edge-attributes-as-module-entries.md) | Node/edge attribute schemas are Module entries | Accepted |
| [013](decisions/013-storage-produces-graphology-format.md) | Storage produces graphology format, flowgraph consumes it | Accepted |
| [014](decisions/014-dereferenced-entry-schemas.md) | Repository stores dereferenced entry schemas | Accepted |
| [015](decisions/015-edge-constraints-as-named-entries.md) | Edge type constraints as named Module entries | Accepted |
| [016](decisions/016-naming-convention-for-module-entries.md) | Naming convention for Module entries | Accepted |
| [017](decisions/017-pointer-abstraction-is-forward-looking.md) | Pointer abstraction is forward-looking, not v1 | Accepted |
| [018](decisions/018-dbtype-integration-is-post-v1.md) | dbtype integration is post-v1 | Accepted |
| [019](decisions/019-json-text-for-schema-columns.md) | JSON text for schema columns in SQLite | Accepted |
| [020](decisions/020-no-nodetypeid-on-nodes.md) | No nodeTypeId on nodes | Accepted |
| [021](decisions/021-edge-identity-uses-consumer-keys.md) | Edge identity uses consumer-defined keys | Accepted |
| [022](decisions/022-composite-fks-for-node-references.md) | Composite foreign keys for node references | Accepted |
| [023](decisions/023-per-attribute-encryption.md) | Per-attribute encryption, not per-node | Accepted |
| [024](decisions/024-encrypted-data-as-node-type.md) | Encrypted data as node type, not standalone table | Accepted |
| [025](decisions/025-password-based-encryption-pbkdf2.md) | Password-based encryption via PBKDF2 | Accepted |
| [026](decisions/026-application-managed-key-ring.md) | Application-managed key ring | Accepted |
| [027](decisions/027-no-key-rotation-utility.md) | No key rotation utility in this package | Accepted |
| [028](decisions/028-additive-only-with-cast-migration.md) | Additive-only for v1, Cast migration when needed | Accepted |
| [029](decisions/029-version-as-breaking-change-signal.md) | Version as a coarse-grained breaking-change signal | Accepted |
| [030](decisions/030-schema-change-detection-via-diff.md) | Schema change detection via Value.Diff | Accepted |
| [031](decisions/031-moduletodbschema-for-updates.md) | moduleToDbSchema() for schema updates | Accepted |
| [032](decisions/032-single-author-not-crdt.md) | Single-author model, not CRDT | Accepted |
| [033](decisions/033-json-path-queries-for-v1.md) | JSON path queries and hand-written CRUD for v1 | Accepted |
| [034](decisions/034-acl-as-metagraph.md) | ACL is a metagraph, not domain-specific tables | Accepted |
| [035](decisions/035-actors-become-acl-nodes.md) | Actors become ACL nodes, standalone table removed | Accepted |
| [036](decisions/036-principal-agent-as-delegation-edges.md) | Principal-agent as delegation edges with scope narrowing | Accepted |
| [037](decisions/037-setup-vs-runtime-separation.md) | Setup-time definitions seed graph types, runtime instances are separate | Accepted |
| [038](decisions/038-sqlite-first-pg-removed.md) | SQLite-first, Postgres removed | Accepted |
| [039](decisions/039-honker-as-sqlite-extension.md) | Honker as SQLite extension and transport | Accepted |
| [040](decisions/040-system-db-tenant-db.md) | System DB + tenant DB separation | Accepted |
| [041](decisions/041-identity-tables-in-storage.md) | Identity tables in storage package | Accepted |
| [042](decisions/042-scoping-columns-on-graphs.md) | Scoping columns on graph instances | Accepted |
| [043](decisions/043-graph-type-scope.md) | Graph type scope — system/tenant/user | Accepted |
| [044](decisions/044-drizzle-honker-adapter.md) | Drizzle-Honker session adapter | Accepted |
| [045](decisions/045-org-members-authoritative-belongsto-derived.md) | organization_members authoritative, BelongsToEdge derived | Accepted |
| [046](decisions/046-fold-drizzlebox-as-utils.md) | Fold @alkdev/drizzlebox as src/sqlite/utils | Accepted |
| [047](decisions/047-honker-event-target.md) | HonkerEventTarget adapter for pubsub | Accepted |
| [048](decisions/048-operation-specs-as-repo-surface.md) | OperationSpecs as repository surface | Accepted |
| [049](decisions/049-identity-schema-restructuring.md) | Identity schema restructuring — separate credential tables, remove Gitea, data→metadata | Accepted |
| [050](decisions/050-sha256-for-api-key-hashing.md) | SHA-256 for machine-generated API keys | Accepted |
### Open Questions
All unresolved design questions are tracked in [open-questions.md](open-questions.md), organized by theme with cross-references between related questions.
## Document Lifecycle
Architecture documents use YAML frontmatter with `status` and `last_updated` fields:
```yaml
---
status: draft | reviewed | stable | deprecated
last_updated: YYYY-MM-DD
---
```
| Status | Meaning | Transitions |
|--------|---------|-------------|
| `draft` | Under active development. Content may change significantly. Implementation should not start until the document reaches `reviewed`. | → `reviewed` when all open questions are resolved and cross-cutting issues are addressed. |
| `reviewed` | Architecture is final and reviewed. Implementation may begin. API contracts are specified but not yet verified by tests. Changes require a review cycle. | → `stable` when implementation is complete and API contracts are verified by tests. → `draft` if a fundamental redesign is needed. |
| `stable` | API contracts are locked and verified by tests. Changes require a review cycle and may warrant an ADR. | → `deprecated` when superseded. |
| `deprecated` | Superseded by another document. Kept for reference. | Removed when no longer referenced. |
ADR documents use a separate `Status` field in their body: `Proposed`, `Accepted`, `Deprecated`, or `Superseded`. ADRs never revert from `Accepted`.
## References
- Source: `src/`
- AGENTS.md: `/workspace/@alkdev/storage/AGENTS.md`
- Honker source: `/workspace/honker/`
- Flowgraph architecture: `/workspace/@alkdev/flowgraph/docs/architecture/`
- ujsx architecture: `/workspace/@alkdev/ujsx/docs/architecture/`
- Operations architecture: `/workspace/@alkdev/operations/docs/architecture/`