Official Python SDK for confish — typed configuration, actions, and webhooks.
Project description
confish
Official Python SDK for confish — typed configuration, actions, and webhook verification.
- One dependency (
httpx) - Sync client with typed exceptions and automatic retry on
429/5xx - Long-running action consumer with
threading.Eventcancellation - HMAC-SHA256 webhook verification (stdlib only)
Install
pip install confish
Requires Python 3.10+.
Quick start
from confish import Confish
client = Confish(
env_id="a1b2c3d4e5f6",
api_key="confish_sk_...",
)
config = client.fetch()
print(config["site_name"])
The methods return dict[str, Any]. To add static typing, use TypedDict and cast:
from typing import TypedDict, cast
class MyConfig(TypedDict):
site_name: str
max_upload_mb: int
maintenance_mode: bool
config = cast(MyConfig, client.fetch())
config["maintenance_mode"] # type-checked as bool
Or with Pydantic:
from pydantic import BaseModel
class MyConfig(BaseModel):
site_name: str
max_upload_mb: int
maintenance_mode: bool
config = MyConfig.model_validate(client.fetch())
Reading and writing config
# GET /c/{env_id}
config = client.fetch()
# PATCH — only listed fields change
client.update({"maintenance_mode": True})
# PUT — replaces everything; omitted fields reset to defaults
client.replace({
"site_name": "My App",
"max_upload_mb": 50,
"maintenance_mode": False,
})
update and replace return the full updated configuration.
Write access must be enabled in environment settings before
updateandreplacewill work.
Logging
client.logger.info("Worker started", {"region": "eu-west-1"})
client.logger.error("Job failed", {"job_id": "abc"})
# Or directly:
log_id = client.log(level="info", message="User logged in", context={"user_id": 123})
Levels: debug, info, notice, warning, error, critical, alert.
Actions
The action consumer polls for pending actions, acknowledges them, runs your handler, and reports completion or failure — including idempotent skip if another consumer claimed the action first.
import threading
from confish import Confish, Action, SkipAction
client = Confish(env_id="...", api_key="...")
stop = threading.Event()
def handler(action: Action, ctx) -> dict | None:
if action.type == "place_order":
ctx.update("Submitting order", {"params": action.params})
# ... do work ...
return {"order_id": "abc123", "filled_price": 66980.0}
raise RuntimeError(f"Unknown action type: {action.type}")
client.actions.consume(
handler=handler,
poll_interval=15.0, # base — defaults to 15s
max_poll_interval=60.0, # adaptive backoff cap
concurrency=2,
stop=stop,
on_error=lambda exc, action: print(f"action {action.id}: {exc}"),
)
# To stop, e.g. on signal:
import signal
signal.signal(signal.SIGTERM, lambda *_: stop.set())
What happens automatically:
- A returned
dictbecomes the action'sresulton completion. - Raising any exception fails the action with
{"error": str(exc)}. - Raising
SkipActionleaves the action acknowledged without resolving it. - A
409 Conflicton ack is silently skipped — safe to run multiple consumers. - Setting
stophalts new work and waits for in-flight handlers to settle. - After 3 consecutive empty polls the loop doubles its sleep up to
max_poll_interval, resetting topoll_intervalthe moment any action is processed. Idle consumers make ~240 requests/hour by default.
You can also drive the lifecycle manually:
actions = client.actions.list()
client.actions.ack("action_id")
client.actions.update("action_id", "progress", {"step": 2})
client.actions.complete("action_id", {"order_id": "abc"})
client.actions.fail("action_id", {"error": "timeout"})
Webhook verification
from flask import Flask, request, abort
from confish.webhook import verify
import os
app = Flask(__name__)
@app.post("/webhook")
def webhook():
if not verify(
body=request.data,
signature=request.headers.get("X-Confish-Signature"),
secret=os.environ["CONFISH_WEBHOOK_SECRET"],
):
abort(401, "invalid signature")
payload = request.get_json()
# handle payload['event'] ...
return "", 200
verify uses constant-time comparison and rejects timestamps older than 5 minutes by default. Pass tolerance_seconds=0 to disable timestamp checking. Always pass the raw, unparsed body — re-serializing parsed JSON breaks verification.
Errors
from confish import (
AuthError,
ConfishError,
ConflictError,
ForbiddenError,
NetworkError,
RateLimitError,
ServerError,
ValidationError,
)
try:
client.fetch()
except RateLimitError as e:
print(f"slow down — retry after {e.retry_after}s")
except ValidationError as e:
for field, msgs in e.errors.items():
print(f"{field}: {msgs}")
except ConfishError as e:
print(f"HTTP {e.status_code}: {e.message}")
By default the client retries 429 (honoring Retry-After) and 5xx responses up to twice. Tune with max_retries on the Confish constructor.
Options
client = Confish(
env_id="a1b2c3d4e5f6",
api_key="confish_sk_...",
base_url="https://confi.sh", # override for self-hosted
user_agent="my-app/1.0",
max_retries=2,
max_retry_delay=30.0,
http_client=None, # inject your own httpx.Client
)
Confish is a context manager:
with Confish(env_id="...", api_key="...") as client:
config = client.fetch()
License
MIT
Project details
Release history Release notifications | RSS feed
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 confish-0.1.0.tar.gz.
File metadata
- Download URL: confish-0.1.0.tar.gz
- Upload date:
- Size: 12.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f3be477844c24b2b8b6e33eca3f03af7e36a36c39d4fcb078faad3c6ada729b
|
|
| MD5 |
799a053b3a192101df5193820588aaa9
|
|
| BLAKE2b-256 |
d7bfb91c1b4037c96b4ffd68e3e2b89012c78cf76de58cefeabeb573fc7263af
|
Provenance
The following attestation bundles were made for confish-0.1.0.tar.gz:
Publisher:
release.yml on confishhq/confish-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
confish-0.1.0.tar.gz -
Subject digest:
8f3be477844c24b2b8b6e33eca3f03af7e36a36c39d4fcb078faad3c6ada729b - Sigstore transparency entry: 1417646291
- Sigstore integration time:
-
Permalink:
confishhq/confish-python@3d486c7d4092d1d5af7e4e4a620582de58c208f4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/confishhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d486c7d4092d1d5af7e4e4a620582de58c208f4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file confish-0.1.0-py3-none-any.whl.
File metadata
- Download URL: confish-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.9 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 |
ca7c2ce04e3ed9f8f4801d69df94435bcc5e0c2ff4705054df16a231e7aacd21
|
|
| MD5 |
f419af0afb6f088bf67628ddbcd1a51f
|
|
| BLAKE2b-256 |
d618cc3cb75d21d4ea40997706fdb5775122561e5b7b225eb6c7933f8d77162e
|
Provenance
The following attestation bundles were made for confish-0.1.0-py3-none-any.whl:
Publisher:
release.yml on confishhq/confish-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
confish-0.1.0-py3-none-any.whl -
Subject digest:
ca7c2ce04e3ed9f8f4801d69df94435bcc5e0c2ff4705054df16a231e7aacd21 - Sigstore transparency entry: 1417646296
- Sigstore integration time:
-
Permalink:
confishhq/confish-python@3d486c7d4092d1d5af7e4e4a620582de58c208f4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/confishhq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d486c7d4092d1d5af7e4e4a620582de58c208f4 -
Trigger Event:
push
-
Statement type: