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.
This commit is contained in:
@@ -1,80 +1,49 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-05-30
|
||||
last_updated: 2026-05-31
|
||||
---
|
||||
|
||||
# Open Questions Tracker
|
||||
|
||||
Cross-cutting compilation of all unresolved questions across the storage architecture documents, organized by theme. Questions that appear in multiple documents are unified here with cross-references.
|
||||
|
||||
When a question is resolved, update its status to `resolved` and add a resolution note. Once all questions in a theme are resolved, the theme section can be removed and the resolution noted in the relevant ADR.
|
||||
Cross-cutting compilation of all unresolved questions across the storage
|
||||
architecture documents, organized by theme.
|
||||
|
||||
## Summary
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| Open | 13 |
|
||||
| Partially resolved | 1 |
|
||||
| Resolved | 11 |
|
||||
| Open | 9 |
|
||||
| Resolved (this revision) | 15 |
|
||||
| Previously resolved | 11 |
|
||||
|
||||
**Open questions requiring decisions:**
|
||||
- **OQ-04** (repository layer host-specific vs host-agnostic) — start host-specific
|
||||
|
||||
- **OQ-07** (encryptRaw performance) — low priority, add if needed
|
||||
- **OQ-10** (Edit[] classification) — needs POC
|
||||
- **OQ-11** (auto-migrate vs explicit consumer action) — conditional on OQ-10
|
||||
- **OQ-11** (auto-migrate vs explicit) — conditional on OQ-10
|
||||
- **OQ-12** (schema evolution vs event-sourced replay) — post-v1 concern
|
||||
- **OQ-13** (schema evolution events in event stream) — post-v1
|
||||
- **OQ-19** (storage-operations bridge package location) — depends on long-term CRUD strategy
|
||||
- **OQ-20** (delegation expiration) — ACL design
|
||||
- **OQ-21** (ACL evaluator location) — ACL design
|
||||
- **OQ-22** (ACL graph instance lifecycle) — ACL design
|
||||
- **OQ-23** (BelongsToEdge derivation) — ACL design
|
||||
- **OQ-24** (identityId reference mechanism) — ACL design
|
||||
- **OQ-25** (scope string semantics for subset validation) — ACL design
|
||||
|
||||
**Partially resolved:**
|
||||
- **OQ-01** (flowgraph Module export) — storage can start without it
|
||||
|
||||
**Resolved (v1 direction decided, long-term question remains open):**
|
||||
- **OQ-17** (attribute query strategy) — JSON path for v1 (ADR-033), hybrid viable with dbtype later
|
||||
- **OQ-18** (auto-generated vs hand-written CRUD) — hand-write for v1 (ADR-033), auto-gen remains an option
|
||||
|
||||
## How to Use This Document
|
||||
|
||||
- Each question has an **ID** (e.g., OQ-01), **status**, **origin** (which doc(s)), and **priority**
|
||||
- **Cross-references** link related questions and ADRs
|
||||
- Resolved questions have a **resolution** note
|
||||
|
||||
## ADR Impact
|
||||
|
||||
| ADR | Resolves | Informs |
|
||||
|-----|----------|---------|
|
||||
| ADR-003 | OQ-01 (partial — storage can start without flowgraph Module) | |
|
||||
| ADR-015 | OQ-05 (constraint semantics) | |
|
||||
| ADR-018 | OQ-17 (v1 decision: dbtype integration deferred, JSON path for v1) | |
|
||||
| ADR-020 | OQ-02 (no nodeTypeId for now, can add later) | |
|
||||
| ADR-033 | OQ-17 (JSON path queries for v1), OQ-18 (hand-written CRUD for v1) | |
|
||||
| ADR-034 | OQ-03 (actors become ACL nodes) | OQ-21 (evaluator location), OQ-23 (BelongsToEdge derivation), OQ-24 (identityId references) |
|
||||
| ADR-035 | OQ-03 (standalone table removed) | |
|
||||
| ADR-036 | | OQ-20 (delegation expiration) |
|
||||
| ADR-037 | | OQ-21 (evaluator location), OQ-22 (graph instance lifecycle) |
|
||||
- **OQ-26** (Honker replaces @alkdev/pubsub Redis transport) — integration design
|
||||
- **OQ-27** (tenant DB schema migration strategy) — multi-tenant operations
|
||||
- **OQ-28** (cross-tenant delegation with separate DBs) — cross-DB coordination
|
||||
- **OQ-29** (standalone drizzle-honker npm package) — community value
|
||||
|
||||
## Theme 1: Package Boundaries and Dependencies
|
||||
|
||||
### OQ-01: Should @alkdev/flowgraph export a Type.Module, or should storage define its own entries with documented correspondence?
|
||||
### OQ-01: Should @alkdev/flowgraph export a Type.Module?
|
||||
|
||||
- **Origin**: [metagraph-module.md](metagraph-module.md)
|
||||
- **Status**: partially resolved
|
||||
- **Status**: resolved
|
||||
- **Priority**: high
|
||||
- **Notes**: Storage can start with standalone schemas and `Type.Composite([BaseNode, CallNodeAttrs])` — no dependency on flowgraph. Adopt `Import()` when flowgraph provides a Module. This avoids a circular dependency: `@alkdev/storage` does NOT depend on `@alkdev/flowgraph`.
|
||||
- **Resolution**: Storage can start with standalone schemas. Adopt `Import()` when flowgraph provides a Module. No circular dependency.
|
||||
- **Cross-references**: ADR-003, ADR-010
|
||||
|
||||
### OQ-02: Should concrete graph type Modules live in storage or in their respective packages?
|
||||
### OQ-02: Should concrete graph type Modules live in storage or their packages?
|
||||
|
||||
- **Origin**: [metagraph-module.md](metagraph-module.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Resolution**: Both. Storage provides reference Modules in `modules/` that consumers can use directly or replace. Flowgraph may also export a Module — the two are compatible via Module `$defs`.
|
||||
- **Resolution**: Both. Storage provides reference Modules; packages may also export their own.
|
||||
- **Cross-references**: ADR-003
|
||||
|
||||
## Theme 2: Data Model
|
||||
@@ -84,55 +53,50 @@ When a question is resolved, update its status to `resolved` and add a resolutio
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Resolution**: Actors become `PrincipalNode` entries in the ACL graph instance. The standalone `actors` table is removed. `ACTOR_TYPE` is replaced by the `IdentityType` enum in the AclGraph Module. See ADR-035.
|
||||
- **Cross-references**: ADR-035, ADR-034, [acl.md](acl.md)
|
||||
- **Resolution**: Actors become `PrincipalNode` in ACL graph. `actors` table removed. `ACTOR_TYPE` replaced by `IdentityType` in AclGraph Module. See ADR-035.
|
||||
- **Cross-references**: ADR-035, ADR-034
|
||||
|
||||
### OQ-04: Should the repository layer be host-specific or host-agnostic?
|
||||
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Notes**: A host-agnostic repository requires an abstraction over Drizzle's query builder. A host-specific repository is simpler but means duplicating query logic for PG. Decision: start host-specific in SQLite, extract common patterns later.
|
||||
- **Cross-references**: [sqlite-host.md](sqlite-host.md)
|
||||
- **Resolution**: Single host (SQLite). Question is moot — no dual-host repository needed. ADR-038.
|
||||
|
||||
### OQ-05: Should *EdgeConstraints entries use Type.Ref or Type.String for allowed source/target types?
|
||||
### OQ-05: *EdgeConstraints entries use Type.Ref or Type.String?
|
||||
|
||||
- **Origin**: [metagraph-module.md](metagraph-module.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: low
|
||||
- **Resolution**: `Type.String()` — the constraint arrays contain node type names, not node type schemas.
|
||||
- **Cross-references**: ADR-015
|
||||
- **Resolution**: `Type.String()` — constraint arrays contain names, not schemas. ADR-015.
|
||||
|
||||
### OQ-06: How does the graph pointer abstraction interact with the repository layer?
|
||||
### OQ-06: Graph pointer abstraction vs repository layer?
|
||||
|
||||
- **Origin**: [metagraph-module.md](metagraph-module.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: low
|
||||
- **Resolution**: For v1, repository functions use direct key-based addressing. Validate on read — if data doesn't match the Module entry, throw. Typed pointers are post-v1 (ADR-017).
|
||||
- **Cross-references**: ADR-017, [forward-look.md](forward-look.md)
|
||||
- **Resolution**: Direct key-based addressing for v1. Typed pointers post-v1. ADR-017.
|
||||
|
||||
## Theme 3: Encryption and Security
|
||||
|
||||
### OQ-07: Should we add encryptRaw() for performance?
|
||||
### OQ-07: Add encryptRaw() for performance?
|
||||
|
||||
- **Origin**: [encrypted-data.md](encrypted-data.md)
|
||||
- **Status**: open
|
||||
- **Priority**: low
|
||||
- **Notes**: PBKDF2 derivation adds ~100ms per operation. For batch operations (e.g., rotating 1000 keys), this adds up. An `encryptRaw()` that skips PBKDF2 would be much faster. Decision: add in a future iteration if performance demands it.
|
||||
- **Notes**: PBKDF2 adds ~100ms. Add if batch operations demand it.
|
||||
|
||||
### OQ-08: Should the key attribute on secret nodes be encrypted?
|
||||
### OQ-08: Should key attribute on secret nodes be encrypted?
|
||||
|
||||
- **Origin**: [encrypted-data.md](encrypted-data.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: low
|
||||
- **Resolution**: Plaintext key names are acceptable for now. If secret names are sensitive, add a `keyHash` attribute for blind lookups.
|
||||
- **Resolution**: Plaintext for now. Add `keyHash` if names are sensitive.
|
||||
|
||||
### OQ-09: Should secret nodes have lastUsedAt and expiresAt as first-class columns?
|
||||
### OQ-09: Should secret nodes have lastUsedAt and expiresAt as columns?
|
||||
|
||||
- **Origin**: [encrypted-data.md](encrypted-data.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: low
|
||||
- **Resolution**: For spoke use (occasional lookups), JSON attributes are fine. For hub use (high-throughput key validation), a standalone `api_keys` table with proper indexes is still needed.
|
||||
- **Resolution**: JSON attributes for spoke, standalone table for hub.
|
||||
|
||||
## Theme 4: Schema Evolution
|
||||
|
||||
@@ -141,126 +105,163 @@ When a question is resolved, update its status to `resolved` and add a resolutio
|
||||
- **Origin**: [schema-evolution.md](schema-evolution.md)
|
||||
- **Status**: open
|
||||
- **Priority**: high
|
||||
- **Notes**: The classification table in schema-evolution.md is theoretical. A POC should validate whether `Edit[]` output contains enough information to distinguish `String → Literal("x")` (narrowing, non-breaking) from `String → Number` (incompatible, breaking). Alternative: skip classification and just use `Value.Check(newSchema, storedData)` for verification.
|
||||
- **Notes**: Theoretical classification needs POC validation.
|
||||
|
||||
### OQ-11: Should the repository layer auto-migrate data on schema change, or require explicit consumer action?
|
||||
### OQ-11: Auto-migrate data on schema change, or explicit consumer action?
|
||||
|
||||
- **Origin**: [schema-evolution.md](schema-evolution.md)
|
||||
- **Status**: open
|
||||
- **Priority**: high
|
||||
- **Notes**: Conditional on OQ-10 POC outcome. If classification is feasible, the repository layer auto-applies `Value.Cast` for non-breaking changes and requires explicit consumer action for breaking changes. If classification is not feasible, the repository layer auto-applies `Value.Cast` only when `Value.Check(newSchema, storedData)` passes for all stored data.
|
||||
- **Notes**: Conditional on OQ-10 POC outcome.
|
||||
|
||||
### OQ-12: How does schema evolution interact with the hub's event-sourced call graph?
|
||||
### OQ-12: Schema evolution vs event-sourced replay?
|
||||
|
||||
- **Origin**: [schema-evolution.md](schema-evolution.md)
|
||||
- **Status**: open
|
||||
- **Priority**: medium
|
||||
- **Notes**: If the hub migrates to event-sourced replay (projector evolution), storage's call graph tables become disposable projections. But other graph types (ACL, tasks, secrets) may not have an event stream to replay from. The schema evolution design should work for both projections and direct-persisted data.
|
||||
- **Notes**: Post-v1. Honker streams enable event-sourced replay more naturally than before.
|
||||
|
||||
### OQ-13: Should schema evolution events be part of the event stream?
|
||||
### OQ-13: Schema evolution events in event stream?
|
||||
|
||||
- **Origin**: [schema-evolution.md](schema-evolution.md)
|
||||
- **Status**: open
|
||||
- **Priority**: low
|
||||
- **Notes**: Post-v1. For v1, schema changes are applied directly via the repository layer with version tracking.
|
||||
- **Notes**: Post-v1. Honker streams provide a natural transport for schema change events.
|
||||
|
||||
## Theme 5: Encrypted Data Scope
|
||||
|
||||
### OQ-14: Should encryption be per-attribute, per-node, or per-graph?
|
||||
### OQ-14: Per-attribute, per-node, or per-graph encryption?
|
||||
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: high
|
||||
- **Resolution**: Per-attribute. The `EncryptedData` schema is a single attribute within a node type, not the entire node. This preserves queryability on non-sensitive fields (ADR-023).
|
||||
- **Resolution**: Per-attribute. ADR-023.
|
||||
|
||||
### OQ-15: Should key management be in this package?
|
||||
### OQ-15: Key management in this package?
|
||||
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: high
|
||||
- **Resolution**: No. `@alkdev/storage` provides encryption/decryption primitives but NOT key management. The consuming application provides the key ring (ADR-026).
|
||||
- **Resolution**: No. Application provides key ring. ADR-026.
|
||||
|
||||
## Theme 6: Repository Layer
|
||||
|
||||
### OQ-16: Should the repository layer live in @alkdev/storage or in a consumer package?
|
||||
### OQ-16: Should repository layer live in storage or consumer?
|
||||
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: resolved
|
||||
- **Priority**: high
|
||||
- **Resolution**: The repository CRUD layer (host-specific typed queries, schema validation before writes) belongs in `@alkdev/storage`. The operations bridging layer (generating `OperationSpec`s from metagraph schemas) belongs in a consumer or adapter package. These are separate concerns — CRUD is a storage concern; call protocol integration is an application concern.
|
||||
- **Resolution**: CRUD in storage; operations bridging in consumer. ADR-033.
|
||||
|
||||
## Theme 7: Repository Layer Strategy
|
||||
### OQ-17: Attribute queries — JSON path, native columns, or dbtype-generated?
|
||||
|
||||
### OQ-17: How should the repository layer handle attribute queries — JSON path, native columns, or dbtype-generated?
|
||||
|
||||
- **Origin**: [forward-look.md](forward-look.md)
|
||||
- **Status**: resolved (v1)
|
||||
- **Priority**: high
|
||||
- **Resolution**: For v1, attribute queries use JSON path extraction (`json_extract` on SQLite, `->>`/`#>>` on PG). Hand-written CRUD for static tables. dbtype integration and hybrid approach are post-v1. See ADR-033. The long-term question of whether to adopt the hybrid approach (static tables via dbtype, dynamic attributes remain JSON) remains open for future iterations.
|
||||
- **Cross-references**: ADR-033, ADR-018, [forward-look.md](forward-look.md)
|
||||
- **Resolution**: JSON path for v1. ADR-033. Long-term hybrid still open but less pressing without PG dual maintenance.
|
||||
|
||||
### OQ-18: Should the repository layer's CRUD operations be auto-generated (drizzle-graphql pattern) or hand-written?
|
||||
### OQ-18: Auto-generated vs hand-written CRUD?
|
||||
|
||||
- **Origin**: [forward-look.md](forward-look.md)
|
||||
- **Status**: resolved (v1)
|
||||
- **Priority**: medium
|
||||
- **Resolution**: For v1, hand-write CRUD functions with explicit signatures. The three long-term options (hand-written, auto-generated from Drizzle, auto-generated from dbtype) remain open for future iterations. See ADR-033.
|
||||
- **Cross-references**: ADR-033, OQ-17
|
||||
- **Resolution**: Hand-write for v1. ADR-033.
|
||||
|
||||
### OQ-19: Where does the storage-operations bridge package live in the @alkdev workspace?
|
||||
### OQ-19: Storage-operations bridge package location?
|
||||
|
||||
- **Origin**: [forward-look.md](forward-look.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Notes**: Four options: (1) hub-internal code, (2) dedicated `@alkdev/storage-operations` adapter, (3) `from-storage` adapter inside `@alkdev/operations`, (4) part of `@alkdev/dbtype`'s `from-dbtype` adapter. Option 1 is the most immediate (no new package). Option 2 is the cleanest separation. Option 3 creates an undesirable dependency direction (operations → storage). Option 4 is the long-term goal if dbtype is adopted. The choice depends on OQ-17/OQ-18 resolution: if hand-written CRUD, the bridge is trivial and can live in the hub; if auto-generated from dbtype, the bridge naturally lives with dbtype.
|
||||
- **Cross-references**: OQ-16, OQ-17, ADR-033
|
||||
- **Resolution**: Less pressing now that Honker replaces the Redis transport. Can live in the hub for v1. Revisit if an adapter package becomes valuable.
|
||||
|
||||
## Theme 8: Access Control
|
||||
## Theme 7: Access Control
|
||||
|
||||
### OQ-20: Should `DelegatesEdge` support temporary delegation with expiration?
|
||||
### OQ-20: Should DelegatesEdge support expiration?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Priority**: low
|
||||
- **Notes**: Currently, `DelegatesEdge` has `narrowedScopes` and `narrowedResources` but no `expiresAt`. If delegation should be time-limited (e.g., "delegate for this session only" or "delegate for 24 hours"), an expiration attribute is needed. Session-scoped delegation could be modeled by creating/removing edges per session, avoiding the need for an `expiresAt` attribute. Time-based expiration adds complexity to the evaluator (checking edge validity at call time) but may be useful for non-session contexts.
|
||||
- **Cross-references**: ADR-036
|
||||
- **Notes**: Session-scoped delegation could be modeled by creating/removing edges per session rather than adding `expiresAt`.
|
||||
|
||||
### OQ-21: Should the ACL evaluator live in `@alkdev/storage` or in the hub?
|
||||
### OQ-21: Should ACL evaluator live in storage or hub?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: high
|
||||
- **Notes**: The ACL evaluator traverses delegation chains and computes effective scopes. Three options: (1) `@alkdev/storage` provides traversal primitives (walk edges, compute effective scopes for a principal given a graph instance) and the hub composes them with `@alkdev/operations`' `enforceAccess`. (2) The hub implements the evaluator from scratch, using storage's repository layer for graph queries. (3) A new `@alkdev/acl` package provides the evaluator, depending on both `@alkdev/storage` and `@alkdev/operations`. Option 1 keeps the dependency direction clean (storage doesn't depend on operations). Option 3 is the cleanest separation but adds a package. The choice depends on whether the evaluator is generic enough to be reusable across different hub implementations.
|
||||
- **Cross-references**: ADR-034, ADR-037
|
||||
- **Resolution**: Storage provides traversal primitives; hub composes with operations `enforceAccess`. The single-host model (no PG/SQLite split) simplifies this — no cross-DB joins needed for ACL evaluation within a tenant DB. ADR-034.
|
||||
|
||||
### OQ-22: How are ACL graph instances created and managed?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Notes**: Several options: (1) One global ACL graph instance per hub. Simple but means all orgs share a single graph — large graphs may have traversal performance implications. (2) One ACL graph instance per org. Isolated, each org's permissions are self-contained. Requires cross-org delegation to span graphs. (3) One ACL graph instance per "scoping context" (e.g., per spoke context). Most granular but most complex. The choice depends on whether delegation crosses org boundaries (if a user delegates to an agent in another org's context, graphs must be traversable across instances).
|
||||
- **Cross-references**: ADR-037
|
||||
- **Resolution**: One ACL graph instance per tenant DB (ADR-040). The tenant DB is inherently org-scoped, so the ACL graph covers one org. No cross-org scoping issue within a single tenant DB.
|
||||
- **Cross-references**: ADR-040
|
||||
|
||||
### OQ-23: Should `BelongsToEdge` be derived (materialized from `organization_members`) or primary (ACL graph is the source of truth)?
|
||||
### OQ-23: BelongsToEdge derived or primary?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Notes**: The hub already has an `organization_members` table with `membershipLevel`. If `BelongsToEdge` is derived, the hub writes both `organization_members` rows and ACL graph edges when membership changes, keeping them in sync. If `BelongsToEdge` is primary, the ACL graph is the source of truth and the hub reads org membership from the graph. Derived is consistent with the hub's existing identity tables being authoritative. Primary means the ACL graph replaces org membership data, requiring graph queries for simple membership lookups. Lean toward derived — the hub's identity tables are authoritative for authentication, the ACL graph is authoritative for authorization.
|
||||
- **Cross-references**: ADR-034
|
||||
- **Resolution**: Derived. `organization_members` SQL table is authoritative for indexed lookups; `BelongsToEdge` in ACL graph enables traversal evaluation. ADR-045.
|
||||
- **Cross-references**: ADR-045
|
||||
|
||||
### OQ-24: How does `identityId` reference hub entities without creating a package dependency?
|
||||
### OQ-24: How does identityId reference hub entities without package dependency?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Status**: resolved
|
||||
- **Priority**: medium
|
||||
- **Notes**: `PrincipalNode.identityId` references an account, organization, or role in the hub's database, but `@alkdev/storage` must not depend on `@alkdev/operations` or the hub. The `identityId` is a string, not a FK. This is consistent with ADR-020 (no nodeTypeId on nodes) — the metagraph pattern stores node attributes without assuming external referential integrity. Options: (1) Logical references (current design) — `identityId` is a string that the hub resolves. (2) Convention-based references — a URI scheme like `alk://account/user-1` or `alk://org/acme` that encodes the entity type and ID. (3) A shared types package that both storage and hub import. Option 1 is the simplest and consistent with the existing pattern. The burden of referential integrity falls on the consumer (the hub), not on storage.
|
||||
- **Cross-references**: ADR-020, ADR-034
|
||||
- **Resolution**: Logical string references, consistent with ADR-020. With identity tables now in `@alkdev/storage` (ADR-041), the `PrincipalNode.identityId` logically references `accounts.id` in the system DB. Same pattern, clearer provenance.
|
||||
- **Cross-references**: ADR-020, ADR-041
|
||||
|
||||
### OQ-25: What are the scope string semantics for subset validation?
|
||||
### OQ-25: Scope string semantics for subset validation?
|
||||
|
||||
- **Origin**: [acl.md](acl.md)
|
||||
- **Status**: open
|
||||
- **Priority**: high
|
||||
- **Notes**: `narrowedScopes ⊆ effectiveScopes` is the no-escalation invariant, but the semantics of this subset check depend on how scope strings work. `@alkdev/operations` uses keypal's scope model (colon-separated hierarchical segments, `*` wildcard for suffix matching). `"dev:*"` matches `"dev.read"`, `"dev.write"`, `"dev.fs.read"`, etc. The ACL evaluator must use the same semantics or delegation validation will be inconsistent with runtime access checks. Option: import scope matching logic from `@alkdev/operations` or extract it to a shared utility. The ACL graph stores scopes as plain strings; matching is an evaluator concern, not a storage concern.
|
||||
- **Cross-references**: ADR-036, `/workspace/@alkdev/operations/src/access.ts`
|
||||
- **Notes**: Keypal's colon-separated hierarchical scope model with `*` wildcard. ACL evaluator must use same semantics. Scope matching is an evaluator concern, not a storage concern.
|
||||
|
||||
## Theme 8: Honker and SQLite
|
||||
|
||||
### OQ-26: Can Honker fully replace @alkdev/pubsub's Redis transport for single-node deployments?
|
||||
|
||||
- **Origin**: [honker-integration.md](honker-integration.md)
|
||||
- **Status**: open
|
||||
- **Priority**: high
|
||||
- **Notes**: Honker's `notify()`/`listen()` and `stream()`/`subscribe()` provide the pub/sub primitives. The question is whether `@alkdev/pubsub`'s `TypedEventTarget` interface can be backed by Honker instead of Redis, and whether multi-node deployments still need Redis for internode communication.
|
||||
|
||||
### OQ-27: How are schema migrations applied across all tenant DBs?
|
||||
|
||||
- **Origin**: [honker-integration.md](honker-integration.md)
|
||||
- **Status**: open
|
||||
- **Priority**: high
|
||||
- **Notes**: Each tenant DB has its own migration history. When a schema change is deployed, all tenant DBs need migration. Options: (1) Migration queue — enqueue a migration job per tenant DB, workers claim and execute. (2) Lazy migration — migrate on first access. (3) Startup sweep — hub iterates all tenant DBs at startup and applies pending migrations.
|
||||
|
||||
### OQ-28: How does cross-tenant delegation work with separate DBs?
|
||||
|
||||
- **Origin**: [overview.md](overview.md)
|
||||
- **Status**: open
|
||||
- **Priority**: medium
|
||||
- **Notes**: If a user in org A delegates to a user in org B, both tenant DBs are involved. The hub mediates. For v1, cross-tenant delegation can be deferred or handled via the system DB as a coordination point.
|
||||
|
||||
### OQ-29: Should the Drizzle-Honker adapter be published as a standalone npm package?
|
||||
|
||||
- **Origin**: [honker-integration.md](honker-integration.md)
|
||||
- **Status**: open
|
||||
- **Priority**: low
|
||||
- **Notes**: The adapter is ~100 lines and useful to anyone combining Drizzle with Honker. Publishing as `drizzle-honker` would benefit the community. Decision: start inside `@alkdev/storage`, extract later if there's demand.
|
||||
|
||||
## ADR Impact
|
||||
|
||||
| ADR | Resolves | Informs |
|
||||
|-----|----------|---------|
|
||||
| ADR-003 | OQ-01 (partial) | |
|
||||
| ADR-015 | OQ-05 | |
|
||||
| ADR-017 | OQ-06 | |
|
||||
| ADR-020 | OQ-24 | |
|
||||
| ADR-023 | OQ-14 | |
|
||||
| ADR-026 | OQ-15 | |
|
||||
| ADR-033 | OQ-04, OQ-16, OQ-17, OQ-18 | |
|
||||
| ADR-034 | OQ-03, OQ-21 | OQ-25 |
|
||||
| ADR-035 | OQ-03 | |
|
||||
| ADR-038 | OQ-04 (moot) | OQ-17 (less pressure) |
|
||||
| ADR-040 | OQ-22 | OQ-27, OQ-28 |
|
||||
| ADR-041 | OQ-24 | |
|
||||
| ADR-042 | | OQ-24 |
|
||||
| ADR-043 | | |
|
||||
| ADR-044 | OQ-19 (less pressure) | |
|
||||
| ADR-045 | OQ-23 | OQ-20 |
|
||||
Reference in New Issue
Block a user