Embedded Python SDK for building multi-tenant SaaS products with users, services, groups, and AI agents.
Project description
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.
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 saasbase-0.1.1.tar.gz.
File metadata
- Download URL: saasbase-0.1.1.tar.gz
- Upload date:
- Size: 58.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e44454f22b20b4f0a88617228fd56b3c3e03b7a898ab566cd7616df670f30d6e
|
|
| MD5 |
86fda4f746fafcc95f05cdff8ca2720b
|
|
| BLAKE2b-256 |
19fc9b5f01daadfcb20caec44f4314a061cd48b6db49711e718695d288eac9aa
|
Provenance
The following attestation bundles were made for saasbase-0.1.1.tar.gz:
Publisher:
publish-python.yml on sireto/saasbase
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saasbase-0.1.1.tar.gz -
Subject digest:
e44454f22b20b4f0a88617228fd56b3c3e03b7a898ab566cd7616df670f30d6e - Sigstore transparency entry: 1343387437
- Sigstore integration time:
-
Permalink:
sireto/saasbase@2454e6d87c26df10edfe412ba475ea848b4c5ab5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sireto
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@2454e6d87c26df10edfe412ba475ea848b4c5ab5 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file saasbase-0.1.1-py3-none-any.whl.
File metadata
- Download URL: saasbase-0.1.1-py3-none-any.whl
- Upload date:
- Size: 105.1 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 |
a66775d4e18cb0950d658f8591a6ef4d077490e19a302209406badf43ed6c791
|
|
| MD5 |
afc7bdb080c9a6a2e6693870b88598b0
|
|
| BLAKE2b-256 |
696b051927be296d69e6086663a433ece7b74e8d363440867b86b7c1c9f0b1f1
|
Provenance
The following attestation bundles were made for saasbase-0.1.1-py3-none-any.whl:
Publisher:
publish-python.yml on sireto/saasbase
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saasbase-0.1.1-py3-none-any.whl -
Subject digest:
a66775d4e18cb0950d658f8591a6ef4d077490e19a302209406badf43ed6c791 - Sigstore transparency entry: 1343387463
- Sigstore integration time:
-
Permalink:
sireto/saasbase@2454e6d87c26df10edfe412ba475ea848b4c5ab5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sireto
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@2454e6d87c26df10edfe412ba475ea848b4c5ab5 -
Trigger Event:
workflow_dispatch
-
Statement type: