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,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-05-30
|
||||
last_updated: 2026-05-31
|
||||
---
|
||||
|
||||
# Access Control Graph
|
||||
@@ -500,18 +500,14 @@ itself, with `actions: ["admin"]` for owners, `["manage"]` for admins.
|
||||
|
||||
## Scoping Model
|
||||
|
||||
### Two-Level Scoping
|
||||
With the system/tenant DB model (ADR-040), ACL scoping is simplified:
|
||||
|
||||
The ACL system operates at two levels:
|
||||
|
||||
1. **Operation-level** (setup-time): `AccessControl` on `OperationSpec` defines
|
||||
*what scopes and resource actions are required* to invoke an operation. This
|
||||
is registered once when the operation is defined and changes infrequently.
|
||||
This data lives in `@alkdev/operations` and the hub's `operations` table.
|
||||
|
||||
2. **Graph-level** (runtime): The ACL graph instance stores *who has what* and
|
||||
*who delegates to whom*. This is queried at call time to resolve whether a
|
||||
specific identity satisfies an operation's access control requirements.
|
||||
- **One ACL graph instance per tenant DB** — The tenant DB is inherently
|
||||
org-scoped. OQ-22 is resolved: each org gets its own ACL graph instance.
|
||||
- **No cross-org scoping within a tenant** — The entire tenant DB is one org.
|
||||
The ACL graph does not need `orgId` columns or cross-org filtering.
|
||||
- **Cross-org delegation** requires the hub to mediate between tenant DBs
|
||||
(OQ-28, open).
|
||||
|
||||
### How They Connect
|
||||
|
||||
@@ -538,9 +534,11 @@ The **evaluator** bridges the two:
|
||||
|
||||
### Org-Scoped Access
|
||||
|
||||
When a `BelongsToEdge` connects an account to an org, and the org has
|
||||
`ScopesEdge` connections to resources, the account inherits org-level access
|
||||
through its membership:
|
||||
Within a tenant DB, ACL evaluation is straightforward — the entire DB is one
|
||||
org. The PrincipalNode's `identityId` logically references `accounts.id` in the
|
||||
system DB (ADR-041). When evaluating ACL, the hub reads the account's org
|
||||
membership from the system DB's `organization_members` table (authoritative per
|
||||
ADR-045) and the ACL graph's `BelongsToEdge` (derived) in the tenant DB.
|
||||
|
||||
```
|
||||
Account PrincipalNode ──belongs_to──→ Org PrincipalNode
|
||||
@@ -565,29 +563,20 @@ own base scopes limit what they can exercise from org membership.
|
||||
### The Disconnected `actors` Table
|
||||
|
||||
The `actors` table in `src/sqlite/tables/actors.ts` is replaced by
|
||||
`PrincipalNode` in the ACL graph. The `ACTOR_TYPE` enum (`Human`, `Llm`,
|
||||
`Agent`) maps to `identityType` values (`account`, `service`, `account` — LLMs
|
||||
are accounts in the hub model per ADR-012). The standalone table has no
|
||||
foreign key relationships and was explicitly deferred pending ACL design (OQ-03).
|
||||
`PrincipalNode` in the ACL graph. The standalone table has been removed
|
||||
(ADR-035, ADR-038).
|
||||
|
||||
This does **not** mean the hub's `accounts` table is replaced. The hub's
|
||||
`accounts` table remains the authoritative identity store with email, access
|
||||
level, and Gitea linking. `PrincipalNode` in the ACL graph **references** the
|
||||
account by `identityId` but does not duplicate its columns. The ACL graph
|
||||
stores *authorization* data; the hub's identity tables store *authentication*
|
||||
data.
|
||||
### Hub's `organization_members` as Authoritative Source
|
||||
|
||||
### Hub's `organization_members` as a Source
|
||||
|
||||
The hub's `organization_members` table is the authoritative source for who
|
||||
belongs to which org. When org membership changes, the hub updates both:
|
||||
The hub's (now storage's) `organization_members` table is the authoritative
|
||||
source for who belongs to which org (ADR-045). When org membership changes,
|
||||
the consumer writes both:
|
||||
1. The `organization_members` row (fast lookup, FK constraints)
|
||||
2. The `BelongsToEdge` in the ACL graph instance (graph traversal, evaluation)
|
||||
|
||||
This dual-write is necessary because the hub needs fast SQL lookups for
|
||||
membership checks (e.g., "list all members of this org"), while the ACL graph
|
||||
needs the edge for traversal-based evaluation (e.g., "compute effective scopes
|
||||
for this account across all orgs").
|
||||
This dual-write is necessary because the SQL table provides O(1) membership
|
||||
lookups and cascade behavior, while the ACL graph needs the edge for
|
||||
traversal-based evaluation.
|
||||
|
||||
### Hub's Permission Resolution
|
||||
|
||||
@@ -608,15 +597,17 @@ the effective scope is `dev:read`.
|
||||
|
||||
- **OQ-20**: Should `DelegatesEdge` support temporary delegation with
|
||||
expiration? (Referenced in [open-questions.md](open-questions.md))
|
||||
- **OQ-21**: Should the ACL evaluator live in `@alkdev/storage` or in the hub?
|
||||
- **OQ-25**: What are the scope string semantics for subset validation?
|
||||
(Referenced in [open-questions.md](open-questions.md))
|
||||
- **OQ-22**: How are ACL graph instances created and managed? (Referenced in
|
||||
[open-questions.md](open-questions.md))
|
||||
- **OQ-23**: Should `BelongsToEdge` be derived (materialized from
|
||||
`organization_members`) or primary (ACL graph is the source of truth)?
|
||||
- **OQ-28**: How does cross-tenant delegation work with separate DBs?
|
||||
(Referenced in [open-questions.md](open-questions.md))
|
||||
- **OQ-24**: How does `identityId` reference hub entities without creating a
|
||||
package dependency? (Referenced in [open-questions.md](open-questions.md))
|
||||
|
||||
## Resolved Questions
|
||||
|
||||
- **OQ-21** (ACL evaluator location): Storage provides traversal primitives; hub composes with operations. Simplified by single-host model — no cross-DB joins needed within a tenant DB.
|
||||
- **OQ-22** (ACL graph instance lifecycle): One per tenant DB. ADR-040.
|
||||
- **OQ-23** (BelongsToEdge derivation): Derived from `organization_members`. ADR-045.
|
||||
- **OQ-24** (identityId reference): Logical reference to `accounts.id` in system DB. ADR-041.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
Reference in New Issue
Block a user