Language SDKs
When a secret needs to live inside your application process — not injected by a
launcher — use a language SDK. Each one authenticates with a service token,
calls GET /v1/resolve, and decrypts locally: the API only ever returns
ciphertext plus a data key wrapped to your token, exactly like every other
seekrit client.
| Language | Package | Repo |
|---|---|---|
| Python 3.9+ | seekrit (PyPI) | seekritdev/python-sdk |
| Go 1.24+ | github.com/seekritdev/go-sdk | seekritdev/go-sdk |
| Node / Bun / Deno / browsers / Workers | @seekrit/sdk (npm) | seekritdev/js-sdk |
| Ruby 3.0+ | seekrit-sdk (RubyGems) | seekritdev/ruby-sdk |
| Elixir 1.18+ / OTP 25+ | seekrit (Hex) | seekritdev/elixir-sdk |
The SDKs are read-only: resolve and decrypt with a service token. Creating, rotating, and granting access to secrets stays in the web dashboard and CLI, which use a human principal's passphrase-protected key. A service token binds to exactly one app environment (plus its composed group slices) — that scope is the SDK's blast radius. See Service tokens.
Python
pip install seekrit
import seekrit
client = seekrit.Client() # token from $SEEKRIT_TOKEN
secrets = client.resolve() # {"DATABASE_URL": "postgres://…", …}
db_url = client.get("DATABASE_URL")
seekrit.Client().into_env() # or load everything into os.environ
Or the one-call form, which resolves, loads os.environ, and returns a
value-free summary of what it loaded — see Jupyter
notebooks, which is what it was built for:
import seekrit
seekrit.load()
Installing the package also registers two Hermes Agent secret sources, inert
until a secrets: section enables one — see
Hermes Agent.
For Cloudflare Python Workers, use seekrit.cloudflare.AsyncClient with the
token from your Worker binding and await resolve() inside the handler.
The Python Workers guide covers
Pywrangler, FastAPI, and deployment.
Go
go get github.com/seekritdev/go-sdk@latest
import seekrit "github.com/seekritdev/go-sdk"
client, err := seekrit.New() // token from $SEEKRIT_TOKEN
secrets, err := client.Resolve(context.Background())
fmt.Println(secrets["DATABASE_URL"])
No external dependencies — just the standard library (crypto/ecdh,
crypto/hkdf).
JavaScript / TypeScript
npm install @seekrit/sdk
import { Seekrit } from "@seekrit/sdk";
const client = new Seekrit(); // token from $SEEKRIT_TOKEN
const { DATABASE_URL } = await client.resolve();
Pure WebCrypto + global fetch, so it runs unchanged on Node 18+, Bun, Deno,
browsers, and Cloudflare Workers. In a Worker there's no ambient env, so pass the
token from your binding:
export default {
async fetch(request, env) {
const secrets = await new Seekrit({ token: env.SEEKRIT_TOKEN }).resolve();
// ...
},
};
On Neon Functions, Node provides process.env.SEEKRIT_TOKEN; see the
Neon Functions guide for deployment and branch
setup.
In a React app, read secrets through
@seekrit/sdk/react rather than calling resolve() in a
component: it deduplicates the resolve across one render and makes passing a
value to a client component a render-time error.
Building with Vite? @seekrit/sdk/vite resolves the environment from inside
vite.config.ts, so the dev server, the build, and Vitest all get it without a
wrapper command — see the Vite plugin.
Ruby
gem install seekrit-sdk
The gem is seekrit-sdk; the module is Seekrit.
require "seekrit/sdk" # or "seekrit" — both work
client = Seekrit::Client.new # token from ENV["SEEKRIT_TOKEN"]
secrets = client.resolve # { "DATABASE_URL" => "postgres://…", … }
Rails apps can load secrets into ENV on boot by opting in from an initializer:
config.seekrit.autoload = true
Elixir
# mix.exs
def deps do
[{:seekrit, "~> 0.1"}]
end
secrets = Seekrit.resolve!() # token from $SEEKRIT_TOKEN
secrets["DATABASE_URL"]
Every function has a {:ok, _} | {:error, exception} form alongside the !
form. No dependencies — :crypto and :public_key for the decrypt path,
:httpc for the request.
In a release, config/runtime.exs runs before your applications start, which is
where secrets belong. Seekrit.load_env!/1 starts :inets and :ssl itself,
so it works there:
# config/runtime.exs
if config_env() == :prod do
Seekrit.load_env!()
config :my_app, MyApp.Repo, url: System.fetch_env!("DATABASE_URL")
end
Never holding the value at all
The Python and JS SDKs can also inject a credential without your code ever
seeing it, by holding a {{seekrit:NAME}} placeholder that a fetch wrapper or
an httpx transport substitutes on the way out:
import httpx
from openai import OpenAI
from seekrit.transport import SeekritTransport
client = OpenAI(
api_key="{{seekrit:OPENAI_API_KEY}}",
http_client=httpx.Client(
transport=SeekritTransport(allow={"api.openai.com": ["OPENAI_API_KEY"]}),
),
)
One transport covers every Python agent toolkit and seekritFetch covers the
TypeScript ones, because they all take a client or a fetch. On LangChain it
extends to scoping a key to one tool call. See
in-process injection — including what it
does and does not guarantee against the proxy.
Common behavior
- Fail-closed. A bad token, an unreachable API, or a layer that won't decrypt raises — the SDKs never return partial results.
- Precedence.
resolvereturns the merged environment: composed groups first, then the app environment on top (later wins on a name collision). - References.
${OTHER_SECRET}references are expanded over the merged result. Opt out per client:interpolate: false(JS/TS),interpolate=False(Python),WithInterpolate(false)(Go),interpolate: false(Ruby and Elixir). - Overrides. Pull a different environment slice of a composed group with the
with/overridesoption — the SDK equivalent of?with=group:env. You can only pull a slice your token holds a key for. - Errors. A non-2xx response surfaces as a typed API error carrying the HTTP
status and the API's error code (
unauthorized,forbidden,not_found, …).
Every SDK reimplements the same decrypt path (ECDH P-256 → HKDF-SHA256 →
AES-256-GCM, AAD-bound to environmentId/NAME) and the same reference-expansion
rules, and is tested against a shared fixture generated from the reference
implementation, so they all recover identical plaintext. See the encryption
model and the resolve
endpoint.