Introduction
Saaslivery is a multi-tenant ecosystem of small, interconnected productivity apps. Every app in a workspace runs under one address, {workspace}.saaslivery.com, inside a shell that keeps each app open in its own tab. Apps stay small on purpose, and they cooperate through an event bus instead of sharing databases.
This documentation is for developers building on the platform: core contributors adding first-party apps, and third-party developers publishing to the marketplace.
What building here looks like
A Saaslivery app is a Heaven plugin: async Python handlers, Jinja2 templates, HTMX for interactivity, and Alpine.js for client-side state. There is no build step, no bundler, and no frontend framework. You write server-rendered HTML and small JSON APIs; the platform provides authentication, sessions, workspace isolation, permissions, billing, and distribution.
from heaven import App
app = App(debug=False)
app.TEMPLATES("templates", relative_to=__file__, prefix="bookmarks")
# UI runs on the workspace subdomain, under your /app/ prefix
ws = app.subdomain("*")
ws.GET("/app/bookmarks/", "plugins.bookmarks.pages.index")
# JSON runs on the shared API subdomain
api = app.subdomain("api")
api.GET("/bookmarks", "plugins.bookmarks.apis.api_list")
Where to start
| If you want to... | Read |
|---|---|
| Build and run your first app | Your first app |
| Understand how the pieces fit | Platform overview |
| See how apps talk to each other | Event system |
| Call the API from outside | Authentication |
| Publish to the marketplace | Publishing your app |
The rules that shape everything
Five constraints define the platform. Every other rule in these docs is a consequence of one of them.
- Workspace isolation is absolute. Every table has a
workspace_id, every query is scoped to it, and the platform enforces it in middleware, events, files, and WebSockets. See Multi-tenancy. - Apps never touch each other's data. Cross-app integration happens over the event bus, with local read models when an app needs to render another app's data.
- Ten tables per app, maximum. An app that needs more is two apps. See What is an app?
- The server renders the HTML. HTMX loads fragments, Alpine.js handles client-only state, and all data mutations go through the JSON API. See HTMX patterns.
- The platform owns the chrome. Sessions, auth, CORS, asset serving, the app shell, and the security layers are platform concerns. Apps declare; the platform enforces.
Tech stack
| Layer | Technology |
|---|---|
| Backend | Heaven, an async Python ASGI framework |
| Templating | Jinja2 via res.render() |
| Interactivity | HTMX for fragment loading |
| Client logic | Alpine.js for client-side-only state |
| Validation | pytastic, TypedDict-based schemas |
| Styling | Bootstrap 5 plus the platform design system, no build step |