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.
84 lines
4.8 KiB
Markdown
84 lines
4.8 KiB
Markdown
---
|
|
status: stable
|
|
last_updated: 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 `parts` only
|
|
- 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`, not `messages`
|
|
|
|
**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:
|
|
|
|
1. **`operations`** (definitions): `id`, `namespace`, `name`, `type` (query/mutation/subscription), `inputSchema`, `outputSchema`, `accessControl`, `description`
|
|
2. **`operation_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` → `operations` across all docs
|
|
- Add `operation_registrations` table 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](../architecture/hub-config.md) and ADR-008 (revised). Task `specify-key-rotation-protocol` addresses 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 |