Baselyra Docs

The Studio

The Studio is compiled into the server and served at /. It talks to /admin/v1 and nothing else — every screen here is a view over an API you can call yourself. Its accounts live in the control database and have nothing to do with your application's users.

Signing in

Open the instance root and sign in with the credentials scripts/setup.sh printed on the first boot. That account lives in control.platform_users, in the control database, and is created by scripts/migrate.js from BASELYRA_ADMIN_EMAIL and BASELYRA_ADMIN_PASSWORD.

POST /admin/v1/login is capped at five attempts per minute per IP: it is the one route on the instance that turns a password into a token holding service_role, and it is reachable without any credential.

The database page

https://api.example.com/database/public/invoices Baselyra acme-production Overview Database SQL Auth Storage Realtime API AI Email Settings baselyra · healthy public / invoices Policies Insert row public invoices articles notes signup_leads RLS off auth users sessions id number amount paid_at 3f2a8c14-… INV-0042 1250.00 2026-08-19 9b41c0d7-… INV-0041 90.00 null c07e5511-… INV-0040 430.50 2026-08-11 18aa9f30-… INV-0039 75.00 null 7d2b6e88-… INV-0038 1899.99 2026-07-30 Row level security on invoices_own · ALL · authenticated using (owner = (select auth.uid())) with check (owner = (select auth.uid()))
Database → public.invoices. The RLS state of every table is shown in the list, because a table with row level security off is the one mistake that ships a world-writable API.

Schemas are grouped the way the server groups them: your schemas first and expanded, auth and storage collapsed under System, and baselyra, pg_catalog, information_schema and pg_toast not returned at all. Every table carries a planner row estimate and its RLS state.

The pages

PageWhat it doesAPI behind it
OverviewInstance health, counts, largest tables, recent audit entries, and the Connect panel with your URL and anon key/admin/v1/overview, /usage, /keys
DatabaseTable list with RLS state, data grid, row editor, column and policy editing/admin/v1/schema, /tables/…/rows, /tables, /policies
SQLEditor with schema-aware autocompletion, a results grid, saved snippets, and a read-only/write toggle/admin/v1/sql
AuthApplication users: list, search, create, edit, ban, delete/auth/v1/admin/users
StorageBuckets, a file browser, upload, preview and signed URLs/storage/v1/*
RealtimeWhich tables are enabled, the live socket count, and an event inspector over a WebSocket/admin/v1/realtime
APIA generated reference per table, the project keys, and copyable snippets/admin/v1/schema, /keys
AIAsk-your-database and SQL generation. Hidden entirely when no key is configured./ai/v1/*
EmailThe six templates, with a live preview and a test send/admin/v1/email-templates/*
SettingsInstance settings, keys, the team, and the audit log/admin/v1/settings, /team, /roles, /logs

Cmd + K opens a command palette that jumps between pages, tables and actions. Below the md breakpoint the sidebar becomes a drawer and every table scrolls inside its own container.

The SQL editor

The editor defaults to read-only. Flipping the segmented control beside Run to Write tints it with the warning colour, because that is the mode that can drop things. Cmd + Enter runs the selection, or the whole statement when nothing is selected.

{"columns":[{"name":"count","type":"int8"}],
 "rows":[{"count":"3"}],
 "rowCount":1,
 "durationMs":4,
 "command":"SELECT"}

Three properties are worth knowing before you use it:

  • It runs as baselyra_sql, not as the server's own login role. That role keeps every object right the owner has — including reading every password hash in auth.users — and loses access to the host. See The SQL editor.
  • The audit row is written before the statement runs, on a separate connection, so a statement that fails or rolls back is still recorded. It contains the statement text: do not type a password into the editor and expect it to be forgotten.
  • A viewer may run read-only statements and nothing else. The write half of that decision is made from the request body, at the one point that can see it.

Studio accounts

RouteCapability
POST /admin/v1/login— (rate limited to 5/min per IP)
GET /admin/v1/me · PUT /admin/v1/meany signed-in account
GET /admin/v1/teamteam.read
POST PUT DELETE /admin/v1/teamteam.manage
GET /admin/v1/rolesteam.read
POST PUT DELETE /admin/v1/rolesteam.manage

Changing your own email or password requires current_password. You cannot delete your own account, and you cannot delete or demote the last owner — an instance with no owner cannot be recovered from the Studio at all, only by an INSERT into control.platform_users from psql against the control database.

Roles and capabilities

Three roles ship, and they are rows rather than constants — an owner can add more with their own capability set.

RoleHolds
ownerEvery capability, including team.manage
adminEverything except managing accounts and roles
viewerEvery read capability, including keys.read. Every mutating route answers 403.

A capability is checked per route, against the pattern the route was registered with, using the list inside the token's signature:

database.read   database.write   database.ddl
sql.read        sql.write
auth.read       auth.write
storage.read    storage.write
realtime.read   realtime.write
import.run
settings.read   settings.write   keys.read
email.read      email.write
logs.read
team.read       team.manage

Tokens are stateless

A Studio token lasts one hour and nothing is stored server-side. No route re-checks the account row, so:

  • Signing out records an audit entry and nothing more.
  • Deleting an operator's row stops them signing in again; a token already issued keeps working until it expires, at most an hour later.
  • To cut every Studio token off at once, rotate JWT_SECRET — which also invalidates both project keys and every application session.

The service key is a second way in and always has been: it is the instance's own credential, it predates any Studio account, and /admin/v1 accepts it so scripts and first-boot recovery keep working. It has no /me, because it is not a person.

What the Studio does not have

MissingDo it here instead
A page for database webhooks/admin/v1/hooks/webhooks — the routes exist, the UI does not yet In progress
A page for scheduled jobs/admin/v1/hooks/jobs, same In progress
ALTER POLICY/admin/v1 creates and drops policies; editing one is a drop and a create, or an alter policy in the SQL editor
A project switchercontrol.projects holds one row that every lookup resolves through, but nothing creates a second In progress
Two-factor authenticationNot built. Rate limiting and the audit log are what there is.

The audit log

Every Studio sign-in, SQL execution, DDL statement, policy change, settings write and import is written to control.audit_log — in the control database, so the SQL editor cannot read it and an operator cannot quietly edit their own trail.

curl -s "$URL/admin/v1/logs?limit=100" -H "authorization: Bearer $ADMIN_TOKEN"

Read it under Studio → Settings. Import runs redact as they go: connection strings, passwords and API keys are replaced in the audit row, in the control.import_runs summary, in the progress stream and in error messages.

Failure modes

What you seeWhyFix
The Studio loads as a blank pageThe server is pointed at the Vite source rather than a built StudioPut studio/dist next to dist/Installation
Sign-in fails with an application user's credentialsStudio accounts are in another database entirelyUse the credentials setup.sh printed
Every request is 401 a moment after signing inJWT_SECRET changed since the token was issuedSign in again
403 on a page that used to workThe account is a viewer, or a custom role is missing a capabilityGET /admin/v1/roles shows the matrix
select * from control.platform_users failsCorrect and deliberate — it is in another databaseNothing. That is the boundary.
The AI pages are missing/ai/v1/status reports enabled: falseSet DEEPSEEK_API_KEY and restart
A new table does not appearThe schema response is cached briefly; DDL through the Studio invalidates itRefresh

Edit this page Report a problem

Esc
navigate open Esc close