Skip to content

Haskell SDK

The Haskell SDK (secretspec-hs) is a thin client over the secretspec-ffi C ABI, linked at build time via the Haskell FFI. Resolution happens in the Rust core, so the SDK inherits every provider with no Haskell-side logic.

import qualified SecretSpec as S
import qualified Data.Map.Strict as Map
import Data.Function ((&))
main :: IO ()
main = do
resolved <-
S.load
( S.builder
& S.withProvider "keyring://"
& S.withProfile "production"
& S.withReason "boot web app"
)
print (S.resolvedProvider resolved, S.resolvedProfile resolved)
case Map.lookup "DATABASE_URL" (S.resolvedSecrets resolved) of
Just db -> print (S.get db) -- the value, or the file path for as_path secrets
Nothing -> pure ()
S.setAsEnv resolved -- export everything into the process environment

A missing required secret throws MissingRequiredError; any other failure throws SecretSpecError (with a stable errorKind).

as_path secrets are materialized to temp files that outlive the call; call S.close resolved when done so they do not accumulate in the temp dir.

S.report returns the inventory/preflight view: per-secret status and provenance, never a value. Unlike load, it does not throw when a required secret is missing — that secret appears as a SecretReport with srStatus "missing_required".

rep <- S.report (S.builder & S.withProfile "production")
mapM_ (\s -> print (S.srName s, S.srStatus s, S.srRequired s)) (S.reportSecrets rep)

Generate a typed record with secretspec schema plus quicktype, then decode S.fieldsJson resolved:

Terminal window
secretspec schema | quicktype -s schema --top-level SecretSpec --lang haskell -o Secrets.hs

The native secretspec-ffi library is linked at build time, so point cabal at the built cdylib and put the same directory on the runtime loader path:

Terminal window
cargo build -p secretspec-ffi
TARGET="$(cargo metadata --no-deps --format-version 1 \
| grep -o '"target_directory":"[^"]*"' | head -1 | sed 's/.*:"\(.*\)"/\1/')"
cabal build --extra-lib-dirs="$TARGET/debug"
LD_LIBRARY_PATH="$TARGET/debug" cabal test --extra-lib-dirs="$TARGET/debug"