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.1.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.1-py3-none-any.whl (31.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sailor_py-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 8bf96fb1c6f5b9a784a58a62d048083c1fcbd896525fe5a53ffbfc75644fa5e2
MD5 e8d34df3de84573b9694ba511cac800c
BLAKE2b-256 9d987a5568695c9d4b18318732eb0338896931098332e7df9c33a6e876a7fdc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for sailor_py-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: sailor_py-0.1.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 eebdb9c9685e8d82c5911ee1ab5a7a5e1b0670bb818e56f31c0bd1d0ad47e3a0
MD5 b9bb5e932db877c421194586ff7c0119
BLAKE2b-256 a0775efb00bd56125e73f06a5fa65fafaf6bf29ac166d6e828db50b7bc3bd42f

See more details on using hashes here.

Provenance

The following attestation bundles were made for sailor_py-0.1.1-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