seekrit
Docs/Language SDKs

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.

LanguagePackageRepo
Python 3.9+seekrit (PyPI)seekritdev/python-sdk
Go 1.24+github.com/seekritdev/go-sdkseekritdev/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
note

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. resolve returns 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 / overrides option — 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.