OS credential storage for small secrets
0

Configure Feed

Select the types of activity you want to include in your feed.

OCaml 67.7%
C 24.8%
Dune 2.5%
Other 5.1%
9 1 0

Clone this repository

https://git.vm.fail/gazagnaire.org/ocaml-keyring https://git.vm.fail/did:plc:mmnwudrqzbv3dwtds7gm42jz
ssh://git@git.recoil.org:2222/gazagnaire.org/ocaml-keyring ssh://git@git.recoil.org:2222/did:plc:mmnwudrqzbv3dwtds7gm42jz

For self-hosted knots, clone URLs may differ based on your setup.


README.md

keyring#

OS credential storage for small secrets.

Overview#

keyring stores, retrieves and deletes small secrets (API tokens, passphrases, passwords) in the operating system's credential store, keyed by a service name and an account name. Secrets live in wipeable byte buffers, never in immutable strings, and pretty-printing is always redacted.

Backends are explicit. There is no automatic fallback: a platform without a usable credential store reports Unavailable, and the caller decides what to do about it. The macOS backend talks to Keychain Services through native Security.framework calls, so authorization attaches to the calling application rather than to a helper executable, and no secret ever crosses a subprocess pipe.

Usage#

Every backend is used through the same three operations:

# let keyring = Keyring.Memory.v ()
val keyring : Keyring.t = <abstr>
# Keyring.set keyring ~service:"example.org" ~account:"alice"
    (Keyring.Secret.of_string "hunter2")
- : (unit, Keyring.Error.t) result = Ok ()
# (match Keyring.get keyring ~service:"example.org" ~account:"alice" with
   | Ok secret -> Keyring.Secret.use secret Bytes.to_string
   | Error _ -> assert false)
- : string = "hunter2"
# Keyring.delete keyring ~service:"example.org" ~account:"alice"
- : (unit, Keyring.Error.t) result = Ok ()
# Keyring.get keyring ~service:"example.org" ~account:"alice"
- : (Keyring.Secret.t, Keyring.Error.t) result = Error Keyring.Error.Missing

On macOS, Keyring.Macos.v () is the user's login keychain; secrets are generic-password items (kSecAttrService / kSecAttrAccount). Keyring.Macos.available says whether the build links against Security.framework, so portable callers can pick a backend at run time.

Custom backends implement Keyring.BACKEND and lift through Keyring.Make.

Secrets#

Keyring.Secret.t wraps a mutable buffer:

  • use borrows the buffer without copying; the callback must not retain it.
  • wipe zero-fills the buffer; later uses raise.
  • equal compares contents in constant time (via eqaf).
  • pp prints <secret:N>, never the contents.

Copies the process cannot wipe still exist transiently (CoreFoundation buffers, IPC serialization); the guarantee is that the library never creates long-lived immutable copies on the OCaml heap.

Errors#

Keyring.Error.t distinguishes Missing, Locked, Interaction_required, Denied, Unavailable and Backend of string, so callers can react (prompt, skip, fall back) instead of parsing message strings. Keychain calls can block on securityd IPC and authorization prompts; run them away from a latency-sensitive event loop.

  • Rust keyring, Python keyring and Go 99designs/keyring define the cross-platform get/set/delete-by-service-and-account shape this library follows.
  • osx-keychain binds the full SecItem* attribute surface (internet passwords, attribute enumeration) for macOS only; its stubs informed the query-dictionary construction and OSStatus handling here. This library instead keeps a minimal cross-platform facade with wipeable secret buffers.
  • The freedesktop Secret Service specification is the target of a future Linux backend; the Locked and Interaction_required errors exist in the vocabulary because that protocol has explicit locking, sessions and prompts.

License#

ISC License. See LICENSE.md for details.