Files
hub/docs/decisions/storage-spec-phase1-resolutions.md
glm-5.1 2b63cda1c7 Setup repo: migrate architecture specs, code stubs, and tasks from alkhub_ts
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.
2026-05-25 10:56:32 +00:00

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 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_specsoperations 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 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