Skip to main content
The my platform command tree exposes the platform-operator surface for managing tenants on a hosted Marfa instance: listing tenants, suspending them, adjusting per-tenant quotas, viewing usage metrics, listing API keys, and forcing a pending-delete sweep. Every command requires a platform-admin key (is_platform: true); a non-platform key is rejected with HTTP 403, which the CLI surfaces as a clear “platform-admin key required” message. The verb name platform telegraphs the authority level the commands need. my admin is reserved for a future tenant-admin command set. The same routes are reachable directly via the SDK’s client.admin namespace (TypeScript) and via raw HTTP under /admin/* (every other client). The SDK keeps the admin namespace internally; only the CLI surface uses platform.

Commands

Confirmation prompts on destructive verbs

suspend, unsuspend, quotas (write path), and account-deletion purge-now prompt for typed-token confirmation by default — operator types the literal verb (suspend, unsuspend, set quotas, purge) to authorise the action. Pass --yes / -y to skip the prompt for scripting. Read-only verbs (list, show, metrics, keys list) never prompt. The CLI errors cleanly in non-TTY contexts (CI, piped output) when the prompt would otherwise hang on stdin that can never arrive — pass --yes explicitly in scripts.

Suspension semantics

When a tenant is suspended:
  • Every non-GET request from a credential whose tenant_id matches the suspended tenant returns HTTP 403 tenant_suspended.
  • Reads (GET / HEAD / OPTIONS) pass through unchanged.
  • Platform-admin keys (is_platform: true) bypass the gate — operators can still write to quotas, unsuspend, etc.
  • Runtime-credential connector keys are not exempt. Suspending a tenant stops their connectors from writing upstream, which is the desired blast radius.
The gate sits at the auth-middleware layer, so every present and future mutation route is covered without per-route opt-in. A per-instance 5-second cache keys the lookup on tenant_id, evicted immediately on suspend/unsuspend so operator changes feel instant on the local instance. Multi-instance deployments tolerate up to 5 seconds of cross-instance staleness.

Bootstrap admin key

Platform-admin keys (is_platform: true) are minted on instance bootstrap. The seed credential is created by your first POST /keys against a fresh deployment (the bootstrapped flag persists once stamped). Subsequent platform-admin keys can be minted by an existing platform admin via my keys create --is-platform. If you’ve lost access to your bootstrap admin key on a self-hosted instance, the recovery path is to delete the bootstrapped: true row from the settings table directly, which re-opens the bootstrap window. See the operator notes in your deployment’s runbook.

Quotas

Per-tenant quota ceilings apply on top of the instance defaults (MARFA_DEFAULT_QUOTA_ITEMS, _WEBHOOKS, _BLOBS, _STORAGE_BYTES, _RATE_PER_MINUTE). A per-tenant override on a field takes precedence; a null (or 0 from the CLI) reverts that field to the env default. When a tenant hits a cap, writes targeting the capped resource fail with HTTP 429 quota_exceeded, with details carrying { resource, limit, current }. The CLI surfaces this verbatim.

Forcing a pending-delete sweep

my platform account-deletion purge-now forces a one-shot run of the server-side PendingDeletePurger. The purger normally runs on a 1-hour cadence; this verb is the operator’s escape hatch when an account has just passed its grace window and the cleanup needs to happen now (stuck-deletion remediation, support-driven flows, end-to-end deletion verification). The route only sweeps accounts already past their pending_deletion_at + grace_days threshold — it does not bypass the grace window. To force-purge an account ahead of schedule, the user has to wait out their own grace window or cancel and reinitiate. Idempotent: re-running with no eligible rows returns purged_count: 0. The action emits a system.activity audit row with action: admin.account_deletion.purge_now, tenant_id: null (instance-wide sweep), and details carrying the purged count + run timestamp. Prompts for typed purge by default; pass --yes to skip in scripts. When MARFA_ACCOUNT_DELETION_GRACE_DAYS=0 (purger disabled), the route returns purged_count: 0 cleanly rather than throwing.

Limitations

  • Metrics is read-only. The endpoint computes counts on-demand from the underlying tables; there’s no eager counter / reconcile machinery yet (a follow-on once per-tenant cardinality justifies the complexity). Cost is O(items_in_tenant) per call.
  • tenants quotas is the only quota-mutation surface today. Suspend/unsuspend are flag-flips; account-deletion purge-now triggers a sweep but doesn’t bypass grace. Account deletion is user-initiated, not admin-initiated — see Account lifecycle.
  • No bulk operations. Each command targets a single tenant.