Skip to main content
Build business apps · Self-hosting
Self-hosting

Recover a failing workspace

Repair sign-in, schema and stalled work, then restore saved records and private files with verified recovery steps.

Updated October 5, 2026 6 min read

You need: the checkout, a .env.local that points at a Supabase project you control, and PostgreSQL client tools for migration commands. Inspect the project ref and database host each command prints before continuing. Do not run mutating verify scripts against a customer production database.

Revenue Recovery at /admin/recovery is a demand playbook. Skip it here.

Setup in the fictional Northline Roofing workspace, showing production readiness at 100 percent with connected providers.
Setup in the fictional Northline Roofing workspace: every required check connected and verified. Fictional demo data. Select the image to view it full size.

Recover sign-in access

On the sign-in screen, choose Forgot password?, enter the configured admin email, and choose Send reset link. Open the recovery message to continue. If the link has expired, request a new one from the same screen. Back to sign in returns you to the email and password fields.

Diagnose on the operator screens

  1. Sign in at /admin.
  2. Open Setup (/admin/setup). Note every Action needed or Degraded row, including Operations health.
  3. Open Today (/admin/today) and read Connection and job health.
  4. Open Tenant operations (/admin/tenants) if sign-in works but the shell has no workspace.
  5. Open Activity (/admin/activity) after a repair to confirm a new receipt.

Then run only the command that matches the row. The commands below are defined in package.json; use the release checkout whose schema you are verifying.

Schema and identity

When Core system schema is Action needed, or login says Connect your Supabase project:

npm run db:migrate:all
npm run db:verify-schema
npm run db:verify-schema -- --record
npm run verify:bootstrap-identity
CommandEnvironmentWorking reference
npm run db:migrate:allLocal checkout; .env.local pooler vars for a project you controlscripts/run-all-migrations.mjs
npm run db:verify-schemaSame; read-only metadatascripts/verify-revenue-schema.ts
npm run db:verify-schema -- --recordSame; writes a schema verification receipt Setup readsscripts/verify-revenue-schema.ts
npm run verify:bootstrap-identitySame; confirms the seed is not the reference identityscripts/verify-bootstrap-identity.mjs

Re-running db:migrate:all after a fix is safe on a new install. On a long-lived database, prefer db:verify-schema instead of replaying the full list; see docs/self-hosting/REVENUE-OS-SETUP.md. Refresh Setup. Ready means the contract receipt matches, not that email is healthy.

Health, stalled jobs, and the scheduler

Health is loadOperationalHealth in src/lib/revenue-os/health.ts. A job stuck running longer than 30 minutes is stalled. Failed webhook receipts stay visible for 48 hours. Empty failed/partial counts must not make the system look green.

There is no operator Unstick button. The next authenticated cron claim takes a stale row over inside claim_revenue_job_run (migrations/20260819-stale-claim-recovery.sql). Setup reports the takeover on Operations health.

If the 15-minute system-health snapshot never wakes:

npm run scheduler:configure
CommandEnvironmentWorking reference
npm run scheduler:configure.env.local with production HTTPS NEXT_PUBLIC_SITE_URL (no localhost) and CRON_SECRET of at least 32 characters. Stores the endpoint and bearer in Supabase Vault and does not print them.scripts/configure-command-center-scheduler.mjs

Prove the health rules on a disposable project:

npm run test:setup-status
npm run verify:health-truth
npm run verify:stale-claim-recovery
CommandEnvironmentWorking reference
npm run test:setup-statusLocal, no database. Keys without receipts stay Action needed.scripts/test-setup-status.ts
npm run verify:health-truthLocal .env.local project you control. Inserts then deletes synthetic stalled-job and failed-webhook rows.scripts/verify-health-truth.ts
npm run verify:stale-claim-recoverySame. Uses a reserved job key and removes it.scripts/verify-stale-claim-recovery.ts

Saved result: Operations health no longer lists the stalled key, and the next snapshot job has a terminal receipt. verify:health-truth and verify:stale-claim-recovery print a pass line and exit 0.

Membership and founder access

If the admin shell has no workspace, follow Founder authority and tenant membership. Activate the tenant, invite the operator, and match ADMIN_EMAIL to the Auth user. There is no CLI that grants platform rights.

Feature Board claims

Feature Board (/admin/features) is founder-only. Leases last 30 minutes. Heartbeat from a worker checkout:

npm run agent:heartbeat -- --card <key>

Release a claim you still hold:

npm run agent:release -- --card <key>
CommandEnvironmentWorking reference
npm run agent:heartbeat -- --card <key>Local; WORK_BOARD_URL and a founder-issued WORK_BOARD_TOKEN. Session files stay outside Git.scripts/agent-dispatch.ts
npm run agent:release -- --card <key>Same; only the holder with the unexpired token can release.scripts/agent-dispatch.ts

Work volume is advisory. Run npm run agent:go -- --card <key> to resume specifically authorized expired work through revision-checked recovery. The runner preserves retained source and audit history and refuses takeover of a live claim. Use the configured private board profile; keep every other worker checkout intact.

Missing providers

Leave unused providers unset. Add one, then prove it with a receipt on Setup:

  • Resend: verified sender, then an outbound sent message.
  • OpenRouter: tenant key, then an AI run receipt.
  • Google: OAuth connect, then a named sync.
  • Calendly: signed booking or cancellation receipt.

Configuration without that receipt stays Action needed. That is the truthful state.

Restore saved records and uploaded files

When an installation must be rebuilt, keep its database backup, uploaded files and encryption configuration together as a recorded recovery point. The database contains file metadata, so it cannot replace the bytes of an uploaded document. Before importing customer records, follow the repository's backup and recovery guide and restore a controlled copy into a separate installation.

With the reviewed source configuration, create a private file copy and checksum manifest:

npm run workspace:files -- backup --project YOUR_SOURCE_REF --directory /absolute/private/new-files-copy

Encrypt that copy before moving it off-host. Select the target configuration and inspect the read-only restore plan:

npm run workspace:files -- restore --project YOUR_TARGET_REF --directory /absolute/private/files-copy

Add --apply only for the reviewed restore. Conflicting files or bucket settings are refused, and matching files remain intact if you repeat a stopped restore. Run the read-only plan again afterward; it should show no pending files.

Keep provider connections and schedules disabled while you verify sign-in, saved records, memberships, document downloads and action receipts. Review the provider's own records for effects since the backup point before preparing retries, because an absent local receipt does not prove a send never happened. The Connected fork workflow's recovery receipt measures a controlled native restoration with real Auth and file APIs; hosted restoration, your Auth settings and a human recovery trial still require installation-specific verification.

Confirm the repair

Return to Setup, press Refresh checks, and confirm the failing row moved. Open Activity for the migrate, sync, or task you ran. If the row is still Degraded, stop and keep the error text; do not rotate keys in a loop.