Open-core licensing for SaaS vendors
Issue, validate, rotate, revoke, and transfer license keys. Five payment providers built in. Rust core is Apache-2.0 — your security team can read every line that touches a customer key.
Apache-2.0 core · SDKs for Python, Rust, TypeScript · Self-host or managed
pip install sawabona-sdk
When you sell software to a regulated buyer, the licensing layer touches keys, payments, and customer identity. Their security review will ask: who wrote this code, and can we audit it? Sawabona's answer is simple — the Rust core is Apache-2.0, published on GitHub. Read it. Fork it. Run it self-hosted if you need to.
Every operation a real customer support team needs — not just “create” and “check”.
POST /licenses
Generate a license key bound to a product, plan, and customer. Hash-only at rest — plaintext shown to the customer exactly once.
POST /licenses/validate
Verify a key at runtime. Returns the tier and feature flags. SDKs cache responses with sane TTLs for offline tolerance.
POST /licenses/{id}/rotate
Issue a fresh key for the same license. Old key invalidated. Plaintext shown once; webhooks never re-expose it.
POST /licenses/{id}/revoke
Disable a license immediately. SDK caches respect a kill flag so the next validation call returns invalid.
POST /licenses/{id}/transfer
Move a license between owners (e.g. company acquisition). New key issued, old one invalidated — full chain visible in audit logs.
GET /licenses/key/{key}
Support-friendly endpoints for “what's the status of this key?” without exposing plaintext anywhere.
All endpoints emit structured audit log events. Keys are stored hashed
(key_hash only) — plaintext exists in your
response body, never in the database.
Five providers, one license event
Each payment provider ships as its own Rust crate. A successful payment fires a license event; a refund or dispute fires a revocation. No glue code in your app.
Stripe
Global
Paddle
Global (MoR)
Square
North America
Flutterwave
Africa
Paystack
Africa
If your buyers are global, Stripe alone leaves money on the table. Each provider is a drop-in crate — add the one your customer's bank actually settles with.
The SDKs validate licenses. That's it. No bundled HTTP client, no async runtime, no framework opinions — embed them in any project without pulling 200 transitive deps.
Python
Pure Python. No native dependencies. Drop into any FastAPI / Django / Flask app.
pip install sawabona-sdk
Rust
Minimal Rust crate. No tokio runtime baked in, no sled, no transitive sawabona-core.
cargo add sawabona-sdk
TypeScript
Browser and Node. ESM and CJS bundles via tsup. Strict TypeScript types.
npm install @sawabona/sdk
Self-host the Rust core for free, or use the managed SaaS. Same engine in both.
From a single product to a fully white-labelled, dedicated, self-hosted-ready deployment — we scope the plan and quotas around your products, your volume, and your compliance needs. Tell us what you're shipping and we'll send a quote.
Or run the Rust core yourself — Apache-2.0, no usage limits, no phone-home.
Every paid product in the theAIstep stack — Jagora, Lisaba premium extensions, Oluso‑pro, Ushahidi‑saas, Kumbukumbu‑saas — validates its tier through Sawabona. Same Rust core, same key shape, same audit surface. If you license commercial software in this ecosystem, you're already running it.
Building under audit?
Sawabona keys also co-sign Oluso intent records (the signed_intent
memory type in Kumbukumbu, the BIM identity in Oluso). One signing story across
licensing and runtime evidence.
The licenses table holds a key_hash — never the
plaintext key. The plaintext is returned exactly once in the API response that creates,
rotates, or transfers a license. Webhooks deliver an opaque license identifier,
never the key itself. Lose the key, rotate it — never restore from the database.
This is a doctrine, not a feature flag. It survives upgrades and refactors because the regression test suite locks it.