Skip to main content

Type-safe Python client for Sailor — configuration & secret management for cloud-native apps

Project description

🐧 Sailor Python - Consumer

A type-safe Python client for Sailor — configuration and secret management for cloud-native applications. This is the Python counterpart to sailor-go and speaks the same wire protocol.

  • Typed config & secrets via Pydantic v2 models
  • Two fetch modes: PULL (poll the Sailor API) and VOLUME (read mounted ConfigMaps/Secrets, hot-reloaded via a file watcher)
  • End-to-end secret decryption — envelope decryption (HKDF-SHA256 KEK → per-record DEK → AES-256-GCM), compatible with the Sailor server's vault
  • Fallback to a local/remote snapshot when the server is unreachable
  • Flexible connection config: URI, explicit options, env vars, or ~/.sailor/config

Install

pip install sailor-py

Requires Python 3.9+.

Quickstart (PULL)

from pydantic import BaseModel
from sailor import Consumer, InitOption, ConnectionOption, defaults


class AppConfig(BaseModel):
    database_url: str
    port: int = 8080


class AppSecrets(BaseModel):
    api_key: str


init = InitOption(
    connection=ConnectionOption(uri="sailor://ACCESS:SECRET@sailor.example.com/team/billing"),
    resources=[
        defaults.config_pull_default(),      # poll config every 10s
        defaults.secrets_pull_default(),     # poll + decrypt secrets
        defaults.misc_pull_default("flags"), # arbitrary blob
    ],
)

with Consumer[AppConfig, AppSecrets](
    init, config_type=AppConfig, secrets_type=AppSecrets
) as consumer:
    cfg = consumer.get()               # -> AppConfig
    secrets = consumer.get_secret()    # -> AppSecrets (decrypted)
    flags = consumer.get_misc("flags") # -> bytes

    print(cfg.database_url, secrets.api_key)

Consumer starts background threads to keep values fresh; get() / get_secret() / get_misc() always return the latest snapshot and are thread-safe. Use it as a context manager (as above) or call .start() / .close() explicitly.

Volume mode (Kubernetes mounts)

init = InitOption(
    connection=ConnectionOption(namespace="team", app="billing", access_key="AK", secret_key="SK"),
    resources=[
        defaults.config_map_default(),   # reads /etc/sailor/_config
        defaults.secrets_default(),      # reads /etc/sailor/_secret
    ],
    watch=True,  # hot-reload on file change (default when volumes are used)
)

Dev mode

config_dev_default() / secrets_dev_default() (fetch strategy DEV) support local iteration: on first run they fetch from the API once and cache the response to ~/.sailor/cache/{ns}-{app}-{env}-{kind}.json; thereafter they read that cache and watch it for edits, so you can tweak a local JSON copy and get live reload without a server round-trip.

init = InitOption(
    connection=ConnectionOption(uri="sailor://AK:SK@host/team/billing"),
    resources=[defaults.config_dev_default(), defaults.secrets_dev_default()],
)

Connection resolution

Fields are resolved in this order (earlier wins, never overwritten):

  1. Explicit ConnectionOption fields
  2. sailor://accessKey:secretKey@host/namespace/app URI — uri field, else SAILOR_URI (the canonical env var), else SAILOR_URL (URI or plain base URL)
  3. Environment: SAILOR_NS, SAILOR_APP, SAILOR_ACCESS_KEY, SAILOR_SECRET_KEY
  4. ~/.sailor/config written by the Sailor CLI (only when use_sailor_config=True)

The optional bearer token is sent as the x-token header.

Fallback

Set fallback_enabled=True on a resource and point SAILOR_FALLBACK_BASE_URL at a directory or http(s):// base. On fetch failure the client reads {base}/{app}-{kind}.sailor.fall.

API surface

Go (sailor-go) Python (sailor-py)
NewConsumer[C,S](opts) Consumer[C,S](init, config_type=, secrets_type=)
Start() start() (or with Consumer(...))
Get() / GetSecret() get() / get_secret()
GetMisc(name) get_misc(name)
opts.* structs sailor.options.* Pydantic models
defaults.* sailor.defaults.*

Errors are Python exceptions under sailor.SailorError (e.g. ConfigsNotLoadedError, FetchFallbackFailedError, DecryptionError) — catch by type instead of Go's errors.Is.

Development

uv venv && uv pip install -e ".[dev]"
uv run pytest        # tests
uv run ruff check .  # lint
uv run mypy          # types

Releasing

Releases publish to PyPI automatically via .github/workflows/publish.yml using PyPI Trusted Publishing (OIDC) — no API tokens or repository secrets.

One-time PyPI setup: create the project's pending publisher pointing at this repo, the publish.yml workflow, and a pypi environment.

To cut a release, bump version in pyproject.toml, then tag and push:

git tag v0.1.0
git push origin v0.1.0

The tag triggers a build (uv build) and upload. Test the flow first with TestPyPI or twine check dist/* locally.

License

GPL-3.0-or-later, matching upstream Sailor.

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

sailor_py-0.1.0.tar.gz (129.6 kB view details)

Uploaded Source

Built Distribution

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

sailor_py-0.1.0-py3-none-any.whl (31.2 kB view details)

Uploaded Python 3

File details

Details for the file sailor_py-0.1.0.tar.gz.

File metadata

  • Download URL: sailor_py-0.1.0.tar.gz
  • Upload date:
  • Size: 129.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sailor_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f761c59e5340d8f58cb00754be0522190cd9ce8ab43654be0bfb4ee0005c6247
MD5 261127db4edcc7f1ab0cde95aa6c1886
BLAKE2b-256 4b3248dbf71f102607fea1170239507634cbc893948559b06d4f7f4b1cb9e24f

See more details on using hashes here.

Provenance

The following attestation bundles were made for sailor_py-0.1.0.tar.gz:

Publisher: publish.yml on sailorhq/sailor-py

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

File details

Details for the file sailor_py-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: sailor_py-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 31.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sailor_py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f63042bcbe65cf9959446549808effd2742b1c527f485d1ff634ecc41c8e2a8b
MD5 cca2864e8506df00ee75953aa0cad223
BLAKE2b-256 804fe464fec9b665c910d165b6c2d76f7a3b5367ff2b0f664fd385dbdce66fc8

See more details on using hashes here.

Provenance

The following attestation bundles were made for sailor_py-0.1.0-py3-none-any.whl:

Publisher: publish.yml on sailorhq/sailor-py

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

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