SaaSBase
Embedded SDK for building multi-tenant SaaS products. First-class support for users, services, groups, and AI agents — all treated as actors with per-resource role-based authorization, durable audit trails, and execution tracing for agentic workflows.
Modelled around a simple resource hierarchy: Organization → Project → { Document | ApiCall }.
Pluggable backends for storage, database, and authorization. No Django/Flask/Spring baggage —
just a Python API you wire into whatever HTTP layer you already use (an optional async FastAPI
adapter ships in the box).
Ships alongside a Java SDK with a byte-identical database schema, so a single database can be driven by either SDK.
Highlights
- Actors as first-class citizens —
USER,SERVICE,AGENT,GROUP,API_KEY. Every operation is attributed to one. - Role-based authorization with scope inheritance — grants on an organization automatically flow to its projects, documents, and API calls.
- Named document versioning — stable document IDs; named versions (
"draft","v2","final") each with their own content and metadata. - Execution tracing — start/complete/fail flows for agent and service operations, with correlation IDs, parent/child relationships, and model provenance.
- API keys — scoped to an organization or project, carrying a single role.
sbk_<prefix>.<secret>tokens, soft revocation, optional expiry,last_used_attracking. - Durable audit log — every mutation flows through a configurable sink with explicit retention policies.
- Pluggable backends — SQLite or PostgreSQL, local filesystem or S3, in-memory or OpenFGA authorization.
- Optional async FastAPI surface —
create_app(sb)exposes every domain API over HTTP with automatic Bearer-token authentication for API keys and OpenAPI docs at/docs.
Install
pip install saasbase # core (SQLite + local storage + in-memory authz)
pip install "saasbase[postgres]" # + PostgreSQL
pip install "saasbase[s3]" # + S3-compatible storage
pip install "saasbase[openfga]" # + OpenFGA authorization
pip install "saasbase[fastapi]" # + async FastAPI REST surface
pip install "saasbase[all]" # everything
Python 3.11 or newer.
Quick start
from saasbase import SaaSBase
sb = (
SaaSBase.builder()
.sqlite() # or .postgres(), or .db_url("...")
.local_storage("./data") # or .s3_storage(...)
.build()
)
with sb.as_user("alice") as ctx:
org = ctx.organizations.create("acme", name="Acme Corp")
project = ctx.projects.create(org.id, "platform")
with open("invoice.pdf", "rb") as f:
doc = ctx.documents.upload(
project.id,
name="invoice.pdf",
content=f,
media_type="application/pdf",
)
print(doc.current_version_name) # "v1"
print(doc.checksum) # sha-256 hex
sb.close()
Change the actor to change who audit events are attributed to:
with sb.as_service("billing") as ctx: ...
with sb.as_agent("ocr-v1") as ctx: ...
API keys
Each key binds to one resource (ORGANIZATION or PROJECT) and carries a single Role. When a
key authenticates, it acts as an API_KEY actor whose permissions are exactly the role's actions
on the scoped resource — and inherited scopes. An ORG_OWNER key, for example, can act on any
project, document, or API call inside its organization.
from saasbase import CreateApiKeyRequest, Role
with sb.as_user("alice") as ctx:
created = ctx.api_keys.create(
CreateApiKeyRequest(scope=project.ref, role=Role.PROJECT_EDITOR, name="ci")
)
# The raw `token` is visible exactly once — store it in a secret manager.
print(created.token) # sbk_<prefix>.<secret>
print(created.api_key.id)
# Authenticate a token back to an active API key:
resolved = ctx.api_keys.authenticate(created.token)
assert resolved is not None and resolved.is_active
# Soft-revoke — record is kept for audit; future auth fails.
ctx.api_keys.revoke(created.api_key.id)
expires_at, last_used_at (touched on each successful authentication), and free-form
metadata are all supported.
FastAPI integration (optional)
saasbase.fastapi exposes every domain API over async HTTP. Handlers are async def and offload
blocking SaaSBase calls to a thread pool via anyio.to_thread.run_sync, so concurrent requests
don't stall the event loop.
from saasbase import SaaSBase
from saasbase.fastapi import create_app
sb = SaaSBase.builder().sqlite().local_storage("./data").build()
app = create_app(sb)
# uvicorn my_module:app --reload
Or let the module build a SaaSBase from env vars — useful in containerized deployments:
export SAASBASE_DB_URL=jdbc:sqlite:./data/saasbase.db
export SAASBASE_STORAGE__LOCAL__BASE_PATH=./data/documents
export SAASBASE_WEB__BASE_PATH=/api
uvicorn my_app:app
Every request is attributed to an actor, resolved in this order:
Authorization: Bearer sbk_<prefix>.<secret>— SaaSBase API key; request acts as theAPI_KEYactor.X-SaaSBase-Actor: <type>:<id>header. Values:user:alice,service:billing,agent:tagger-v1,group:admins,api_key:<uuid>.- The configured anonymous fallback (default
service:anonymous). Set it empty to force 401.
# Authenticate as an API key
curl -H "Authorization: Bearer sbk_abcdef123456.xxxxxxx" \
https://api.example.com/api/projects/$PROJECT_ID
# Or as an impersonated user
curl -H "X-SaaSBase-Actor: user:alice" \
-H "Content-Type: application/json" \
-d '{"slug":"acme"}' \
https://api.example.com/api/organizations
create_app(actor_resolver=...) accepts a custom async resolver — e.g. one that decodes a JWT.
Endpoints under the configurable base path (default /api):
/organizations,/projects,/documents(multipart upload, streamed download, versioning)/api-calls,/executions(start/complete/fail + correlation listing + children)/memberships,/api-keys/audit-events(query, by-resource, by-correlation, prune)/authz/{check,explain,effective-roles,who-can,who-can-explain}
Interactive OpenAPI docs at /docs.
Examples
Runnable scripts live in the examples/ directory in the repository:
example_app.py— end-to-end CLI demodocument_centric.py— upload + list + filter documentsapicall_centric.py— track API calls with correlation IDsagent_assisted.py— agent execution lifecycle with provenancedocument_versioning.py— named document versionsfastapi_server.py— run the async REST API with uvicorn
Authorization model
Roles carry a fixed action set (see saasbase.AuthorizationPolicy):
| Role | Typical grant |
|---|---|
ORG_OWNER |
Full control on an org and everything beneath it |
ORG_ADMIN |
Manage projects and members; cannot delete the org |
ORG_MEMBER |
Read-only at the org level |
PROJECT_OWNER |
Full control on one project and its resources |
PROJECT_EDITOR |
Create/update documents and API calls |
PROJECT_VIEWER |
Read-only within a project |
DOCUMENT_* |
Resource-level grants on a single document |
API_CALL_* |
Resource-level grants on a single API call |
Grants are evaluated against the resource hierarchy, so a role granted at a higher scope applies
to descendants. Use authorization.explain(actor, action, resource) to see which role in which
scope allowed (or denied) a call.
Links
- 📦 PyPI: https://pypi.org/project/saasbase/
- 🐙 Source code: https://github.com/sireto/saasbase
- ☕ Java SDK (same database schema): https://github.com/sireto/saasbase/tree/main/java
- 💬 Issues: https://github.com/sireto/saasbase/issues
License
Licensed under the Apache License, Version 2.0. See LICENSE in the source repository.
Release files for saasbase 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 | |
|---|---|---|---|
| saasbase-0.1.1.tar.gz | 58.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| saasbase-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 163.9 kB
Release files / saasbase-0.1.1.tar.gz
| Download URL | saasbase-0.1.1.tar.gz |
|---|---|
| Size | 58.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e44454f22b20b4f0a88617228fd56b3c3e03b7a898ab566cd7616df670f30d6e
|
|
BLAKE2b-256 checksum How to use checksums |
19fc9b5f01daadfcb20caec44f4314a061cd48b6db49711e718695d288eac9aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Apr 20, 2026.
Transparency logRelease files / saasbase-0.1.1-py3-none-any.whl
| Download URL | saasbase-0.1.1-py3-none-any.whl |
|---|---|
| Size | 105.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a66775d4e18cb0950d658f8591a6ef4d077490e19a302209406badf43ed6c791
|
|
BLAKE2b-256 checksum How to use checksums |
696b051927be296d69e6086663a433ece7b74e8d363440867b86b7c1c9f0b1f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Apr 20, 2026.
Transparency log