hush_os_keychain reads configuration secrets from macOS Keychain, Linux Secret
Service, or Windows Credential Manager through Hush.
It also provides an Elixir API to store, fetch, and delete generic passwords.
It is intended for local development with an unlocked desktop session, not
production or headless servers.
Installation
Requires Elixir ~> 1.19 and Hush ~> 1.3. Add this dependency to mix.exs:
def deps do
[
{:hush_os_keychain, "~> 0.1.0"} # x-release-please-version
]
endHush configuration
Use the service name as the Hush key in your application's configuration:
config :app, MyApp.Repo,
password: {:hush, Hush.Provider.OsKeychain, "myapp.db_password"}Optionally select an account globally:
config :hush, Hush.Provider.OsKeychain, account: "ygor"Hush handles tuple options such as default: and optional: itself:
config :app, MyApp.Repo,
password: {:hush, Hush.Provider.OsKeychain, "myapp.db_password", default: "local-only"}The account is global provider configuration, not a per-tuple option. Set up Hush
resolution as described in the Hush documentation.
Use app-prefixed keys such as myapp.db_password to avoid collisions with other
applications, especially on Windows where target names share one namespace.
Storing secrets
Elixir API
Given a variable secret containing the value obtained through secure input:
HushOsKeychain.put("myapp.db_password", secret, account: "ygor")
#=> :ok
HushOsKeychain.fetch("myapp.db_password", account: "ygor")
#=> {:ok, secret}
HushOsKeychain.delete("myapp.db_password", account: "ygor")
#=> :okThe account: option is optional on all three functions. Without an option or
global account, put/3 writes under "hush"; fetch/2 and delete/2 search by
service alone. A write replaces the existing item for the selected account,
except on Windows where each service has only one credential.
Services, explicit accounts, and values must be non-empty valid UTF-8 strings.
The API returns {:error, reason} on failure. fetch/2 returns {:ok, value} on
success; put/3 and delete/2 return :ok. Use
HushOsKeychain.format_error(reason, "myapp.db_password") to format an error with
the OS store and service name. It adds an account-selection hint for ambiguous
matches.
Do not print fetched values or type real secrets into commands or sessions that save history. The API returns secrets to its caller; the caller must keep them out of logs and inspection output.
Supported platforms
Precompiled targets:
aarch64-apple-darwinx86_64-apple-darwinaarch64-unknown-linux-gnux86_64-unknown-linux-gnux86_64-pc-windows-msvc
Contributing
Building from source requires Rust 1.91+ because Rustler 0.38 requires it.
Rust is needed only for source builds. In :dev and :test, the NIF is always
built from source. Set HUSH_OS_KEYCHAIN_BUILD=1 to force a source build in other
environments.
mix deps.get
mix compile --warnings-as-errors
mix test
mix format --check-formatted
mix docs
mix test runs unit tests only and does not access the real OS store. Run the
integration suite separately in an unlocked desktop session:
mix test --only integration
Integration tests write temporary hush_os_keychain_test.* items to the real OS
store and delete them during cleanup. They may trigger access dialogs. Do not run
them against a store you cannot unlock.
License
Apache-2.0. See the LICENSE file in the repository.