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 |
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()
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();
// ...
},
};
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
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). - 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.