Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0571360
UI-SP3 spec — Infrastructure & Governance (Executors / Audit / Quota …
l17728 May 19, 2026
54ac39b
UI-SP3 implementation plan — 16 bite-sized TDD tasks across M1-M4
l17728 May 19, 2026
a242815
UI-SP3 plan: fix 2 BLOCKERs + 2 IMPORTANT + 1 contract-sync from pre-…
l17728 May 19, 2026
cc695b3
UI-SP3 M1: openapi (listExecutors+extend ExecutorRead+ExecutorListRes…
l17728 May 19, 2026
7f1afed
UI-SP3 M1: audit-log search endpoint (tenant-scoped, cursor-paginated)
l17728 May 19, 2026
09c61f1
UI-SP3 M1: GET /executors browser-facing list (tenant filter; system_…
l17728 May 19, 2026
680c10d
UI-SP3 M1 fix: test_executors_list reset PKs between tests (avoid dup…
l17728 May 19, 2026
d45ab40
UI-SP3 M2: SP3 DTO types (Executor / Audit / HealthActive)
l17728 May 19, 2026
9a63df1
UI-SP3 M2: formatDateTime + formatTimeAgo utils
l17728 May 19, 2026
4c67d5e
UI-SP3 M2: 3 live composables (executors / audit / health) on the use…
l17728 May 19, 2026
7dbb7ff
UI-SP3 M3: HealthPill + ExecutorRow
l17728 May 19, 2026
28e880a
UI-SP3 M3: AuditRow
l17728 May 19, 2026
b9c52e8
UI-SP3 M3: QuotaCard
l17728 May 19, 2026
c263ba5
UI-SP3 M4: i18n — nav + executors + audit + quotaPage + settings (en/…
l17728 May 19, 2026
c5d9141
UI-SP3 M4: Executors page (host-grouped, status filter)
l17728 May 20, 2026
62451be
UI-SP3 M4: Audit page (filters + cursor-paginated)
l17728 May 20, 2026
0f3594a
UI-SP3 M4: Quota + Settings pages + router/nav additions
l17728 May 20, 2026
ed79577
UI-SP3 M4: operator docs for the infrastructure & governance pages
l17728 May 20, 2026
76db12b
UI-SP3: final-review HIGH fix + MEDIUM hardening (Audit pagination)
l17728 May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@

# ========== Tasks ==========
/tasks:
get:

Check warning on line 169 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: List tasks
operationId: listTasks
Expand Down Expand Up @@ -194,7 +194,7 @@
schema: {$ref: '#/components/schemas/RbacDenied'}
'429': {$ref: '#/components/responses/RateLimited'}

post:

Check warning on line 197 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Create download task
operationId: createTask
Expand Down Expand Up @@ -296,7 +296,7 @@
parameters:
- $ref: '#/components/parameters/TaskId'

get:

Check warning on line 299 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Get task by ID
operationId: getTask
Expand All @@ -315,7 +315,7 @@
'404':
description: Task not found or cross-tenant ID (existence not leaked)

patch:

Check warning on line 318 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Update task (priority only)
operationId: updateTask
Expand Down Expand Up @@ -368,7 +368,7 @@
/tasks/{taskId}/cancel:
parameters:
- $ref: '#/components/parameters/TaskId'
post:

Check warning on line 371 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Cancel task (async)
operationId: cancelTask
Expand Down Expand Up @@ -402,7 +402,7 @@
/tasks/{taskId}/retry:
parameters:
- $ref: '#/components/parameters/TaskId'
post:

Check warning on line 405 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Retry failed subtasks
operationId: retrySubtasks
Expand Down Expand Up @@ -448,7 +448,7 @@
/tasks/{taskId}/upgrade:
parameters:
- $ref: '#/components/parameters/TaskId'
post:

Check warning on line 451 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Upgrade to new revision (incremental)
operationId: upgradeTask
Expand All @@ -472,7 +472,7 @@
/tasks/{taskId}/subtasks:
parameters:
- $ref: '#/components/parameters/TaskId'
get:

Check warning on line 475 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [subtasks]
summary: List subtasks of a task
operationId: listSubtasks
Expand All @@ -495,7 +495,7 @@
/tasks/{taskId}/source-allocation:
parameters:
- $ref: '#/components/parameters/TaskId'
get:

Check warning on line 498 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: View source allocation (multi-source visualization)
operationId: getSourceAllocation
Expand All @@ -509,7 +509,7 @@
/tasks/{taskId}/events:
parameters:
- $ref: '#/components/parameters/TaskId'
get:

Check warning on line 512 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI lint

operation-description Operation "description" must be present and non-empty string.
tags: [tasks]
summary: Task event log
operationId: getTaskEvents
Expand Down Expand Up @@ -644,6 +644,24 @@
controller_state: { type: string, enum: [standby] }

# ========== Executors ==========
/executors:
get:
tags: [executors]
summary: List executors visible to the principal (UI-SP3)
operationId: listExecutors
parameters:
- in: query
name: status
schema:
type: string
enum: [joining, healthy, degraded, suspect, faulty]
responses:
'200':
description: Executors visible to the principal
content:
application/json:
schema: {$ref: '#/components/schemas/ExecutorListResponse'}

/executors/register:
post:
tags: [executors]
Expand Down Expand Up @@ -1289,10 +1307,15 @@
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items: {$ref: '#/components/schemas/AuditEntry'}
next_cursor:
type: string
nullable: true
description: Opaque cursor for the next page; null when no more rows.

# ========== Storage Backends ==========
/storage-backends:
Expand Down Expand Up @@ -2499,7 +2522,11 @@
ExecutorRead:
type: object
required: [id, status, health_score, epoch]
description: Returned by /join and /heartbeat to confirm registration.
description: |
Returned by /join, /heartbeat, and the browser-facing /executors list.
UI-SP3 extends this with optional/nullable fields needed by the
Executors page; pre-existing consumers (mTLS /join, /heartbeat) are
unaffected because all new fields are nullable / optional.
properties:
id:
type: string
Expand All @@ -2515,6 +2542,42 @@
type: integer
minimum: 0
description: Current epoch (fence token). Increments on every /join.
host_id:
type: string
nullable: true
description: Physical host identifier (groups co-located executors).
tenant_id:
type: integer
format: int64
nullable: true
description: Owning tenant; null for shared-infra executors.
last_heartbeat_at:
type: string
format: date-time
nullable: true
nic_speed_gbps:
type: integer
nullable: true
disk_free_gb:
type: integer
format: int64
nullable: true
disk_total_gb:
type: integer
format: int64
nullable: true
created_at:
type: string
format: date-time
nullable: true

ExecutorListResponse:
type: object
required: [items]
properties:
items:
type: array
items: {$ref: '#/components/schemas/ExecutorRead'}

StorageConfig:
type: object
Expand Down
23 changes: 23 additions & 0 deletions docs/operator/web-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,26 @@ actions are not exposed (no endpoints); the file table uses a height-capped
`el-table` (true `el-table-v2` windowing is a documented follow-up); the event
log reads existing `audit_log` rows only; real-time push (SSE/WS) arrives in
UI-SP5 with no view changes.

## UI-SP3 — Infrastructure & Governance

Four new pages backed by **two additive read-only endpoints** (zero migration):

- `GET /api/v1/audit/log` — tenant-scoped audit search (filters: action prefix,
actor_user_id, from/to time range; cursor-paginated; matches the on-disk
`searchAuditLog` contract).
- `GET /api/v1/executors` — browser-facing executor list (own-tenant +
shared-infra view; `system_admin` sees all). Lives in a new module
(`src/dlw/api/executors_read.py`) because the existing `api/executors.py` is
mTLS-only per `tools/lint_invariants.py:check_no_bearer_on_executor_routes`.

Pages: **/executors** (host-grouped list + status filter), **/audit** (filterable +
cursor pagination), **/quota** (3 cards over the existing `/quota/current`),
**/settings** (frontend-only: principal info from `stores/session.ts`,
theme/locale from `stores/ui.ts`, controller state from `/health/active`).

**Known deferrals** (intentional, no backend support today): executor
drain/restart, metrics history, heartbeat history; HF-token rotation;
license-policy CRUD; source-driver registration; maintenance mode;
`/quota/usage` (declared but no backing tables); ML forecast; chargeback PDF;
real-time audit tail (UI-SP5).
Loading
Loading