portal-mojo 0.2.20 → 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-deployments.md +201 -0
- package/docs/admin-dns-certificates.md +3 -3
- package/docs/admin-dns-domains.md +92 -0
- package/docs/admin-dns.md +4 -2
- package/docs/admin-edge.md +200 -196
- package/docs/admin-geolocation.md +100 -0
- package/docs/admin-maintenance.md +133 -0
- package/docs/admin-modals.md +17 -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/client.md +7 -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/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/DomainDetail.tsx +10 -6
- 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 +22 -13
- package/src/admin/dns/SharedValidationDialog.tsx +1 -1
- 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/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 +22 -7
- 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/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/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/client.ts +6 -0
- package/src/client/deployments-mock.ts +375 -0
- package/src/client/domain-operations-mock.ts +37 -0
- package/src/client/geo-provider-mock.ts +82 -0
- package/src/client/maintenance-mock.ts +285 -0
- package/src/client/mock.ts +52 -10
- 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-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/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.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# GeoLocation provider setup
|
|
2
|
+
|
|
3
|
+
`GeoLocationPage` (`portal-mojo/admin/operations`) is the global Operations →
|
|
4
|
+
GeoLocation surface. GeoIP Cache inspects IPs; Fleet Configuration applies
|
|
5
|
+
published settings. Dashboard is unchanged. Hosts may override the mount-derived Fleet link with
|
|
6
|
+
`fleetConfigurationPath`; Showcase opens the actual Fleet Configuration page with a return link.
|
|
7
|
+
|
|
8
|
+
Admission requires literal `is_superuser === true` and a current interactive
|
|
9
|
+
session. The backend also rejects key-backed sessions, requires same-origin
|
|
10
|
+
POSTs and authentication within 600 seconds. Global/group grants do not replace
|
|
11
|
+
these checks. `usePrivateOperator` remounts on session change, aborts requests and
|
|
12
|
+
owned dialogs, and rechecks authority before sending and accepting responses.
|
|
13
|
+
|
|
14
|
+
## Tasks
|
|
15
|
+
|
|
16
|
+
Primary, fallback and ordered additional providers form one visible configuration task,
|
|
17
|
+
with add, remove and move controls. MojoVerify credentials and threat sharing sit
|
|
18
|
+
below the chain. One retained draft and Save/Discard footer cover the form; there
|
|
19
|
+
is no tab navigation for parts of the same task. The uncontrolled password element
|
|
20
|
+
stays mounted while editing, and shared Tabs is unchanged.
|
|
21
|
+
|
|
22
|
+
MojoVerify always uses `https://api.mojoverify.com`; no URL control is shown.
|
|
23
|
+
Older origins remain readable, but this page stages the hosted URL and warns
|
|
24
|
+
that switching requires a replacement key. Provider names are human-readable;
|
|
25
|
+
unknown configured identifiers are retained. New additional rows exclude primary,
|
|
26
|
+
fallback and existing rows. Runtime deduplicates the chain; existing overlaps are
|
|
27
|
+
not silently rewritten. Non-Mojo credentials are configured outside this endpoint.
|
|
28
|
+
The server's values are authoritative: setup defaults differ from runtime defaults.
|
|
29
|
+
|
|
30
|
+
No key shows **Add key**; a configured key shows **Replace key** and **Remove
|
|
31
|
+
key**. Test key sits with the credential. Blank replacement preserves the stored
|
|
32
|
+
key; confirmed removal is staged until Save changes and does not revoke remotely.
|
|
33
|
+
Save/Discard reflect actual draft changes. Refresh and the page-owned Open Fleet Configuration link confirm before discarding an
|
|
34
|
+
unsaved draft. Keep editing preserves both provider selections and the entered key;
|
|
35
|
+
explicit departure wipes the key before navigation. An in-flight operation cannot
|
|
36
|
+
be interrupted through that link. Info popovers hold secondary explanations; Configuration information
|
|
37
|
+
opens a focused native dialog for revision and publication facts. No disclosure
|
|
38
|
+
sections or sidebar modal are used.
|
|
39
|
+
|
|
40
|
+
Last stored-key check shows its historical outcome and timestamp separately from
|
|
41
|
+
the draft result. Pending activation and Open Fleet Configuration remain visible.
|
|
42
|
+
A false pending flag concerns only this request node, never proves fleet health.
|
|
43
|
+
|
|
44
|
+
## Private protocol
|
|
45
|
+
|
|
46
|
+
`readGeoLocation()` positively projects only `provider_setup` from schema-v1
|
|
47
|
+
`/api/account/admin/settings`. Catalog, SMS, unknown fields and raw credentials
|
|
48
|
+
are discarded. Form state is imperative, outside Query/Mutation caches.
|
|
49
|
+
|
|
50
|
+
`buildGeoPayload(action, configuration_revision, draft, keyEdit)` sends exactly
|
|
51
|
+
`{action,topic:'geoip',providers:{expected_revision,geoip}}`. The revision is sent
|
|
52
|
+
even when null. The five static keys are primary, fallback, additional, Mojo URL
|
|
53
|
+
and sync. Untouched/blank replacement omits credentials; nonempty replacement
|
|
54
|
+
adds `GEOIP_API_KEY_MOJO`; confirmed removal adds `clear_api_key:true`.
|
|
55
|
+
Additional providers are unique and ordered, at most 16. Identifiers match
|
|
56
|
+
`^[a-z][a-z0-9_-]{0,31}$`. HTTPS origins permit no credentials, path, query,
|
|
57
|
+
fragment or alternate port (443 allowed); keys are at most 4096 characters.
|
|
58
|
+
|
|
59
|
+
Secrets stay only in the password DOM and short-lived request objects, whose
|
|
60
|
+
raw key is deleted in `finally`. They never enter React/Query/Mutation state,
|
|
61
|
+
URLs, storage, logs or mock observations. Editing retains the element and candidate;
|
|
62
|
+
Save attempts, explicit discard, fresh-auth refusal, session change and unmount
|
|
63
|
+
clear it. A stable ref callback wipes the password element before detachment;
|
|
64
|
+
removing the DOM node alone is insufficient. Writes are single-flight. A 440 invokes fresh authentication without
|
|
65
|
+
replaying; Refresh and a deliberate retry are required. Stale revisions
|
|
66
|
+
(400 “changed; reload before publishing”, or 409), unreadable state and uncertain
|
|
67
|
+
saves require authoritative Refresh before another write.
|
|
68
|
+
|
|
69
|
+
Test verifies **only MojoVerify credentials**, regardless of primary. It requires
|
|
70
|
+
the five delegated descriptor keys but not Fleet bucket/object/KMS/restart
|
|
71
|
+
availability; Save requires the publishing plane. Sharing requires the remote
|
|
72
|
+
key's `geoip_sync` permission. The current backend also verifies Mojo on every
|
|
73
|
+
Save except explicit removal: selecting another primary does not enable an
|
|
74
|
+
independent no-Mojo setup. This page does not invent provider-chain testing,
|
|
75
|
+
other-provider key editing, provisioning, revocation or an Apply endpoint.
|
|
76
|
+
|
|
77
|
+
A draft-key test does not rewrite stored verification. The backend's stored-test
|
|
78
|
+
predicate is no replacement plus the node-loaded origin and sync value; chain
|
|
79
|
+
changes do not affect it. Tests reread stored evidence without rebasing the
|
|
80
|
+
draft's revision. Revision changes or lost normalization authority block retry;
|
|
81
|
+
publishing-only unavailability does not block diagnostics. Loaded/desired origin
|
|
82
|
+
drift can prevent stored-key reuse until Fleet apply or explicit replacement.
|
|
83
|
+
|
|
84
|
+
Save rereads authoritative configuration. `published:true` means static
|
|
85
|
+
publication with restart pending; `unchanged:true` can still mean the database
|
|
86
|
+
credential changed. Neither result proves activation. Failed, stale and uncertain
|
|
87
|
+
requests keep errors actionable and never become successful saves.
|
|
88
|
+
|
|
89
|
+
## Verification
|
|
90
|
+
|
|
91
|
+
`geo-provider-mock.ts` supplies isolated safe observations (action, key action,
|
|
92
|
+
stored-test predicate, expected revision), with no submitted key retained.
|
|
93
|
+
Scenarios include no-key setup, real registry names, unknown/empty registry,
|
|
94
|
+
publishing unavailable, stale/fresh-auth/uncertain outcomes and loaded-origin drift.
|
|
95
|
+
Showcase's GeoLocation demo mounts the actual page. Run
|
|
96
|
+
`npm run verify:admin-geolocation` and `npm run verify:admin-geolocation-mounted`;
|
|
97
|
+
JSDOM behavior does not establish native focus or layout. Inspect both apps,
|
|
98
|
+
light/dark, desktop/390px, opened dialogs and error recovery per
|
|
99
|
+
[Admin presentation](admin-presentation.md). The scoped stylesheet is included
|
|
100
|
+
through `portal-mojo/styles.css`.
|