Files
storage/docs/architecture/sqlite-host.md
glm-5.1 ae242f33b9 Restructure identity tables: separate credential types, add peer_credentials, specify FK cascades and indexes
Identity tables were derived from hub's PostgreSQL schema but simplified
without documenting what was removed or why. This restructures them for the
current auth landscape (API key + wraith SSH/cert-authority):

- ADR-049: Separate api_keys and peer_credentials tables (different lookup
  patterns, columns, lifecycles), remove Gitea columns, map hub data→metadata
- ADR-050: Extract SHA-256 vs KDF decision from inline spec text
- Add peer_credentials table for SSH key and cert-authority auth
- Specify all FK cascade behaviors within system DB (RESTRICT, CASCADE, SET NULL)
- Complete index specifications for all identity tables
- Add scope boundary section (storage owns schemas, not auth/authorization)
- Update audit_logs with credentialId+credentialType polymorphic reference
- Add 3 new open questions (OQ-33/34/35) for credential type expansion
2026-06-02 12:33:20 +00:00

39 KiB

status, last_updated
status last_updated
draft 2026-06-02

SQLite Host

The SQLite database host for @alkdev/storage. Uses Drizzle ORM with Honker for database operations, pub/sub, event streams, and task queues. TypeBox schemas are generated from Drizzle table definitions via src/sqlite/utils/ (folded from @alkdev/dbtype/@alkdev/drizzlebox, ADR-046).

Overview

The SQLite host provides:

  1. Metagraph tables — 6 tables for graph types, node types, edge types, graphs, nodes, and edges (ADR-002)
  2. Identity tables — accounts, organizations, organization_members, api_keys, audit_logs for multi-tenant authentication and authorization (ADR-041)
  3. Drizzle relations for the relational query API
  4. TypeBox schemas generated from Drizzle tables (select/insert validation) via src/sqlite/utils/ (folded from @alkdev/drizzlebox, ADR-046)
  5. Drizzle-Honker adapter — thin session adapter for Honker integration (ADR-044, POC validated)
  6. HonkerEventTarget — pubsub TypedEventTarget on Honker primitives (ADR-047, POC validated)
  7. Client factoriescreateSystemDatabase(client) and createTenantDatabase(client) for the system/tenant DB model (ADR-040)

Package Structure

src/sqlite/
├── tables/
│   ├── common.ts               # commonCols
│   ├── identity/
│   │   ├── accounts.ts         # accounts table + select/insert schemas
│   │   ├── organizations.ts    # organizations table + select/insert schemas
│   │   ├── organization_members.ts # org membership + select/insert schemas
│   │   ├── api_keys.ts         # API key credentials + select/insert schemas
│   │   ├── peer_credentials.ts # SSH key / cert-authority credentials + select/insert schemas
│   │   ├── audit_logs.ts       # audit trail + select/insert schemas
│   │   └── index.ts            # barrel re-export
│   ├── metagraph/
│   │   ├── graphTypes.ts       # graph_types table + select/insert schemas
│   │   ├── nodeTypes.ts        # node_types table + select/insert schemas
│   │   ├── edgeTypes.ts        # edge_types table + select/insert schemas
│   │   ├── graphs.ts           # graphs table + select/insert schemas
│   │   ├── nodes.ts            # nodes table + select/insert schemas
│   │   ├── edges.ts            # edges table + select/insert schemas
│   │   └── index.ts            # barrel re-export
│   └── index.ts                # barrel re-export
├── utils/                       # folded from @alkdev/dbtype Phase 0 (ADR-046)
│   ├── schema.ts               # createSelectSchema, createInsertSchema, createUpdateSchema
│   ├── column.ts               # Column→TypeBox mappings (SQLite-only dispatch)
│   ├── types.ts                 # Public + internal TypeScript interfaces
│   ├── constants.ts             # Integer range constants
│   └── utils.ts                 # isColumnType, isWithEnum, type helpers
├── relations.ts                 # Drizzle relational mappings
├── adapter.ts                   # Drizzle-Honker session adapter
├── event-target.ts              # HonkerEventTarget (pubsub TypedEventTarget on Honker)
├── schema.ts                    # re-exports all tables + relations
└── client.ts                    # createSystemDatabase(), createTenantDatabase()

Common Columns

All tables share these columns:

{
  id: text("id").primaryKey(),
  metadata: text("metadata", { mode: "json" }).$type<Record<string, unknown>>().default({}),
  createdAt: integer("created_at", { mode: "timestamp" })
    .default(sql`(strftime('%s', 'now'))`)
    .notNull(),
  updatedAt: integer("updated_at", { mode: "timestamp" })
    .default(sql`(strftime('%s', 'now'))`)
    .notNull(),
}
  • id is a consumer-generated UUID text PK (no $defaultFn)
  • metadata is an extension namespace following _subsystem.key convention
  • createdAt/updatedAt are Unix epoch integers with timestamp mode
  • No $onUpdate — consumers must set updatedAt explicitly

Metagraph Tables

graph_types

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
name text not null, unique Graph type name (e.g., "call-graph", "acl")
description text default "" Human-readable description
config text (JSON) not null GraphConfig — directed/undirected/mixed, multi, self-loops
version integer not null, default 1 Breaking schema version (ADR-029)
scope text not null, default "system" system / tenant / user (ADR-043)

The scope column (ADR-043) controls who can create and modify graph type definitions. System-scoped types (acl, call-graph) are seeded at setup time and cannot be modified through the repository API.

node_types

Column Type Constraints Notes
id text PK
metadata text (JSON) default {}
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
graphTypeId text not null, FK → graphTypes.id (cascade) Parent graph type
name text not null Node type name (e.g., "call", "account")
description text default ""
schema text (JSON) not null TypeBox schema for node attributes

Unique constraint: (graphTypeId, name) — node type names are unique within a graph type.

edge_types

Column Type Constraints Notes
id text PK
metadata text (JSON) default {}
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
graphTypeId text not null, FK → graphTypes.id (cascade) Parent graph type
name text not null Edge type name (e.g., "triggered", "can_read")
description text default ""
schema text (JSON) not null TypeBox schema for edge attributes
allowedSourceTypes text (JSON) default [] Node type names valid at source endpoint
allowedTargetTypes text (JSON) default [] Node type names valid at target endpoint

Unique constraint: (graphTypeId, name). Empty array semantics: [] means "no restriction" — any node type is valid.

graphs

Column Type Constraints Notes
id text PK
metadata text (JSON) default {}
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
graphTypeId text FK → graphTypes.id (set null) Set null on graph type deletion (orphan graph)
name text not null Graph instance name
description text default ""
status text not null, enum: active, archived, draft Default: draft
ownerId text nullable Logical reference to accounts.id (ADR-042)
projectId text nullable Logical reference to project identity (ADR-042)

Scoping columns (ADR-042): ownerId and projectId are logical references to entities in the system DB (accounts, projects). No FK constraint because the referenced tables live in a different database file. The consumer enforces referential integrity at the application layer.

No orgId column — the tenant DB file itself IS the org scope (ADR-040).

Indexes: idx_graphs_owner_id on (ownerId), idx_graphs_project_id on (projectId), idx_graphs_owner_id_project_id on (ownerId, projectId).

On graphTypeId set null: Orphan graphs cannot validate their node/edge types against a missing type definition. The application should prevent graph type deletion if active graphs reference it.

nodes

Column Type Constraints Notes
id text PK
metadata text (JSON) default {}
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
graphId text not null, FK → graphs.id (cascade) Parent graph
key text not null Consumer-defined identity within the graph
attributes text (JSON) not null, default {} Node attributes validated by node type schema

Unique constraint: (graphId, key) — node keys are unique within a graph. No nodeTypeId column: ADR-020.

edges

Column Type Constraints Notes
id text PK
metadata text (JSON) default {}
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
graphId text not null, FK → graphs.id (cascade) Parent graph
key text Consumer-defined identity (null for anonymous edges)
sourceNodeKey text not null Source node key within the graph
targetNodeKey text not null Target node key within the graph
attributes text (JSON) not null, default {} Edge attributes validated by edge type schema
undirected integer (boolean) default false Treat as undirected regardless of graph type

Unique constraint: (graphId, key). Foreign keys: sourceNodeKey and targetNodeKey reference (nodes.graphId, nodes.key) with cascade delete (ADR-022).

Identity Tables

Identity tables live in the system DB (ADR-040, ADR-041). They provide multi-tenant authentication and authorization infrastructure. Storage owns the table schemas and FK constraints; it does not own authentication logic, authorization rules, key lifecycle, or credential verification — those are consumer concerns.

The identity schemas are derived from the hub's PostgreSQL identity tables (ADR-049). Gitea-specific columns are removed (git hosting integration is a consumer concern, modeled in metagraph instances or consumer metadata). The hub's data JSONB columns map to commonCols.metadata (same extension namespace, _subsystem.key convention).

Scope Boundary

Storage's identity tables provide persistence and structural constraints. Consumer concerns NOT in storage's scope:

  • Key generation, hashing, and verification (keypal, wraith handle this)
  • Authentication protocol flow (hub/wraith handle this)
  • Authorization and scope evaluation (ACL graph + operations enforce this)
  • Account lifecycle policy (when to suspend, deactivate, transfer ownership)
  • Key rotation and revocation orchestration
  • Session and connection management

accounts

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace (_subsystem.key). Replaces hub's data JSONB column (ADR-049). Account preferences, profile data.
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
email text not null, unique Primary identifier. Service accounts may use deployment-configured reserved patterns.
displayName text Display name
accessLevel text not null, default user admin, user, service
status text not null, default active active, suspended, deactivated

accessLevel semantics: admin manages all resources across organizations. user manages own resources and org-scoped resources. service is an automated account (LLM workers, spoke credentials, CI tokens) — no git hosting link required.

status semantics: active can authenticate. suspended is admin-locked (security hold). deactivated is user-initiated shutdown. Suspended and deactivated accounts retain owned resources (RESTRICT FK) but cannot authenticate.

Indexes: unq_accounts_email UNIQUE on (email), idx_accounts_access_level on (accessLevel), idx_accounts_status on (status).

No giteaUsername column — git hosting integration is a consumer concern (ADR-049). When needed, store git associations in metadata or a metagraph instance.

organizations

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace. Replaces hub's data JSONB column (ADR-049). Org settings, billing data.
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
name text not null, unique Organization name
slug text not null, unique URL-friendly identifier
ownerId text not null, FK → accounts.id (RESTRICT) Administrative/transferable owner. Cannot delete owner account while org exists. Transfer ownership first.

ownerId semantics: The administrative owner of the organization. This account MUST also have membershipLevel: 'owner' in organization_members (enforced by consumer). To change the owner, the consumer calls a transfer ownership operation that: (1) validates the new owner has membershipLevel: 'owner', (2) updates ownerId, (3) optionally demotes the old owner's membership level. RESTRICT cascade prevents deleting the owner account while the org exists.

Indexes: unq_organizations_name UNIQUE on (name), unq_organizations_slug UNIQUE on (slug), idx_organizations_owner_id on (ownerId).

Dual ownership representation: organizations.ownerId and organization_members.membershipLevel: 'owner' both represent ownership. The column exists for efficient lookup (a single indexed read for "who owns this org?") and RESTRICT FK semantics (cannot delete the owner account while the org exists). The membership row exists for relational queries ("list all owners of this org"). The consumer-enforced invariant is: ownerId always references an account that also has membershipLevel: 'owner' in organization_members. The consumer must maintain this invariant on membership changes and ownership transfers.

No giteaOrgName column — git hosting integration is a consumer concern (ADR-049).

organization_members

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
orgId text not null, FK → organizations.id (CASCADE) Org deletion removes memberships
accountId text not null, FK → accounts.id (CASCADE) Account deletion removes memberships
membershipLevel text not null owner, admin, member

Unique constraint: (orgId, accountId) — one membership per account per org.

membershipLevel semantics: owner has full control including member management. admin can manage projects and members. member can access org resources. Distinct from organizations.ownerIdmembershipLevel is runtime access control; ownerId is the administrative/transferable owner.

This table is the authoritative source for org membership (ADR-045). The ACL graph's BelongsToEdge is derived from it — when membership changes, the consumer writes the SQL row first, then creates or removes the ACL edge.

Indexes: unq_org_members_org_account UNIQUE on (orgId, accountId), idx_org_members_account_id on (accountId), idx_org_members_org_id on (orgId).

api_keys

API key credentials for bearer token authentication. The client sends a raw key; the consumer hashes it and looks up by keyHash. Storage does not perform hashing or verification — that is a consumer concern (keypal, hub).

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace. Scope data: metadata.scopes (string[]), metadata.resources (Record<string, string[]>), metadata.tags (string[]). Consumer provides the adapter (e.g., HubKeyStorage for keypal). Scopes remain in metadata rather than as native columns because scope schemas vary by consumer — keypal uses colon-separated hierarchies, other consumers may differ.
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
ownerId text not null, FK → accounts.id (CASCADE) Account deletion removes API keys
keyHash text not null, unique SHA-256 hash of raw key. Never stores raw key.
name text Human-readable key label
enabled integer not null, default 1 Immediate disable switch (1 = enabled, 0 = disabled)
expiresAt integer (timestamp) When the key expires (null = never)
revokedAt integer (timestamp) When the key was revoked (null = active). Permanent.
rotatedToId text Self-reference to api_keys.id — the key that replaced this one (null if not rotated).
lastUsedAt integer (timestamp) Last authentication time. Null if never used.

Key lifecycle states: enabled+not expired = active. enabled+expired = rejected. disabled = rejected regardless of expiration. revoked = permanently disabled regardless of enabled/expiry.

Rotation: When a key is rotated, the consumer creates a new api_keys row and sets the old key's rotatedToId to the new key's id. The old key's revokedAt is set at the same time. This provides an audit trail of key rotation without requiring a separate rotation history table.

SHA-256 rationale: API keys are high-entropy machine-generated strings (128-bit+). Brute-force against SHA-256 is infeasible for such inputs. Slow KDFs (bcrypt, Argon2) are unnecessary for machine keys — they add latency without meaningful security improvement. (ADR-050)

Indexes: unq_api_keys_key_hash UNIQUE on (keyHash), idx_api_keys_owner_id on (ownerId), idx_api_keys_enabled on (enabled), idx_api_keys_active on (ownerId) WHERE revokedAt IS NULL AND enabled = 1

peer_credentials

SSH key and certificate-authority credentials for wraith transport authentication. The client presents an Ed25519 public key or OpenSSH certificate; the consumer validates against the stored fingerprint. Storage does not perform SSH authentication — that is a consumer concern (wraith, hub).

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace. Cert data: metadata.principals (string[]), metadata.restrictions (string[]), metadata.caFingerprint (string, for cert-authority entries only).
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
ownerId text not null, FK → accounts.id (CASCADE) Account deletion removes peer credentials
credentialType text not null ssh_key, cert_authority
fingerprint text not null, unique Ed25519 key fingerprint (SHA-256, OpenSSH format)
publicKeyData text not null Full public key in OpenSSH format (ssh-ed25519 AAAA...)
name text Human-readable label
enabled integer not null, default 1 Immediate disable switch
expiresAt integer (timestamp) When the credential expires (null = never). Certificates carry expiry; standalone keys typically don't.
revokedAt integer (timestamp) When the credential was revoked (null = active).

credentialType semantics: ssh_key is an individual public key. The consumer verifies the key against known fingerprints. cert_authority is a trusted CA public key. The consumer validates certificates signed by this CA against the stored fingerprint. Both types share the same lookup pattern (present fingerprint → find by fingerprint → check owner + enable + expiry + revocation), which is why they share a table.

Adding new credential types (ADR-049): Credential types sharing the same lookup column as peer_credentials (fingerprint-based) add a new credentialType value to this table. Credential types requiring different lookup columns warrant their own table. (OQ-33) Current types assume Ed25519 only; additional SSH key types may require credentialType expansion.

Fingerprint format: OpenSSH SHA-256 fingerprint (base64, no prefix). Used for lookup during SSH authentication. The publicKeyData column stores the full key for reconstruction/verification when needed.

Indexes: unq_peer_credentials_fingerprint UNIQUE on (fingerprint), idx_peer_credentials_owner_id on (ownerId), idx_peer_credentials_credential_type on (credentialType), idx_peer_credentials_active on (ownerId) WHERE revokedAt IS NULL AND enabled = 1

audit_logs

Append-only audit trail for security-relevant events. The consumer (hub) writes entries for key operations, authentication events, membership changes, and other auditable actions. The consumer is responsible for reading and displaying audit data.

Column Type Constraints Notes
id text PK Consumer-generated UUID
metadata text (JSON) default {} Extension namespace. Session context: metadata.sessionId (when relevant).
createdAt integer (timestamp) not null, default now
updatedAt integer (timestamp) not null, default now
action text not null created, revoked, rotated, enabled, disabled, login, access_denied
ownerId text not null, FK → accounts.id (RESTRICT) The identity performing the action. RESTRICT prevents account deletion when audit entries exist — deactivate instead.
credentialId text Logical reference to api_keys.id or peer_credentials.id (nullable — not all events are credential-related).
credentialType text api_key, peer_credential, or null. Discriminator for credentialId — tells the consumer which table to look up.
orgId text FK → organizations.id (SET NULL) Organization context. Null for personal actions. Set null on org deletion to preserve audit trail.
details text (JSON) Action-specific context (IP, user agent, scope changes, etc.)

action enum is extensible: The initial set covers API key operations and basic auth events. Additional actions for account, membership, and organization lifecycle events (e.g., account_created, membership_added, org_created) should be added by consumers as those features are implemented.

credentialId + credentialType polymorphic reference: Replaces the previous keyId column (API key only). The pair allows audit entries to reference either credential table. No FK constraint — the consumer resolves the table based on credentialType (ADR-049).

orgId FK with SET NULL: Unlike credentialId (polymorphic, no single target table), orgId always references organizations.id within the same system.db. A real FK with SET NULL preserves the audit trail on org deletion (nulling the org reference without deleting the audit entry) while enforcing referential integrity at the database level rather than relying on consumer discipline.

Indexes: idx_audit_logs_owner_id on (ownerId), idx_audit_logs_credential_id on (credentialId), idx_audit_logs_action on (action), idx_audit_logs_created_at on (createdAt), idx_audit_logs_org_id on (orgId).

FK Cascade Behavior (System DB)

All identity table FKs are intra-database (same system.db file). Real constraints apply, not logical references.

Relationship onDelete Rationale
organizations.ownerId → accounts.id RESTRICT Cannot delete owner account while org exists. Transfer ownership first.
organization_members.orgId → organizations.id CASCADE Org deletion removes memberships
organization_members.accountId → accounts.id CASCADE Account deletion removes memberships
api_keys.ownerId → accounts.id CASCADE Account deletion removes API keys
peer_credentials.ownerId → accounts.id CASCADE Account deletion removes peer credentials
audit_logs.ownerId → accounts.id RESTRICT Audit integrity — deactivate accounts instead of deleting. Preserves accountability.
audit_logs.orgId → organizations.id SET NULL Org deletion preserves audit trail (org reference nulled, entry retained).

Polymorphic references (no FK, consumer resolves): audit_logs.credentialIdapi_keys.id or peer_credentials.id (disambiguated by audit_logs.credentialType).

Cross-DB logical references (no FK, different database file): graphs.ownerIdaccounts.id, graphs.projectId → project identity (ADR-042). Consumer enforces referential integrity at application layer.

Relations

System DB Relations

  • accounts → organizations: one-to-many (via organizations.ownerId)
  • accounts → organization_members: one-to-many (via organization_members.accountId)
  • accounts → api_keys: one-to-many (via api_keys.ownerId)
  • accounts → peer_credentials: one-to-many (via peer_credentials.ownerId)
  • accounts → audit_logs: one-to-many (via audit_logs.ownerId)
  • organizations → organization_members: one-to-many (via organization_members.orgId)

Tenant DB Relations

  • graphTypes → nodeTypes: one-to-many
  • graphTypes → edgeTypes: one-to-many
  • graphTypes → graphs: one-to-many
  • graphs → nodes: one-to-many
  • graphs → edges: one-to-many
  • nodes → outgoing edges (sourceNode): one-to-many
  • nodes → incoming edges (targetNode): one-to-many

Client Factories

createSystemDatabase(client)

Creates a Drizzle database instance with the identity schema (accounts, organizations, organization_members, api_keys, peer_credentials, audit_logs) attached.

import { createSystemDatabase } from "@alkdev/storage/sqlite";
import { open } from "@russellthehipp/honker-node";

const honkerClient = open("system.db");
const db = createSystemDatabase(honkerClient);

// Drizzle typed queries
const admins = db.select().from(accounts).where(eq(accounts.accessLevel, "admin"));

// Honker features on the same connection
db.$client.notify("account:created", { accountId: "user-1" });

createTenantDatabase(client)

Creates a Drizzle database instance with the metagraph schema (graph_types, node_types, edge_types, graphs, nodes, edges) attached.

import { createTenantDatabase } from "@alkdev/storage/sqlite";
import { open } from "@russellthehipp/honker-node";

const honkerClient = open("tenant-acme.db");
const db = createTenantDatabase(honkerClient);

// Drizzle typed queries
const activeGraphs = db.select().from(graphs).where(eq(graphs.status, "active"));

// Transactional: insert node + notify in one commit
db.transaction((tx) => {
  tx.insert(nodes).values({ graphId, key: "call-1", attributes: {} }).run();
  tx.$honkerTx.notify("nodes:created", { graphId, key: "call-1" });
});

Design Decisions

ADR Decision Summary
038 SQLite-first, PG removed Single database host
039 Honker as SQLite extension DB + pub/sub + queues in one file
040 System DB + tenant DB Identity in system.db, graphs in tenant-{orgId}.db
041 Identity tables in storage accounts, organizations, api_keys, peer_credentials, audit_logs
049 Identity schema restructuring Separate credential tables, remove Gitea, data→metadata, FK cascades
050 SHA-256 for API keys Fast hash for high-entropy machine keys, not slow KDF
042 Scoping columns on graphs ownerId, projectId on graphs table
043 Graph type scope system / tenant / user scope on graph_types
044 Drizzle-Honker adapter ~100-line session adapter, POC validated
045 org_members authoritative SQL table is source of truth; BelongsToEdge is derived
046 Fold drizzlebox as utils SQLite-only column mappings in src/sqlite/utils/
047 HonkerEventTarget pubsub TypedEventTarget on Honker
048 OperationSpecs as repo surface Table-defined operation contracts
019 JSON text for schema columns SQLite uses text with JSON mode
020 No nodeTypeId on nodes Node type enforced at application layer
022 Composite FKs for node refs Edges reference (graphId, sourceNodeKey)
008 Common columns pattern id, metadata, createdAt, updatedAt

Removed: actors Table

The actors table is removed per ADR-035. ACTOR_TYPE is replaced by the IdentityType enum in the AclGraph Module. Identity data lives in the accounts table (system DB) and PrincipalNode in ACL graph instances (tenant DB).

Removed: PostgreSQL Porting Notes

PostgreSQL is no longer a target (ADR-038). All porting notes from the previous version of this document are obsolete. The single database host is SQLite via Honker.

Concurrency Model

Honker opens databases in WAL mode with a bounded reader pool and single writer slot. This handles the expected concurrency for the hub use case:

  • Reader pool: Up to maxReaders (default 4) concurrent read connections. db.$client.query() uses the pool automatically.
  • Writer slot: Single exclusive writer, acquired by transaction(). If the slot is occupied, subsequent transaction() calls block until released.
  • Write timeout: Honker sets busy_timeout=5000 by default. Configurable at open() time.
  • WAL mode: Enables concurrent reads during writes. Required by Honker's reader pool architecture.

For multi-process deployments, set WAL mode and ensure the busy timeout is sufficient for expected lock contention.

References

  • Honker source: /workspace/honker/
  • Honker Node binding: /workspace/honker/packages/honker-node/
  • Hub identity tables (provenance): /workspace/@alkdev/hub/docs/architecture/storage/identity.md
  • Operations AccessControl: /workspace/@alkdev/operations/docs/architecture/api-surface.md
  • Source: src/sqlite/