/admin, the queue dashboard at /admin/queues, the OpenAPI surface at /docs, the metrics scrape at /metrics and the support tooling at /admin/support. Two more operator endpoints sit beside the support tooling: /admin/capacity and /admin/proxy-pool. Anyone who is not the platform operator is refused all of them, and what the caller sees depends on the kind of address.
The second, third and fourth rows are literal: an anonymous caller, a caller with a refused credential, a customer and a read scoped token all get the identical
404 envelope, for every HTTP method but the OPTIONS preflight that every /api/backend path answers with 204, from a gate that runs before any other check, so no 401, 402, 403, 405 or 429 can tell a scanner that the route exists. An anonymous visitor to an admin page is sent to the sign in page, like every other page of the app.
The admin pages answer 200 because the not found view is chosen by the admin layout, which runs inside the app shell after the response has started streaming, and a streamed response cannot change its status. The robots tag keeps search engines from indexing it. A signed in customer can therefore tell /admin/users from /admin/whatever by the status alone. That is accepted: the admin area is described on this page, the view carries no instance data and the page never requests any.
The last two matter to a hosted operator rather than a self hoster, and Observability for a hosted instance describes what they serve.
Who the platform operator is
The platform operator is the owner of the bootstrap workspace, the first account the instance ever created. It is a different principal from a workspace owner. That distinction matters because registration decides who becomes anowner. By default every account after the first gets a workspace of its own and owns it, on a self hosted instance as much as on a hosted one, so owner alone cannot authorize an instance wide surface. A one person instance has a single account, which is both the owner of the bootstrap workspace and the operator. A customer who owns their own workspace is not an operator and receives 404 from every surface on this page, and neither is a member who joined the bootstrap workspace because the instance opted into AUTH_REGISTRATION_WORKSPACE=shared. See Where a self registration lands.
Owning your own workspace still authorizes everything inside it, including managing its team and scheduling its deletion. Only the cross tenant surfaces require the operator.
Deleting the operator workspace
The operator’s own workspace is the one exception to scheduling a deletion. While any account exists outside it,POST /account/deletion answers 409 Conflict, because erasing the bootstrap workspace would leave an instance with accounts and no operator. Registration only hands out the role to the first account the instance ever creates, so a later sign up would never take it over.
The instance is allowed to reset itself. When the operator’s account is the only one, deletion works as for any workspace, and the next registration becomes the operator, because an instance with no accounts bootstraps again. The web app explains the restriction in Settings, under Workspace.
The retention run checks again when the grace period ends. A deletion scheduled while the operator was alone is cancelled, not erased, if an account has registered since, and the log records its scheduled deletion was cancelled instead of erased. asobeast has no way to hand the operator role to another account, so to clear your own data delete its apps and keywords. If an instance has already lost its operator, see Every admin surface answers 404 and no account is the operator.
Views only the operator sees
The web app keeps the views that describe the whole instance for the operator too. Capacity, the request budget and the API status are facts about the machine collecting for every workspace, not about one customer’s account, so only the operator sees the Capacity section of the settings page, the request budget share on the dashboard status line and its 85 percent warning, the capacity step of app setup and the API status badge in the header. Every other account sets up an app in four steps and reads its own usage on the plan card. A one person self hosted instance looks the same as before, because its only account is the operator. The public/health endpoint is unchanged, so probes keep working without credentials. See Health checks.
These views hide the figures rather than restrict them. GET /jobs/budget still answers any signed in account and any personal token with the same estimate, so a script can read it. The MCP surface does not offer it as a tool.
The admin area
The web app has an Admin area at/admin for the platform operator. It appears in the sidebar, the command palette and the account menu only for the operator, and any other account that opens the address sees the not found view of the app, with the status described above.
Keyword markets count one phrase once per workspace, however many of its apps track it, which is the number the workspace’s own plan card shows. The Overview and Workspaces tabs follow that rule, and the Overview total adds the workspaces together, so a phrase two workspaces track counts once in each. The Apps tab counts what each app tracks, so its rows can add up to more than the workspace figure.
Users and Apps accept
?workspaceId= and return the newest 1,000 rows with the total. Reading either list is written to the support audit trail, the same trail the workspace list writes, because both return personal data. The overview returns counts only and is not written to the trail. The capacity report names its top consumers by workspace id only, because a self registered workspace is named after its owner’s email; the Capacity tab takes the names from the workspace list, so reading them is written to the trail too. Every response from these endpoints carries Cache-Control: no-store.
The admin area is read only. It lists accounts and apps; it never opens a workspace, so an app name is not a link: the operator’s own session cannot read another workspace’s pages.
Activation
The Activation tile counts the apps imported in the last 30 days, across every workspace, that had a position for one of their keywords within 24 hours of the import, as 9 of 12. That is the time to value the guided setup is built for. Its note adds how many of those apps completed the guided setup. Competitors are left out, and the count reads the positions and setup rows the instance already keeps, so it needs no configuration.When the operator’s workspace has no plan
With billing on, the support tooling, the queue dashboard, the metrics scrape and the OpenAPI surface under the defaultowner setting need an entitled operator. An operator whose own workspace has no plan in force receives 404 from every one of them, exactly as a customer does, although GET /auth/me still reports platformOperator: true next to entitled: false. A plan is in force while a paid plan has not expired or a trial is running. The state is a natural one on a hosted instance: the bootstrap trial waits for the email confirmation, and every trial ends after TRIAL_DAYS.
The admin area reads platformOperator, entitled and trialAwaitsConfirmation from GET /auth/me and follows the same rule, so it says why instead of showing five tabs that cannot load.
The Admin entry stays in the sidebar, the command palette and the account menu, so the explanation is always one click away. To open the area again, choose a plan on the upgrade page, or set
plan on the bootstrap workspace to indie or ultimate in the database, as described in Authentication and accounts.
The final gate on /admin/capacity and /admin/proxy-pool checks only that the caller is the operator, so they keep answering 200 to an operator session without a plan, and 402 to an operator token, exactly as before. Anyone who is not the operator gets 404, plan or no plan. The web app closes the whole area in that state so it never half loads.
An admin request can also be refused while the operator’s workspace has a plan, for example on an instance an earlier release left with that workspace suspended. The tab then says that the admin endpoints answer not found unless the workspace has a plan in force and is not suspended, rather than saying a record was deleted.
Why 404 and not 403?
A403 confirms that the thing you asked for exists. A 404 does not.
Anonymous callers therefore get the same answer whether the surface is present, disabled or was never built. There is no login prompt to attack, and a scanner learns nothing from the response. This holds for the queue dashboard, the OpenAPI routes, the metrics scrape and the API endpoints. The admin pages are the exception described above, because they load nothing for a caller who is not the operator.
If you expected a sign in page on /admin/queues and got a not found page, nothing is broken. Sign in as the operator in the same browser and reload.
The queue dashboard
/admin/queues serves Bull Board, which shows every queue, its waiting, active, completed and failed jobs, and lets the operator retry a failed job.
The operator gate is unconditional. There is no configuration that opens the queue dashboard to anonymous callers.
The OpenAPI surface
API_DOCS controls who may read /docs, /docs-json and /docs-yaml.
public is a deliberate choice, not an accident, and it is reasonable when the API surface is meant to be read by people you have not given accounts to.
Both are reached through the web origin
You never reach either surface on the API port. The web app serves route handlers at the identical paths and proxies them through. Identical paths matter: both surfaces are single page applications that build their asset URLs from the path they are mounted on, so serving/admin/queues at any other path breaks its own assets.
The gate itself runs before the application framework’s guards, because both surfaces register their own routing outside them. It resolves the session cookie or the asob_ token itself, and the queue dashboard re asserts the grant afterwards, so a deployment that forgot the wiring fails closed rather than open.
The gate covers the whole /admin prefix on the API, not a list of routes. /admin/queues and /admin/support are checked first with their own rules; every other path below /admin, /admin/capacity and /admin/proxy-pool among them, is checked by a final gate that asks only whether the caller is the operator, so an operator endpoint added later is hidden from the moment it is mounted. The web app’s /docs, /docs-json, /docs-yaml and /metrics handlers answer any method other than GET with the same 404 themselves, and so does OPTIONS on /admin/queues, so the web origin never answers 405 or an Allow header that confirms a route.
A refused credential costs the same here as anywhere else in the API: it is counted against the per address budget described in Rate limits, under the same rule about which credentials count, and an accepted credential that does not belong to the operator is never counted. Once an address has spent that budget these surfaces still answer 404 rather than 429. Every other route answers that address with 429, so a caller who spends the budget here can tell that a gate counted the refusals. That reveals nothing about the queue dashboard, the metrics scrape or the support tooling, which every instance mounts, and for the OpenAPI routes only whether API_DOCS is owner rather than off.
The web app hides the links to both surfaces from anyone who is not the platform operator, using a capability the API computes and returns with the signed in user. That is presentation only. Authorization is the gate above, and a customer who follows a link anyway still gets 404.
A read scoped token cannot change queue state
The queue dashboard is the one admin surface that writes. Pausing, resuming, retrying, promoting, cleaning and obliterating a queue all arrive asPUT, POST or DELETE, so the gate applies the token scope the rest of the API applies, and a read scoped token gets 404 on any of them.
A reporting or MCP integration therefore only needs a read scoped token, and a leaked one cannot stop the daily run. See Personal API tokens.
A suspended workspace loses the queue dashboard
The gate applies the same suspension rules the rest of the API applies, because running outside the guards would otherwise mean running outside the policy.
A suspended account keeps reading and exporting its data, and keeps billing, so the account can always pay and always leave. See Rate limits and quotas.
The operator workspace is the one workspace that cannot be suspended. The support surface answers
409 and the command line refuses too, because a suspended operator workspace takes the support surface and the queue dashboard away from the operator, and with them the way to lift the suspension. An instance that an earlier release left with its operator workspace suspended restores it from the command line.
Retired variables
BULL_BOARD_USER and BULL_BOARD_PASSWORD are ignored. The dashboard used to use basic authentication and now requires an operator session or a personal API token instead.
The API logs a warning at boot if either is still set. Remove them from your environment.
Related
Personal API tokens
An operator token reaches both surfaces.
Health checks and monitoring
Queue health without opening the dashboard.