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.3.tar.gz (135.4 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.3-py3-none-any.whl (31.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for sailor_py-0.1.3.tar.gz
Algorithm Hash digest
SHA256 1191e2a4aa06d412f37d54a66a90c3c200ffa74ef6a87014961ce68e62c516c2
MD5 e967886b14f98d3edda31db35374ce10
BLAKE2b-256 629a8314038e7c59b3954d8dfc4ea8b063be0a55a6e962f53cc54fd5f172201b

See more details on using hashes here.

Provenance

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

File metadata

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

File hashes

Hashes for sailor_py-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 52e8b08f93acd32856b121338d78ae5756be1fe8585f4d302d63b89c98e6cbb8
MD5 5d3e988e7d37c0bacc6a3970edbe9470
BLAKE2b-256 0f80a1f758acef5a4bc28f349dcc3ce15026ca4a789818f06ce6b0421f02cedd

See more details on using hashes here.

Provenance

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