fix(ui): keep device details open across identity changes
This commit is contained in:
12 files changed
+324
-32
No files matched your search
@@ -27,6 +27,12 @@ filters or the list API's 1,000-row limit. It borrows the view and clones only t
|
||||
selected route. Command dispatch still checks the current session;
|
||||
a cached route cannot grant access to a disconnected agent.
|
||||
|
||||
`GET /api/v1/control/fleet/devices/{device_key}` returns one exact row from the
|
||||
complete view, or `404 fleet_device_not_found`. Details do not inherit list
|
||||
filters or limits. After Remember/Attach/Merge, the UI follows the saved ID
|
||||
returned by the mutation, not matching MAC/IP heuristics. It also refreshes the
|
||||
full saved identity independently of list results; Forget still closes details.
|
||||
|
||||
## External Writes
|
||||
|
||||
The revision belongs to the running CC process. If another process directly
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# Multi-CC Identity And Configuration
|
||||
|
||||
Status: proposal, 2026-10-12. This is not an implemented config format.
|
||||
|
||||
## Goal
|
||||
|
||||
One agent process can serve production and test CCs simultaneously. Collect
|
||||
locally once, forward to independent connections, and show human names alongside
|
||||
IDs. A failed CC must not block another or kill local terminal processes.
|
||||
|
||||
## Identity And Names
|
||||
|
||||
- **CC ID:** stable identity of a logical control plane, independent of URL,
|
||||
hostname, display name and WebSocket connection. Renaming or moving it must
|
||||
not require agents to enroll again.
|
||||
- **CC name:** configured on CC and advertised to authenticated agents. Start
|
||||
with config-based rename plus reload/restart; no rename API required initially.
|
||||
- **Agent instance ID/name:** local daemon identity and reported human name,
|
||||
shared across its connections. Not the enrollment identity of every CC.
|
||||
- **Enrollment:** each CC issues its own `agent_id` and token. Never send one
|
||||
CC's credentials to another. Existing per-CC agent nicknames override the
|
||||
reported name in that CC's UI; names never become routing/authentication keys.
|
||||
- **Connection alias:** local config key such as `cloud` or `test`, useful for
|
||||
CLI selection and pre-auth logs. It is not the remote CC ID or authoritative
|
||||
remote name.
|
||||
|
||||
CC ID is generated once and retained in SQLite metadata when not configured;
|
||||
an explicit config ID must agree with an existing stored identity, not silently
|
||||
replace it. New init-config output can include a generated ID. Agent instance
|
||||
ID belongs in its local config. Copying either deployment for an independent
|
||||
test instance requires a distinct identity; do not mint a new ID each startup.
|
||||
|
||||
## Proposed Config Shape
|
||||
|
||||
CC keeps existing operational settings and adds:
|
||||
|
||||
```toml
|
||||
[identity]
|
||||
id = "cc-<uuid>"
|
||||
name = "Cloud"
|
||||
```
|
||||
|
||||
Agent separates local identity/settings from per-CC enrollment:
|
||||
|
||||
```toml
|
||||
[identity]
|
||||
id = "node-<uuid>"
|
||||
name = "Router"
|
||||
|
||||
[local]
|
||||
pid_file = "/var/run/wakey-agent.pid"
|
||||
dhcp_leases_path = "/tmp/dhcp.leases"
|
||||
mac_name_cache_path = "/tmp/wakey_mac_names.json"
|
||||
observation_store_path = "/tmp/wakey_observations.json"
|
||||
|
||||
[sync]
|
||||
interval = "60s"
|
||||
hook_retention = "7d"
|
||||
|
||||
[reconnect]
|
||||
base = "1s"
|
||||
max = "30s"
|
||||
|
||||
[control_planes.cloud]
|
||||
server_url = "https://wakey.example.com"
|
||||
agent_id = "agent-<cloud-issued-id>"
|
||||
agent_token = "<cloud-token>"
|
||||
# expected_cc_id = "cc-<uuid>"
|
||||
|
||||
[control_planes.test]
|
||||
server_url = "http://127.0.0.1:8080"
|
||||
agent_id = "agent-<test-issued-id>"
|
||||
agent_token = "<test-token>"
|
||||
```
|
||||
|
||||
Existing `[terminal]` and `[telemetry]` remain daemon-wide. Reconnect defaults
|
||||
apply independently to each connection; avoid per-CC copies of every setting.
|
||||
The field names above are proposed, not a reason to rename unrelated CC settings.
|
||||
|
||||
Keep accepting the legacy single-CC root fields as one `default` connection,
|
||||
with a deprecation warning. Reject mixed legacy/new connection declarations
|
||||
rather than guessing. Init-config emits the new shape; `--from-config` preserves
|
||||
all connections and their credentials. Enrollment targets a connection alias:
|
||||
one connection can be implicit, multiple connections require explicit selection.
|
||||
|
||||
## Connection And Forwarding Rules
|
||||
|
||||
- Add negotiated, post-auth CC identity/name metadata. Current protocol has
|
||||
Hello/Auth but no explicit successful-auth response; sending credentials is
|
||||
not proof of authentication. Old clients must not receive unsupported enum
|
||||
variants unconditionally. Keep legacy single-CC compatibility explicit.
|
||||
- Forward the agent's configured name during hello; CC sends its own name/ID
|
||||
after auth. Show `name (id)` or a short ID in operator/debug views. Retain the
|
||||
local alias and last reported name for disconnected status, marking it offline.
|
||||
- Each CC gets its own worker, retry state, bounded outbound queue and request
|
||||
replies. Reject duplicate authenticated connections to the same CC ID; changed
|
||||
expected IDs require an explicit operator decision, not silent rebinding.
|
||||
- CC ID is identification, not a substitute for TLS/server authentication.
|
||||
- Share local inventory/hook storage and periodic collection. Fan out complete
|
||||
snapshots under each CC's enrollment ID; a slow connection cannot hold the
|
||||
collector hostage. This does not turn snapshots into a durable event journal.
|
||||
- Requests/replies and their trace contexts stay scoped to their connection.
|
||||
Never broadcast command results or terminal bytes to every CC.
|
||||
- Saved devices, nicknames, permissions and history remain independent on each
|
||||
CC. No CC-to-CC synchronization or automatic identity promotion.
|
||||
|
||||
## Terminal And Telemetry Scope
|
||||
|
||||
The agent owns PTYs and one daemon-wide session registry. Session limits apply
|
||||
globally, not once per connection. Initially, sessions belong to their creating
|
||||
CC: another CC cannot attach, resume or close them. Attachment ownership is
|
||||
enforced on the agent, not just inferred from one CC's registry. Disconnecting
|
||||
one CC preserves its PTYs until existing TTL/explicit-close/agent-exit policy acts.
|
||||
Reconnecting the same CC ID can reconcile its own sessions; removing a configured
|
||||
CC must have an explicit terminal cleanup policy before implementation ships.
|
||||
|
||||
Use local agent instance ID and CC ID for `service.instance.id`, addressing the
|
||||
current unspecified CC instance. Add configured display names as resource
|
||||
metadata, not a replacement for stable IDs. Grafana presentation/label mapping
|
||||
needs validation; do not promise names will automatically become `instance`.
|
||||
CPU/RSS/host metrics describe one local process and must not be duplicated per CC.
|
||||
Document whether snapshot counters count collections or per-CC sends before fan-out.
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Stable CC/local-agent identities, names, telemetry metadata and config adapters.
|
||||
2. Negotiated authenticated metadata exchange; keep the one-CC path working.
|
||||
3. Shared local managers plus isolated connection workers and snapshot fan-out.
|
||||
4. Global terminal ownership checks and multi-CC operator/debug status.
|
||||
|
||||
Acceptance tests must cover config/enrollment isolation, names changing without
|
||||
IDs changing, one CC failing while another proceeds, shared collection without
|
||||
cross-CC replies, duplicate CC identity rejection, and cross-CC terminal access
|
||||
rejection. Use two test CCs and one agent before deploying this on the router.
|
||||
+16
-3
@@ -29,7 +29,7 @@ live browser recovery and shutdown behavior are not established by those checks.
|
||||
- [ ] Check and complete editing of remembered-device notes.
|
||||
- [x] Replace generic unnamed-row labels with useful fallbacks. Prefer saved name, hostname, then `Device: <mac>`, then `Device: <ip>`; retain a generic label only when neither address exists. Presence stays in the badge, not the name. Inactive IPs can label IP-only rows without becoming active IPs or wake routes. Unknown-only fleet groups remain unknown.
|
||||
- [ ] Review frontend visibility defaults. Keep an explicit all-rows option; do not hide evidence just to fix poor labels. The user deliberately enabled all-row querying.
|
||||
- [ ] Keep device details current after identity mutations change the grouping key or active list filters exclude the result. Follow the explicit identity returned by create/attach/merge and load details independently of list filters; do not guess identity from a shared recyclable IP or retain stale mutation controls. Review detachment/split behavior separately, since a former group can become multiple rows.
|
||||
- [x] Keep device details current after identity mutations change the grouping key or list filters exclude the result. Exact fleet details and full saved-identity reads follow the mutation's explicit ID; no shared-IP matching or stale-control fallback. Remember/Attach/Rename verified with mocked browser flows on desktop, Remember/Rename on mobile; backend tests cover filtering, old-key rejection and lookup beyond the list cap. Forget deliberately closes; detaching stays on the saved identity instead of guessing among split observations.
|
||||
|
||||
Identifier detachment already exists in the current frontend source; it is **not**
|
||||
the requested whole-device removal. No automatic identity promotion or deletion
|
||||
@@ -59,8 +59,10 @@ or current fleet size. Metric labels should remain bounded; no per-device labels
|
||||
- [x] Add cumulative process CPU time (counter) and current open-FD count (gauge) through the same Linux reader and OS-independent instruments. Exclude child processes, omit failed samples, and document one-core CPU-rate interpretation and sampling FD overhead. Extend collector/reader coverage rather than adding timing-based tests; live validation pending.
|
||||
- [ ] Add start-time/version metadata if needed. Keep version as resource metadata rather than repeating it on every sample.
|
||||
- [x] Add total/available host memory and 1/5/15-minute load gauges through the optional Linux reader, with byte conversion owned by procfs and fixed load-period labels. Omit unavailable MemAvailable instead of substituting free memory; document kernel views versus container limits. Collector/reader coverage included; live validation pending.
|
||||
- [ ] Complete a small host summary with hostname, OS/kernel and CPU count, plus protected UI presentation of collected measurements. Sources must tolerate missing Linux files; not every agent host is a router.
|
||||
- [ ] Expose changing measurements through OTLP on a modest sampling interval; show selected process/host details on Fleet Health and agent details. Keep host information behind the protected operator interface, not public health endpoints. Avoid expensive reads on every request and unbounded labels.
|
||||
- [ ] Complete a small host summary with hostname, OS/kernel and CPU count. Sources must tolerate missing Linux files; not every agent host is a router.
|
||||
- [ ] Display CC and agent process/host information in Fleet Health and agent details: version/name, process/host uptime, RSS, CPU time/usage, open FDs, available/total host memory and load where supported. OTLP export is not the UI's data source; the UI must work without a collector.
|
||||
- [ ] Add typed agent status snapshots over the existing authenticated WebSocket, on a modest timer and when requested. Reuse Linux collection rather than introducing separate parsers; CC retains each agent's latest sample with received time and connection status. Show stale/disconnected or unavailable values explicitly, never as zero/current. Keep routine samples in memory initially, not an unbounded DB history.
|
||||
- [ ] Expose the CC's own sampled status and retained agent summaries through protected control APIs. Keep `/healthz` unchanged and do not expose environment variables, credentials or command lines. CPU percentages require successive cumulative CPU/time samples; do not label raw CPU seconds as utilization. No expensive host reads on every UI request.
|
||||
- [ ] Keep `/healthz` as cheap liveness; consider a separate bounded `/readyz` database-access check for CC. Collector outages and disconnected fleet agents must not make a functioning CC unready.
|
||||
- [ ] Add application resource gauges where useful: DB pool usage, connected agents, pending commands, and active terminal sessions. Check existing instruments first to avoid duplicates; distinguish pool pressure from host/process pressure.
|
||||
|
||||
@@ -85,6 +87,17 @@ event hooks; [ubus](https://openwrt.org/docs/techref/ubus) is local IPC/RPC, and
|
||||
None is a Kafka-style retained observation history. Verify what the vendor
|
||||
firmware actually exposes before making any of them an agent requirement.
|
||||
|
||||
## Origin Authentication
|
||||
|
||||
- [ ] Replace Caddy's nonempty `Cf-Access-Jwt-Assertion` matcher: header presence does not authenticate a request that reaches the origin directly. The `lb.ldlda.com` probe reached Cloudflare and redirected to Access login, so a direct-origin bypass was not reproduced; the matcher remains insufficient.
|
||||
- [ ] Add a CC-owned, loopback-accessible `/internal/auth/verify` endpoint for Caddy `forward_auth`. Validate the Access JWT signature, permitted algorithm, exact team issuer, protected Wakey application AUD and expiry using a standard JWT library. Cache signing keys with bounded fetches/timeouts and rate-limited refreshes; arbitrary token key IDs must not trigger unlimited upstream requests. Return 204 for valid credentials, 401 for missing/invalid credentials, and 503 when verification cannot be completed safely. Never log tokens.
|
||||
- [ ] Apply `forward_auth` to `/`, `/ui*` and `/api/v1/control/*`, including operator WebSocket handshakes. Keep health, agent enrollment/WS/terminal relay and ACME challenge paths outside Access verification; agent endpoints retain their own authentication. Do not accept the bypass application's AUD as an operator credential or expose the internal verifier through public Caddy routes. Keep CC bound to loopback: Caddy owns enforcement, so direct CC access would bypass it.
|
||||
- [ ] Test forged/expired/wrong-audience tokens, signing-key rotation and unavailable key retrieval, protected HTTP/WS rejection, and unaffected public agent routes before deployment. Validate the Caddy configuration and test a real direct-origin connection as well as the Cloudflare-facing hostname. Authentication failures must never fall back to header-only acceptance.
|
||||
|
||||
## Multi-CC Planning
|
||||
|
||||
- [ ] Consolidate stable CC/local-agent IDs, forwarded display names, per-CC enrollments, and agent config restructuring. See [multi-CC proposal](multi-cc-plan.md). Identity/name exchange and terminal ownership must be defined before adding connection workers; no CC-to-CC state synchronization is planned.
|
||||
|
||||
## Validation And Reliability
|
||||
|
||||
- [x] Bound fleet-cache rebuilds to three attempts under sustained mutations; return retryable HTTP 503 `fleet_busy` rather than silently serving stale wake routes. Deterministic limit/recovery tests included.
|
||||
|
||||
Reference in new issue
Block a user