portal-mojo 0.2.19 → 0.2.21
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.
- package/docs/README.md +7 -1
- package/docs/admin-credentials.md +27 -0
- package/docs/admin-deployments.md +201 -0
- package/docs/admin-dns-certificates.md +160 -0
- package/docs/admin-dns-domains.md +92 -0
- package/docs/admin-dns.md +18 -0
- package/docs/admin-edge.md +200 -196
- package/docs/admin-fleet-configuration.md +98 -2
- package/docs/admin-geolocation.md +100 -0
- package/docs/admin-identity-directories.md +12 -0
- package/docs/admin-maintenance.md +133 -0
- package/docs/admin-modals.md +45 -2
- package/docs/admin-operational-fleet.md +206 -0
- package/docs/admin-presentation.md +51 -0
- package/docs/admin-signin.md +12 -2
- package/docs/auth-pages.md +18 -6
- package/docs/auth.md +16 -2
- package/docs/client.md +7 -0
- package/docs/collection-select.md +14 -0
- package/docs/combobox.md +18 -0
- package/docs/popover.md +5 -4
- package/package.json +1 -1
- package/src/admin/core/OperationsHelp.tsx +10 -0
- package/src/admin/core/index.ts +6 -4
- package/src/admin/core/route-context.ts +6 -0
- package/src/admin/credentials/AcmeConsumerKeyDialog.tsx +88 -0
- package/src/admin/credentials/acme-consumer-key.ts +34 -0
- package/src/admin/credentials/group-api-keys.tsx +5 -3
- package/src/admin/deployments/ApiDeploymentDetail.tsx +58 -0
- package/src/admin/deployments/DeploymentsPage.tsx +73 -0
- package/src/admin/deployments/WebAppDeploySetup.tsx +49 -0
- package/src/admin/deployments/WebAppDeploymentDetail.tsx +59 -0
- package/src/admin/deployments/WebAppDetail.tsx +104 -0
- package/src/admin/deployments/WebAppEditDialog.tsx +70 -0
- package/src/admin/deployments/WebAppKeyDialog.tsx +25 -0
- package/src/admin/deployments/WebAppOnboardingDialog.tsx +217 -0
- package/src/admin/deployments/WebAppRollbackDialog.tsx +82 -0
- package/src/admin/deployments/WebAppUploadPanel.tsx +132 -0
- package/src/admin/deployments/api.ts +158 -0
- package/src/admin/deployments/authority-state.ts +9 -0
- package/src/admin/deployments/deployment-state.ts +13 -0
- package/src/admin/deployments/index.ts +4 -0
- package/src/admin/deployments/onboarding-state.ts +11 -0
- package/src/admin/deployments/permissions.ts +37 -0
- package/src/admin/deployments/receipts.ts +78 -0
- package/src/admin/deployments/sanitize.ts +73 -0
- package/src/admin/deployments/shared.tsx +39 -0
- package/src/admin/deployments/types.ts +238 -0
- package/src/admin/deployments/upload.ts +53 -0
- package/src/admin/deployments/useDeploymentObservation.ts +104 -0
- package/src/admin/dns/CertificateDetail.tsx +8 -3
- package/src/admin/dns/CertificateRequestDialog.tsx +45 -7
- package/src/admin/dns/DomainDetail.tsx +14 -4
- package/src/admin/dns/DomainOnboardingContent.tsx +44 -0
- package/src/admin/dns/DomainOnboardingDialog.tsx +5 -24
- package/src/admin/dns/DomainPurchaseWizard.tsx +1 -1
- package/src/admin/dns/DomainRegisterExisting.tsx +63 -0
- package/src/admin/dns/DomainRegistrarSettings.tsx +52 -0
- package/src/admin/dns/DomainsPage.tsx +26 -13
- package/src/admin/dns/SharedValidationDialog.tsx +298 -0
- package/src/admin/dns/SharedValidationPanel.tsx +76 -0
- package/src/admin/dns/api.ts +4 -2
- package/src/admin/dns/domain-navigation.ts +16 -0
- package/src/admin/dns/registrar-api.ts +13 -0
- package/src/admin/dns/shared-validation-api.ts +90 -0
- package/src/admin/dns/shared-validation-data.ts +106 -0
- package/src/admin/domains/assistant.ts +5 -4
- package/src/admin/domains/communications.ts +10 -9
- package/src/admin/domains/identity.ts +8 -7
- package/src/admin/domains/infrastructure.ts +19 -13
- package/src/admin/domains/lazy-page.ts +6 -0
- package/src/admin/domains/observability.ts +6 -5
- package/src/admin/domains/operations.ts +27 -10
- package/src/admin/domains/security.ts +16 -15
- package/src/admin/edge/EdgeHelp.tsx +10 -0
- package/src/admin/edge/ServingReadiness.tsx +54 -0
- package/src/admin/edge/VhostDetail.tsx +145 -139
- package/src/admin/edge/VhostWizard.tsx +204 -115
- package/src/admin/edge/VhostsPage.tsx +27 -28
- package/src/admin/edge/api.ts +24 -10
- package/src/admin/edge/models.ts +4 -4
- package/src/admin/edge/serving-data.ts +78 -0
- package/src/admin/fleet/CapacityEditor.tsx +81 -0
- package/src/admin/fleet/CapacityInspector.tsx +19 -0
- package/src/admin/fleet/CapacityProgress.tsx +9 -0
- package/src/admin/fleet/FleetHelp.tsx +7 -0
- package/src/admin/fleet/FleetOverview.tsx +150 -0
- package/src/admin/fleet/FleetPage.tsx +11 -0
- package/src/admin/fleet/FleetWorkspace.tsx +82 -0
- package/src/admin/fleet/ScaleFleetDialog.tsx +169 -0
- package/src/admin/fleet/access.ts +22 -0
- package/src/admin/fleet/api.ts +77 -0
- package/src/admin/fleet/data.ts +148 -0
- package/src/admin/fleet/index.ts +8 -0
- package/src/admin/fleet/operations.ts +179 -0
- package/src/admin/fleet/standalone-data.ts +87 -0
- package/src/admin/fleet/types.ts +208 -0
- package/src/admin/fleet-configuration/FleetConfigurationPage.tsx +14 -8
- package/src/admin/fleet-configuration/SharedValidationConnectionPanel.tsx +166 -0
- package/src/admin/fleet-configuration/data.ts +4 -0
- package/src/admin/fleet-configuration/operator-destinations.ts +20 -0
- package/src/admin/fleet-configuration/shared-validation-data.ts +143 -0
- package/src/admin/geolocation/GeoLocationPage.tsx +171 -0
- package/src/admin/geolocation/data.ts +138 -0
- package/src/admin/geolocation/index.ts +3 -0
- package/src/admin/identity/groups/group-sections/SubGroupsSection.tsx +73 -2
- package/src/admin/identity/groups/models.ts +2 -0
- package/src/admin/identity/members/GroupMembersPanel.tsx +2 -8
- package/src/admin/index.ts +8 -0
- package/src/admin/maintenance/MaintenanceDialogs.tsx +161 -0
- package/src/admin/maintenance/MaintenancePage.tsx +442 -0
- package/src/admin/maintenance/data.ts +486 -0
- package/src/admin/maintenance/index.ts +3 -0
- package/src/admin/maintenance/operations.ts +208 -0
- package/src/admin/public/infrastructure.ts +1 -0
- package/src/admin/public/operations.ts +3 -0
- package/src/admin/registry.ts +6 -2
- package/src/admin/settings/SettingDetail.tsx +7 -2
- package/src/admin/settings/SettingEditor.tsx +2 -1
- package/src/admin/settings/SettingsPage.tsx +3 -1
- package/src/admin/settings/model.ts +1 -0
- package/src/admin/signin/SigninPage.tsx +17 -3
- package/src/admin/signin/api.ts +2 -0
- package/src/charts/MetricsChartView.tsx +4 -11
- package/src/charts/MetricsMiniWidget.tsx +3 -15
- package/src/client/auth.ts +77 -25
- package/src/client/client.ts +6 -0
- package/src/client/deployments-mock.ts +375 -0
- package/src/client/domain-operations-mock.ts +37 -0
- package/src/client/fleet-mock.ts +73 -1
- package/src/client/geo-provider-mock.ts +82 -0
- package/src/client/index.ts +2 -2
- package/src/client/maintenance-mock.ts +285 -0
- package/src/client/mock.ts +280 -11
- package/src/client/operational-fleet-mock.ts +279 -0
- package/src/client/operations-mock.ts +85 -0
- package/src/client/serving-mock.ts +22 -0
- package/src/styles/components/admin-deployments.css +35 -0
- package/src/styles/components/admin-dns-certificates.css +33 -0
- package/src/styles/components/admin-domains.css +12 -0
- package/src/styles/components/admin-edge.css +125 -1
- package/src/styles/components/admin-geolocation.css +40 -0
- package/src/styles/components/admin-maintenance.css +127 -0
- package/src/styles/components/admin-operational-fleet.css +38 -0
- package/src/styles/components/admin-operations.css +9 -0
- package/src/styles/components/admin-signin.css +2 -0
- package/src/styles/components/combobox.css +12 -2
- package/src/styles/components/fleet-page.css +9 -0
- package/src/styles/index.css +7 -0
- package/src/ui/CollectionSelect.tsx +30 -8
- package/src/ui/ComboBox.tsx +39 -8
- package/src/ui/FilterBar.tsx +3 -5
- package/src/ui/ModelTable.tsx +2 -8
- package/src/ui/Popover.tsx +15 -7
- package/src/ui/date/TimePicker.tsx +4 -14
- package/src/ui/image-editor/ImageEditor.tsx +4 -5
- package/src/ui/schema-form-core.tsx +3 -5
package/docs/admin-edge.md
CHANGED
|
@@ -1,210 +1,214 @@
|
|
|
1
|
-
# Edge Admin
|
|
1
|
+
# Edge Admin · Serving
|
|
2
2
|
|
|
3
3
|
```ts
|
|
4
4
|
import {
|
|
5
5
|
EDGE_ADMIN_SECTION, VhostsPage, VhostDetail, VhostWizard, openVhostWizard,
|
|
6
6
|
UpstreamsPage, BlocklistPage, EdgeVhostModel, EdgeRouteModel,
|
|
7
7
|
EdgeUpstreamModel, EdgeBlocklistModel, buildVhostPayload,
|
|
8
|
-
} from 'portal-mojo/admin';
|
|
8
|
+
} from 'portal-mojo/admin'; // also portal-mojo/admin/infrastructure
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
**House rows.** A vhost on a group-less domain is platform property. It never
|
|
41
|
-
appears in a non-superuser's list, even with a global grant, and its detail,
|
|
42
|
-
its routes and creating one are refused to everyone else. The vhost graphs
|
|
43
|
-
carry `domain: basic`, which has no `group`, so house-ness cannot be read off a
|
|
44
|
-
vhost row. The wizard's domain picker passes `group__isnull: false` for
|
|
45
|
-
non-superusers so a house domain is never offered to someone who would be
|
|
46
|
-
refused at Finish.
|
|
11
|
+
`EDGE_ADMIN_SECTION` contributes Infrastructure routes `edge/vhosts` and
|
|
12
|
+
`edge/upstreams`, relative to the standalone or embedded `/system` mount.
|
|
13
|
+
Network Security owns `security/network/edge-blocklist`. Records open in
|
|
14
|
+
native modals, not record routes. Consumer source: django-mojo `mojo/apps/edge/`;
|
|
15
|
+
`client/mock.ts` mirrors its graphs and refusals, with wording limits below.
|
|
16
|
+
|
|
17
|
+
## Permissions and ownership
|
|
18
|
+
|
|
19
|
+
All clauses are global; section and route gates both apply.
|
|
20
|
+
|
|
21
|
+
| Resource/action | Authority |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Vhost, route, upstream view | `sys.view_dns OR sys.manage_dns OR sys.security` |
|
|
24
|
+
| Vhost/route change or delete; upstream enabled switch | `sys.manage_dns OR sys.security` |
|
|
25
|
+
| Upstream declare/retire | Literal `me.is_superuser`; server `require_platform_admin` also refuses API keys |
|
|
26
|
+
| Blocklist view | `sys.view_security OR sys.manage_security OR sys.security` |
|
|
27
|
+
| Blocklist change/delete | `sys.manage_security OR sys.security` |
|
|
28
|
+
|
|
29
|
+
`EDGE_BLOCKLIST_VIEW_PERMS` deliberately includes `manage_security`: manage does
|
|
30
|
+
not imply view in the client. Security-only operators cannot enter DNS-gated
|
|
31
|
+
Edge, hence the separate blocklist route. Member grants and `?group=` do not
|
|
32
|
+
open the fleet-wide blocklist.
|
|
33
|
+
|
|
34
|
+
A vhost on a group-less domain is a **house row**: non-superusers cannot list,
|
|
35
|
+
inspect or create it or its routes, even with global DNS grants. Vhost graphs'
|
|
36
|
+
`domain: basic` omits group, so never infer ownership there. Non-superuser domain
|
|
37
|
+
choices use `group__isnull: false`. Resolve the domain's own graph before offering
|
|
38
|
+
enabled upstreams: `group=<owner>` includes owned and shared rows; platform domains
|
|
39
|
+
use `group__isnull: true`. Unresolved ownership disables selection.
|
|
47
40
|
|
|
48
41
|
## Endpoints
|
|
49
42
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
The
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
43
|
+
| `/api/edge/…` | Contract |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `vhost`, `vhost/<id>` | list/read/create/update/delete; table graph `list`, record graph `default` |
|
|
46
|
+
| `route`, `route/<id>` | list/read/create/update/delete; `site_api` only; filter `vhost` |
|
|
47
|
+
| `upstream`, `upstream/<id>` | list/read/update `is_enabled`; no collection create/delete |
|
|
48
|
+
| `upstream/declare` | POST `{name, kind, host+port OR socket_path, group?}` |
|
|
49
|
+
| `upstream/retire` | POST `{upstream}`; disables and retains row |
|
|
50
|
+
| `blocklist`, `blocklist/<id>` | list/read/create/update/delete; fleet-wide |
|
|
51
|
+
|
|
52
|
+
Refused saves reject at the one client boundary. Public save/delete helpers
|
|
53
|
+
accept an optional `beforeWrite` guard, invoked before **every** fresh-auth attempt.
|
|
54
|
+
|
|
55
|
+
## Serving tasks
|
|
56
|
+
|
|
57
|
+
The lean table shows hostname/domain, type and enabled intent. Pool and timestamps
|
|
58
|
+
belong in inspection. The Domain picker writes URL filter state; search supports
|
|
59
|
+
label/type/pool, not derived full hostnames. Persisted key remains
|
|
60
|
+
`admin:edge:vhosts`; public `Vhost*` exports and `/edge/vhosts` remain compatible.
|
|
61
|
+
|
|
62
|
+
- **Add hostname:** Hostname → Certificate → Review. Name, type and destination
|
|
63
|
+
belong to one Hostname task. Compact type choice,
|
|
64
|
+
only relevant destination controls, visible action footer. Other settings holds
|
|
65
|
+
pool/body/static/quiet paths without an accordion. Success opens the saved row.
|
|
66
|
+
- **Edit:** Hostname / Certificate / Settings share one controlled draft.
|
|
67
|
+
Validation selects the relevant tab and focuses the invalid input. Saving retains
|
|
68
|
+
the active panel. Domain is immutable. Info-icon popovers carry secondary help.
|
|
69
|
+
- **Inspect:** Overview / Routes / Settings; Routes only for Site + API.
|
|
70
|
+
Overview shows actual redirect/API destination first, followed by the certificate
|
|
71
|
+
name, stored status/expiry and direct Change certificate action. Settings holds other knobs, timestamps and
|
|
72
|
+
read-only platform policy. Header owns enable/disable, Edit and Delete.
|
|
73
|
+
- **Routes:** paginated local table, focused Add/Edit dialogs and explicit removal
|
|
74
|
+
confirmation. Before rename/removal, inspect current quiet paths and complete
|
|
75
|
+
bounded routes. Block orphaning a quiet path and identify what to repair in
|
|
76
|
+
Settings first; no atomic multi-row operation is implied.
|
|
77
|
+
|
|
78
|
+
Styles are scoped to these surfaces; shared Tabs/DetailView and Dashboard behavior
|
|
79
|
+
are unchanged. See [presentation QA](admin-presentation.md).
|
|
80
|
+
|
|
81
|
+
## Kinds, payloads and validation
|
|
82
|
+
|
|
83
|
+
| Kind | Required | Supported options | Refused |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| `api` | `upstream` | quiet paths, static files | SPA, redirect |
|
|
86
|
+
| `site` | — | SPA | upstream, quiet paths, static, redirect |
|
|
87
|
+
| `site_api` | explicit routes on create | SPA, quiet paths, static | whole-host upstream, redirect |
|
|
88
|
+
| `redirect` | `redirect_to` | — | other kind-specific knobs |
|
|
89
|
+
|
|
90
|
+
`buildVhostPayload(draft, {create})` allowlists the chosen kind and sends empty
|
|
91
|
+
values for its unsupported knobs, clearing values from a former kind. It never
|
|
92
|
+
sends `mojosec_policy`. The server can refuse kind/SPA changes that conflict with
|
|
93
|
+
that policy's response class; show its refusal.
|
|
94
|
+
|
|
95
|
+
Client mirrors in `edge/models.ts`; server remains authoritative:
|
|
96
|
+
|
|
97
|
+
- Label: empty apex, `*` wildcard or one lowercase DNS label. Derive `server_name`.
|
|
98
|
+
- Body: integer 1–4096 MB, default 50; absent for redirect.
|
|
99
|
+
- Pool: deployment-declared `EDGE_POOLS`, default `["default"]`; no client catalog.
|
|
100
|
+
- Redirect: hostname only; no scheme/path/port/wildcard.
|
|
101
|
+
- Route/quiet path: leading `/`, letters/digits/`._-/`, at most 128 characters,
|
|
102
|
+
no `//` or `..` segment. A route cannot be `/`.
|
|
103
|
+
|
|
104
|
+
Only enabled rows require a certificate belonging to the domain and covering the
|
|
105
|
+
address, and a unique enabled domain/label. Stage a replacement disabled, disable
|
|
106
|
+
the old row, then enable the replacement. `certificateCovers` knows a wildcard
|
|
107
|
+
covers one label and never the apex. Coverage stays Unknown until the full
|
|
108
|
+
certificate graph (including SANs) is read; disabled staging remains available.
|
|
109
|
+
|
|
110
|
+
## Staging and uncertain writes
|
|
111
|
+
|
|
112
|
+
`site_api` quiet paths require stored route prefixes, so create in this order:
|
|
113
|
+
|
|
114
|
+
1. Vhost disabled, without quiet paths.
|
|
115
|
+
2. Routes sequentially.
|
|
116
|
+
3. Final vhost save with quiet paths and chosen enabled intent; always perform this
|
|
117
|
+
step so enable-time validation runs last.
|
|
118
|
+
|
|
119
|
+
The wizard requires at least one valid create route, though the server accepts
|
|
120
|
+
route-less enabled rows. Edits and Site → Site + API conversion may remain
|
|
121
|
+
route-less; author routes in detail. Existing routes block conversion away from
|
|
122
|
+
Site + API. Unread routes defer quiet-path validation to the server.
|
|
123
|
+
|
|
124
|
+
Retain acknowledged steps on explicit refusals. Transport loss, malformed receipts
|
|
125
|
+
and 5xx may follow committed writes and stay **unconfirmed**, never “refused” or
|
|
126
|
+
“still disabled”. An unacknowledged create cannot replay: close and inspect Serving.
|
|
127
|
+
An acknowledged partial create offers **Inspect saved hostname**.
|
|
128
|
+
|
|
129
|
+
**Observe outcome** reads only. Route continuation requires the matching stored
|
|
130
|
+
prefix/upstream; absence stays blocked. Edit/final-save observation compares the
|
|
131
|
+
exact normalized submitted payload, including FK IDs and arrays; unchanged or
|
|
132
|
+
conflicting fields retain the guard and show differences. Only an exact match
|
|
133
|
+
clears it. Continuing requires explicit **Add hostname** or **Save changes**;
|
|
134
|
+
observation never automatically replays a mutation.
|
|
135
|
+
|
|
136
|
+
Detail edits, route writes, enablement and deletion serialize through one pending
|
|
137
|
+
state. Lock Escape/backdrop/X during preflight and writes. Before each write and
|
|
138
|
+
fresh-auth attempt, check original session/current permission; owner disposal
|
|
139
|
+
closes nested dialogs and prevents later staged writes. Closing cannot cancel an
|
|
140
|
+
already submitted server operation or prove its outcome.
|
|
141
|
+
|
|
142
|
+
Route safety reads paginate to at most 2,000 rows and reject changed counts,
|
|
143
|
+
duplicate IDs, incomplete pages and excess inventory. Same-count concurrent edits
|
|
144
|
+
cannot all be detected; there is no atomic snapshot guarantee.
|
|
145
|
+
|
|
146
|
+
Delete confirms the address, stopped serving and route cascade, warns linked sites
|
|
147
|
+
can stop serving, retains domain/certificate and offers Disable as reversible.
|
|
148
|
+
|
|
149
|
+
## Serving evidence
|
|
150
|
+
|
|
151
|
+
Read-only `ServingReadiness.tsx`/`serving-data.ts` keep positive projections outside
|
|
152
|
+
Query. `GET /api/account/admin/bootstrap` requires an authenticated matching global
|
|
153
|
+
identity with `view_admin OR manage_users OR manage_settings OR admin` (literal
|
|
154
|
+
superuser bypass); DNS grants alone never request it. Retain only matching user
|
|
155
|
+
ID/superuser, setup capability and edge available/HTTP/issuance fields.
|
|
156
|
+
|
|
157
|
+
`GET /api/account/admin/setup/readiness?section=hosting_vhosts` additionally requires
|
|
158
|
+
successful same-identity bootstrap with setup capability and literal superuser in
|
|
159
|
+
both Me and bootstrap. Server still refuses key-backed/noninteractive sessions;
|
|
160
|
+
client invents no provenance flag. Schema 1 validates status/time/count/coverage,
|
|
161
|
+
keeps the selected section only, caps checks at 64 and scalar details at 16 keys.
|
|
162
|
+
|
|
163
|
+
The unboxed summary sits within the page header and keeps timestamp and
|
|
164
|
+
partial-report warning visible. DNS-only sessions that cannot request bootstrap
|
|
165
|
+
see no global checks summary; admitted observation failures remain visible. Check
|
|
166
|
+
details holds technical evidence. HTTP is a file-rendering setting; DNS-01 is an
|
|
167
|
+
issuance method. Checks cover enabled rows' domain/certificate status, **not** live
|
|
168
|
+
serving, expiry/coverage/material installation, fleet convergence/nodes/generation.
|
|
169
|
+
An omitted hostname has no individual verdict. Enabled/saved is configuration intent.
|
|
170
|
+
|
|
171
|
+
Errors, denied attempted reads, old-backend 404s and malformed responses show Unavailable or
|
|
172
|
+
Unknown. Failed refresh clears old evidence. UID/session revision fence reads;
|
|
173
|
+
same-session loss of admission/bootstrap/superuser/setup authority disposes report
|
|
174
|
+
modals. Reads occur only while mounted/admitted, without polling; configuration
|
|
175
|
+
changes refresh the panel.
|
|
176
|
+
|
|
177
|
+
## Upstreams and blocklist
|
|
178
|
+
|
|
179
|
+
An upstream's name/destination are fixed at declaration; only enabled is editable.
|
|
180
|
+
Lists include owned and shared rows. Retiring disables and retains it: reaching
|
|
181
|
+
vhosts/routes stop serving and are not repointed. Confirm that consequence.
|
|
182
|
+
|
|
183
|
+
Blocklist modes: `log` (default, still served), `enforce` (refused), `allow`
|
|
184
|
+
(exemption), `off` (retained inactive). Start in log, inspect the watch log, then
|
|
185
|
+
enforce. Propagation takes about ten minutes. Save IPs as canonical networks:
|
|
186
|
+
`10.1.2.3/8` becomes `10.0.0.0/8`; show the returned value. Declare and blocklist
|
|
187
|
+
editors guard native dismissal; blocklist save/delete share pending state.
|
|
188
|
+
|
|
189
|
+
## Boundaries and pitfalls
|
|
190
|
+
|
|
191
|
+
- No claim-reserved action: django-mojo removed it in `bc2c57b1` (v1.7.1 onward).
|
|
192
|
+
Platform-owned `mojosec_policy` is read-only; never send it.
|
|
193
|
+
- No owning-WebApp/alias graph, pool catalog, route-disable flag, atomic multi-route
|
|
194
|
+
write or per-host live readiness. Aliases look like other rows; show refusals.
|
|
195
|
+
- Mock uniqueness wording (enabled hostname, route prefix, blocklist value,
|
|
196
|
+
upstream name) and missing-relation wording were not measured on the server.
|
|
197
|
+
- Blocklist parsing follows Python `ipaddress`/`re`, not JS RegExp: reject
|
|
198
|
+
`010.1.2.3` and `\q`; accept `10.0.0.0/255.0.0.0`, `(?i)bot`, `a*+`.
|
|
199
|
+
Compilation reason text is mock-specific; local validation is only a first pass.
|
|
200
|
+
- Client certificate checks are advisory; only the server validates a save.
|
|
199
201
|
|
|
200
202
|
## Themes and showcase
|
|
201
203
|
|
|
202
|
-
|
|
203
|
-
`portal-mojo/styles.css
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
`
|
|
204
|
+
Canonical token-only `src/styles/components/admin-edge.css` ships through
|
|
205
|
+
`portal-mojo/styles.css`, imported by Admin and Showcase. Demo: Components → Admin
|
|
206
|
+
→ Edge, under platform administrator, DNS manager/viewer and security manager/viewer.
|
|
207
|
+
Verify both themes, desktop/390px, opened dialogs, keyboard/help, busy and error states.
|
|
208
|
+
|
|
209
|
+
Contracts: `npm run verify:admin-edge` (API plus held dialog writes),
|
|
210
|
+
`node scripts/verify-admin-serving-mounted.mjs` (all shapes, staging, draft/focus,
|
|
211
|
+
conversion, partial inspection, uncertainty, route guards/session races),
|
|
212
|
+
`node scripts/verify-admin-serving-mounted-evidence.mjs` (DTO bounds, exact gates,
|
|
213
|
+
unknown evidence, failed refresh, auth/permission replacement). Mounted tests do
|
|
214
|
+
not establish native layout; actual browser QA remains required.
|
|
@@ -7,8 +7,9 @@ page checks `is_superuser === true` before fetching. Backend authority remains
|
|
|
7
7
|
mandatory, including rejection of diagnostic/key-backed sessions and fresh auth.
|
|
8
8
|
|
|
9
9
|
Applications register their schema in django-mojo. The page renders the returned
|
|
10
|
-
section, label, description, default, type and restart requirement;
|
|
11
|
-
|
|
10
|
+
section, label, description, default, type and restart requirement; application schemas remain server-owned. The three managed shared-validation
|
|
11
|
+
connection fields route to the dedicated topic below, rather than appearing as
|
|
12
|
+
independently editable generic fields. Each field defaults to Keep current value. Set value
|
|
12
13
|
and Remove published override are explicit actions. Clearing falls back to base
|
|
13
14
|
configuration/default; it does not promise erasure of a bootstrap secret.
|
|
14
15
|
|
|
@@ -48,3 +49,98 @@ example. Mock Portal uses representative registered settings and an offline node
|
|
|
48
49
|
Django receives this page only after a canonical Portal artifact is built and
|
|
49
50
|
vendored; follow `docs/admin-artifact.md` and the Django vendor tool. Ordinary
|
|
50
51
|
Vite build output is not the canonical artifact.
|
|
52
|
+
|
|
53
|
+
## Shared validation connection
|
|
54
|
+
|
|
55
|
+
Operations → Fleet Configuration → Shared validation connection is a private, lazy
|
|
56
|
+
route. It requires a current literal superuser; the server additionally requires
|
|
57
|
+
an active interactive session, same-origin writes and fresh authentication.
|
|
58
|
+
Key-backed sessions are rejected by the server even when their user is an owner.
|
|
59
|
+
|
|
60
|
+
On the validation hub, update every expected node using the existing Deployments
|
|
61
|
+
controls, then use **Enable shared validation** once. The compact hub section is
|
|
62
|
+
independent of consumer connection configuration and uses the same Portal login,
|
|
63
|
+
fresh authentication and native confirmation as the existing page. No separate
|
|
64
|
+
operator service, login or wizard is required. Missing or incompatible deployment
|
|
65
|
+
proof shows the ordinary prerequisite instead of an enabled action. Each issuer
|
|
66
|
+
keeps its own ACME account, certificates and private keys; allocation trust still
|
|
67
|
+
requires explicit approval in the DNS setup screen. Renewal remains automatic.
|
|
68
|
+
|
|
69
|
+
`GET /api/account/admin/fleet/shared-validation/hub` returns
|
|
70
|
+
`{version:1, enabled, configured, deployment_uuid, can_enable, reason}`.
|
|
71
|
+
The deployment UUID is canonical lowercase with hyphens, unlike consumer project
|
|
72
|
+
UUIDs. `POST` takes only `{deployment_uuid}` after confirmation. The backend
|
|
73
|
+
rechecks the complete deployed roster and framework version atomically before
|
|
74
|
+
enabling. A stale deployment, failed read or fresh-auth refusal requires Refresh
|
|
75
|
+
and another deliberate review; mutations are never replayed. Keep the compatible
|
|
76
|
+
framework on every node after enabling sharing.
|
|
77
|
+
|
|
78
|
+
The panel consumes the private `/api/account/admin/fleet/shared-validation`
|
|
79
|
+
contract. Choose a named approved hub profile, inspect its exact origin and
|
|
80
|
+
original project UUID, then Keep, Replace or Disable the connection. Replacement
|
|
81
|
+
is a normal write-only password field; blank preserves the existing credential.
|
|
82
|
+
The approved catalog fixes the origin. Arbitrary URLs and raw JSON are not inputs.
|
|
83
|
+
|
|
84
|
+
Project identity fields also accept existing lowercase16hex Group IDs, preserving
|
|
85
|
+
the exact value through profiles, observations and protected key issuance. Fleet
|
|
86
|
+
revisions/operation IDs remain32hex and deployment IDs remain canonical UUIDs.
|
|
87
|
+
The legacy identity showcase fixture changes wire data only; no Group identity
|
|
88
|
+
is migrated or rewritten.
|
|
89
|
+
|
|
90
|
+
Preview probes the candidate without saving it. Publication has a separate
|
|
91
|
+
confirmation, frozen against the preview and auth session. Publishing revalidates
|
|
92
|
+
the catalog and expected revision; it does not Apply. Apply has another
|
|
93
|
+
confirmation and targets the exact current revision. Every expected node must
|
|
94
|
+
report installed, restarted and combined request-service/job-engine/scheduler
|
|
95
|
+
health at that revision and the expected topic profile/project identity. Offline,
|
|
96
|
+
draining, stale, partial and missing-node results remain unconfirmed. The UI
|
|
97
|
+
polls at most120 observations and offers deliberate Refresh afterward. Verified
|
|
98
|
+
connection identity with `sharing_ready=false` still needs sharing enabled on the
|
|
99
|
+
approved hub. Refresh the connection after enabling it there.
|
|
100
|
+
|
|
101
|
+
Create the replacement on the approved hub with the named **ACME validation
|
|
102
|
+
consumer** key form. Preview/Publish, Apply, verify every expected node, then
|
|
103
|
+
explicitly review and deactivate the old key in the hub inventory. Issuance,
|
|
104
|
+
publication and successful dispatch never retire keys. Disable masks the whole
|
|
105
|
+
connection tuple and base credential without deleting base settings or revoking
|
|
106
|
+
remote trust; retained/shared identities can make disabling ineligible.
|
|
107
|
+
|
|
108
|
+
Secrets remain in an uncontrolled mounted password input and a short-lived
|
|
109
|
+
imperative request draft. They never enter Query/Mutation caches, URL/history,
|
|
110
|
+
storage, receipts or logs. These new writes call `requestFreshAuth` without
|
|
111
|
+
replaying the attempted action; the existing generic Fleet helpers keep their
|
|
112
|
+
prior behavior. Auth/session change or unmount invalidates drafts and closes
|
|
113
|
+
owned confirmations. Double activation is single-flight. A409 revision/catalog
|
|
114
|
+
conflict or `configuration_busy` requires Refresh and a deliberate retry. An
|
|
115
|
+
uncertain publication or issuance may have persisted; read authoritative status
|
|
116
|
+
or exact Group inventory before another attempt.
|
|
117
|
+
|
|
118
|
+
Unavailable APIs, missing DNSMan/publisher/delegation/schema/enrollment or an empty
|
|
119
|
+
approved catalog render a concrete blocked state. Bootstrap supplies `admin_path`
|
|
120
|
+
and permission facts for existing legacy System Setup, Deployments, topology and
|
|
121
|
+
Fleet Scaling links. Links are same-origin and capability-gated. The backend's
|
|
122
|
+
semantic `capacity` hint maps to the actual registered `#/fleet` route. These
|
|
123
|
+
legacy destinations retain their protected confirmations, policy, release holds
|
|
124
|
+
and IaC restrictions. This React topic reuses those controls without a new
|
|
125
|
+
deployment or root authority service.
|
|
126
|
+
|
|
127
|
+
The Runtime settings showcase mounts the actual connection panel with central
|
|
128
|
+
mock scenarios and operator/ordinary-manager identities. The named
|
|
129
|
+
`verify:admin-shared-validation-config` gate includes private DTO/API and mounted
|
|
130
|
+
StrictMode contracts, and is included once by `verify:admin-contracts`. Browser
|
|
131
|
+
proof remains necessary for real native dialogs, keyboard/focus and390px in both
|
|
132
|
+
themes. Mock receipts are fixtures, never live configuration evidence.
|
|
133
|
+
|
|
134
|
+
The private form uses an inline-size container: fact labels stack above UUIDs and
|
|
135
|
+
revision values below420px of available content, including a narrow embedded
|
|
136
|
+
Admin host. The existing Admin sidebar's Collapse control supplies its normal
|
|
137
|
+
compact host at390px; the topic does not change global navigation behavior.
|
|
138
|
+
|
|
139
|
+
Every successful authoritative refresh invalidates a preview when revision,
|
|
140
|
+
catalog, profile, identity or credential facts change. Late preview/operation
|
|
141
|
+
responses cannot restore spent consent or overwrite a newer observation. After
|
|
142
|
+
the automatic observation limit, explicit Refresh re-observes the tracked exact
|
|
143
|
+
operation and resets the read budget; it never dispatches another Apply. Only
|
|
144
|
+
known healthy report/node statuses with no top-level or node error can turn the
|
|
145
|
+
combined proof green. Apply/read replies remain bound to their exact operation
|
|
146
|
+
ID and requested/tracked revision.
|