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) andVOLUME(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):
- Explicit
ConnectionOptionfields sailor://accessKey:secretKey@host/namespace/appURI —urifield, elseSAILOR_URI(the canonical env var), elseSAILOR_URL(URI or plain base URL)- Environment:
SAILOR_NS,SAILOR_APP,SAILOR_ACCESS_KEY,SAILOR_SECRET_KEY ~/.sailor/configwritten by the Sailor CLI (only whenuse_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8bf96fb1c6f5b9a784a58a62d048083c1fcbd896525fe5a53ffbfc75644fa5e2
|
|
| MD5 |
e8d34df3de84573b9694ba511cac800c
|
|
| BLAKE2b-256 |
9d987a5568695c9d4b18318732eb0338896931098332e7df9c33a6e876a7fdc0
|
Provenance
The following attestation bundles were made for sailor_py-0.1.1.tar.gz:
Publisher:
publish.yml on sailorhq/sailor-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sailor_py-0.1.1.tar.gz -
Subject digest:
8bf96fb1c6f5b9a784a58a62d048083c1fcbd896525fe5a53ffbfc75644fa5e2 - Sigstore transparency entry: 2126696935
- Sigstore integration time:
-
Permalink:
sailorhq/sailor-py@7fbc3425074216a6fa426af6c1cfe26b85350bb2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/sailorhq
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7fbc3425074216a6fa426af6c1cfe26b85350bb2 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eebdb9c9685e8d82c5911ee1ab5a7a5e1b0670bb818e56f31c0bd1d0ad47e3a0
|
|
| MD5 |
b9bb5e932db877c421194586ff7c0119
|
|
| BLAKE2b-256 |
a0775efb00bd56125e73f06a5fa65fafaf6bf29ac166d6e828db50b7bc3bd42f
|
Provenance
The following attestation bundles were made for sailor_py-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on sailorhq/sailor-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sailor_py-0.1.1-py3-none-any.whl -
Subject digest:
eebdb9c9685e8d82c5911ee1ab5a7a5e1b0670bb818e56f31c0bd1d0ad47e3a0 - Sigstore transparency entry: 2126697116
- Sigstore integration time:
-
Permalink:
sailorhq/sailor-py@7fbc3425074216a6fa426af6c1cfe26b85350bb2 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/sailorhq
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7fbc3425074216a6fa426af6c1cfe26b85350bb2 -
Trigger Event:
push
-
Statement type: