Secretspec 0.20

We’ve released secretspec 0.20, which includes 5 new providers, git and Docker credential helpers.

Most importantly we’re working towards releasing resolver IPC that Nix will be able to use without introducing new dependencies, which was the main review objection to existing PR.

We’ve also split out secrets handling refactoring in Nix (PR).

Disclaimer: LLMs have been used

6 Likes

Thank you for the update, I was curious about the IPC proposal and I stumbled on IPC architecture | SecretSpec and I’m genuinely surprised by the claims or the needs of SecretSpec here.

Version 1 requires private stdio sessions on Linux, macOS, and Windows plus a pure-C client. zlink is Rust-only and its ready-made transport is Unix-domain sockets. Passing anonymous sockets is a transport-specific zlink extension, not standard Varlink, and Windows would need a separate handle-transfer design.

Varlink has C bindings (referenced from varlink.org): GitHub - varlink/libvarlink: C implementation of the Varlink protocol and command line tool · GitHub and passing anonymous socket is as transport specific as JSON-RPC no? Windows supports AF_UNIX (since Windows 10, recent builds), but, I mean whatever transport you’ll use for JSON-RPC is going to be useful for Windows as well. So I’m really confused about the portability claim when compared to JSON-RPC 2.0 ^^

Missing lifecycle semantics. SecretSpec would still have to define version and capability negotiation, deadlines, cancellation, message bounds, leases, shutdown, and the trust model.

It seems like to me this is also true of JSON-RPC 2.0, no? That’s the problem with these “very simple” protocol, they are not as nice as Cap’n’Proto or gRPC when it comes to that, albeit even those won’t go and say what is the trust model or specify your cap negotiation algorithm.

No net simplification. Named sockets require authentication, permissions, discovery, and stale-socket cleanup. Connection pools replace the current request-ID table with another security-sensitive state machine.

I genuinely don’t get this argument, why this property of named sockets are problematic? Varlink has a Linux registry in /run and varlinkctl handle discoverability for you. The UNIX permission model (or file descriptor passing) handle permissions and authentication, systemd handle stale-socket cleanup.

What does JSON-RPC 2.0 does here that makes it nicer?

No multiplexing. Varlink pipelines calls in order but has no request IDs. One blocked call delays later replies and cannot be cancelled independently. A pool of connections avoids this, but adds channel scheduling, descriptor limits, and replacement after cancellation.

Why does that matter for something like SecretSpec? Like, I suspect that something like SecretSpec handles interactive prompts or permission prompts, so you open 1 connection for each of those and cancellation is well just dropping that connection (that was blocked) no? Is there a reason to have a mono-connection where you can send many requests with request IDs there? I get that JSON-RPC 2.0 has an ID here. Arguably, if you are going to operate over a UDS thing, you can do the exact same thing as JSON-RPC 2.0 and add your own correlation ID and have the server side ack any request and process them async and offer a Stream function (that’s a classical technique that many Varlink servers use to achieve this).

So, really, I’m just curious if you can spare more technical details of the specific needs of SecretSpec, reading the wire protocol did not help me to figure out what is really special about JSON-RPC that you cannot do with Varlink. Thank you in advance!

5 Likes

Seems like varlink is popular in Nix circles! :slight_smile:

That’s a bit of sloppy explanation, but Windows doesn’t support socketpair() that makes it avoid filesystem, which makes is secure.

The Rust claim is about that libvarlink lags behind features, I forgot exactly what it was when I was doing analysis.

Yeah, that’s why the argument why not just JSON+RPC instead of another abstraction.

I don’t want filesystem involvement with the IPC. If we’re making things trying to fix security, there should be no permissions involved, only child/parent processes.

There might be secretspec provider/resolver implementations that handle a lot of traffic so we don’t want a serial channel. I consider performance important, something like 1Password takes 1s just to fetch a secret and these things add up (remember nix-daemon). I was consider having one channel per connection, but then you’re starting to deal with multiplexing, which makes this harder to implement than it should be.

Overall I’m not that much against varlink and arguments aren’t that strong, I’m just not sure relying on another upstream that doesn’t seem to 100% fit will long term be good for IPC. It’s one less OSS project I need to contribute to. We’re going to support many SDKs and I just don’t want to be able to contribute something upstream and make my ways to get it merged into specific coding style. That’s a huge drain of energy from our side.

There’s also something nice to have all the freedom to design the spec we want and let agent implement it for any language from scratch against a strong test suite.