You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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-baseddestroy/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.
Why this repo exists
A clean fork of
cachix/secretspecwhose single purpose is to develop thesqlite provider for merge into upstream. No downstream distribution, no
privilege-separation code, no historical cruft. The privileged side lives in
frdminc/sudo-secretspecand 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)
secretspec/src/provider/has nosqlite.rs; norusqlitedependency; nosqlitefeature. The entire1,416-line provider is a downstream invention.
NativeAddresscarriespub version: Option<String>— "the secret versionto 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 inan interface upstream already designed.
onepassword.rsis2,939 lines — more than double our sqlite provider.
pub(crate) mod provider;(
secretspec/src/lib.rs:62) and registration through alinkmeslice(
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"]inCargo.tomlplus#[cfg(feature = "sqlite")] pub mod sqlite;inprovider/mod.rs.Explicitly rejected, with reasons
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-37rejects any query string, andif an upstream sqlite provider did the same,
sqlite://…?history=truebreaks.That URI is load-bearing downstream (
sudo-secretspec-cli/src/broker.rs:931-936).secretspec-sqlite-providercrate. Not possible — seepub(crate) mod providerabove. Would require forking high-churn core files.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#64 → feat: add versioned client and provider IPC cachix/secretspec#362), not
linkme.issue.
Work items
provider/sqlite.rscompiles against upstreammain(writtenagainst 0.19.1; upstream has moved). Unverified — do this first, it is
the cheapest test of everything below.
pub use rusqliteseam. Downstreamlib.rscurrently exportspub use rusqliteandpub mod sqlite_history { pub use crate::provider::sqlite::capture_history; }so the broker can append tothe same hash chain with its own connection. Re-exporting a dependency in
the public API makes a
rusqlitemajor bump a secretspec breakingchange — upstream will likely refuse. Two candidate fixes:
a path-based
destroy/restore/captureAPI taking a path ratherthan a connection, or a
SqliteConnnewtype wrappingConnection(
capture_historyispub fn capture_history(conn: &Connection, operation: &str)atprovider/sqlite.rs:618, so either is a small change).main, feature-gated.CLAUDE.md— sidebar,providers table, reference page, landing page, quick-start, README. Seven
places that silently drift if missed.
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. Treatmodel agreement as a prompt to verify, not as corroboration: earlier the same
day two reviewers agreed on a P0 (
busy_timeoutmissing) that was simply false.