# 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](https://github.com/seekritdev/python-sdk) |
| Go 1.24+ | `github.com/seekritdev/go-sdk` | [seekritdev/go-sdk](https://github.com/seekritdev/go-sdk) |
| Node / Bun / Deno / browsers / Workers | `@seekrit/sdk` (npm) | [seekritdev/js-sdk](https://github.com/seekritdev/js-sdk) |
| Ruby 3.0+ | `seekrit-sdk` (RubyGems) | [seekritdev/ruby-sdk](https://github.com/seekritdev/ruby-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](/docs/guides/web-app) and [CLI](/docs/guides/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](/docs/guides/service-tokens).

## Python

```bash
pip install seekrit
```

```python
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
```

## Go

```bash
go get github.com/seekritdev/go-sdk@latest
```

```go
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

```bash
npm install @seekrit/sdk
```

```ts
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:

```ts
export default {
  async fetch(request, env) {
    const secrets = await new Seekrit({ token: env.SEEKRIT_TOKEN }).resolve();
    // ...
  },
};
```

## Ruby

```bash
gem install seekrit-sdk
```

The gem is `seekrit-sdk`; the module is `Seekrit`.

```ruby
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:

```ruby
config.seekrit.autoload = true
```

## 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).
- **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 is tested byte-for-byte
against a shared fixture generated from the reference implementation, so they all
recover identical plaintext. See the [encryption
model](/docs/concepts/encryption) and the [resolve
endpoint](/docs/reference/api).
