portal-mojo 0.2.20 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/docs/README.md +7 -1
  2. package/docs/admin-assistant.md +75 -23
  3. package/docs/admin-deployments.md +201 -0
  4. package/docs/admin-dns-certificates.md +3 -3
  5. package/docs/admin-dns-domains.md +98 -0
  6. package/docs/admin-dns-records.md +1 -1
  7. package/docs/admin-dns-registrar.md +29 -4
  8. package/docs/admin-dns.md +4 -3
  9. package/docs/admin-edge.md +200 -196
  10. package/docs/admin-geolocation.md +100 -0
  11. package/docs/admin-maintenance.md +133 -0
  12. package/docs/admin-modals.md +38 -2
  13. package/docs/admin-operational-fleet.md +206 -0
  14. package/docs/admin-presentation.md +51 -0
  15. package/docs/admin-signin.md +12 -2
  16. package/docs/client.md +7 -0
  17. package/docs/combobox.md +18 -0
  18. package/docs/menus-and-access.md +24 -0
  19. package/docs/popover.md +5 -4
  20. package/package.json +1 -1
  21. package/src/admin/assistant/AssistantFeed.tsx +7 -4
  22. package/src/admin/assistant/AssistantSetupPage.tsx +141 -64
  23. package/src/admin/assistant/api.ts +9 -7
  24. package/src/admin/assistant/pages.tsx +125 -109
  25. package/src/admin/assistant/setup.ts +1 -1
  26. package/src/admin/assistant/skill-lifecycle.ts +5 -4
  27. package/src/admin/core/OperationsHelp.tsx +10 -0
  28. package/src/admin/core/index.ts +16 -6
  29. package/src/admin/core/route-context.ts +6 -0
  30. package/src/admin/deployments/ApiDeploymentDetail.tsx +58 -0
  31. package/src/admin/deployments/DeploymentsPage.tsx +73 -0
  32. package/src/admin/deployments/WebAppDeploySetup.tsx +49 -0
  33. package/src/admin/deployments/WebAppDeploymentDetail.tsx +59 -0
  34. package/src/admin/deployments/WebAppDetail.tsx +104 -0
  35. package/src/admin/deployments/WebAppEditDialog.tsx +70 -0
  36. package/src/admin/deployments/WebAppKeyDialog.tsx +25 -0
  37. package/src/admin/deployments/WebAppOnboardingDialog.tsx +217 -0
  38. package/src/admin/deployments/WebAppRollbackDialog.tsx +82 -0
  39. package/src/admin/deployments/WebAppUploadPanel.tsx +132 -0
  40. package/src/admin/deployments/api.ts +158 -0
  41. package/src/admin/deployments/authority-state.ts +9 -0
  42. package/src/admin/deployments/deployment-state.ts +13 -0
  43. package/src/admin/deployments/index.ts +4 -0
  44. package/src/admin/deployments/onboarding-state.ts +11 -0
  45. package/src/admin/deployments/permissions.ts +37 -0
  46. package/src/admin/deployments/receipts.ts +78 -0
  47. package/src/admin/deployments/sanitize.ts +73 -0
  48. package/src/admin/deployments/shared.tsx +39 -0
  49. package/src/admin/deployments/types.ts +238 -0
  50. package/src/admin/deployments/upload.ts +53 -0
  51. package/src/admin/deployments/useDeploymentObservation.ts +104 -0
  52. package/src/admin/dns/DnsRecordsPanel.tsx +45 -9
  53. package/src/admin/dns/DomainAssignment.tsx +55 -0
  54. package/src/admin/dns/DomainDetail.tsx +56 -35
  55. package/src/admin/dns/DomainOnboardingContent.tsx +49 -0
  56. package/src/admin/dns/DomainOnboardingDialog.tsx +5 -24
  57. package/src/admin/dns/DomainPurchaseDetail.tsx +42 -7
  58. package/src/admin/dns/DomainPurchaseWizard.tsx +102 -72
  59. package/src/admin/dns/DomainPurchasesPage.tsx +1 -1
  60. package/src/admin/dns/DomainRegisterExisting.tsx +78 -0
  61. package/src/admin/dns/DomainRegistrarSettings.tsx +32 -0
  62. package/src/admin/dns/DomainRegistrationPanel.tsx +127 -0
  63. package/src/admin/dns/DomainsPage.tsx +30 -20
  64. package/src/admin/dns/ProviderCredentialsPage.tsx +32 -18
  65. package/src/admin/dns/RegistrantContactPage.tsx +73 -27
  66. package/src/admin/dns/SharedValidationDialog.tsx +1 -1
  67. package/src/admin/dns/api.ts +28 -25
  68. package/src/admin/dns/domain-authority.ts +11 -0
  69. package/src/admin/dns/domain-navigation.ts +16 -0
  70. package/src/admin/dns/index.ts +2 -2
  71. package/src/admin/dns/purchase-data.ts +5 -3
  72. package/src/admin/dns/registrar-api.ts +59 -0
  73. package/src/admin/domains/assistant.ts +5 -4
  74. package/src/admin/domains/communications.ts +10 -9
  75. package/src/admin/domains/identity.ts +8 -7
  76. package/src/admin/domains/infrastructure.ts +22 -16
  77. package/src/admin/domains/lazy-page.ts +6 -0
  78. package/src/admin/domains/observability.ts +6 -5
  79. package/src/admin/domains/operations.ts +23 -8
  80. package/src/admin/domains/security.ts +16 -15
  81. package/src/admin/edge/EdgeHelp.tsx +10 -0
  82. package/src/admin/edge/ServingReadiness.tsx +54 -0
  83. package/src/admin/edge/VhostDetail.tsx +145 -139
  84. package/src/admin/edge/VhostWizard.tsx +204 -115
  85. package/src/admin/edge/VhostsPage.tsx +32 -28
  86. package/src/admin/edge/api.ts +24 -10
  87. package/src/admin/edge/models.ts +4 -4
  88. package/src/admin/edge/serving-data.ts +78 -0
  89. package/src/admin/fleet/CapacityEditor.tsx +81 -0
  90. package/src/admin/fleet/CapacityInspector.tsx +19 -0
  91. package/src/admin/fleet/CapacityProgress.tsx +9 -0
  92. package/src/admin/fleet/FleetHelp.tsx +7 -0
  93. package/src/admin/fleet/FleetOverview.tsx +150 -0
  94. package/src/admin/fleet/FleetPage.tsx +11 -0
  95. package/src/admin/fleet/FleetWorkspace.tsx +82 -0
  96. package/src/admin/fleet/ScaleFleetDialog.tsx +169 -0
  97. package/src/admin/fleet/access.ts +22 -0
  98. package/src/admin/fleet/api.ts +77 -0
  99. package/src/admin/fleet/data.ts +148 -0
  100. package/src/admin/fleet/index.ts +8 -0
  101. package/src/admin/fleet/operations.ts +179 -0
  102. package/src/admin/fleet/standalone-data.ts +87 -0
  103. package/src/admin/fleet/types.ts +208 -0
  104. package/src/admin/geolocation/GeoLocationPage.tsx +171 -0
  105. package/src/admin/geolocation/data.ts +138 -0
  106. package/src/admin/geolocation/index.ts +3 -0
  107. package/src/admin/identity/members/GroupMembersPanel.tsx +2 -8
  108. package/src/admin/index.ts +8 -0
  109. package/src/admin/jobs/JobDashboardPage.tsx +14 -4
  110. package/src/admin/maintenance/MaintenanceDialogs.tsx +161 -0
  111. package/src/admin/maintenance/MaintenancePage.tsx +442 -0
  112. package/src/admin/maintenance/data.ts +486 -0
  113. package/src/admin/maintenance/index.ts +3 -0
  114. package/src/admin/maintenance/operations.ts +208 -0
  115. package/src/admin/public/infrastructure.ts +1 -0
  116. package/src/admin/public/operations.ts +3 -0
  117. package/src/admin/registry.ts +6 -2
  118. package/src/admin/settings/SettingDetail.tsx +7 -2
  119. package/src/admin/settings/SettingEditor.tsx +2 -1
  120. package/src/admin/settings/SettingsPage.tsx +3 -1
  121. package/src/admin/settings/model.ts +1 -0
  122. package/src/admin/signin/SigninPage.tsx +17 -3
  123. package/src/admin/signin/api.ts +2 -0
  124. package/src/charts/MetricsChartView.tsx +4 -11
  125. package/src/charts/MetricsMiniWidget.tsx +3 -15
  126. package/src/client/client.ts +6 -0
  127. package/src/client/deployments-mock.ts +375 -0
  128. package/src/client/domain-operations-mock.ts +37 -0
  129. package/src/client/geo-provider-mock.ts +82 -0
  130. package/src/client/maintenance-mock.ts +285 -0
  131. package/src/client/mock.ts +66 -18
  132. package/src/client/operational-fleet-mock.ts +279 -0
  133. package/src/client/operations-mock.ts +85 -0
  134. package/src/client/serving-mock.ts +22 -0
  135. package/src/styles/components/admin-assistant.css +83 -20
  136. package/src/styles/components/admin-deployments.css +35 -0
  137. package/src/styles/components/admin-domains.css +68 -0
  138. package/src/styles/components/admin-edge.css +126 -1
  139. package/src/styles/components/admin-geolocation.css +40 -0
  140. package/src/styles/components/admin-jobs.css +1 -0
  141. package/src/styles/components/admin-maintenance.css +127 -0
  142. package/src/styles/components/admin-operational-fleet.css +38 -0
  143. package/src/styles/components/admin-operations.css +9 -0
  144. package/src/styles/components/admin-signin.css +2 -0
  145. package/src/styles/components/combobox.css +12 -2
  146. package/src/styles/components/fleet-page.css +9 -0
  147. package/src/styles/index.css +7 -0
  148. package/src/ui/ComboBox.tsx +39 -8
  149. package/src/ui/FilterBar.tsx +3 -5
  150. package/src/ui/ModelTable.tsx +2 -8
  151. package/src/ui/Popover.tsx +15 -7
  152. package/src/ui/SidebarNav.tsx +10 -1
  153. package/src/ui/date/TimePicker.tsx +4 -14
  154. package/src/ui/image-editor/ImageEditor.tsx +4 -5
  155. package/src/ui/menu-registry.ts +2 -0
  156. package/src/ui/schema-form-core.tsx +3 -5
package/docs/README.md CHANGED
@@ -14,11 +14,17 @@ everything: run the showcase (`npm run dev:showcase`) → **Develop → Componen
14
14
  | [realtime.md](realtime.md) | Auth-challenged WebSocket lifecycle, heartbeat/backoff, refcounted topics, typed event projection, deterministic mock |
15
15
  | [auth-pages.md](auth-pages.md) | The in-app auth pages: routes + guard, `VITE_MOJO_AUTH` switch, fresh-auth (440 step-up) modal, MFA panel, password tools |
16
16
  | [admin-credentials.md](admin-credentials.md) | Group API keys and webhook subscriptions — dual-mount pages, lossless per-endpoint rate limits, permission registry, secret-safe create/reveal/rotation |
17
+ | [admin-presentation.md](admin-presentation.md) | KISS presentation rules, shared styles and required rendered visual QA |
17
18
  | [admin-dashboard.md](admin-dashboard.md) | Package-owned global Admin landing, authoritative signals, independent gates, caller-isolated caches, and root fallback |
18
19
  | [admin-cloudwatch.md](admin-cloudwatch.md) | Global EC2/RDS/Redis CloudWatch charts, exact resource/fetch wires, constrained controls, refresh ownership, and modal details |
19
20
  | [admin-monitoring.md](admin-monitoring.md) | Monitoring Admin registration, logs, and exact Redis-backed metrics-permission mutations |
20
21
  | [admin-metrics-explorer.md](admin-metrics-explorer.md) | Read-only recorded-metrics discovery, full-slug adapters, KPI/fan-out/scalar contracts, and cache identity |
21
22
  | [admin-settings.md](admin-settings.md) | Runtime settings admin — direction-aware atomic writes, write-only secrets, and global/group scope |
23
+ | [admin-deployments.md](admin-deployments.md) | API serving/attempt evidence, WebApp release recovery, durable onboarding, transient CI keys and real uploads |
24
+ | [admin-dns-domains.md](admin-dns-domains.md) | Simple domain inventory, exact-name registration/adoption/purchase, focused registrar settings and DNS links |
25
+ | [admin-maintenance.md](admin-maintenance.md) | Independent AWS upgrade scans and framework policy, exact confirmation, observation and uncertain operation guards |
26
+ | [admin-geolocation.md](admin-geolocation.md) | Owner-only GeoIP provider setup, ordered fallbacks, transient credential verification and publishing |
27
+ | [admin-operational-fleet.md](admin-operational-fleet.md) | Operational health matrix, exact backend grants, reviewed stored capacity plans and bounded batch observation |
22
28
  | [admin-fleet-configuration.md](admin-fleet-configuration.md) | Superuser structured configuration publishing, version restore and per-node application evidence |
23
29
  | [admin-bouncer.md](admin-bouncer.md) | Bouncer decisions, device investigations, and bot-signature administration with token-safe caching |
24
30
  | [admin-security-tickets.md](admin-security-tickets.md) | Ticket queue, KISS detail modal, notes, approvals, and Maestro sync contract |
@@ -36,7 +42,7 @@ everything: run the showcase (`npm run dev:showcase`) → **Develop → Componen
36
42
  | [admin-dns-registrar.md](admin-dns-registrar.md) | Domain search/quote/purchase, one-use token custody, durable-ledger reconciliation, scoped registrant PII, and manual House adoption |
37
43
  | [admin-storage.md](admin-storage.md) | Global S3 buckets, masked storage backends, explicit policy-backed File uploads, capability-safe FileView/shares, and finite rendition convergence |
38
44
  | [admin-media-rendering.md](admin-media-rendering.md) | Storage → Media rendering: the three FILEMAN_RENDITIONS_* settings as a descriptor-driven form — delta-from-defaults writes, inline 400 routing, reset by writing `{}` |
39
- | [admin-edge.md](admin-edge.md) | Edge vhosts by template kind with a shape-first wizard, `site_api` routes and their create order, declared upstreams, and the fleet blocklist with log-first modes. |
45
+ | [admin-edge.md](admin-edge.md) | Serving hostname configuration with Address → Routing → Certificate → Review creation, independently admitted node posture and bounded hosting readiness, `site_api` routes and their create order, declared upstreams, and the fleet blocklist with log-first modes. |
40
46
  | [admin-shortlinks.md](admin-shortlinks.md) | Global shortlink operations, destination-free caches, bounded click history, and reconciled one-shot mutations |
41
47
  | [admin-messaging.md](admin-messaging.md) | SES domains/mailboxes/sent/templates plus public contact requests, with write-only credentials and offline HTML previews |
42
48
  | [admin-phonehub.md](admin-phonehub.md) | Global Phone Hub: evidence-backed lookup, sanitized SMS audits, and write-only provider configuration |
@@ -15,24 +15,44 @@ Assistant permissions and member/group grants cannot satisfy it. The server
15
15
  also rejects confined ApiKey/GroupScopedToken sessions and enforces person
16
16
  ownership; the client does not invent a broader per-user-key ban.
17
17
 
18
- The basic section shows deployment readiness, exact provider/model/key
19
- selection, Automatic — latest Sonnet or an explicit manual pin, credential source and optional server hint, blank replacement input,
20
- Check and save key, Check current key and enable/disable. A configured key and
21
- an enabled switch do not prove readiness. `route.ready` describes configuration,
22
- not current provider health. Unknown routes remain unknown: a missing
23
- credential can cause the backend to omit the entire route. Only an explicit
24
- `handler` route points to the Platform credential section. Bedrock uses AWS
25
- identity from deployment. Assistant, Platform and AWS credentials never
26
- substitute for one another. Missing Assistant/realtime applications are also
27
- reported. Runtime model identifiers preserve provider punctuation, including
28
- Bedrock colons; only a manual pin uses the backend's restricted pin syntax.
29
-
30
- Advanced native details separate Platform credentials, deployed policy/safety,
31
- remote access/connections and incident triage. Policy activation acknowledges
32
- the deployed policy on this node, does not edit it and never establishes
33
- fleet-wide agreement. Static, database and effective emergency stops are
34
- separate; only the database switch is writable. Activation and breaker reset
35
- cannot release a stop.
18
+ The page separates **AI Assistant** from **MCP connections** with two task
19
+ tabs. AI Assistant is the built-in provider-backed chat and incident processing;
20
+ MCP serves tools to external agents that bring their own AI. MCP does not
21
+ require an Assistant provider key, model selection, policy acknowledgement or
22
+ enabled chat switch. The MCP tab shows its address, independent access switch,
23
+ public discovery check and connections directly; AI readiness warnings remain
24
+ in the AI tab. Ordinary reads probe neither provider nor public discovery.
25
+
26
+ The AI tab starts with a compact status overview: the enabled switch, current
27
+ provider/model and selected connection, followed by Safety & usage and Incident
28
+ triage. Configure model and Manage keys open focused native dialogs. The API
29
+ keys dialog separates Assistant and Platform keys with two task tabs; Safety &
30
+ usage separates Policy & stops from Usage & recovery. Incident triage is a
31
+ direct dialog. Keyboard-accessible info icons hold catalogue metadata,
32
+ credential explanations, policy/admission diagnostics, connection dates and
33
+ the triage watermark. Errors, failed checks, emergency stops and operation
34
+ consequences stay visible beside their actions. No expandable sections or
35
+ single-item side navigation are used.
36
+
37
+ A configured key and enabled switch do not prove readiness. `route.ready`
38
+ describes configuration, not current provider health. Unknown routes remain
39
+ unknown: a missing credential can cause the backend to omit the entire route.
40
+ A handler route opens Manage keys on Platform key. Bedrock uses deployment AWS
41
+ identity; the key dialog explains that browser keys cannot configure it.
42
+ Assistant, Platform and AWS credentials never substitute for one another.
43
+ Missing Assistant/realtime applications are reported. Runtime model identifiers
44
+ preserve provider punctuation, including Bedrock colons; only a manual pin uses
45
+ the backend's restricted pin syntax.
46
+
47
+ Policy activation acknowledges the deployed policy on this node, does not edit
48
+ it and never establishes fleet-wide agreement. Static, database and effective
49
+ emergency stops are separate; only the database switch is writable. Activation
50
+ and breaker reset cannot release a stop. Destructive and consequential actions
51
+ use a nested confirmation, initially focused on Cancel. During an operation,
52
+ repeat submission, task tabs and native Escape/backdrop/close are locked. Cancel
53
+ returns to the task without making a request. Closing a task or changing key
54
+ types clears unsaved secret drafts; reopening model selection restores the
55
+ saved value. All dialogs unmount with the owner/session boundary.
36
56
 
37
57
  Model selection controls the protected runtime selection only when the backend
38
58
  returns a valid capability for the explicit Anthropic `auto:sonnet` policy.
@@ -42,8 +62,7 @@ unavailable until deployment removes that pin. Mode is authoritative even when
42
62
  a fixed route retains an inert legacy `selected` value.
43
63
 
44
64
  The exact resolved runtime identifier (`model.effective`) and source are
45
- separate from the selection draft and `route.ready`. Advanced → Deployed policy
46
- and safety contains the raw policy model and admission diagnostic. The additive
65
+ separate from the selection draft and `route.ready`. Safety & usage → Policy & stops → the Deployed policy info icon contains the raw policy model and admission diagnostic. The additive
47
66
  `route.can_invoke` reports admission to the guarded request path: a cold
48
67
  automatic catalogue can admit a request while configuration readiness remains
49
68
  false. It does not prove that a usable model or healthy provider is ready.
@@ -78,7 +97,7 @@ changes conservatively discard it: the shared auth API cannot distinguish
78
97
  renewal from same-person replacement across tabs. A 440 uses the existing
79
98
  fresh-auth UI after clearing credential drafts; even successful reauthentication
80
99
  requires explicit re-entry/retry. No secret is silently replayed. Confirmation
81
- is page-local, focuses Cancel, and ends with the page/session scope.
100
+ is a page-owned native dialog, focuses Cancel, and ends with the page/session scope.
82
101
 
83
102
  Protocol helpers are exported from `portal-mojo/admin/assistant`:
84
103
  `getAssistantSetup(options, probe?)`, `mutateAssistantSetup(action, options)`,
@@ -153,7 +172,7 @@ fake password `mojo`. No real credentials or deployment changes belong in demos.
153
172
  `verify:admin-assistant-setup` covers wire contracts, owner navigation/admission,
154
173
  projections and mounted auth/draft/confirmation/fresh-auth/transport/cache
155
174
  lifecycle; it is wired into `verify:release`. Browser checks separately establish
156
- both-theme layout, native details, keyboard controls and console behavior.
175
+ both-theme layout, native dialogs, help popovers, keyboard controls and console behavior.
157
176
 
158
177
  ## Conversation transport
159
178
 
@@ -218,9 +237,42 @@ per-type schemas/caps; unknown/malformed blocks drop. Context references are
218
237
  inert and allowlisted. Action choices require confirmation, then their bounded
219
238
  value is sent attachment-free through the ordinary REST POST.
220
239
 
240
+ ## Management pages
241
+
242
+ Conversations and Skills use a clean searchable list with URL-backed pagination
243
+ and Refresh. Searches target conversation titles or skill names. Record details
244
+ open in compact native dialogs without tabs or a side rail. Conversations label
245
+ messages with their actual owner and compare the viewer separately; foreign
246
+ history shows an explicit viewing-only notice and no composer. Owned history
247
+ retains realtime/REST transport selection, completed attachments and no automatic
248
+ resend after an unknown outcome. New conversations start from the app's Assistant
249
+ button.
250
+
251
+ Skill details show trigger phrases and readable ordered steps. Tool identifiers,
252
+ dates and secondary explanations use info popovers. Automatic execution remains
253
+ visibly called out: read-only steps can run automatically; changes still require approval. The sole lifecycle action deactivates/reactivates through the
254
+ existing endpoint, then reloads authoritative state; no create/delete UI is added.
255
+
256
+ The page lifetime is keyed to person, auth-session revision and global permissions.
257
+ Late reads cannot replace newer results. Writes check live authority after auth
258
+ preflight, use synchronous single-flight guards and lock native dismissal while
259
+ pending. Private history/memory stays component-local. The Dashboard is unchanged.
260
+
221
261
  ## Memory
222
262
 
223
- Global and user tiers require the global Assistant grant. Group memory first chooses from a bounded normal `/api/group` request and supplies that id as normal request context (`?group=<id>`). A group id is never put in the memory body and ambient group state is never consulted. Keys are limited to 64 lowercase alphanumeric/colon/underscore/hyphen characters; values are limited to 500 characters.
263
+ Global and user tiers require the global Assistant grant. Group memory first chooses from a searchable normal `/api/group` picker and supplies that id as normal request context (`?group=<id>`). A group id is never put in the memory body and ambient group state is never consulted. Keys start with a lowercase letter or number and are limited to 64 lowercase alphanumeric/colon/underscore/hyphen characters; values are limited to 500 characters.
264
+
265
+ Memory uses Personal, Global and Group tabs because they select different
266
+ Assistant audiences. Lists show saved values with Edit and Remove actions; Add
267
+ opens a focused Key/Value dialog. Existing keys are immutable, duplicate new keys
268
+ cannot overwrite a saved value, and changing audience/group discards unsaved
269
+ drafts. The searchable group picker replaces the previous first-50 limit. Group
270
+ membership checks remain explicit and inherited; missing/refused checks are
271
+ visible. Members with the global Assistant grant can inspect group memory;
272
+ editing additionally requires inherited member Assistant authority or superuser. Shared edits name their audience, removal names its key and audience and
273
+ initially focuses Cancel. Writes keep errors in the dialog, retain drafts on
274
+ refusal and never replay automatically. Refresh failures are distinct from empty
275
+ memory. Saved entries are not silently truncated to the first 100.
224
276
 
225
277
  ## Shell ownership
226
278
 
@@ -0,0 +1,201 @@
1
+ # Deployments
2
+
3
+ `DeploymentsPage` contributes one global Operations workspace. The Admin section
4
+ singleton belongs to `admin/domains/operations.ts`; its route lazily imports the
5
+ page. `WebAppDetail({id,onClose})` is a focused `modal.open` task dialog.
6
+ Import these through `portal-mojo/admin/operations` after central registration.
7
+ The domain's owned stylesheet is `styles/components/admin-deployments.css`.
8
+
9
+ API and WebApp lanes are independently admitted. API sections require exact global
10
+ `view_platform|manage_platform|admin`; `view_platform_security` alone never fetches
11
+ deployments. WebApps require exact global `view_dns|manage_dns|security` in this
12
+ Admin workspace. The finite Fleet `backendGlobalCan` helper reproduces backend
13
+ category expansion; the frontend's broader admin wildcard does not admit reads.
14
+ Inactive lanes and denied identities mount no domain query. No active group is
15
+ inherited. Explicit owning-group action scope is separate from page scope.
16
+
17
+ API uses `GET /api/account/admin/platform?sections=deployments,api`. The last
18
+ converged serving SHA and its observation date remain separate from the latest
19
+ attempt. Server node summary distinguishes restart proof, reported success,
20
+ dispatched and failed nodes. Durable diagnosis is separate from replaceable node
21
+ observations. Unknown and stale states remain visible. Recovery supports retry
22
+ same SHA, verify and converge according to the exact attempt status matrix; each
23
+ confirmation refreshes the attempt first. Retry sends a stable bounded
24
+ `Idempotency-Key`, then watches its returned attempt ID. There is no arbitrary SHA
25
+ input, API rollback, framework update or pin editor.
26
+
27
+ WebApp summary lists are bounded to 50 with honest truncation disclosure. Full
28
+ summary, default graph ownership, registered release history and deployment
29
+ attempt history are separate reads. The default graph is positively projected:
30
+ no bucket, prefix, key, unrelated group data or raw metadata reaches Query.
31
+ Full summary's latest attempt has no release identity; the detail resolves its
32
+ exact app/attempt-filtered generic history record before naming the version.
33
+ Generic attempt graphs omit the parent, so the request binds both `webapp` and
34
+ `id`, asks for one row, and validates the returned attempt and release IDs.
35
+ Ordinary viewers are never sent to the stronger rollout-status endpoint: it
36
+ requires WebApp SAVE authority. Admitted operators can inspect node evidence
37
+ through that endpoint. Selected active attempts use bounded, owner-fenced
38
+ observation; terminal outcomes invalidate the owning summary and history.
39
+ The current release pointer is desired content, never proof of fleet convergence.
40
+ HTTPS health and active certificates are independent observations.
41
+
42
+ Rollback requires `manage_webapp` plus the owning object's SAVE authority (`manage_dns|security`).
43
+ Current-user membership may be resolved only for that known app's owning group;
44
+ global grants bypass unnecessary membership reads. Bootstrap eligible groups do
45
+ not grant or narrow existing-app custom actions. Manual release registration has
46
+ its own exact object SAVE predicate (`manage_dns|security`); read-only `view_dns` does not grant release registration; workflow generation requires security or
47
+ both WebApp and DNS management. A 403 removes app mutation controls for that
48
+ session. The backend remains authoritative for all object, actor, origin,
49
+ interactive and freshness rules.
50
+
51
+ Release and deployment history have separate paged views. Eligible release rows
52
+ open a preselected rollback; the focused chooser also pages beyond the first 25.
53
+ Rollback selects a concrete noncurrent release in `uploaded|live|superseded`,
54
+ requires typed `app/version`, refreshes ownership/current pointer and the exact app/target-filtered release, then
55
+ sends `{webapp,release,group}`. It watches the exact returned deployment through
56
+ `/api/edge/release/deployment/ID`, validating deployment/app/release identities.
57
+ Only `live` is success. `rolled_back` means the attempted version was unsuccessful;
58
+ a failed rollout can leave mixed nodes. An uncertain rollback is not replayed on
59
+ remount. Its safe receipt blocks conflicting app mutations while it is unsettled.
60
+
61
+ Before creation, App and Build options share one retained draft. After creation,
62
+ only the current server task appears beside noninteractive App → Address →
63
+ Repository → HTTPS progress; operation/revision/activity live in Setup details.
64
+ Closing preserves setup; cancelling server setup requires explicit confirmation.
65
+ New WebApp uses separately admitted bootstrap eligibility and server onboarding
66
+ options/precheck. Missing destination or allowed bucket blocks creation with a concrete prerequisite. Existing workspaces precheck their concrete address before create. New-workspace intent deliberately has no domain before create and does not require an invented ready verdict. Defaults are production, main,
67
+ dist and the first allowed bucket. The durable server operation owns cursor,
68
+ revision, status, activity and resources. Choose/cancel use its latest observation;
69
+ a revision/lease refusal requires another detail read, never repeated POST.
70
+ Unknown cursors and parked waiting states stop automatic reads. Optional GitHub
71
+ verification preserves `unavailable` versus explicit attestation. Closing does not
72
+ cancel server work. A matching actor/origin safe receipt resumes by operation UUID;
73
+ an app without such a receipt shows unfinished diagnostics without creating a
74
+ replacement resource. An uncertain create can be deliberately retried only with
75
+ its immutable profile and UUID after an exact 404 observation.
76
+
77
+ Deploy opens its GitHub, Upload and Other CI methods directly. Workflow and
78
+ Other CI instructions use focused dialogs. Linked-key controls follow observed
79
+ state: create when unlinked; rotate when linked; revoke only an active key. Both
80
+ CI methods expose these controls. Deploy setup lazy-loads key/upload surfaces.
81
+ Generated YAML stays outside
82
+ Query and references one repository secret named `MOJO_DEPLOY_KEY`. Copy/download
83
+ instructions leave installation to the operator. Keys are revealed in one focused
84
+ transient dialog, cleared on close, authority/session replacement or unmount.
85
+ Receipt replay with `token:null` says the secret is unavailable and requires an
86
+ explicit new rotation. Rotate/revoke explain CI impact; existing content remains
87
+ independent. Revoke's backend freshness window is 300 seconds; other sensitive
88
+ custom actions use 600. A 440 prompts through `requestFreshAuth` but never replays
89
+ the POST; refresh and renewed explicit consent are required.
90
+
91
+ Built artifact upload is register → anonymous checksum-bound PUT → complete →
92
+ exact deployment observation. Files are normalized relative paths, reject
93
+ traversal/collisions, and are hashed with SHA-256 before registration. Defaults
94
+ are 5000 files and 1 GiB; backend limits may be lower. Signed destinations and File
95
+ objects stay transient. Each PUT uses only the returned exact checksum header,
96
+ raw bytes, `credentials:omit`, `redirect:error` and no API bearer. A failed/CORS
97
+ PUT stops completion. Resume requires reselecting and hashing an identical
98
+ manifest before registration remints URLs; a verified release with no uploads is
99
+ handled. Saved receipts contain only actor/origin, finite IDs, immutable safe
100
+ profile/address or normalized manifest, never key material or signed URLs.
101
+
102
+ `useDeploymentObservation` reads only known identities, stops on terminal,
103
+ parked/unknown cursor, failure, owner disposal or a bounded deadline, and skips
104
+ hidden or blurred documents. Refresh/Resume resets the read budget without
105
+ creating a new server operation. Native Escape/backdrop dismissal and repeated
106
+ submission are locked during HTTP writes. Observations can stop after close;
107
+ submitted server work persists and is reconciled from safe receipts.
108
+
109
+ Mock integration uses `deploymentsMock(path,opts,{user,keyBacked,authTime,group?,
110
+ interactive?})`, returning `undefined` for unrelated paths. The central bootstrap
111
+ composer consumes `readDeploymentBootstrapFacts(caller)` and merges only its
112
+ explicit WebApp fields. `deploymentMockStorageFetch` is an explicit mock-only
113
+ secondary PUT simulator; real deployments use browser fetch. Scenario controls
114
+ and `AdminDeploymentsDemo` exercise authority splits, current-A/failed-B,
115
+ reported/partial/unknown/stale evidence, same-SHA retry, rolled-back/mixed WebApps,
116
+ setup prerequisites, cursor/lease refusal, uncertain create, key replay, upload
117
+ failure and bounded observation. Fixtures establish UI contracts, not live rollout.
118
+
119
+ The supported profile editor writes only `display_name`, `environment`,
120
+ `github_repository`, `deployment_ref` and `build_output` through the existing
121
+ WebApp model save. It requires exact SAVE authority independently of
122
+ `manage_webapp`; owner, bucket, release pointer, slug and credential fields are
123
+ not editable here. The API wire field is `github_repository`, while the display
124
+ DTO calls it `repository`. Save re-reads ownership and all baseline values,
125
+ refuses observed drift, then reads back the saved profile. This is a client
126
+ stale-value check, not an atomic server revision condition. Profile changes do
127
+ not deploy content or update a workflow already installed in GitHub.
128
+
129
+ A lost profile-save response retains its submitted receipt and blocks another
130
+ save. Reload shows current values but does not prove the earlier writer finished.
131
+ Only after that read may an operator explicitly acknowledge the unknown prior
132
+ writer with the app-specific phrase. Acknowledgment sends no POST; a new change
133
+ and fresh preflight are still required. A late observed commit refuses the next
134
+ save. Known profile 440 refusal settles its unsent receipt and never replays the
135
+ POST. No profile patch or secret is retained in the receipt.
136
+
137
+ Run `node scripts/verify-admin-deployments.mjs` and
138
+ `node scripts/verify-admin-deployments-mounted.mjs`, then repository typecheck.
139
+ The first covers positive projection, authority/status/receipt/wire contracts and
140
+ actual client idempotency headers in both transports. Mounted StrictMode tests
141
+ hold responses to exercise single-flight, unsettled rollback remounts, owner
142
+ replacement, fresh auth without replay, one-time key replay, denied no-request
143
+ lanes, safe operation resume, terminal/parked/error/timeout and observer disposal.
144
+ The additional `verify:admin-deployments-setup-mounted` and
145
+ `verify:admin-webapp-inspection-mounted` commands cover state-aware setup,
146
+ later-page target preflight, ordinary CI viewers/observation, scoped refresh and
147
+ profile stale/unknown/fresh-auth journeys. The integration owner runs the
148
+ reviewed modal inventories and actual
149
+ light/dark, narrow and native-dialog browser verification.
150
+
151
+
152
+ New workspace continuation uses the exact `operation.group.id` returned by durable
153
+ create. That group identity is saved separately from the immutable create profile;
154
+ resuming never rewrites `group_intent:new` into another create body. The address
155
+ cursor reads options for that concrete group and explicitly links to the host's
156
+ registered Domains destination when configured and independently admitted. A new
157
+ workspace starts without an own/ancestor domain; configure or connect one for the
158
+ named group, refresh options, then precheck the address and submit the returned
159
+ `domain`/normalized `label` with the latest operation revision. Continue stays
160
+ unavailable with a concrete prerequisite until an actual address choice exists.
161
+ The frontend never borrows an installation-wide or unrelated workspace domain.
162
+
163
+ A WebApp deployment marked `superseded` with `terminal:true` stops observation and
164
+ settles its receipt, with `success:false`. It is not a successful rollout.
165
+
166
+ Upload receipts durably record `completion_submitted`, the completion timestamp,
167
+ and bounded prior deployment IDs before sending completion. Completion is a new
168
+ promotion request: after an earlier failed, rolled-back or superseded attempt,
169
+ repeating it can create another deployment. A lost response therefore blocks
170
+ implicit registration/completion even after remount or same-manifest reselection.
171
+ Reconcile reads the owning app summary/history and validates exact app, release,
172
+ version and newly observed deployment identity before observing terminal outcome.
173
+ Ambiguity remains unknown. Only a separately typed, freshly rechecked new deployment
174
+ consent permits resetting the local upload phase; that consent sends no POST and
175
+ requires a subsequent deliberate reselect/upload/deploy action. It neither erases
176
+ an earlier attempt nor silently replays it. The mock follows the backend's narrow
177
+ reuse rule: current release plus an active/live attempt, not permanent reuse for
178
+ every release ID.
179
+
180
+
181
+ Owner lifetimes survive React development StrictMode's setup/cleanup/setup replay.
182
+ Query closures keep a stable identity/scope handle whose live signal is renewed
183
+ before read effects resume; they do not retain an aborted first-render controller.
184
+ Actual unmount, actor/session replacement and authority or active-state changes
185
+ abort the old lifetime and discard late results. The mounted harness places
186
+ StrictMode above the providers/router so React 19 actually replays initial effects,
187
+ and verifies cold authorized bootstrap/platform reads through an asynchronous
188
+ transport gate as well as real owner cancellation.
189
+
190
+ ### KISS presentation
191
+
192
+ API and WebApps are distinct tasks; hosts admitting only one lane show no tab
193
+ strip. Creation appears only in WebApps. The API lane shows last convergence and
194
+ attempts, keeps unavailable/stale evidence visible, and puts secondary collector
195
+ facts behind an info icon. API inspection separates Overview, Nodes and Activity
196
+ and offers recovery only on the latest applicable attempt. WebApp inspection
197
+ separates Overview, Deployments, Releases, Deploy and Settings; wholly read-only
198
+ viewers omit Deploy. Useful node evidence adds tabs to an attempt dialog only
199
+ when there is more than one content area. No single-item side rail or long
200
+ collapsible section is introduced. Admin Dashboard stays unchanged. See
201
+ [visual QA](admin-presentation.md).
@@ -123,8 +123,8 @@ without separate operator authorization.
123
123
 
124
124
  ## Shared validation setup
125
125
 
126
- Use **Domains → Shared validation setup**, or **Set up shared validation** in a
127
- group-managed Domain or Certificate header menu. Domain **Validation** and
126
+ Use **Domains → Advanced → Shared validation setup**, or **Set up shared validation** in a
127
+ group-managed Domain or Certificate header menu. Domain **Advanced validation** and
128
128
  Certificate **Delegation** show the same read-only facts. No new route or sidebar
129
129
  resource is added. The flow never reads certificate material.
130
130
 
@@ -143,7 +143,7 @@ resource is added. The flow never reads certificate material.
143
143
  stays available after copy/share denial or cancellation. The canonical file is
144
144
  at most 2048 UTF-8 bytes and has exactly `version:1`, `consumer_project_uuid`,
145
145
  `client_ref`, `domain`, `allocation_ref`, with no wrapper, key, token or scope.
146
- 5. The controller opens their deployment’s **Domains → Shared validation setup →
146
+ 5. The controller opens their deployment’s **Domains → Advanced → Shared validation setup →
147
147
  Controller review**, checks access and uses **Import request**. There is no JSON
148
148
  editor or controller URL. Empty, oversized, malformed, noncanonical or extra-field
149
149
  files are refused before installation. Import alone grants nothing. The named
@@ -0,0 +1,98 @@
1
+ # Domains
2
+
3
+ Import `DomainsPage` from `portal-mojo/admin/infrastructure`. The global DNS
4
+ inventory lives at the host's `/dns/domains` mount. Server search, filters,
5
+ sort and paging stay on `DomainModel` with `admin:dns:domains` persistence.
6
+ Scope comes from each row, never the active product group. An absent expiry
7
+ means **Unknown**, not Never. Narrow layouts combine provider, scope and status
8
+ under the name so the Records action stays visible.
9
+
10
+ ## Everyday tasks
11
+
12
+ **Add domain** opens three explicit choices: **Connect GoDaddy domain**,
13
+ **Buy domain**, and literal-superuser **Adopt Route 53 zone**. Purchasing has its
14
+ own read-only ledger shortcut. **More domain actions → Shared validation setup**
15
+ keeps external-domain/controller setup available without an existing domain.
16
+ Viewers get inspection and Purchases; writes require global
17
+ `sys.manage_dns | sys.security`. Platform adoption/assignment additionally require
18
+ literal interactive `is_superuser`; backend gates remain authoritative.
19
+
20
+ A row opens a compact native dialog with Overview, provider-applicable DNS
21
+ records, Certificates, and manager Registration. It has no side rail or nested
22
+ registration disclosure. Only the selected task mounts. The same record panel
23
+ serves the standalone Records page; its type filter is controlled by that page's
24
+ URL or locally when embedded. Serving is an explicit sibling route under the
25
+ observed Admin mount. Extra registered domain links retain their own gates.
26
+
27
+ ## Connect, repair and adopt
28
+
29
+ Connect selects one explicit authorized group through bounded
30
+ `/api/dnsman/credential/group-choice`, then an active, verified GoDaddy
31
+ credential in that exact group. Changing group clears credential state and
32
+ fences late replies. **Link credential** opens the existing write-only credential
33
+ editor locked to that group; a verified return resumes the same domain draft.
34
+ There is no generic group list, free-form group id or provider-account discovery.
35
+ The ownership probe sends exactly `{ group, domain, credential }` and spends no
36
+ money. Domain names are normalized before submission.
37
+
38
+ An existing GoDaddy domain with an unusable linked credential offers **Repair
39
+ linked credential**. Rotation verifies the same credential/group, clears raw
40
+ password refs, then refreshes safe credential and domain projections. This does
41
+ not expose an arbitrary credential-FK reassignment: the backend has no same-group
42
+ validation for that generic save. A domain with no linked credential cannot be
43
+ silently attached to a newly linked one.
44
+
45
+ Adoption reviews one exact existing public zone name and Platform scope before
46
+ posting `{ domain }`. It never supplies `group`/`create_zone` or discovers the AWS
47
+ account. Native dismissal is guarded during review and mutation. Cancel review
48
+ writes nothing. Unknown onboarding outcomes require inventory/provider review;
49
+ submitted forms never repeat their request.
50
+
51
+ A Platform domain offers **Assign to group** to literal superusers. Choose the
52
+ destination, review domain/from/to, and type the exact destination name. The UI
53
+ checks current authority, current domain ownership and the exact authorized group
54
+ again before `{ domain, group }`. Already-grouped rows never offer reassignment.
55
+ A lost receipt retains **Refresh owner** and refuses replay, even if the fresh
56
+ read still says Platform; a read cannot prove an older write has finished.
57
+
58
+ All onboarding writes check their owner at the transport's `beforeSend` boundary,
59
+ after any asynchronous auth refresh. Credential/purchase step-up continuations
60
+ perform that same check. A replacement session never inherits old consent.
61
+
62
+ ## Registration
63
+
64
+ Registration starts with an explicit manager-only **Read registration**. WHOIS
65
+ contacts stay in the mounted dialog, outside Query, URLs, storage and logs.
66
+ Route 53 DNS hosting alone never admits registrar edits: the returned registrar
67
+ must identify Amazon Registrar/Route 53. Hosted-zone-only, unavailable or unknown
68
+ registrations remain read-only. GoDaddy registration tasks hand off to its account;
69
+ Mojo remains certificate-only. No manual Renew action is invented.
70
+
71
+ Confirmed Route 53 registration offers focused settings and contacts dialogs.
72
+ Auto-renew uses the existing best-effort boolean save; privacy uses its dedicated
73
+ all-contact action. Fresh owner/registrar/value preflight precedes writes. Only
74
+ live WHOIS readback can establish observed values; an accepted DB save is not
75
+ provider proof. Unknown outcomes lock the submitted editor and never replay.
76
+ An unchanged desired DB value may not re-run a failed model hook; the registrar
77
+ account handoff remains available.
78
+
79
+ Contact inspection groups identity, email/phone and postal address. Identical
80
+ Registrant/Admin/Technical contacts share one view; differing contacts get useful
81
+ role tabs. Editing keeps one draft across Contact/Address tabs. **Replace all
82
+ contacts** explicitly replaces all three roles, matching the backend contract.
83
+ Entering edit mode never submits. Registry extension fields are preserved locally.
84
+ Pending operations guard tab switches, native dismissal and repeated writes.
85
+
86
+ ## Purchase and QA
87
+
88
+ Buying is restricted to one quoted year until the backend binds multi-year
89
+ payable totals. Privacy limits and acknowledged downgrades stay visible.
90
+ See [registrar flows](admin-dns-registrar.md) for exact confirmation and recovery.
91
+
92
+ `verify:admin-domains` includes delayed-auth transport fences.
93
+ `verify:admin-domains-mounted` runs onboarding, assignment/filter/credential,
94
+ registration and purchase/contact journey contracts, including StrictMode and
95
+ stale-session/scope cases. JSDOM establishes behavior only. Closing acceptance
96
+ uses actual Portal and built Showcase screenshots and interactions in light/dark,
97
+ desktop and 390px; see [visual QA](admin-presentation.md). No live registrar
98
+ purchase or infrastructure mutation is required for this mock acceptance.
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  The one canonical DNS section is `id: "dns"`, `basePath: "dns"`, in Infrastructure. Its routes are ordered `domains`, `records`, `certificates`, `purchases`, `registrant`, `credentials`. The read surfaces accept the global, system-pinned any-of view clause `sys.view_dns | sys.manage_dns | sys.security`; registrant and record mutation controls use `sys.manage_dns | sys.security`. Group/member grants never open Admin.
16
16
 
17
- Domains uses the server-backed `DomainModel` list and `modal.detail`. Detail composes Overview, DNS Records, and the bounded Certificates section; Records is the same `DnsRecordsPanel` used by the standalone page. That page owns only `domain` and `record_type` in the URL. It may select the first active domain only when `domain` is absent. Invalid, denied, missing, and inactive deep links are preserved and shown as errors.
17
+ Domains uses the server-backed `DomainModel` list and compact native task dialog. Detail composes Overview, applicable DNS records, bounded Certificates and manager Registration; Records is the same `DnsRecordsPanel` used by the standalone page. That page owns only `domain` and `record_type` in the URL. An embedded panel owns a local type filter when no controlled value is supplied. It may select the first active domain only when `domain` is absent. Invalid, denied, missing, and inactive deep links are preserved and shown as errors.
18
18
 
19
19
  ## Wire contract
20
20
 
@@ -10,13 +10,28 @@ The wizard asks for one explicit group through bounded `/api/dnsman/credential/g
10
10
 
11
11
  `POST /registrar/quote` returns `{purchase,name,price,currency,years,token,expires,privacy_supported}`. The raw token lives only in an immutable ref in the mounted wizard. It is never state, URL data, Query/Mutation cache data, mock request history, or durable mock data. The exact tuple binds purchase id, token, group, normalized domain, years, decimal price, currency, and expiry. Any upstream change or expiry clears it.
12
12
 
13
- The operator types the exact normalized domain before the single-flight, nonretry `POST /registrar/purchase`. The token is removed from the ref before the request. Success and every exception reconcile through an imperative `GET /purchase/<id>?graph=default`; one non-overlapping bounded-backoff loop follows `submitted` to a durable terminal row. Timeout hands the operator to the ledger.
14
-
15
- After purchase is attempted, the wizard cannot retry or requote. A failed, ambiguous, or timed-out result always says that money may have moved and directs the operator to the AWS Route 53 Domains operation console and durable ledger first.
13
+ The portal purchases **one year** only. The backend currently keeps raw TLD
14
+ registration price separately from years, so the UI does not multiply it into an
15
+ allegedly authoritative multi-year payable total. `privacy_supported` is part of
16
+ the retained quote identity; an unsupported registry is explained before consent,
17
+ and an acknowledged `privacy_downgraded` result stays visible.
18
+
19
+ The operator types the exact normalized domain before the single-flight, nonretry `POST /registrar/purchase`. The token is removed from the ref before the request. Success and every exception reconcile through an imperative `GET /purchase/<id>?graph=default`; one non-overlapping bounded-backoff loop follows `submitted` to a durable terminal row. Timeout keeps exact **View purchase** and **Refresh** actions available. Each
20
+ refresh reads the retained purchase ID and never submits another purchase. A
21
+ completed receipt opens the acknowledged domain ID when available; the ledger
22
+ graph has no domain FK, so other completed receipts use a mount-relative
23
+ name-filtered inventory handoff.
24
+
25
+ After purchase is attempted, the wizard cannot retry or requote. A final
26
+ transport owner check runs after the asynchronous auth gate and before every
27
+ permitted 440 continuation. Session replacement stops that continuation. Only
28
+ the purchase ID/owner survives a same-user revision change, giving an exact-ID
29
+ recovery view; a different user receives none of the previous recovery state. A failed, ambiguous, or timed-out result always says that money may have moved and directs the operator to the AWS Route 53 Domains operation console and durable ledger first.
16
30
 
17
31
  ## Purchase ledger
18
32
 
19
- `DomainPurchaseModel` lists only `graph=basic`: id, domain, status, price, and currency. Export/download and alternate graphs are rejected. Clicking a row performs an imperative default-graph read into a KISS modal; the detail response and redacted error are modal-local and never Query-cached.
33
+ `DomainPurchaseModel` lists only `graph=basic`: id, domain, status, price, and currency. Export/download and alternate graphs are rejected. Clicking a row performs an imperative default-graph read into a focused modal
34
+ with an exact-ID Refresh action; the detail response and redacted error are modal-local and never Query-cached.
20
35
 
21
36
  ## Registrant PII
22
37
 
@@ -24,6 +39,16 @@ The contact page starts with no scope and no contact. Operators explicitly choos
24
39
 
25
40
  An inherited group response renders a blank form and only the backend-authored boolean status; inherited values are never disclosed. Normal editable values may seed from that scope's database row or the House settings-file response. `Fax` and opaque `ExtraParams` are preserved only when the original response is a direct database row for the exact same scope. They are never carried across scope, inheritance, or deployment-file override boundaries.
26
41
 
42
+ ## Scoped purchase contact setup
43
+
44
+ A missing-contact search offers **Set up contact** without discarding the explicit
45
+ purchasing group or intended name. `RegistrantContactPage` accepts optional
46
+ `initialGroup`, `lockScope`, `onSaved`, `onClose`, and `onBusyChange` for that
47
+ focused native dialog. Reads are explicit manager-only, fenced by scope/session,
48
+ and never Query-cached. Contact/Address tabs keep one draft; save returns to the
49
+ same purchase search and rechecks scoped capabilities. There is no active-group
50
+ fallback. The standalone page retains its explicit scope chooser.
51
+
27
52
  ## House adoption
28
53
 
29
54
  House adoption is a separate, literal-superuser-only menu option. The operator types one exact existing name and submits `/registrar/adopt` without `group`. The UI never calls `/registrar/discover`, auto-discovers account domains, or silently assigns the adopted asset to a tenant.
package/docs/admin-dns.md CHANGED
@@ -37,6 +37,8 @@ domain includes registration/hosted-zone presence, safe zone metadata,
37
37
  tracking/adoption state, and a reason. `untracked=true` returns only untracked
38
38
  rows; discovery and assignment remain interactive-superuser operations.
39
39
 
40
+ The everyday inventory and three onboarding actions are documented in [admin-dns-domains.md](admin-dns-domains.md).
41
+
40
42
  The operator registrar workflow is documented in [admin-dns-registrar.md](admin-dns-registrar.md). It does not use discovery: House adoption is manual exact-name only. Purchase tokens remain transient, all post-confirmation outcomes reconcile through the read-only ledger, and contact PII remains imperative/local.
41
43
 
42
44
  These responses require local call-flow handling and must never enter Query or
@@ -87,9 +89,8 @@ rotation remains write-only and imperative. Built-in credential Delete is absent
87
89
 
88
90
  ## Shared wildcard validation
89
91
 
90
- The existing Domains header offers **Shared validation setup** to global DNS
91
- managers even when server setup needs an operator. Domain detail includes
92
- read-only **Validation**; its header action and Certificate Delegation compose
92
+ Domains offers **More domain actions → Shared validation setup** to global DNS
93
+ managers even when server setup needs an operator. Domain Overview offers the focused **Shared validation** action; Certificate Delegation composes
93
94
  the same focused flow. See [the human walkthrough and exact
94
95
  contracts](admin-dns-certificates.md#shared-validation-setup).
95
96