SDK Overview
SecretSpec ships SDKs for Rust, Python, Go, Ruby, Node.js/TypeScript,
Haskell, PHP, C# (0.16+), Swift (0.18+) and JVM languages (0.20+). They
all resolve secrets from the same declarative secretspec.toml, and
they all behave identically, because they share one resolver.
C# compatibility: Available since SecretSpec 0.16. The 0.15.0 NuGet package is an unsupported package-ID bootstrap; use version 0.16 or later for the API shown in the C# guide.
Native library name: Starting with SecretSpec 0.20, the embedded C ABI is
libsecretspec. It was calledsecretspec-ffithrough SecretSpec 0.19; current runtime SDKs retain shared-library filename compatibility.
One resolver, thin clients
Resolution (providers, fallback chains, profiles, generation, as_path
materialization) lives in a single Rust core. Each SDK is a thin client
over that core rather than a reimplementation:
- Rust uses the library directly, with a compile-time derive macro for strongly-typed access.
- Ruby (a native C extension) statically links the
libsecretspecC ABI at build time; Go (purego) loads it at runtime with no cgo. Both exchange a small JSON request/response with the core. - Haskell links the same C ABI at build time via the GHC FFI.
- C# (0.16+) loads the same C ABI with P/Invoke from a runtime-specific native asset in the NuGet package.
- Swift (0.18+) imports the same C ABI as a Clang module from a
macOS XCFramework and maps the shared JSON envelope to
Codablevalue types. - Python uses a pyo3 native extension, and Node.js/TypeScript uses a napi-rs native addon; both embed the same resolver directly and exchange the same JSON request/response shape as the C ABI.
- PHP prefers an
ext-php-rs extension
that embeds the resolver (working under FPM with no
ffi.enable), and falls back to loading the same C ABI at runtime throughext-ffi. - JVM (0.20+) loads the same C ABI with JNA from a runtime-specific native asset in the Jar package.
Because resolution happens in one place, every provider, chain, profile, and generator works the same in every language, and a new provider added to the core is immediately available everywhere with no per-SDK change. A cross-language conformance suite asserts that all the SDKs reduce the same inputs to the same result.
The runtime API
Each SDK mirrors the Rust derive crate’s vocabulary: a builder that
takes a provider, profile, and an access reason, and a load/resolve
that returns the resolved secrets plus the provider and profile used. A
missing required secret is a typed error, distinct from a transport
failure (which carries a stable kind). Secrets exposed as_path come
back as a readable file path.
from secretspec import SecretSpec
resolved = SecretSpec.builder().with_provider("keyring://").with_reason("boot").load()print(resolved.secrets["DATABASE_URL"].get)
See each language’s page for the idiomatic spelling: Rust, Python, Go, Ruby, Node.js, Haskell, PHP, C# (0.16+), Swift (0.18+) and JVM languages (0.20+).
Every builder also takes a scope (0.17+), resolving only a named subset of the profile and returning the selected name on the result. The one exception is Rust’s typed loader, which always resolves the full profile because a generated struct has a field per declared secret; see Rust.
Typed access
Beyond the Rust derive macro, typed accessors for the other languages
are generated from the manifest. secretspec schema emits a JSON Schema
for the secret shape; quicktype turns it into an
idiomatic type and deserializer for any language, which you build from
the SDK’s fields() map:
$ secretspec schema | quicktype -s schema --top-level SecretSpec --lang <language>
Terminal window
This keeps the per-language surface tiny: the SDK only provides
fields(), and quicktype owns the type generation.
The schema models successful resolution: required, defaulted, and
generated secrets are non-nullable. A profile schema includes fields
inherited from the default profile and is exhaustive.
Distribution
The resolver ships inside each package, so there is nothing extra to install and no runtime library path to set:
- Python builds the resolver into a pyo3 extension shipped as a
cp39-abi3wheel, and Ruby statically links thelibsecretspecarchive into a native C extension in the gem. - Haskell statically links the same archive at build time via the GHC FFI.
- C# (0.16+) ships the
cdylibas runtime-specific native assets in one NuGet package and loads the matching asset through P/Invoke. The managed client supports trimming and NativeAOT; glibc/musl Linux, Intel/Arm macOS, and x64/Arm64 Windows assets are included. - Swift (0.18+) ships Intel and Apple-silicon macOS cdylibs in one checksummed XCFramework binary target. SwiftPM selects and embeds the matching architecture.
- Go embeds the
cdylibin the module and loads it at runtime via purego (no cgo); an opt-in-tags staticbuild links it statically instead. - Node.js builds the resolver into a napi-rs addon.
- PHP ships as a normal PHP extension (provisioned like
ext-redis), with anext-ffifallback that dlopens the bundledcdylib. - JVM ships the
cdylibas runtime-specific native assets in one Jar package and loads the matching asset through JNA; glibc/musl Linux, Intel/Arm macOS, and x64/Arm64 Windows assets are included.
Because the resolver is linked or embedded directly, the SDKs do not
depend on a separately installed cdylib or an
LD_LIBRARY_PATH/SECRETSPEC_FFI_LIB override at runtime — the one
exception being PHP’s optional ext-ffi fallback, where
SECRETSPEC_FFI_LIB can point at a specific library build.
Platform support
Prebuilt packages cover the following platforms. Windows support for the Python wheel, the Ruby gem, and the PHP extension binaries is added in SecretSpec 0.17.
| SDK | Linux x64 | Linux arm64 | macOS Intel | macOS Apple silicon | Windows x64 | Windows arm64 |
|---|---|---|---|---|---|---|
| Rust (source crate) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Python | ✓ | ✓ | — | ✓ | ✓ (0.17+) | — |
| Node.js | ✓ | ✓ | — | ✓ | ✓ | — |
| Go | ✓ | ✓ | — | ✓ | ✓ | — |
| Ruby | ✓ | ✓ | — | ✓ | ✓ (0.17+) | — |
| C# | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Swift (0.18+) | — | — | ✓ | ✓ | — | — |
| PHP | ✓ | ✓ | — | ✓ | ✓ (0.17+) | — |
| Haskell (source) | ✓ | — | — | — | ✓ (0.17+) | — |
| JVM (0.20+) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Most Linux packages build against a manylinux_2_28 baseline (glibc 2.28 or newer); the C# and JVM packages additionally ship musl Linux assets. Hackage distributes the Haskell SDK as source, so its row records the platforms CI builds and tests. Contributors: the SDK development page documents how these artifacts are built and how to add a platform.
Last updated Oct 08, 2026