Skip to Content
Moving to the Buildpad EngineWith the Command Line (Operators)

Moving with the Command Line

This page is for platform operators. Most members move a project with Move to the Buildpad Engine on the project page, which keeps the project’s ID and URLs. See Moving to the Buildpad Engine.

With the command line, you create a new Buildpad Engine project and move your data, access rules and app code to it with the migration tool. Then you move your users to the new project and retire the old one. Use it when the button is not available on your platform, or for a trial run.

Plan the move with your platform operator. They give you the migration tool and the old project’s Supabase URL and service-role key, and they raise the new project’s upload limit if you have files over 100 MB.

What Moves and What You Redo

PartHow it moves
Collections, fields and relationsThe migration tool
Roles, policies, permissions and their linksThe migration tool. On a new project with Buildpad Engine 0.1.0-dev.13 or later, every permission rule moves; you review the rules whose meaning changes before you switch over. On an older Engine, some rules need a rewrite by hand.
UsersThe migration tool. Users keep their IDs, their passwords, their roles (with the tenant each role is for) and their authenticator apps.
Folders and filesThe migration tool. File IDs stay the same, so records that point to files still work. Large files move too, when your operator raises the new project’s upload limit.
RecordsThe migration tool. Primary keys stay the same, and ID counters continue after the imported records.
Project settings: name, look, outgoing email and MCPThe migration tool
Hooks, cron jobs and custom servicesThe migration tool copies the ones that run on the Buildpad Engine, switched off. You rewrite the others with your AI agent’s migrate-to-engine skill, switched off and under the same name. When you switch over, the tool switches on every one that was on in the old project, the rewritten ones included.
Scope types and items (tenants)The migration tool. They keep their IDs and go beneath the new project’s own tenant.
WorkflowsThe migration tool
App codeThe fix-app tool makes the routine changes on a new branch that you review and merge. You port the rest with your AI agent’s migrate-to-engine skill.

These do not move:

  • Passwords of users who signed in only with Google or another provider. They sign in with XAppHub, or an administrator sets a password.
  • Authenticator apps that the old project stored encrypted. Those users keep their account and set up MFA again.
  • Static tokens. Give their holders new tokens on the Buildpad Engine. The export lists the holders by ID in users/static_token_holders.json.
  • CORS settings. The Engine needs none.
  • Values in hashed or concealed fields.
  • Database views, functions and triggers.
  • Links from records to workflow instances. The records move, but their link to a workflow instance is cleared.
  • The old project’s system account and its seeded administrator. The new project has its own. The import lists them as “not moved by design”.

Before You Start

Update the old project. The old project must run a Buildpad DaaS version that has the September 2026 schema release. Each Platform numbers its DaaS versions separately, so the minimum depends on where the project is:

PlatformMinimum Buildpad DaaS version
app.buildpad.ai0.1.96
The dev Platform (app-dev.buildpad.ai)0.1.109

The DaaS page shows the version under Service Status. If the version is older:

  1. On the old project’s DaaS page, open Backups and click Create Backup.
  2. Click Update Version, enter the minimum version from the table or a later one, and click Update Version.

The update keeps your data. An older version gives its schema in a format that the migration tool cannot read.

Create the new project. In the same organization, click Create Project. When the Buildpad Engine is your platform’s default, the new project is a Buildpad Engine project. Wait until its setup completes.

Collect these values:

FromValueWhere
Old projectThe DaaS URLDetails › Services & Integrations › DaaS Studio
Old projectThe administrator email and passwordDetails › Services Configuration
New projectThe Engine URLDetails › Services & Integrations › API
New projectThe Studio administrator email and password (the project administrator)Details › Credentials
New projectThe host administrator password. Only an organization Admin, or the project’s creator while their organization role is Admin or Member, can reveal it.Details › Credentials › Advanced configuration › Reveal Engine administrator password
New projectThe Engine’s bcrypt cost (AUTH_BCRYPT_COST), if it is not the default 10From your platform operator
Old projectThe Supabase URL and the service-role keyFrom your platform operator
New projectThe tenant IDNEXT_PUBLIC_BUILDPAD_TENANT_ID in the starter kit’s .env.local

Back up the new project. On the new project’s DaaS page, open Backups and click Create Backup. If the import goes wrong, you can restore this backup and start again.

Prepare your computer. You need Node.js 20 or later and the migration tool from your platform operator.

The tool writes your project’s data to a folder on your computer: users’ emails, records and files. Keep that folder on an encrypted disk, do not commit it, and delete it when you finish.

Set Up the Tool

The tool reads its settings from environment variables. In the terminal where you run it, set them as below. read -s lets you type each password without showing it and without saving it in your shell history.

export OLD_DAAS_URL="<old project's DaaS URL>" export OLD_DAAS_EMAIL="<old administrator email>" read -s OLD_DAAS_PASSWORD && export OLD_DAAS_PASSWORD export SOURCE_KONG_URL="<old project's Supabase URL>" read -s SOURCE_SERVICE_ROLE_KEY && export SOURCE_SERVICE_ROLE_KEY export ENGINE_URL="<new project's Engine URL>" export ENGINE_TENANT_ID="<new project's tenant ID>" export ENGINE_ADMIN_EMAIL="admin@example.com" read -s ENGINE_ADMIN_PASSWORD && export ENGINE_ADMIN_PASSWORD read -s ENGINE_HOST_ADMIN_PASSWORD && export ENGINE_HOST_ADMIN_PASSWORD export EXPORT_DIR="$HOME/migration/my-project"

The export and the import both read the old database directly, through SOURCE_KONG_URL, with read-only queries only. The old DaaS Studio shows an administrator only the rows of the tenant they work in, so the tool does not rely on it for role assignments and tenant rows.

The export also signs in to the old Studio. A static token is the best way to do this. A password sign-in fails when the old administrator uses an authenticator app or must set one up, and the export then stops with an error that asks for OLD_DAAS_TOKEN. To get a token, open Users in the old Studio and open an active administrator. In the Token field, click Generate Token, copy the token, and then save the user. The Studio does not show the token again after you save. Then run read -s OLD_DAAS_TOKEN && export OLD_DAAS_TOKEN instead of setting OLD_DAAS_EMAIL and OLD_DAAS_PASSWORD. When OLD_DAAS_TOKEN is set, the tool uses it and does not use the email and password.

The tool signs in to the new project twice. The host administrator imports the user accounts, because the new project accepts accounts from nobody else. The project administrator (ENGINE_ADMIN_EMAIL) does everything else, including the ID counters. If your operator says the Engine’s bcrypt cost is not 10, also run export ENGINE_AUTH_BCRYPT_COST=<cost>.

Run every step in that same terminal. The tool prints only counts, statuses and IDs. It reads password hashes, authenticator secrets and the email password into memory only, and never writes them to the folder.

Step by Step

Look at the old project

Run the export as a dry run. It writes nothing and prints how many collections, users, records, files and automation items the old project has.

node export-old.mjs --dry-run

Freeze the old project

Tell your users the date. Before the final export, stop all writes: stop the app or put it in maintenance mode, and turn off hooks and cron jobs in the old DaaS Studio. Changes made after the export do not move.

For a practice run, skip the freeze and export a live copy.

Export

node export-old.mjs

If the export stops, run it again. It continues where it stopped.

The export ends by counting each table it exported in the old database. If a count differs, it prints DIFF with the table name and stops with an error. Export again: rows may have changed during the export.

Plan the import

Run the import as a dry run. It changes nothing on the new project.

node import-engine.mjs --dry-run --pending-link=xapphub

Read the plan it prints. It lists the schema changes, the permission rules you must review or rewrite, the automation that needs a rewrite, and how many of each thing it will create. The new project checks every user account and reports any it would refuse. If a step fails, fix the cause before you import.

--pending-link=xapphub lets users who signed in only with an external provider sign in with XAppHub on the new project. Leave it out if the new project has no XAppHub sign-in.

Before it sends any account, the tool checks the bcrypt cost of every old password. If a password costs more than the new project’s bcrypt cost, the tool stops and names the cost to set. Ask your platform operator to set it on the new project, then run again with --engine-bcrypt-cost=<cost>. To import those users without a usable password instead, add --allow-unusable-hashes.

Import

node import-engine.mjs --pending-link=xapphub

The import creates the schema, then the scope tree (tenants), roles, policies, users, permissions, folders, files and records. It sends users 150 at a time, at most 6 times a minute, so 3,000 users take about four minutes. Then it moves the ID counters past the imported records, copies the project settings, and copies the automation. If it stops, fix the cause and run it again. It skips what it already did. When the new project refuses something, the error line shows the new project’s reason.

If a file is larger than the new project’s upload limit, the import skips it and prints SKIPPED with its ID. Ask your platform operator to raise the limit for the move, then run node import-engine.mjs --only=files --max-upload-bytes=<new limit>.

Check the counts

The import ends by comparing counts. It counts the role assignments, the access links and each collection’s records in the old database itself, then compares them with the export and the new project. Each line must say OK. It also prints how many users, authenticator apps, settings and automation items it copied, and how many permission rules it copied, flagged for your review, and could not place.

The new project shows more roles, policies, permissions and users than the old one. This is expected: every Buildpad Engine project has its own Workers role and policy, their permissions and a worker account.

Sign in to the new app as a migrated user, with the old password.

Rewrite the automation that the check flagged

The Engine runs hook, cron and custom service code in its own JavaScript runtime, not in Node.js. The import checks each one. It copies the ones that pass, switched off. It lists the others in engine-plan/automation-to-review.json, with each problem, its line, and a prompt for your AI agent’s migrate-to-engine skill.

  • Give each item’s prompt to your AI agent. The prompt names the migrate-to-engine skill, which is in the new project’s starter kit. The agent rewrites the item and creates it switched off, under the same name.
  • Your agent can create the rewritten items over MCP, as an administrator: the Buildpad Engine gives administrators the MCP tools extensions, cron and services. You can also create them in the Studio.
  • Keep each item’s name. When you switch over, the tool finds a rewritten item by its kind and name, and switches it on if it was on in the old project.
  • Test each copied and rewritten item on the new project before you switch over.

Review the permission rules

The tool reads the new project’s Buildpad Engine version.

Buildpad Engine 0.1.0-dev.13 or later: the tool copies every permission rule. Some rules can give access to more records than before, because the old project treated some variables as empty (null) and the Engine fills them in:

VariableOld projectNew project
$CURRENT_ROLESemptythe user’s roles for the tenant of the request
$CURRENT_ROLE.<field> and $CURRENT_USER.role.<field>emptythat field of the user’s role
$NOW with an adjustment, such as $NOW(-1 day)emptythe time of the request, adjusted

The tool lists each copied rule that uses one of them in engine-plan/permissions-to-acknowledge.json, with its collection, action, policy, the variables and an explanation. Before you switch over, review each rule with the project owner. Your agent’s migrate-to-engine skill can show them to you. Change a rule in the Studio if it does not do what its author meant. Then acknowledge the rules:

  1. Copy permissions-to-acknowledge.json, for example to reviewed-permissions.json.
  2. In the copy, set "acknowledged": true on each rule.
  3. Give the copy to the switch-over command (see Deploy and switch over).

The switch-over command does not run until each rule is acknowledged. --acknowledge-all-permissions accepts all the rules at once and prints a warning that lists each of them. Use it only when you reviewed the rules in another way.

If the new project refuses a rule when the tool saves it, the tool does not copy it. The rule is in engine-plan/permissions-to-rewrite.json with the reason “refused by the Engine” and the new project’s message. So is a rule that names a tenant the new project does not have. Rewrite each one in the Studio.

The tool also copies the tenants that each new role is limited to. A role that already exists on the new project under the same name does not change. If a role names an old tenant that the new project does not have, the tool still creates the role, but that part of its limit names no tenant. The import prints a warning and lists each one in engine-plan/roles-scope-not-mapped.json. Correct those roles in the Studio.

Older Buildpad Engine: the tool copies the rules that use $CURRENT_USER, $CURRENT_USER.<field>, $CURRENT_ROLE and $NOW. It does not copy rules that use other variables, such as $CURRENT_ROLES, $CURRENT_POLICIES or $NOW(-1 day). They are in engine-plan/permissions-to-rewrite.json. Rewrite each one in the Studio, or ask your platform operator to update the new project’s Engine and run node import-engine.mjs --only=permissions again.

Check the users who need a password

Users sign in with their old password and their old authenticator app. Nobody gets a new password.

The import writes users-need-password.csv only when some users cannot sign in: for example users who signed in with Google and have no XAppHub link. The file lists their emails and the reason, and no passwords. An administrator sets a password for each of them in the Studio. Then delete the file.

  • A Buildpad Engine project has no sign-up and no password recovery email. An administrator creates users and resets passwords.
  • Users who signed in only with an external provider sign in with XAppHub. See XAppHub.
  • A user whose email already had a Buildpad Engine account keeps that account and its password.
  • Each user arrives with their roles. A user with no role cannot sign in to the project. The import warns about them and lists them under no_role in engine-plan/accounts-report.json. Give each one a role in the Studio.
  • Users under mfa_to_set_up_again in engine-plan/accounts-report.json sign in with their password and set up their authenticator app again.
  • The old project’s system account and seeded administrator are not moved: the new project has its own. They are under not_moved_by_design in engine-plan/accounts-report.json, and are not errors.

Port the app code

  1. On the new project, download a starter kit from AI IDE Setup. It has the right .env.local and the .buildpad/project.json file that tells your AI agent the project uses the Buildpad Engine. The fix-app tool in step 3 adds that file to your app.

  2. Run the scanner from the migration tool on your app’s repository. It lists each place where the code uses Supabase-only features.

    node scan-app.mjs ../my-app
  3. Run fix-app on a clean copy of your app’s repository. First do a dry run: it prints the changes and changes nothing. Then run it again without --dry-run.

    node fix-app.mjs ../my-app --dry-run --engine-url="$ENGINE_URL" --tenant-id="$ENGINE_TENANT_ID" node fix-app.mjs ../my-app --engine-url="$ENGINE_URL" --tenant-id="$ENGINE_TENANT_ID"

    The tool creates the local branch move-to-engine/fix-app and makes one commit for each kind of change. It does not push the branch: it prints the push command. Review the branch and merge it yourself. The tool makes only these changes:

    • Environment: in .env.example, and in .env.local when you give both values, NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_BUILDPAD_DAAS_URL become the Engine URL, NEXT_PUBLIC_SUPABASE_ANON_KEY becomes the fixed public value, NEXT_PUBLIC_BUILDPAD_TENANT_ID is added and SUPABASE_SERVICE_ROLE_KEY is removed. In amplify.yml, the service-role key is removed from the build’s environment file. The tool does not change your Amplify app’s settings.
    • .buildpad/project.json: it says that the backend is the Buildpad Engine.
    • Password sign-in: a server route signs in with the X-Tenant-Id header. A browser page signs in through your app’s /api/auth/login route. If your app has no such route, the tool adds the starter’s route.
    • Files: a Supabase Storage URL or call that names a file ID becomes the Engine’s /api/files/assets/:id or a signed URL. A page must show a file through a signed URL.
    • Migrations: with --delete-migrations, it deletes the supabase/migrations folder.

    The tool does not change anything else, such as service-role code, database functions, realtime, OAuth, magic links, one-time codes, password reset, data calls or tests. It lists each place in MOVE-TO-ENGINE.md on the branch, with the file, the line and the scanner’s pattern ID. Then it runs the scanner again, and the scanner must find exactly the places in the list.

  4. Change each place in the list:

    • Environment: use the new kit’s values. NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_BUILDPAD_DAAS_URL are the Engine URL. Add NEXT_PUBLIC_BUILDPAD_TENANT_ID. Remove SUPABASE_SERVICE_ROLE_KEY.
    • Sign-in: sign in with a password from a server route, as the starter does. Remove sign-up, OAuth, magic links, one-time codes and password reset.
    • Data calls: send the user’s access token as Authorization: Bearer <access token>. Data paths start with /api.
    • Service-role code: remove it. Do administrator tasks in the Studio, or on the server with an administrator session.
    • Files: replace Supabase Storage with the Engine’s files API and signed URLs. Image transform parameters work on /api/files/assets/:id and on signed URLs. A request for WebP or AVIF gets JPEG or PNG.
    • Schema: delete the supabase/migrations folder. Change the schema in the Studio or through your AI agent.
    • Database functions: move their logic into an API route, a hook or a custom service.
    • CORS: remove any CORS setup. The Engine needs none.
  5. Let your AI agent help. With .buildpad/project.json committed, ask it to use the migrate-to-engine skill and give it MOVE-TO-ENGINE.md. The skill is in the new project’s starter kit. It works through each place, and uses the other skills’ Buildpad Engine guidance (create-api-route, add-files, add-users, create-rbac).

See Projects for the sign-in and session rules.

Deploy and switch over

A microapp belongs to one project, so the app moves as a new microapp:

  1. In the new project, create a microapp. See Microapps.

  2. Push the ported code to the microapp’s repository, and check that the build succeeds.

  3. After the freeze, do the final export and import.

  4. Acknowledge the flagged permission rules (see Review the permission rules), and switch on the hooks, cron jobs and custom services that were on in the old project, the rewritten ones included:

    node import-engine.mjs --only=activate --acknowledge-permissions reviewed-permissions.json

    The command prints each item it switches on. An item that was on in the old project and has no item of the same kind and name on the new project is printed as “still to rewrite”.

  5. Point your users, and any custom domain, to the new app.

  6. Keep the old project stopped, but not deleted, for an agreed time, such as 30 days. Then delete the old project.

Schema Changes the Tool Makes

The Buildpad Engine accepts a fixed list of column types and default values. The tool adjusts the schema, and the plan lists each change.

  • Array columns become text arrays. Enum columns become text columns, so the database no longer limits their values.
  • The default uuid_generate_v4() becomes gen_random_uuid(), and timezone('utc', now()) becomes now().
  • Other defaults that are not a literal value, a number, a boolean, NULL, gen_random_uuid() or a current date or time are removed. Set them again in the Studio if you need them.
  • System collections and database views are not moved.
  • Projects: the two backends, and how to tell which one a project uses
  • Microapps: create the microapp for the moved app
  • Workers: workers on a Buildpad Engine project
Last updated on