Skip to main content

Python client for Selectel Secrets Manager (sync + async)

Project description

selectel-sm

Tests Lint codecov PyPI - Python Version

A typed Python client for Selectel Secrets Manager, with both synchronous and asynchronous clients sharing a single transport-agnostic core.

⚠️ Disclaimer

This is a non-commercial, community project built out of pure enthusiasm. I am not an employee of Selectel and have no affiliation with them whatsoever. I built this simply because I couldn't find a maintained library for working with Selectel's Secrets Manager.

Installation

pip install selectel-sm          # library
pip install "selectel-sm[cli]"   # + CLI (typer + rich)

Requires Python 3.12+.

Authentication

Secrets Manager requires a project-scoped IAM token. (The public docs mention an account-scoped token, but in practice SM rejects it — a project-scoped token is required.) The library obtains one for you from service-user credentials, caches it, and refreshes it before expiry:

from selectel_sm import SecretsManagerClient

with SecretsManagerClient.from_credentials(
    region="ru-7",
    account_id="123456",
    username="my-service-user",
    password="...",
    project_name="my-project",
) as client:
    secret = client.secrets.get("database_password")
    print(secret.value)  # -> b"..."

Or bring your own project-scoped token (the client introspects it to discover the service catalog):

client = SecretsManagerClient.from_token(region="ru-7", token="gAAAAAB...")

The async client mirrors the same API:

from selectel_sm import AsyncSecretsManagerClient

async with AsyncSecretsManagerClient.from_credentials(region="ru-7", ...) as client:
    secret = await client.secrets.get("database_password")

Endpoint resolution

The Secrets Manager URL is not hardcoded. After authenticating, the library reads the service catalog returned in the Keystone token and resolves the secrets-manager endpoint for the configured region and interface (default public). Set sm_base_url on the client to bypass catalog resolution (e.g. for testing).

Usage

All operations live under client.secrets (and identically on the async client with await).

Secrets

# Create a secret with its first version. `value` is plain data (bytes or str);
# it is base64-encoded for you before being sent.
client.secrets.create("api_key", "s3cr3t", description="Third-party API key")

# Read the current value.
secret = client.secrets.get("api_key")
secret.value          # b"s3cr3t"
secret.description    # "Third-party API key"  ("" from the API becomes None)
secret.version        # the current SecretVersion

# Update / clear the description (None clears it).
client.secrets.update_description("api_key", "Rotated key")

# List all secrets (metadata only — no values).
for summary in client.secrets.list():
    print(summary.name, summary.type, summary.description, summary.created_at)

# Delete a secret and all of its versions.
client.secrets.delete("api_key")

Versions

# Add a new version. Pass activate=True to make it the current version.
client.secrets.create_version("api_key", "rotated-secret", activate=True)

# The secret plus metadata for all of its versions (no values).
sv = client.secrets.get_versions("api_key")
sv.versions           # tuple[SecretVersion, ...]
sv.current            # the version flagged is_current, if any

# A single version, including its value.
version = client.secrets.get_version("api_key", version_id=1)
version.value         # b"..."

# Make a specific version current.
client.secrets.activate_version("api_key", version_id=1)

Error handling

Every error derives from SelectelSMError, so you can catch broadly or narrowly:

from selectel_sm import NotFoundError, SelectelSMError

try:
    client.secrets.get("does-not-exist")
except NotFoundError:
    ...
except SelectelSMError:
    ...

HTTP statuses map to BadRequestError (400), ForbiddenError (403), NotFoundError (404), ConflictError (409), and ServerError (5xx). Authentication and endpoint-resolution problems raise AuthenticationError and EndpointNotFoundError respectively.

A note on quirks

Selectel's Secrets Manager API has a few undocumented behaviors this client handles for you, for example:

  • Listing requires a ?list=<any value> flag and a trailing slash (/v1/?list=true), otherwise it returns 404 page not found.
  • Secret values must be valid base64 — the client always encodes plain input for you.
  • activate version is documented as 204 No Content but actually returns 200 with the version's metadata.

Support & maintenance

Because I don't work with Selectel, I may not be aware of the latest changes to their API, and something could break unexpectedly. I do my best to keep this library up to date, but that isn't always possible. Contributions, bug reports, and help with its development are very welcome — please open an issue or a pull request.

Development

uv sync
uv run ruff check . && uv run ruff format --check .
uv run mypy selectel_sm
uv run pytest

License

MIT © Andrew Krylov

Project details


Download files

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

Source Distribution

selectel_sm-0.1.1.tar.gz (68.0 kB view details)

Uploaded Source

Built Distribution

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

selectel_sm-0.1.1-py3-none-any.whl (32.6 kB view details)

Uploaded Python 3

File details

Details for the file selectel_sm-0.1.1.tar.gz.

File metadata

  • Download URL: selectel_sm-0.1.1.tar.gz
  • Upload date:
  • Size: 68.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"x86_64","distro":{"id":"resolute","libc":{"lib":"glibc","version":"2.43"},"name":"Ubuntu","version":"26.04"},"implementation":{"name":"CPython","version":"3.14.4"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.5.5 27 Jan 2026","python":"3.14.4","system":{"name":"Linux","release":"7.0.0-22-generic"}} HTTPX2/2.4.0

File hashes

Hashes for selectel_sm-0.1.1.tar.gz
Algorithm Hash digest
SHA256 23b0618893b2ca8081ed900104f1ed978580d0ec96e1d510534ed4981a55162b
MD5 9c9244b5eb7297869e0befde9dd66fba
BLAKE2b-256 453262504ef3f488002af32c3f162cc82e35b0c660a704fede8b9f7db722b1a3

See more details on using hashes here.

File details

Details for the file selectel_sm-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: selectel_sm-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 32.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.17.0 {"ci":null,"cpu":"x86_64","distro":{"id":"resolute","libc":{"lib":"glibc","version":"2.43"},"name":"Ubuntu","version":"26.04"},"implementation":{"name":"CPython","version":"3.14.4"},"installer":{"name":"hatch","version":"1.17.0"},"openssl_version":"OpenSSL 3.5.5 27 Jan 2026","python":"3.14.4","system":{"name":"Linux","release":"7.0.0-22-generic"}} HTTPX2/2.4.0

File hashes

Hashes for selectel_sm-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3befcc630a1166fbd465763f5dcac99d1e8f85d8e47fde4cdb5b5aeb4a2a0d2c
MD5 58261ebe9274671dab5a92aa22c4dd5f
BLAKE2b-256 4d198d6319194dbf440becde5b0bc1d141f13be3e7896d846c4a6397c42891b5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page