Skip to main content

seekrit — Python SDK

Read-path SDK for seekrit. Authenticate with a service token, resolve your environment, and get decrypted secrets — the API only ever returns ciphertext; decryption happens in your process.

This repo is a read-only mirror published from seekrit's monorepo so the code that holds your token and decrypts plaintext is auditable. Don't commit here — it's overwritten on each sync. Issues and PRs welcome.

Install

pip install seekrit

Requires Python 3.9+. The only dependency is cryptography.

Usage

import seekrit

client = seekrit.Client()            # token from $SEEKRIT_TOKEN
secrets = client.resolve()           # {"DATABASE_URL": "postgres://…", …}

db_url = client.get("DATABASE_URL")
api_key = client.get("API_KEY", default="")

Load everything into the process environment:

import os, seekrit
seekrit.Client().into_env()          # existing os.environ vars win by default
print(os.environ["DATABASE_URL"])

Configuration

Argument Env var Default
token SEEKRIT_TOKEN — (required)
api_url SEEKRIT_API_URL https://api.seekrit.dev
overrides {}
timeout 30.0 (seconds)

A service token binds to a single app environment (plus its composed group slices). To pull a different environment slice of a composed group, pass overrides (the ?with= override):

seekrit.Client(overrides={"shared": "dev"}).resolve()

Errors

  • SeekritApiError — non-2xx from the API; has .status and .code ("unauthorized", "forbidden", "not_found", …).
  • SeekritCryptoError — a token or ciphertext could not be parsed/decrypted.
  • SeekritError — base class (also covers network failures).

The client is fail-closed: any resolve or decrypt failure raises rather than returning partial results.

Notebooks

seekrit.load() is the one-call form: resolve, load os.environ, done. Put it at the top of a notebook or script.

import seekrit

seekrit.load()

It's built around the two ways a notebook leaks a credential:

  • No token in a cell. load() takes the token from $SEEKRIT_TOKEN, and when there isn't one it asks through a password prompt (ipykernel routes getpass to the notebook frontend) — so the token stays in kernel memory instead of being saved into the .ipynb. Pass prompt=False to never ask, or set SEEKRIT_TOKEN for headless runs like papermill.
  • No values in cell outputs. load() returns the names it loaded and the scope they came from — never the values — so displaying it in a cell writes a summary into the notebook file and nothing more.
loaded = seekrit.load()
loaded                       # <seekrit: 7 secrets loaded from acme/analytics/staging: API_KEY, …>
len(loaded)                  # 7
"DATABASE_URL" in loaded     # True
os.environ["DATABASE_URL"]   # the value lives here, not on the result

Re-running the cell refreshes: load() defaults to override=True, unlike into_env(), so a rotated secret takes effect on a re-run rather than being skipped as already-set. Pass override=False to keep what the environment already has (those names are then listed in loaded.skipped).

This guards the summary, not your own cells — print(os.environ["API_KEY"]) still writes a secret into the notebook. Strip outputs before committing.

Hold a placeholder instead of a key

seekrit.transport substitutes {{seekrit:NAME}} placeholders into outbound requests, so a provider key is never in your source, your .env, or os.environ:

pip install 'seekrit[httpx]'
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, because they all reach the network through the same http_client=: LangChain's ChatOpenAI, Pydantic AI's OpenAIProvider, the OpenAI Agents SDK's set_default_openai_client, LlamaIndex's OpenAI. Use AsyncSeekritTransport for the async client.

The allowlist is the boundary, and it is default-deny: a name that is not permitted toward that host, method, and path is refused, and so is a name that did not resolve. Neither sends the request.

A refusal answers with the same 403 the proxy answers with, carrying x-seekrit-refusal and the secret's name but never its value. That is on purpose: a provider SDK wraps anything its HTTP layer raises into an opaque connection error and retries it, so raising would turn a denied placeholder into "Connection error" after six attempts. Pass refusal="raise" to get the typed error instead.

LangChain middleware

pip install 'seekrit[langchain]' adds agent middleware that scopes credentials to a single tool call:

from langchain.agents import create_agent
from seekrit.langchain import SeekritCredentials

agent = create_agent(
    model=model,
    tools=[refund, search],
    context_schema=Context,
    middleware=[
        SeekritCredentials(
            scope=lambda ctx: {"tenants": ctx.tenant},
            tools={"refund": ["STRIPE_SECRET_KEY"]},
        ),
    ],
)

refund may substitute the Stripe key; search may substitute nothing. scope also picks which tenant's secrets to resolve, per request, without rebuilding the model. Pair it with require_scope=True on the transport so a lost context fails closed. Details: https://seekrit.dev/docs/guides/agent-proxy/in-process.

Pydantic AI

pip install 'seekrit[pydantic-ai]' adds a WrapperToolset that scopes each tool call:

from pydantic_ai import Agent
from pydantic_ai.toolsets import FunctionToolset
from seekrit.pydantic_ai import SeekritToolset, Scope, use_scope

agent = Agent(
    "openai:gpt-5.6-terra",
    deps_type=Deps,
    toolsets=[
        SeekritToolset(
            FunctionToolset([refund, search]),
            scope=lambda deps: {"tenants": deps.tenant},
            tools={"refund": ["STRIPE_SECRET_KEY"]},
        )
    ],
)

# A toolset only wraps tools; wrap the run to cover the model call too.
with use_scope(Scope(overrides={"tenants": tenant})):
    result = await agent.run(prompt, deps=Deps(tenant=tenant))

Because it runs in your process, this is a weaker boundary than the egress proxy. What it does buy: the value exists only inside one HTTP call, so it never reaches model context, a tool result, or a trace exporter — nor an environment-scraping bug in a dependency.

Hermes Agent

Installing this package registers two secret sources for Hermes Agent, so the agent's provider credentials arrive from seekrit at startup instead of sitting in ~/.hermes/.env. Both are inert until a secrets: section enables one.

secrets:
  sources: [seekrit]
  seekrit:
    enabled: true

seekrit is the bulk source — one environment, whole. seekrit_refs is the mapped one, for explicit VAR: skt://NAME bindings, renames, and reading more than one environment. Full guide: seekrit.dev/docs/guides/ai-agents/hermes.

Secret references

A secret's value may reference another with ${OTHER_SECRET}. References are stored literally and expanded here, after the layers are merged — so a reference picks up whichever layer won that name, and rotating the referenced secret updates every value that uses it. $${OTHER_SECRET} is a literal; an unknown name is left as written; a reference cycle raises. Full rules: seekrit.dev/docs/guides/references.

client = seekrit.Client(interpolate=False)   # get the stored text instead

Zero-knowledge

GET /v1/resolve returns ciphertext plus a data-encryption key wrapped to your token's public key. This SDK recovers the token's private key, unwraps the DEK (ECDH P-256 → HKDF-SHA256 → AES-256-GCM), and decrypts each secret (AES-256-GCM, AAD-bound to environmentId/NAME) — the exact scheme used by the CLI, seekrit run, and every other seekrit client. See seekrit.dev/docs.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

seekrit-0.7.0.tar.gz (51.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

seekrit-0.7.0-py3-none-any.whl (40.1 kB view details)

Uploaded Python 3

File details

Details for the file seekrit-0.7.0.tar.gz.

File metadata

  • Download URL: seekrit-0.7.0.tar.gz
  • Upload date:
  • Size: 51.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for seekrit-0.7.0.tar.gz
Algorithm Hash digest
SHA256 7abc439fc763d58fc3c482594ba600aad08d7b3f722cd05b97f551d3dfefb371
MD5 bebd1f36740ecb88a08f3837419b8afd
BLAKE2b-256 e69679cd374e00670f2de7f090e5f4a8735c9f81b77a46a316fcfdadc9f1f3d0

See more details on using hashes here.

Provenance

The following attestation bundles were made for seekrit-0.7.0.tar.gz:

Publisher: publish.yml on seekritdev/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file seekrit-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: seekrit-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 40.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for seekrit-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5bb813118d0ace73ac78e0c0932e8fb9229e5e562b4635fb65fb91b3e8a364ae
MD5 00c5c90a35366ac26c159ebeb7353aa4
BLAKE2b-256 836c56fd8cd95c0a180bfab00e7b024701f42a717425d5773a72739ee761123f

See more details on using hashes here.

Provenance

The following attestation bundles were made for seekrit-0.7.0-py3-none-any.whl:

Publisher: publish.yml on seekritdev/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 files

0.6.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page