Skip to content

Upstream the sqlite provider (with opt-in history) — scope and rejected alternatives #1

Description

@djbclark

Why this repo exists

A clean fork of cachix/secretspec whose single purpose is to develop the
sqlite provider for merge into upstream. No downstream distribution, no
privilege-separation code, no historical cruft. The privileged side lives in
frdminc/sudo-secretspec and stays there.

Objective function: minimise long-term fork friction, not maximise upstream
feature acceptance. A small, stable, low-conflict delta beats a large accepted
one.

Established facts (verified 2026-08-18)

  • Upstream has no sqlite provider at all. secretspec/src/provider/ has no
    sqlite.rs; no rusqlite dependency; no sqlite feature. The entire
    1,416-line provider is a downstream invention.
  • Versioned reads are already a first-class upstream abstraction.
    NativeAddress carries pub version: Option<String>"the secret version
    to read on stores that support version-pinned reads; defaults to the latest"

    (secretspec/src/config.rs:1639). Honored by openbao / AWS PS / AKV /
    Scaleway; explicitly rejected by 1Password ("1Password items are not
    versioned"
    , onepassword.rs:1178). A sqlite provider with history fills in
    an interface upstream already designed.
  • Upstream tolerates large, feature-rich providers. onepassword.rs is
    2,939 lines — more than double our sqlite provider.
  • Out-of-tree providers are impossible today. pub(crate) mod provider;
    (secretspec/src/lib.rs:62) and registration through a linkme slice
    (provider/macros.rs). A separate provider crate cannot register itself.

Decision: upstream the provider with opt-in history

Ship it as a normal in-tree, feature-gated provider, the same shape as
kdbx / vault: sqlite = ["dep:rusqlite", "dep:sha2"] in Cargo.toml plus
#[cfg(feature = "sqlite")] pub mod sqlite; in provider/mod.rs.

Explicitly rejected, with reasons

  • Upstream a minimal provider and keep history as a downstream patch.
    This is the shared-file trap and is worse than carrying the whole provider in
    the fork
    — a file upstream does not have can never conflict; a file both
    sides edit will. Concretely: file.rs:33-37 rejects any query string, and
    if an upstream sqlite provider did the same, sqlite://…?history=true breaks.
    That URI is load-bearing downstream (sudo-secretspec-cli/src/broker.rs:931-936).
  • A separate secretspec-sqlite-provider crate. Not possible — see
    pub(crate) mod provider above. Would require forking high-churn core files.
  • Asking upstream to make the provider registry public, or for a sealed
    extension trait.
    Wrong ask: the maintainer's answer to out-of-tree providers
    is IPC (Support out-of-tree providers via gRPC interface cachix/secretspec#64feat: add versioned client and provider IPC cachix/secretspec#362), not linkme.
  • Upstreaming privilege separation. Not a provider concern; see the linked
    issue.

Work items

  • Confirm provider/sqlite.rs compiles against upstream main (written
    against 0.19.1; upstream has moved). Unverified — do this first, it is
    the cheapest test of everything below.
  • Remove the pub use rusqlite seam. Downstream lib.rs currently exports
    pub use rusqlite and pub mod sqlite_history { pub use crate::provider::sqlite::capture_history; } so the broker can append to
    the same hash chain with its own connection. Re-exporting a dependency in
    the public API makes a rusqlite major bump a secretspec breaking
    change — upstream will likely refuse. Two candidate fixes:
    a path-based destroy/restore/capture API taking a path rather
    than a connection, or a SqliteConn newtype wrapping Connection
    (capture_history is pub fn capture_history(conn: &Connection, operation: &str) at provider/sqlite.rs:618, so either is a small change).
  • Port the provider onto upstream main, feature-gated.
  • Follow the provider-documentation checklist in CLAUDE.md — sidebar,
    providers table, reference page, landing page, quick-start, README. Seven
    places that silently drift if missed.
  • Label the provider with its target version (upstream is 0.19.1; target
    0.20+).

Related

Companion issue in the privileged fork: frdminc#2 — frdminc/sudo-secretspec (privilege separation, the cachix#362 gate, and what this supersedes)

Reviews behind these decisions: two independent model reviews on 2026-08-18
(Cursor Grok 4.6 Extra High, and GPT-OSS 120B). They disagreed — the second
recommended the minimal-provider and separate-crate options rejected above; its
separate-crate recommendation is refuted by pub(crate) mod provider. Treat
model agreement as a prompt to verify, not as corroboration: earlier the same
day two reviewers agreed on a P0 (busy_timeout missing) that was simply false.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions