Skip to content

feat: add SQL authoritative host storage#222

Merged
ian-pascoe merged 6 commits into
mainfrom
worktree/quiet-valley-a73d
Jul 18, 2026
Merged

feat: add SQL authoritative host storage#222
ian-pascoe merged 6 commits into
mainfrom
worktree/quiet-valley-a73d

Conversation

@ian-pascoe

Copy link
Copy Markdown
Contributor

Summary

  • make SQLite the default authoritative host-state backend and support PostgreSQL for clustered deployments
  • persist Caplet records, revisions, installations, security state, Vault state, coordination, and operator activity through relational storage
  • add deterministic Markdown import/export, filesystem overlay precedence, legacy migration, dashboard/CLI administration, S3 asset verification, and deployment guidance
  • harden cluster parity, database-time leases, transaction boundaries, and fail-closed readiness
  • clarify issue-tracker and triage guidance

Verification

  • pnpm verify
  • focused SQLite/PostgreSQL storage and parity tests
  • live PostgreSQL lease/fencing checks
  • live S3 current-asset health checks
  • Docker Compose PostgreSQL migration/runtime/health smoke test

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 192 files, which is 92 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4629cf73-884f-44a0-a2f9-ea29e6bc19eb

📥 Commits

Reviewing files that changed from the base of the PR and between f857e5f and ee8fff6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (192)
  • .changeset/sql-host-storage.md
  • AGENTS.md
  • CONCEPTS.md
  • CONTEXT.md
  • README.md
  • apps/dashboard/src/components/DashboardApp.test.tsx
  • apps/dashboard/src/components/DashboardApp.tsx
  • apps/dashboard/src/components/StoredCapletsPage.test.tsx
  • apps/dashboard/src/components/StoredCapletsPage.tsx
  • apps/dashboard/src/components/ui/empty.tsx
  • apps/dashboard/src/components/ui/field.tsx
  • apps/dashboard/src/components/ui/label.tsx
  • apps/dashboard/src/components/ui/textarea.tsx
  • apps/dashboard/src/pages/dashboard/stored-caplets.astro
  • apps/docs/src/content/docs/reference/config.mdx
  • apps/landing/public/config.schema.json
  • deploy/postgres/finalize-runtime-grants.mjs
  • deploy/postgres/init-caplets-roles.sh
  • deploy/postgres/render-config.mjs
  • docker-compose.postgres.yml
  • docs/adr/0004-sql-authoritative-host-state.md
  • docs/adr/0005-caplet-file-layers-over-sql-records.md
  • docs/agents/issue-tracker.md
  • docs/agents/triage-labels.md
  • docs/operations/sql-authoritative-host-state.md
  • docs/specs/2026-07-18-sql-authoritative-host-state.md
  • package.json
  • packages/cli/package.json
  • packages/core/drizzle.postgres.config.ts
  • packages/core/drizzle.sqlite.config.ts
  • packages/core/package.json
  • packages/core/rolldown.config.ts
  • packages/core/scripts/copy-storage-migrations.mjs
  • packages/core/scripts/normalize-storage-migrations.mjs
  • packages/core/src/auth.ts
  • packages/core/src/auth/store.ts
  • packages/core/src/caplet-files-bundle.ts
  • packages/core/src/caplet-files.ts
  • packages/core/src/caplet-sets.ts
  • packages/core/src/cli.ts
  • packages/core/src/cli/auth.ts
  • packages/core/src/cli/code-mode.ts
  • packages/core/src/cli/commands.ts
  • packages/core/src/cli/completion.ts
  • packages/core/src/cli/setup-caplet.ts
  • packages/core/src/cli/vault.ts
  • packages/core/src/config-runtime.ts
  • packages/core/src/config.ts
  • packages/core/src/current-host/catalog-operations.ts
  • packages/core/src/current-host/client-operations.ts
  • packages/core/src/current-host/operations.ts
  • packages/core/src/current-host/record-operations.ts
  • packages/core/src/current-host/vault-operations.ts
  • packages/core/src/dashboard/activity-log.ts
  • packages/core/src/dashboard/session-store.ts
  • packages/core/src/dashboard/types.ts
  • packages/core/src/downstream.ts
  • packages/core/src/engine.ts
  • packages/core/src/google-discovery/manager.ts
  • packages/core/src/graphql.ts
  • packages/core/src/http-actions.ts
  • packages/core/src/install.ts
  • packages/core/src/lockfile.ts
  • packages/core/src/openapi.ts
  • packages/core/src/project-binding/types.ts
  • packages/core/src/remote-control/dispatch.ts
  • packages/core/src/remote-control/types.ts
  • packages/core/src/remote/server-credential-store.ts
  • packages/core/src/remote/server-credentials.ts
  • packages/core/src/serve/http.ts
  • packages/core/src/serve/stdio.ts
  • packages/core/src/setup/local-store.ts
  • packages/core/src/setup/runner.ts
  • packages/core/src/storage/asset-store.ts
  • packages/core/src/storage/backend-auth.ts
  • packages/core/src/storage/caplet-records.ts
  • packages/core/src/storage/catalog-lifecycle.ts
  • packages/core/src/storage/coordination.ts
  • packages/core/src/storage/dashboard-sessions.ts
  • packages/core/src/storage/database.ts
  • packages/core/src/storage/drizzle/postgres/0000_ancient_firedrake.sql
  • packages/core/src/storage/drizzle/postgres/0001_lean_hellcat.sql
  • packages/core/src/storage/drizzle/postgres/0002_motionless_magneto.sql
  • packages/core/src/storage/drizzle/postgres/0003_nice_colonel_america.sql
  • packages/core/src/storage/drizzle/postgres/0004_big_rawhide_kid.sql
  • packages/core/src/storage/drizzle/postgres/0005_steady_george_stacy.sql
  • packages/core/src/storage/drizzle/postgres/0006_gray_deathstrike.sql
  • packages/core/src/storage/drizzle/postgres/0007_heavy_kingpin.sql
  • packages/core/src/storage/drizzle/postgres/0008_old_gladiator.sql
  • packages/core/src/storage/drizzle/postgres/0009_massive_union_jack.sql
  • packages/core/src/storage/drizzle/postgres/0010_melted_captain_flint.sql
  • packages/core/src/storage/drizzle/postgres/meta/0000_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0001_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0002_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0003_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0004_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0005_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0006_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0007_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0008_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0009_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/0010_snapshot.json
  • packages/core/src/storage/drizzle/postgres/meta/_journal.json
  • packages/core/src/storage/drizzle/sqlite/0000_charming_kulan_gath.sql
  • packages/core/src/storage/drizzle/sqlite/0001_plain_puma.sql
  • packages/core/src/storage/drizzle/sqlite/0002_mature_wolfpack.sql
  • packages/core/src/storage/drizzle/sqlite/0003_next_eddie_brock.sql
  • packages/core/src/storage/drizzle/sqlite/0004_nasty_praxagora.sql
  • packages/core/src/storage/drizzle/sqlite/0005_strange_franklin_richards.sql
  • packages/core/src/storage/drizzle/sqlite/0006_illegal_harry_osborn.sql
  • packages/core/src/storage/drizzle/sqlite/0007_polite_ser_duncan.sql
  • packages/core/src/storage/drizzle/sqlite/0008_dry_supreme_intelligence.sql
  • packages/core/src/storage/drizzle/sqlite/0009_gray_sister_grimm.sql
  • packages/core/src/storage/drizzle/sqlite/0010_friendly_stone_men.sql
  • packages/core/src/storage/drizzle/sqlite/meta/0000_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0001_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0002_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0003_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0004_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0005_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0006_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0007_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0008_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0009_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/0010_snapshot.json
  • packages/core/src/storage/drizzle/sqlite/meta/_journal.json
  • packages/core/src/storage/index.ts
  • packages/core/src/storage/installations.ts
  • packages/core/src/storage/legacy-migration.ts
  • packages/core/src/storage/migrations.ts
  • packages/core/src/storage/operator-activity.ts
  • packages/core/src/storage/project-bindings.ts
  • packages/core/src/storage/remote-security.ts
  • packages/core/src/storage/schema/postgres.ts
  • packages/core/src/storage/schema/sqlite.ts
  • packages/core/src/storage/setup-state.ts
  • packages/core/src/storage/types.ts
  • packages/core/src/storage/vault-grants.ts
  • packages/core/src/storage/vault-resolver.ts
  • packages/core/src/storage/vault-values.ts
  • packages/core/src/vault/index.ts
  • packages/core/test/auth.test.ts
  • packages/core/test/backend-auth-state-table.test.ts
  • packages/core/test/backend-auth-storage.test.ts
  • packages/core/test/caplet-asset-s3.test.ts
  • packages/core/test/caplet-record-storage.postgres.test.ts
  • packages/core/test/caplet-record-storage.test.ts
  • packages/core/test/caplets-lockfile.test.ts
  • packages/core/test/catalog-indexing.test.ts
  • packages/core/test/cli-completion.test.ts
  • packages/core/test/cli-install-output.test.ts
  • packages/core/test/cli-remote.test.ts
  • packages/core/test/cli.test.ts
  • packages/core/test/config.test.ts
  • packages/core/test/current-host-administration.test.ts
  • packages/core/test/current-host-catalog-operations.test.ts
  • packages/core/test/dashboard-activity.test.ts
  • packages/core/test/dashboard-api.test.ts
  • packages/core/test/dashboard-catalog.test.ts
  • packages/core/test/dashboard-runtime.test.ts
  • packages/core/test/dashboard-session-store.test.ts
  • packages/core/test/dashboard-session.test.ts
  • packages/core/test/dashboard-static.test.ts
  • packages/core/test/dashboard-vault.test.ts
  • packages/core/test/downstream.test.ts
  • packages/core/test/exposure-discovery.test.ts
  • packages/core/test/google-discovery.test.ts
  • packages/core/test/host-coordination.test.ts
  • packages/core/test/host-storage-config.test.ts
  • packages/core/test/host-storage-domain-parity.postgres.test.ts
  • packages/core/test/host-storage.test.ts
  • packages/core/test/installation-storage.test.ts
  • packages/core/test/legacy-migration.test.ts
  • packages/core/test/openapi.test.ts
  • packages/core/test/operator-activity-storage.test.ts
  • packages/core/test/package-boundaries.test.ts
  • packages/core/test/project-binding-storage.test.ts
  • packages/core/test/remote-control-dispatch.test.ts
  • packages/core/test/remote-login-cli.test.ts
  • packages/core/test/remote-security-storage.postgres.test.ts
  • packages/core/test/remote-security-storage.test.ts
  • packages/core/test/runtime-fingerprint.test.ts
  • packages/core/test/serve-http.test.ts
  • packages/core/test/setup-state-storage.test.ts
  • packages/core/test/storage-catalog-cli.test.ts
  • packages/core/test/storage-records-cli.test.ts
  • packages/core/test/vault-grant-storage.test.ts
  • packages/core/test/vault-value-storage.test.ts
  • packages/opencode/test/opencode.test.ts
  • pnpm-workspace.yaml
  • schemas/caplets-config.schema.json
  • scripts/verify-sqlite-postgres-transfer.sh

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree/quiet-valley-a73d

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​better-sqlite3@​7.6.131001007180100
Added@​types/​pg@​8.20.01001007383100
Addedbetter-sqlite3@​12.11.18810010088100
Addedpg@​8.22.09910010089100
Addeddrizzle-kit@​0.31.10991009697100
Added@​aws-sdk/​client-s3@​3.1089.09910010098100

View full report

@socket-security

socket-security Bot commented Jul 18, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm better-sqlite3 is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/core/package.jsonnpm/better-sqlite3@12.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/better-sqlite3@12.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces SQLite as the default authoritative host-state backend and adds PostgreSQL support for clustered deployments, persisting caplet records, revisions, installations, vault state, coordination, and operator activity through Drizzle ORM. All of the previously-flagged blocking issues (SQLite SERIAL primary key, migration hash tracking, list() N+1 queries, activeNodeCount clock-skew, SQLite deferred-transaction lease race, and Postgres heartbeat deadlock) have been addressed in follow-up commit c2f6f8b8.

  • SQL storage layer (packages/core/src/storage/) provides dual-dialect (SQLite/Postgres) implementations for every domain store, with exclusive SQLite transactions, Postgres advisory locks, and database-clock timestamps for cluster safety.
  • Deployment tooling (deploy/postgres/) supplies role-provisioning, least-privilege grant scripts, and a config renderer; the Docker Compose file covers migration, runtime, and health-check containers.
  • Dashboard & CLI gain a new Stored Caplets page and CLI commands for browsing, editing, and importing/exporting SQL-backed caplet records via a deterministic Markdown format.

Confidence Score: 5/5

Safe to merge. All previously-flagged correctness issues have been addressed; the one remaining finding is a minor memory-efficiency concern in the GC path that does not affect correctness.

The previously-flagged bugs — SQLite lease deferred-transaction race, Postgres heartbeat deadlock, migration hash tracking, and the list() N+1 pattern — are all fixed. The only new finding is that collectAssetGarbage fetches the full payload column (binary blob content) when only hash and createdAt are needed, which wastes memory on SQL-only deployments but does not affect correctness or cluster safety.

packages/core/src/storage/caplet-records.ts — the collectAssetGarbage full-column scan is the only file worth a second look.

Important Files Changed

Filename Overview
packages/core/src/storage/caplet-records.ts N+1 list() fix is in place (5 bounded batch queries); collectAssetGarbage does a full-table SELECT including the potentially large payload/bytea column when only hash and createdAt are needed for GC candidate selection.
packages/core/src/storage/coordination.ts Cluster coordination: previously-flagged deadlock (Postgres heartbeat) and SQLite deferred-transaction (lease acquisition) bugs are fixed; clock-authoritative activeNodeCount uses clock_timestamp(); advisory locks protect config-generation and lease operations.
packages/core/src/storage/migrations.ts SQLite migration runner now uses INTEGER PRIMARY KEY and validates the full applied history with hashes; Postgres delegates to Drizzle migrator; assertSchemaIsNotNewer prevents downgrade.
packages/core/src/storage/database.ts HostStorage factory correctly opens SQLite/Postgres, auto-migrates SQLite on createHostStorage, validates Postgres health before serving, and properly closes all resources.
packages/core/src/storage/schema/postgres.ts Comprehensive Postgres schema covering caplet records, revisions, installations, vault, remote auth, dashboard sessions, and cluster coordination; proper FK cascades and unique indexes throughout.
packages/core/src/storage/schema/sqlite.ts SQLite schema mirrors Postgres with appropriate type translations (jsonb→text/json, bytea→blob, boolean→integer); consistent FK cascades and index definitions.
deploy/postgres/finalize-runtime-grants.mjs Post-migration script grants SELECT/INSERT/UPDATE/DELETE to caplets_runtime on all tables except the migration tracking tables; revokes sequences tied to caplets_migrations/caplets_schema.
deploy/postgres/init-caplets-roles.sh Idempotent shell script that provisions caplets_migrator and caplets_runtime roles with least-privilege grants; schema name validation mirrors the TypeScript regex.
deploy/postgres/render-config.mjs Renders a Caplets config JSON with the Postgres connection string using URL encoding for special characters in passwords; writes with mode 0o600 to protect credentials.
packages/core/src/storage/legacy-migration.ts Migrates legacy file-based state (OAuth tokens, vault, remote auth, setup state, operator activity) to SQL storage; supports dry-run mode and optional backup.
apps/dashboard/src/components/StoredCapletsPage.tsx New dashboard page for viewing, editing, importing, and exporting caplet records from SQL storage; uses React state, no unsafe HTML rendering found.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App
    participant HostStorage
    participant SQLite/Postgres
    participant S3

    App->>HostStorage: createHostStorage(config)
    HostStorage->>SQLite/Postgres: migrateHostDatabase()
    SQLite/Postgres-->>HostStorage: schema ready

    App->>HostStorage: caplets.importBundle(input)
    HostStorage->>SQLite/Postgres: prepareAssetStorage (upsert blobs)
    opt S3 configured
        HostStorage->>S3: upload blob
    end
    HostStorage->>SQLite/Postgres: insert record + revision + tags + backends + entries

    App->>HostStorage: caplets.list()
    HostStorage->>SQLite/Postgres: SELECT caplet_records
    HostStorage->>SQLite/Postgres: SELECT caplet_revisions (batch)
    HostStorage->>SQLite/Postgres: SELECT tags + backends + entries (parallel)
    SQLite/Postgres-->>HostStorage: rows
    HostStorage-->>App: CapletRecordView[]

    App->>HostStorage: maintainAssets()
    HostStorage->>SQLite/Postgres: acquireLease (advisory lock / exclusive tx)
    HostStorage->>SQLite/Postgres: collectAssetGarbage
    opt S3 configured
        HostStorage->>S3: delete orphaned objects
    end
    HostStorage->>SQLite/Postgres: checkpointLease
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App
    participant HostStorage
    participant SQLite/Postgres
    participant S3

    App->>HostStorage: createHostStorage(config)
    HostStorage->>SQLite/Postgres: migrateHostDatabase()
    SQLite/Postgres-->>HostStorage: schema ready

    App->>HostStorage: caplets.importBundle(input)
    HostStorage->>SQLite/Postgres: prepareAssetStorage (upsert blobs)
    opt S3 configured
        HostStorage->>S3: upload blob
    end
    HostStorage->>SQLite/Postgres: insert record + revision + tags + backends + entries

    App->>HostStorage: caplets.list()
    HostStorage->>SQLite/Postgres: SELECT caplet_records
    HostStorage->>SQLite/Postgres: SELECT caplet_revisions (batch)
    HostStorage->>SQLite/Postgres: SELECT tags + backends + entries (parallel)
    SQLite/Postgres-->>HostStorage: rows
    HostStorage-->>App: CapletRecordView[]

    App->>HostStorage: maintainAssets()
    HostStorage->>SQLite/Postgres: acquireLease (advisory lock / exclusive tx)
    HostStorage->>SQLite/Postgres: collectAssetGarbage
    opt S3 configured
        HostStorage->>S3: delete orphaned objects
    end
    HostStorage->>SQLite/Postgres: checkpointLease
Loading

Reviews (4): Last reviewed commit: "fix: serialize PostgreSQL node heartbeat..." | Re-trigger Greptile

Comment thread packages/core/src/storage/migrations.ts Outdated
Comment thread packages/core/src/storage/migrations.ts
Comment thread packages/core/src/storage/caplet-records.ts Outdated
Comment thread packages/core/src/storage/coordination.ts Outdated
Comment thread packages/core/src/storage/coordination.ts
Comment thread packages/core/src/storage/coordination.ts
@ian-pascoe
ian-pascoe merged commit 7dbfb74 into main Jul 18, 2026
8 checks passed
@ian-pascoe
ian-pascoe deleted the worktree/quiet-valley-a73d branch July 18, 2026 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant