# Fabric user administration

The login link's `return_to` tracks the current route after in-app navigation, history changes and refresh. Configure `admin-login` to the Auth login entry; the local browser acceptance follows the full Auth/OAuth callback into the PAT page without pre-injecting session cookies. Production GitHub client and callback-origin settings still belong to Auth configuration.

Registry detail links use `/registry/services/{id}`; include that path pattern in static fallback. Details read GET `/v2/registry/services/{id}` and display public identity metadata, leaving absent legacy fields as unavailable. The same row includes ACL/admission, public-status/probe and delete controls; Host bindings are filtered to that service. Use the service-directory navigation to return to the full catalog. Credentials are omitted from the view model and all metadata is text-rendered. ACL management continues to use the versioned complete-rule editor instead of separate unversioned rule toggles.

`/` and `/overview` now show the current administrator, registered/healthy service counts and latest deployment jobs. Proxy the existing Auth, Registry and admin deployment APIs; route both overview URLs to index.html. Registry and deployment queries settle independently: unavailable data is labelled unavailable, never zero. Authentication/authorization errors invalidate the whole private view. Counts reflect returned records (deployment jobs capped at 100); lifecycle phase is not substituted for Registry heartbeat health. The user-management page remains `/auth/users`.

Comment moderation at `/comments/moderation` uses existing current-administrator CommentHttp endpoints. `admin-comments` selects the API mount (default `/comments`). Serve the exact frontend route **before** proxying other `/comments/*` paths, or give the API a separate proxy prefix. Optional `admin-blog` sets the HTTP(S) blog base for post links; without it the post slug remains plain text.

Pending, LLM-rejected and recent groups each load up to 50 records. Hide/restore use the existing moderation endpoint; confirmed delete clears the body while retaining the deleted record. It is not recoverable through restore. Body and reviewer context render as text. Failed mutations are not automatically replayed, and navigation/session changes clear all three groups. This page does not invoke an LLM or change the background review policy.

Pages management at `/platform/pages` uses the `admin-pages` API base (default `/pages`), with `/api/admin/pages` and `/api/pages` below it. Proxy `/pages/api/*` to runtime management and include the frontend route in static fallback. A current administrator may list all pages; publish/delete additionally require the existing Pages write ACL. Hosted HTML remains on the separately configured content origin. The console displays safe HTTP(S) links, never renders uploaded HTML in its own document.

Load 100/200/500 entries, filter those loaded rows by owner/title/ID, and inspect aggregate loaded-row counts/bytes. Paste HTML or choose a local file (maximum 10 MiB), publish, open/copy the resulting URL, or delete with confirmation. The backend may impose smaller configured size/quota limits. Cancelled file reads cannot repopulate a navigated-away form. Publication/deletion are never automatically retried after uncertain results; refresh first. Navigation and session changes clear rows and draft content.

Notification management at `/platform/notify` uses the current administrator cookie session and existing NotificationHttp authority. `admin-notify` selects the API mount (default `/notify`); proxy this mount and add the page to static fallback. Devices can be added and removed with confirmation. Device keys are reduced to hints in the frontend model and inputs clear after submission or navigation; the existing administrator API still returns full keys over the authenticated connection.

Test-send accepts title/body/level/optional email and invokes configured routing once. Recent 100 native attempts show channel results, including partial/unknown outcomes. A failed/unknown send prompts manual log refresh, never automatic replay. A separate table displays the latest 100 imported records via `/notify/admin/legacy-log`, preserving numeric IDs, Unix-second times and arbitrary legacy result JSON (including null), without exposing idempotency keys. Expand a row for body/results/errors; values render as text. Page records and forms clear on session or navigation changes. Browser tests use only local channels and do not send external notifications.

Outbound email at `/platform/email` reads the existing `/email/admin/log` with the current administrator cookie session. Configure `admin-email` to change the API mount, proxy it to the runtime, and include `/platform/email` in static fallback. Recipient filtering is exact; the page offers 50/100/200/500 rows. Subject, provider ID, status, error and caller are rendered as text. `unknown` is an uncertain delivery outcome, not a failed delivery suitable for automatic retry. No send/retry operation or idempotency key is exposed by this page. Navigation, session changes and failed authorization clear the records.

Platform messages at `/platform/messages` use the existing MessageHttp mount (default `/message`, configurable by `admin-messages` meta). Proxy this API mount before static fallback and route `/platform/messages` to index.html. The page requires a current administrator session; the backend additionally requires `messageservice` read ACL for queries and write ACL for mark-read. Deletion uses its existing administrator capability. Configure explicit `admin` rules through the ACL editor; the UI never supplies machine credentials.

Channel counts show unread/total. Queries return the latest 100 messages, optionally filtered by channel and unread status. Read/expired status comes from the server, not browser TTL calculations. Mark-read refreshes the view; delete asks for confirmation. Cancel performs no request. Failed or unconfirmed mutations are never replayed automatically: refresh before acting again. Navigation, reload and session failure clear records. The old console has no message-compose flow, so this migration does not add one.

Registry historical audit at `/registry/audit` reads GET `/v2/admin/registry-audit` in 50-row pages. Exact actor/action and inclusive Unix-second `since` filters preserve the legacy audit vocabulary. Metadata is rendered as text, missing fields as unavailable. The page displays imported history only; it does not claim to aggregate current runtime operation audits. Configure static fallback for the page and proxy the API. Current administrator checks apply; navigation/authentication failure clears displayed records.

Service availability appears in the service row's ACL editor. GET/PUT `/v2/admin/availability/{service}` reads or changes `{enabled, expectedRevision}` using a separate revision and audit trail. Disabled services reject new Host unary/local/stream/dependency invocations with `service_disabled` (503), including health methods, without unloading code or cancelling already-admitted work. Settings persist across replacement and disk restart. This controls Host service calls; independently hosted upstream transports are not covered by this switch. The lifecycle `active` label remains independent of admission policy.

Disabling `auth` also stops its profile calls, so a subsequent full admin-page reload may fail. The still-open editor can re-enable it using a valid JWT; operators can also GET/PUT `/v2/control/availability/auth` using a configured Host credential with `control` scope. That recovery path does not depend on Auth. Load the current revision before PUT; missing policy means enabled with a null revision. No automatic re-enable occurs on deployment.

Deployment management at `/deployer/deployments` uses GET/POST `/v2/admin/deployments`. Add the page to static fallback and proxy the API to the runtime. It lists current plugins, configured source bindings and the latest 100 deployment jobs. Refresh explicitly to observe queued/running/succeeded/failed/unknown/cancelled states. A task being accepted is not completion. Restart and rollback use the displayed generation; rollback can fail when no previous artifact exists. Source updates require a configured binding and an exact 40-character lowercase commit SHA; browser input cannot choose a repository URL or validation command. Existing runtime fetch, validation, publication and rollback logic performs the work.

Each submission uses a fresh request ID and is never automatically retried. The UI displays that ID after an uncertain response so an operator can investigate before issuing another action. Backend deduplication is scoped by user and job kind. Admission and a deployment audit entry commit together after a current-admin check; cookie Origin and PAT admin scope checks also apply. The task queue must be started by runtime boot. No source credentials, validation commands or raw validation output appear in the dashboard. This dedicated administrator API grants these deployment operations without turning business tokens into general Host control credentials.

ACL editing: choose **编辑 ACL** in a service catalog row. The editor reads GET `/v2/admin/acl/{service}` and saves the complete rule array with PUT to the same path and its loaded `expectedRevision`. Proxy `/v2/admin/acl/` to the runtime. The JSON editor accepts the existing `{action, principal, effect}` rules, displays their syntax and deny precedence, and uses the existing Host ACL store/audit. A concurrent change returns 409; reload before editing again. Unknown write outcomes are not retried automatically. Empty rules remove all ACL allows; individual service authorization remains in force. Navigating away, logging out or receiving an authorization failure clears the editor.

Writes require a current administrator (PAT also needs admin scope), enforce cookie origin policy, reauthenticate after reading the body, and recheck the account's enabled/admin flags within the policy-write transaction. Host control credentials and control routes retain their prior behavior. This grants ACL management only, not deployment or restart authority.

Service catalog at `/registry/services` displays the current Host service bindings, plugin version, generation, lifecycle phase, contract digest and method kind/effect/transport/required scopes. Expand a row to inspect methods. `active` is a lifecycle phase, not a health probe of external dependencies. The page uses GET `/v2/admin/services`; proxy this path to the runtime and include `/registry/services` in the static fallback. Optional `admin-runtime` meta content selects a runtime base (default `/`), separately from `admin-auth`. Use a same-origin proxy for the cookie session; cross-origin deployment needs explicit existing session/proxy configuration. Reload, navigation and authorization failures clear catalog data. No Host credential is placed in the browser.

The read-only API requires a currently enabled administrator; a PAT must additionally have `admin` scope and be valid for `auth`. This API does not grant control/restart/deployment privileges or execute methods. Configuration, handlers and schema examples are omitted. ACL editing and deployment management remain separate migration work.

The current console manages user enable/disable and administrator roles. It requires an enabled Auth administrator session. Self-disable and self-role changes are unavailable here. Disable retains the account and historical data, invalidating its existing sessions and PATs; it does not permanently erase the account.

User reads use `/api/users/me` and `/api/users`; changes send PUT `/api/users/{id}`. The admin-auth meta tag configures the API base (default site root). The admin-login meta tag configures the login link (default /login). Session renewal is explicit through POST /token/refresh; logout uses POST /logout. Credentials remain HttpOnly cookies; no Host token belongs in the page.

Build from the full repository with `node apps/admin-web/build.mjs` after installing locked root dependencies. Publish only dist/index.html, dist/agents.md and dist/assets/app.js and app.css. Route /auth/users to index.html and proxy Auth routes before any static fallback. Configure Auth's exact public browser origin and HTTPS sessions. Use no-cache for these stable asset names. Login must be served by the existing Auth frontend. Build with exclusive directory ownership, publish to a separate release directory and switch the live target; local build replacement is not an atomic live deployment.

Failed writes are not retried: refresh to inspect actual state. Authority is rechecked by the backend, not inferred from displayed buttons. Reload and authentication errors clear private view data. Deployment, platform service and moderation routes are implemented as documented above; production configuration and external-provider acceptance remain separate.

PAT management at /auth/pats supports any enabled signed-in account, not just administrators. GET/POST /v2/auth/pats and POST /v2/auth/pats/{id}/revoke act only on its own tokens. Creation uses comma-separated scopes/audiences and an optional integer Unix-second expiry. Empty audiences mean unrestricted audience; empty scopes grant no implicit permissions. Nonfinite expiry rejects before JSON encoding. The raw token appears only after creation, is never persisted in browser storage, and is cleared by hiding, reload, navigation or logout. Losing it requires revocation and recreation. List views distinguish active/expired/revoked and show creation, last use and expiry. Backend listing currently has a 10,000-row bound; full-history pagination remains open. Configure the static fallback for /auth/pats too.

Audit management at /auth/audit requires a current administrator. It queries /api/audit in 50-row offset pages with optional exact action and user_id filters. Rows include timestamps, action, user/provider/IP/UA and JSON metadata rendered as text. Missing context remains unavailable; the page does not reconstruct it. Offset pagination can move when new records arrive. Configure the static fallback for /auth/audit. Reload, route changes and authorization failures clear the displayed audit rows.
# Platform logs

`/platform/logs` queries the existing LogHttp JSON endpoint using the administrator's cookie session. The `admin-logs` meta tag selects its mount (default `/logs`). Proxy that mount to the runtime before static fallback and route `/platform/logs` to this app's index.html. The console requires a current administrator; the backend additionally enforces the existing `logservice` read ACL. Configure an explicit `admin` / `read` / `allow` rule through the ACL editor when appropriate; this page does not grant access itself.

Filters preserve the old console's five levels, exact source, UTC server-time ranges (all, 15 minutes, hour, day, week), and 100/200/500/1000 newest-row limits. Expand a message to inspect ID, client timestamp and metadata. Content is rendered as text. Reads are cancellable and explicit; route changes, reload, logout and authorization failures clear records. This is the business log service, separate from Host diagnostics. It does not add log deletion to the old read-only console flow.
