Open-core licensing for SaaS vendors

The licensing engine
you can audit.

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

Validate a license in your app
pip install sawabona-sdk

Your customer's SecOps team wants to know what's in the box.

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.

License lifecycle, full surface.

Every operation a real customer support team needs — not just “create” and “check”.

POST /licenses

Issue

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

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

Rotate

Issue a fresh key for the same license. Old key invalidated. Plaintext shown once; webhooks never re-expose it.

POST /licenses/{id}/revoke

Revoke

Disable a license immediately. SDK caches respect a kill flag so the next validation call returns invalid.

POST /licenses/{id}/transfer

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}

Lookup & list

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

Payments tied to license lifecycle.

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.

Three SDKs. Thin on purpose.

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

sawabona-sdk

Pure Python. No native dependencies. Drop into any FastAPI / Django / Flask app.

pip install sawabona-sdk

Rust

sawabona-sdk

Minimal Rust crate. No tokio runtime baked in, no sled, no transitive sawabona-core.

cargo add sawabona-sdk

TypeScript

@sawabona/sdk

Browser and Node. ESM and CJS bundles via tsup. Strict TypeScript types.

npm install @sawabona/sdk

Pricing.

Self-host the Rust core for free, or use the managed SaaS. Same engine in both.

Managed plans, scoped to you.

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.

The licensing layer behind theAIstep.

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.

See the Compliance Bundle →

Hash-only at rest. Plaintext shown once.

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.