Concepts
The Ellivate contract
Ellivate is opinionated: it picks the database, the auth, the session format. If your code fits, publishing is a single verb. If it doesn't, we fail loudly and tell you exactly what to change — never silently misbehave.
Why there's a contract
Ellivate is a *shared-shell platform*, not a hosting provider. Every app runs inside the same identity system, the same persistence layer, and the same mobile super-app experience. That shared shell is what lets a user open Ellivate on their phone and see every app their circle has shipped, without logging into each one separately.
For that to work, apps have to agree on a few things. We call that agreement the contract. It's enforced in three places in parallel:
- The MCP server teaches your LLM the conventions while the code is being written.
- The publish pipeline rewrites non-conforming code into conforming code where it can (storage calls, missing SDK files, host bindings).
- Invariant checks reject publishes that still don't fit, with exact file+line pointers to the violation.
§1 — Identity
Ellivate handles sign-in. Your app never does.
What the contract says
- No third-party auth libraries:
next-auth,@clerk/*,@auth0/*,firebase/auth,Flask-Login, hand-rolled JWT cookies for sessions — all rejected. - No
/login,/signup, or/auth/callbackroutes. Sign-in happens in the Ellivate shell, before the user ever reaches your app. - Viewer identity is read from a single source, depending on your runtime:
ellivate_viewer_tokencookie (Next.js server components, Flask, FastAPI)X-Ellivate-Viewer-Tokenheader (server-to-server)window.ELLIVATE_VIEWER_TOKEN(browser, mobile WebView)- URL fragment
#__ellivate_auth=...on first page load (for vanilla HTML)
- The viewer object your code sees:
{ id, email, username, verified }.
Public vs gated apps
Two independent axes control access:
| Flag | Controls |
|---|---|
isPublic | Whether the app shows up on your public profile / discovery pages. |
requiresAuth | Whether visitors must be signed in to reach any route. |
All four combinations are legal: private+gated (default), public+gated (Notion-workspace pattern — listed on your profile, but visitors have to sign in), public+open (a demo anyone can try), or private+open (rare — "obscure but unauthenticated").
§2 — Persistence
All user-generated data goes through Ellivate's cloud KV. Filesystem and browser-local state don't survive the platform's natural boundaries (redeploys, device switches, session expiry).
What the contract says
- No
localStorage,sessionStorage,IndexedDB,AsyncStorage, or persistence plugins (redux-persist, zustand/persist, pinia-plugin-persistedstate) for user data. - No filesystem writes for user data:
fs.writeFile,sqlite3.connect,shelve.open,pickle.dump,sqlalchemyfor app state. - Keys are plain strings; values are anything JSON-serializable.
- Scope is picked by the classifier at publish time, or by explicit key prefix:
personal:— scoped to the viewer. Each user has their own copy.shared:— scoped to the share group. Everyone with access sees the same data.public:— readable by anonymous visitors on public apps.
- If your app genuinely needs a relational database (joins, transactions, full-text search), the classifier emits a loud warning and publish pauses. We don't smuggle sqlite into a container that gets wiped on redeploy.
§3 — SDK integration
The Ellivate SDK is a local file written into your project at publish time. It is NOT an npm or pip package — installing it from a registry would just 404.
What the contract says
- JavaScript / TypeScript:
ellivate-client.jsandellivate-client.d.tsland at your project root. Import with a relative path —import { ellivate } from "../ellivate-client". Bare specifier"ellivate-client"won't work (npm would try to resolve from the registry). - Python:
ellivate_client.pylands at project root. Import withimport ellivate. - Vanilla HTML:
ellivate-client.global.jsandellivate-config.jsland at project root ANDpublic/. Include via<script src="ellivate-client.global.js">. - The SDK degrades gracefully when the env vars are missing (local dev outside the container) — returns
null, warns once, never throws.
§4 — Deploy environment
Ellivate runs your app in a Railway container with a fixed set of env vars and a specific network binding.
What the contract says
- Bind to
0.0.0.0, notlocalhostor127.0.0.1. Container networking requires the shared interface. - Read the port from the
PORTenv var; framework defaults only as fallback. - Env vars Ellivate provides at runtime:
ELLIVATE_DATA_URL,ELLIVATE_DATA_KEY,ELLIVATE_APP_ID. You never set these yourself. - No user-writable paths outside the project directory.
- If your app declares a secret it needs (an API key for a third-party service), the publish-gate pauses and asks you to fill it in via the dashboard. No env var is ever silently defaulted to empty.
§5 — Out of scope (explicit refusals)
A few app shapes can't run on Ellivate today. Publish fails with a clear, specific error — never a silent no-op:
- React Native / Expo apps. Ellivate's mobile shell is a WebView, not a native host.
- Electron / native desktop apps. Same reason.
- GPU / dedicated hardware / physical display requirements.
- Code that reads or writes paths outside the project directory.
- Playwright apps that hardcode a Chrome channel or require a persistent user profile. Use Ellivate's cloud browser runtime instead.
How the contract evolves
Every new invariant lands in three places together: a server translator or gate, an MCP tool update, and a fixture in the end-to-end test harness. Adding a rule without all three lets the contract drift from reality — which means a publish can "pass" while the app misbehaves in production. Loud failures over silent ones is the first rule of the platform.
Where to go next
- Bring your code — three publish paths (MCP / CLI / dashboard).
- Flask walkthrough — concrete, end-to-end.