I have two seemingly conflicting goals with vdirsyncer:
- I want to keep the scope and code simple, reach a stable state, and stop adding new features.
- I want to support a variety of use cases, like being able to use client TLS certificates, custom http transports, etc.
After thinking about this for along time, I’ve concluded that support for Unix domain sockets is the best compromise to satisfy both goals.
Supported connection transports
Essentially, vdirsyncer will support two transports to talk to a CalDav server:
- Using HTTP(s) with the default route, default connection parameters and the system certificate authorities. This is the typical use case.
- Using a Unix domain socket, suitable for any other use case which requires additional flexibility.
The first transport is straightforward, so I’ll focus on the second one in the rest of this article.
Potential usages of this approach
By having vdirsyncer communicate with a Unix domain socket, traffic can be routed wherever necessary. The socket must be set up by an external process. Here as some examples of what that process could be:
- A local CalDav server which is only exposed via a socket.
- An HTTP proxy that handles client-side TLS certificates.
- An HTTP proxy that uses a custom TLS validation mechanism.
- A special proxy service that re-writes authentication to something non-standard.
- An SSH client forwarding a port to a remote host.
- A proxy that handles TLS with a specific, approved, implementation.
The ability to connect to a local socket becomes an escape hatch for infinite possibilities without having to grow vdirsyncer’s codebase by infinite complexity.
Scope
I believe that this approach keeps a sane scope for vdirsyncer. It doesn’t require implementing all sorts of TLS flags, but allows integrating equivalent functionality.
I find that this approach aligns well with the Unix philosophy of “do one thing”. E.g.: vdirsyncer just does the synchronisation; some external process handles establishing and encrypting connections. I am not choosing this approach because it aligns with the Unix philosophy: I’m choosing it because it allow supporting all kind of special scenarios while minimising the amount of code that vdirsyncer needs to in order to support each scenario.
Personally, I would consider it cleaner design to only support sockets and require that even HTTP(s) connections be handled by a dedicated program. Such an approach would likely be impractical for the typical use cases.
Alternatives
I also considered making vdirsyncer take file descriptors as input on invocation. Vdirsyncer would use these for network communication. This would require complex scaffolding to use, and I fear would quickly become too complex when using a few different storages. That aside, it requires a lot of custom glue code for almost any use case.
I considered using a connection-cmd
, a command that is executed to obtain file
descriptors for a socket to the target server. This provides similar flexibility
as both of the above. This alternative is more unorthodox and doesn’t allow
leveraging existing tools as easily (e.g.: you’d require special glue code to
communicate to a local server listening on a Unix socket).
Impact on security
If you need to use vdirsyncer with a specific (e.g.: audited) TLS implementation, then you can use that external implementation without vdirsyncer requiring any changes and without needing to link to it.
This also means that I can drop all custom code for custom TLS configurations. Less custom security-related code correlates to less chances of a security bugs.