S
supero.docs
Documentation/Managing Your App/Continuity & Exit

Continuity & Exit

What you own, how to get your source and your data out, what you would have to replace if you left, and what does not exist yet. Written for the security review, not the sales deck.

The Question This Page Answers

If Supero disappeared tomorrow, or you decided to leave, what would you be holding? This page answers that plainly, including the parts of the answer that are not flattering. It is the page to send to whoever is running your vendor review.
What you wantWhere you stand today
The source of your applicationYou can have it. Every generated app is a directory of readable Python and JavaScript. Download it as an archive, push it into a GitHub or Bitbucket repository you own, or build over MCP so the files are authored on your machine in the first place.
Your dataReadable over the ordinary REST API — records through CRUD, query and bulk-list; the data model through the schema API. A full export is a short script, shown below.
The runtime your app callsYou do not get this. Tenancy, authentication, role and policy enforcement, persistence, query, workflow execution and the managed integrations are the platform, and a generated app is written against them. Leaving means replacing them.
A way to keep running without usSelf-hosting. The platform runs on-premise under a licence. App generation and customer-cloud deploy are not part of that edition.
A contractual guaranteeNone is published. No source escrow arrangement and no uptime SLA appear in these docs. If you need either in writing, ask.

ℹ️ The honest summary

The code and the data are genuinely portable, and getting both out is a scripted afternoon rather than a project. The platform runtime is not portable, and the honest replacement cost is a backend team rebuilding multi-tenancy, RBAC and record-level policy enforcement. Anyone telling you a low-code platform has no lock-in is describing the files, not the runtime.

What a Generated Application Actually Is

It is a directory of files, not an opaque artifact. Nothing about it is compiled, minified or hidden from you. The parts that matter:
FileWhat it holds
schemas.pyYour entities, their fields, types, defaults and relationships. The data model as code.
ui_schemas.pyHow each entity is presented — list columns, form layout, labels, which fields appear where.
setup.pyTenants, roles and the access policies. Entity, record and field-level rules are declared here as data, not buried in logic.
config.pyApp identity, the platform URL it talks to, seed users and the managed services it imports.
ui/app.jsThe application front end.
run.sh, run.bat, run_on_prem.shBootstrap and launch — install dependencies, register the app against a backend, upload schemas, serve the UI.
schemas.json, ui_schemas.json, app_manifest.jsonThe same model in JSON, for tooling that would rather not import Python.
Read any of them, change any of them, and re-run. There is no step where the platform stops you editing what it produced.

Getting the source out

Three routes. All three end with the same files in your hands.
RouteWhat you getNotes
Build over MCP from your own editorThe files are authored directly on your machine as the app is built, so they are yours from the first minute and you commit them yourself.The path to choose if source-in-your-own-version-control is a standing requirement rather than an exit plan. See Build an App over MCP.
Download from the builderThe generated app as a .tar.gz archive. The download sits on the package-and-review step, the deploy step, and the app card in App Studio.A point-in-time archive of one generation. Download again after each regeneration — nothing keeps an old archive current.
Sync to GitA push of the generated app into a GitHub or Bitbucket repository you own — a new repository or an existing one, private by default, on a branch you name.You connect GitHub or Bitbucket first, as an integration on your project. Secrets and .gitignore-d files are stripped before the push.

ℹ️ The CLI runs a bundle, it does not fetch one

A common misreading: the local CLI is not a download tool. It expects the app files to already be in the directory you run it from, and it launches them. Use one of the three routes above to get the files, then the CLI to run them. See CLI (App Runner).

⚠️ Sync to Git is a push, not a two-way sync

It writes the generated bundle into your repository. It does not watch the repository, and edits you make there do not flow back into the app in Supero. Treat the repository as an export target and an audit trail, not as the place you edit and expect the platform to follow. Bringing an externally edited bundle back into a project is a separate path and is not generally available: the upload answers 404 unless it has been enabled for your workspace, so do not design a round-trip workflow around it without asking us first.

ℹ️ Container files are written on first run

A fresh bundle does not contain a Dockerfile. The first time you run the app locally, the SDK writes a Dockerfile and a docker-compose.yml into the directory alongside your files, and it will not overwrite either one if it already exists — so you can edit them and keep your changes. For the cloud deploy targets the equivalent container definition is produced by the build, not taken from the bundle.

Getting Your Data Out

There is no dedicated export button, and you do not need one. Your records are readable through the same REST API your application uses, and your data model is readable through the schema API. A complete export is a loop over two endpoints.
The script below enumerates every object type in a project, pages every record of each type using cursor pagination, and writes one JSON file per type. It uses only documented endpoints — see CRUD Endpoints, Querying & Pagination and Schema API for the full contracts.
python
import json, os, urllib.request

BASE    = "https://api.supero.dev"
DOMAIN  = "acme-corp"
PROJECT = "550e8400-e29b-41d4-a716-446655440000"
KEY     = os.environ["SUPERO_API_KEY"]        # ak_...

def call(method, path, body=None):
    req = urllib.request.Request(
        BASE + path,
        method=method,
        data=json.dumps(body).encode() if body is not None else None,
        headers={"X-API-Key": KEY, "Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req) as r:
        return json.load(r)

# 1. The data model. Use the PROJECT-scoped schema endpoint: the domain-scoped
#    one returns every schema in the domain, not just this project's.
schemas = call("GET", f"/api/v1/projects/{PROJECT}/schemas")["schemas"]
json.dump(schemas, open("_schemas.json", "w"), indent=2)

# Only object schemas hold records. Qualify with the namespace when there is
# one - a bare name is resolved against the caller's own namespace.
types = []
for s in schemas:
    if s.get("schema_type") != "object":
        continue
    ns = s.get("namespace")
    types.append(f"{ns}:{s['name']}" if ns else s["name"])

# 2. The records. Always send limit - with no pagination parameter at all the
#    response has no pagination object and a paging loop never terminates.
for name in types:
    rows, cursor = [], None
    while True:
        body = {"type": name, "limit": 500}
        if cursor:
            body["cursor"] = cursor
        page = call("POST", f"/api/v1/crud/{DOMAIN}/list-bulk", body)
        rows += page.get("results", [])
        pg = page.get("pagination") or {}
        cursor = pg.get("next_cursor")
        if not pg.get("has_more") or not cursor:
            break

    json.dump(rows, open(f"{name.replace(':', '_')}.json", "w"), indent=2)
    print(f"{name}: {len(rows)} records")
Points worth knowing before you rely on it. The page size ceiling is 1000. Cursor paging is the one to use for a full sweep — offset paging degrades on deep pages. And an export runs with the permissions of whatever credential you used: an API key scoped to one tenant exports one tenant. If you are exporting a multi-tenant application in full, run it with a credential that can actually see every tenant, then check the record counts against what the app reports rather than assuming a clean 200 means a complete extract.

⚠️ This exports records, not attachments

Uploaded files, images and documents are not in the JSON. A record carries a file identifier; the bytes live in file storage and are fetched separately through the file API. A complete extract means a second pass that walks the file identifiers in your exported records and downloads each one. If your application holds signed documents, invoices or user uploads, that second pass is the half of the export that actually matters — do not discover it during a migration.

🚨 A deleted record is gone

The CRUD API has no soft delete, no trash, no undo and no restore. A delete is permanent the moment it succeeds, and Supero cannot bring the record back for you. There is no self-service point-in-time restore. The audit trail records that a delete happened — actor, time, which record — but not the record's contents, so it is not a recovery mechanism. Your own export is the only recovery path that exists for a deleted record, which is an argument for running the script above on a schedule rather than only on your way out of the door.

What Does Not Come With You

This is the part worth being blunt about. A generated application is a client of the platform, not a self-contained server. It serves your UI and it holds your model, and essentially every operation it performs is a call to a backend it is configured to point at. Nothing in the bundle stores a record, checks a permission, or runs a workflow — it asks. That backend is what you would have to replace.
CapabilityWhere it runsWhat you would have to build
Authentication, sessions, password and token handlingPlatformA full identity layer, including refresh and revocation.
Tenancy and data isolationPlatformTenant scoping enforced on every read and write. This is the expensive one.
Roles and permissionsPlatformRole resolution and permission checks on every call.
Access policies (entity, record and field level)Platform — declared in your app, enforced by the platformA policy engine. You keep the policy declarations; you lose the thing that enforces them.
Persistence, CRUD, query, aggregationPlatformA database, plus the filter, sort, pagination and aggregation engine your app already calls.
Transactional services — cart, order, payment, booking, inventory, membership, approvals and the restPlatformEvery service handler your app imports. Your app declares which entity extends which service; the behaviour behind that declaration is not in the bundle. For most applications this is the single largest rewrite.
Workflow executionPlatform — definitions in your files, engine on the platformA workflow runner: step types, branching, retries and the run history. You keep the workflow definitions; you lose the thing that executes them.
Managed integrations (email, SMS, payments, storage and the rest)PlatformDirect vendor integrations, with your own credential storage.
The admin panelPlatformAn internal administration UI.
Your data model and UI definitionsYour filesNothing — these come with you.
Your application front endYour filesNothing — but it is written against the platform API and would need repointing.
Put concretely: exporting gives you a working description of your application and all of its data. It does not give you a running application, because the thing it talks to is not in the box. Budget the replacement as backend work, not as a port.

The SDK Version Policy, and the Risk In It

A generated application depends on the Supero SDK, and it takes that dependency as a version floor rather than an exact pin. Its launcher installs the newest published SDK at or above the version that was current when the app was generated.
bash
pip install --no-cache-dir --upgrade "supero>=3.5.951"
That is a real line from a real generated app. Three things follow from it, and you should weigh all three.
What it means
Why it is a floorAn exact pin is unsatisfiable if that version is not on the index — because it was never published there, or was later yanked. The install then fails hard and the deploy fails with it. A floor always resolves to something installable.
What it buys youA published platform fix reaches every application on its next deploy, with no regeneration and nothing for you to do. You are not stranded on the SDK that happened to be current the week you generated.
What it costs youYour application can change without you changing it. Redeploy an app you have not touched in three months and it runs against a newer SDK than it was written against. A breaking SDK release can therefore reach a running application on redeploy.

⚠️ You cannot pin the SDK, and it is worth knowing where this bites

There is no setting, field or flag that pins an application to an SDK version. Every containerised deploy path installs the SDK fresh at build time and resolves to the newest release above the floor, so redeploying is the moment the version can move. Running locally is the exception — the launcher only installs the SDK when it is missing, so an environment that already has it is left alone. The practical consequence for a regulated or change-controlled deployment: a redeploy is not a no-op even when nothing in your app changed. Treat every redeploy as a change, and redeploy to a preview before you redeploy production.
For an air-gapped or fully controlled install, the on-premise launcher accepts a directory of SDK wheels and installs from it offline instead of reaching the public index. That is the supported way to control exactly which SDK an application runs against.

Self-Hosting as the Continuity Answer

If the concern is that Supero the company becomes unavailable, the answer is not the exported files — it is running the platform yourself. The platform ships as an on-premise package you install on your own hosts. It is licence-gated, and the licence is a signed file verified locally and offline: there is no licence server your installation has to reach, and no runtime dependency on Supero once it is running. See On-Premise Deployment.

⚠️ What the on-premise edition does not include

App generation is cloud-only. The AI builder that turns a description into an application is not part of the on-premise package and cannot be enabled there — you build and generate in Supero Cloud, then deploy the resulting app against your own backend. Deploying into a customer AWS account or GCP project is likewise a cloud-only capability. Plan for the on-premise edition to run your applications, not to build new ones.
If a continuity plan matters to your review, the version of it that actually holds is: generated source in your own repository, a scheduled data export, and a rehearsed on-premise install. Each of those three exists today and you can test all of them before you sign anything.

What Does Not Exist Today

Stated so you do not have to discover it during a security review.
  • •No two-way source sync. Source leaves the platform as an archive or a Git push. Editing the repository does not change the app in Supero, and bringing an edited bundle back in is not a generally available path.
  • •No source escrow arrangement is documented. If your procurement requires escrow, it has to be negotiated rather than switched on.
  • •No uptime SLA is published in this documentation.
  • •No self-service data-restore. Deletes are permanent and there is no point-in-time recovery you can trigger yourself.
  • •No SDK version pinning on the hosted deploy paths.
  • •No self-service custom domains — see Custom Domains.

ℹ️ Ask for the answer in writing

Escrow, an SLA, retention and recovery commitments, and anything else your review needs on paper: mail [email protected] and ask for it in writing. An answer in a mail is worth more to your reviewer than a sentence on a documentation page, and this page will not claim a commitment that has not been made.
Related: Ways to Build compares the build paths and where the code ends up. Anatomy of a Supero App walks through the generated files. On-Premise Deployment covers running the platform yourself. Contact & Links lists who to write to.