Keelson Python SDK
Python SDK for building apps on the Keelson platform. Provides three modules:
Note: This repository is a read-only release mirror. Development happens in the private Keelson monorepo; issues are welcome here, but pull requests are not accepted — changes land through the next release.
| Module | Import | Description |
|---|---|---|
keelson_media |
from keelson import media |
Media storage (upload, serve by ID) |
keelson_files |
from keelson import files |
Data files (key-addressed, overwrite, private) |
keelson_identity |
from keelson import identity |
User identity and directory |
Cross-language parity across Node, Python, and Go is defined in
./PARITY.md. APIs below are labelled as
guaranteed (same capability in all 3 languages) or
Python-specific (convenience helpers unique to this SDK).
Installation
pip install keelson-sdk
The PyPI distribution is named keelson-sdk; Python imports continue to use
keelson and the domain modules shown below.
Media SDK (keelson_media)
Upload immutable media (images, PDFs, generated assets) and serve it by ID. On Keelson it uses the managed media storage (internal media API); for local development it uses the local filesystem. The runtime-mode contract is fail-closed: it never silently writes to ephemeral local storage when platform Media configuration is missing or incomplete (see Modes below).
from keelson import media
# Store a file — returns a generated ULID file ID
file_id = media.put(b"Hello, world!", content_type="text/plain")
# Store with filename (MIME type auto-detected)
with open("report.pdf", "rb") as f:
pdf_id = media.put(f.read(), filename="report.pdf")
# Retrieve
data = media.get(file_id)
# Check existence and metadata
if media.exists(file_id):
info = media.stat(file_id)
print(info.content_type, info.content_length)
# Public URL (for embedding in HTML)
src = media.url(file_id) # "/media/01HXYZ..."
# Delete
media.delete(file_id)
Cross-language guaranteed API
| Function | Signature | Description |
|---|---|---|
put |
(data, *, content_type=None, filename=None) -> str |
Store file, returns ULID file_id |
get |
(file_id) -> bytes |
Download file content |
delete |
(file_id) -> None |
Delete file |
exists |
(file_id) -> bool |
Check if file exists |
stat |
(file_id) -> MediaStat |
Get metadata (content_type, content_length, status) |
url |
(file_id) -> str |
Generate public URL path |
Python-specific helpers
| Function | Signature | Description |
|---|---|---|
open |
(file_id) -> BinaryIO |
Get file as binary stream (wraps get() in BytesIO) |
Data class: MediaStat (frozen dataclass with content_type: str, content_length: int, status: int).
Exception: MediaError (subclass of RuntimeError).
Modes (fail-closed runtime-mode contract)
KEELSON_MODE |
Condition | Behaviour |
|---|---|---|
keelson |
Both Media env set | Remote (the Keelson media service) |
keelson |
Media env missing | MediaError — capability unavailable (covers files_enabled=false); never local |
| any | Exactly one of base URL / token set | MediaError — incomplete remote config |
local |
— | Local filesystem (MEDIA_DIR, default ./media) |
| unset | Both Media env set | Remote (backward compatibility) |
| unset | No Media env, platform core env visible (KEELSON_APP_ID / KEELSON_WORKSPACE_ID / KEELSON_DEPLOY_ID) |
MediaError — refuses silent local fallback |
| unset | No Media env, no platform env | Local filesystem (local development) |
The SDK never silently falls back to ephemeral local storage on Keelson: set
KEELSON_MODE=local explicitly for local development.
Environment variables
| Variable | Description |
|---|---|
KEELSON_MODE |
keelson (remote, fail-closed) / local (local FS) / unset (local development). Platform injects keelson. |
KEELSON_INTERNAL_MEDIA_BASE_URL |
Internal endpoint for the Keelson media service (Keelson mode; required with the token). |
KEELSON_APP_MEDIA_TOKEN |
App-scoped bearer token for the Keelson media service; validated by the platform. |
KEELSON_MEDIA_URL_PREFIX |
Public URL prefix (default: /media/). |
MEDIA_DIR |
Local storage directory (default: ./media). Used whenever the SDK resolves to local mode — either explicit KEELSON_MODE=local, or zero-config local development (KEELSON_MODE unset with no Media env and no platform core env). |
Files (data) SDK (keelson_files)
Durable file storage for your app's own files — state, settings, caches. Reads
and writes are always whole-file, and write() is write-through: once it
returns, the data is persisted. There is no background sync and nothing is
stored on ephemeral local disk. Overwriting an existing key is the normal case;
updates to the same key are limited to about once per second. For user-uploaded
or generated media referenced by ID and served over HTTP, use keelson_media;
for data read/written on every request, use the database.
from keelson import files
files.write("seen_urls.json", json.dumps(seen))
seen = json.loads(files.read("seen_urls.json") or "[]") # read() -> bytes | None
keys = files.list() # sorted list[str]
files.delete("seen_urls.json") # idempotent
Cross-language guaranteed API
| Function | Description |
|---|---|
write(key, data) |
Overwrite key with bytes/str (str stored UTF-8); write-through |
read(key) |
bytes, or None when the key is absent (only a 404 is missing) |
delete(key) |
Idempotent delete |
list(prefix="") |
Full, lexicographically-sorted key list; paging absorbed |
Key grammar: /-separated relative path, well-formed UTF-8 ≤ 512 bytes total and
≤ 255 bytes per segment, no leading/trailing /, no empty / . / .. segments,
no control characters. One-object soft limit 10 MiB. There is no exists() —
read() returning None covers it.
Environment variables
| Variable | Description |
|---|---|
KEELSON_MODE |
keelson (remote) or local; the single mode signal |
KEELSON_FILES_BUCKET / KEELSON_FILES_PREFIX |
Platform-injected in keelson mode (managed object storage) |
KEELSON_FILES_DIR |
Local-mode directory (default ./.keelson/files) |
Fail-closed: KEELSON_MODE=keelson requires bucket + prefix + platform identity;
missing config raises FilesError. See ./PARITY.md for the
full contract.
Identity SDK (keelson_identity)
User identity and workspace directory lookup. In production, the Keelson auth
gateway injects trusted X-Keelson-User-* headers before requests reach the app.
Use get_current_user when the basic user profile is enough; use
get_current_identity when the app needs workspace role, app permissions, app
roles, or group attributes.
import os
from keelson_identity import (
get_current_user,
get_current_identity,
list_members,
get_user,
list_groups,
)
user = get_current_user(headers=request.headers)
print(user.email)
identity = get_current_identity(
headers=request.headers,
app_token=os.environ["KEELSON_DIRECTORY_TOKEN"],
)
print(identity.workspace.role)
print(identity.app.permissions) # ["manage", "view"]
if identity.attributes:
print(identity.attributes.groups) # ["developers", "everyone"]
# Directory lookup as the app actor
page = list_members(
app_token=os.environ["KEELSON_DIRECTORY_TOKEN"],
limit=25, offset=0, q="alice",
)
for member in page.items:
print(member.id, member.email, member.name)
member = get_user("user-id-here", app_token=os.environ["KEELSON_DIRECTORY_TOKEN"])
groups = list_groups(app_token=os.environ["KEELSON_DIRECTORY_TOKEN"])
Cross-language guaranteed API
| Function | Signature | Description |
|---|---|---|
get_current_user |
(headers=...) -> UserIdentity |
Parse the current user's basic profile from trusted X-Keelson-User-* headers; no network call |
get_current_identity |
(headers=..., app_token=...) -> CurrentIdentity |
Fetch the current user's full identity as the app actor |
list_members |
(*, base_url=None, cookie=None, authorization=None, app_token=None, **filters) -> PaginatedMembers |
List workspace members |
get_user |
(user_id, *, base_url=None, cookie=None, authorization=None, app_token=None) -> MemberItem |
Get user by ID |
list_groups |
(*, base_url=None, cookie=None, authorization=None, app_token=None) -> list[GroupItem] |
List workspace groups |
get_current_user and get_current_identity accept common request header
mappings. The required header is x-keelson-user-id; x-keelson-user-email
and x-keelson-user-name are optional.
Directory functions also support app-as-actor access with app_token or the
KEELSON_DIRECTORY_TOKEN env fallback. Keep app tokens on the server.
Python-specific helpers
| Function | Signature | Description |
|---|---|---|
is_local_mode |
() -> bool |
Check if running in local mode |
Data classes: CurrentIdentity, UserIdentity, WorkspaceIdentity, AppIdentity, AttributesIdentity, MemberItem, PaginatedMembers, GroupItem.
Exception: IdentityError.
The former TenantIdentity class, identity.tenant attribute, tenant wire key,
KEELSON_TENANT_ID, and KEELSON_LOCAL_TENANT_ID /
KEELSON_LOCAL_TENANT_ROLE remain deprecated aliases through at least the next
major SDK version.
Filtering members by group
list_members narrows results to a single group via either group_id or
group_key:
group_key— the group's code-facing identifier. Always present, stable, and immutable. Prefer this for code references. Keys may be non-ASCII (e.g. a Japanese経理).group_id— a stable UUID for machine integration / internal wiring.
Passing both raises IdentityError. On GroupItem, key is str | None
kept nullable for backward compatibility, but the server always populates it;
id is the UUID.
attributes.groups vs list_groups()
attributes.groups(fromget_current_identity()): the group keys the current user belongs to, scoped to the current app — the system groups the caller holds by role (a subset ofowners/admins/developers/everyone, not all four: an OWNER getsowners/developers/everyone, an app user gets onlyeveryone; exposed regardless of app binding) plus custom groups bound to this app (via a view/manage permission binding or an app-role binding). Custom groups not bound to the app are excluded, and a system group the caller does not hold never appears. Keys are stable and immutable, so they are safe for authorization checks; bind a group to the app if you need to branch on it.list_groups(): all groups in the workspace (each with its stableidandkey). Use for building UI pickers and admin views.
Modes
| Mode | Condition | Behaviour |
|---|---|---|
| Local | KEELSON_LOCAL_MODE=1 |
Returns deterministic fixture data (no HTTP calls) |
| Keelson | Default | Calls the Keelson auth gateway via the platform-injected KEELSON_DIRECTORY_BASE_URL. |
Environment variables
| Variable | Description |
|---|---|
KEELSON_LOCAL_MODE |
Set to 1 to enable local mode (returns fixture data, no HTTP calls). |
KEELSON_LOCAL_WORKSPACE_ID |
Override the local workspace ID. |
KEELSON_LOCAL_WORKSPACE_ROLE |
Override the local workspace role. |
KEELSON_DIRECTORY_BASE_URL |
Canonical, platform-injected base URL for get_current_identity and Directory calls. Use this. |
KEELSON_DIRECTORY_TOKEN |
App token for app-as-actor identity and Directory access. |
KEELSON_IDENTITY_BASE_URL |
Deprecated compatibility alias used only when neither base_url nor KEELSON_DIRECTORY_BASE_URL is set. |
Testing
Run all SDK tests:
uv sync --group dev
uv run pytest
Build the source distribution and wheel from the repository root:
uv build
Run tests for individual modules:
uv run pytest keelson_media/tests/
uv run pytest keelson_identity/tests/
Lint:
uv run ruff check .
Metadata
Release files for keelson-sdk 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| keelson_sdk-0.1.1.tar.gz | 41.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| keelson_sdk-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 82.9 kB
Release files / keelson_sdk-0.1.1.tar.gz
| Download URL | keelson_sdk-0.1.1.tar.gz |
|---|---|
| Size | 41.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b958913366c9c1392f625901fccb5dcc95bf1b9a9f0ed8c532421f054b2ece6a
|
|
BLAKE2b-256 checksum How to use checksums |
4bbcdb868a7108a258e5402c775241a75e9e1d497909d04cada7f42080621fe8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency logRelease files / keelson_sdk-0.1.1-py3-none-any.whl
| Download URL | keelson_sdk-0.1.1-py3-none-any.whl |
|---|---|
| Size | 40.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f6564cfd9d14c4417d3c1e55f12f5d2df69d1944feef4fbf687a40ceb4ff7837
|
|
BLAKE2b-256 checksum How to use checksums |
16f3b7f303e1ab8d5e8b73cac3fb1e6e1d4de4679747491770805d697c35f56d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.
Transparency log