Copy architecture docs, ADRs, storage domain specs, research, reviews, and 56 storage architecture tasks from the alkhub_ts monorepo. Adapt for standalone @alkdev/hub repo structure (src/ not packages/hub/). Sanitize all sensitive information: - Replace private IPs (10.0.0.1) with localhost defaults - Remove internal server hostnames (dev1, ns528096) - Replace /workspace/ private paths with npm package references - Remove hardcoded credentials from examples - Rewrite infrastructure.md without private network details Add Deno project scaffolding: deno.json (pinned deps), .gitignore, AGENTS.md, entry point. Migrate existing code stubs (crypto, config types, logger) with updated import paths.
4.8 KiB
status, last_updated
| status | last_updated |
|---|---|
| stable | 2026-04-22 |
Storage Spec Phase 1 Resolutions
Architectural decisions made during the storage spec stabilization planning session on 2026-04-22. These resolutions inform all downstream task execution.
Decisions
D1. Cascade Policy Defaults
| Data Category | Default Cascade | Rationale |
|---|---|---|
| Audit/traceability data | RESTRICT on NOT NULL FKs; SET NULL on nullable FKs | NOT NULL FKs (ownerId) prevent account deletion. Nullable FKs (keyId, sessionId, orgId) preserve the row while clearing the reference. Both patterns prevent data loss. |
| Live session data | Nullable FK + SET NULL | Orphaned sessions preserve conversation history for audit/debugging |
| Ephemeral config (spoke ops, etc.) | CASCADE | Delete with parent — these are runtime artifacts |
| Transferable ownership | RESTRICT + transfer workflow | Cannot delete account that owns an org; must transfer first |
D2. Message IDs — Composite Index Approach
Decision: Messages table keeps UUIDv4 (commonCols.id). Ordering is handled by composite index (session_id, created_at, id).
Rationale:
- ADR-003's sortable IDs remain in effect for
partsonly - Composite index provides efficient ordering for messages without requiring sortable IDs
- Simpler opencode conversation import — opencode uses UUIDv4 message IDs natively
- ADR-003 is amended to scope sortable IDs to
parts, notmessages
Action: Amend ADR-003, update sessions.md, update table-reference.md
D3. Operations Schema — Definitions + Registrations Split (Option A)
Decision: Split operation_specs into two tables:
operations(definitions):id,namespace,name,type(query/mutation/subscription),inputSchema,outputSchema,accessControl,descriptionoperation_registrations:id,operationId → operations.id,providerType(spoke|client),providerId,status(active|inactive),registeredAt
Rationale:
- Separation of "what an operation is" from "who provides it right now"
- Multiple instances of the same client (e.g., 5 opencode instances) share definitions but have separate registrations
- OpenAPI/MCP spec imports create definitions; spoke/client connection creates registrations
- On spoke disconnect: registration rows are deactivated (not deleted). Definitions survive.
- On admin spoke-row deletion: registrations CASCADE (ephemeral config pattern from D1)
- Call routing: resolve from definition → active registrations → provider
- More upfront schema work, but avoids a confusing refactor later when multi-instance clients arrive
Namespace convention: operations.namespace/name store post-remap identifiers (e.g., dev.{spokeId}.fs.read). This ensures uniqueness across multiple providers of the same logical operation. Pre-remap identifiers are stored in operation_registrations.metadata for traceability.
Actions:
- Rename
operation_specs→operationsacross all docs - Add
operation_registrationstable spec to spokes.md - Update table-reference.md with new FK relationships and cascade policies
- Update spokes.md disconnect lifecycle to deactivate registrations, not delete
- Update ADR-006 to reflect the split
D4. Key Rotation
- API key rotation: Handled by keypal library (ADR-004)
- Client secret encryption: Needs multi-key format specification. Current
HUB_ENCRYPTION_KEY(singular, env var) was insufficient — superseded by the two-layer key model in hub-config.md and ADR-008 (revised). Taskspecify-key-rotation-protocoladdresses this.
D5. Account Deactivation
Decision: Add status enum column (active | suspended | deactivated) to accounts table, not a boolean.
Rationale: More extensible — allows distinguishing "admin suspended" from "user deactivated" in the future. Consistent with having meaningful status semantics rather than overloaded booleans.
Action: Update identity.md accounts table spec, update table-reference.md
D6. System Account Email Convention
Decision: Email reservation for system/LLM accounts is deployment-configurable, not hardcoded to any domain.
Convention: Deployments MAY reserve an email domain or pattern (e.g., {model}@llm.example.com or {model}@system.example.com) for non-human accounts. This prevents collision between human and system-generated accounts and enables attribution in git and audit logs.
Anti-pattern: Do NOT hardcode any specific domain (e.g., alk.dev) in architecture documentation. The convention is generic; the specific domain is a deployment concern.
Action: Update identity.md to document the configurable pattern convention, not a specific domain.
References
- docs/reviews/storage-architecture-review-2026-04-21.md — source review
- tasks/architecture/storage/* — downstream implementation tasks