phase-2-3-inventory-operations #1

Merged
Luxxy merged 3 commits from phase-2-3-inventory-operations into main 2026-08-01 22:01:58 +00:00
Owner
No description provided.
Phase 2 completes the observation half of the platform: the agent collects a
full inventory, the panel reconciles it against what it believes, and node
health is computed from the heartbeat clock rather than read from a column
that lags.

Phase 3 adds the action half: the operation state machine end to end, an
append-only audit log, and the customer-facing instance views.

Backend
- repositories/instances: scoped reads, drift detection in both Rust and SQL
  with a test asserting the two agree, desired vs observed state as separate
  writes
- repositories/projects: memberships resolve a session into a ProjectScope;
  aggregates use subqueries so member and instance counts cannot inflate
  each other
- repositories/operations: rows are written before dispatch, idempotency is
  enforced by the partial unique index rather than check-then-write, and
  transitions carry their expected status into the WHERE clause so a lost
  race loses safely
- repositories/audit: insert-only, with no function that could attempt an
  UPDATE the trigger would refuse
- domain/audit: one builder, redaction covering passwords, tokens, private
  keys and cloud-init user data at any nesting depth
- domain/instances: power-action rules with reasons, consulted by both the
  API and the UI so an enabled button cannot produce a refusal
- services/operations: persist, audit, then dispatch; a dispatch failure
  fails the operation explicitly rather than leaving a spinner running
- api/customer: instances and dashboard, mounted; the customer DTOs have
  nowhere to put a node name, so the omission is enforced by the type

Agent
- commands/inventory: a report claims completeness only when every listing
  succeeded, which is what lets the panel distinguish "deleted" from "we
  could not see it"
- commands/instance: journal write precedes execution; a suppressed
  duplicate reports success because the work already happened
- connection/client: commands are executed rather than refused, and
  heartbeats now carry storage totals derived from the runtime's pools

752 tests passing, clippy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A single in-process broadcast channel that browser websockets subscribe to.
Every event is a hint that something changed; the durable answer stays in the
database, so a subscriber that falls behind is dropped and recovers rather
than being buffered without limit.

- services/events: authorisation happens at the subscriber, once per socket,
  because a publisher does not know who is listening. The project rides on the
  envelope rather than inside each variant, so a new event type cannot be
  added without its author deciding who may see it — an untagged event is
  operator-only by default
- api/websocket/customer and admin: origin-checked upgrades, bounded session
  lifetime so a revoked membership eventually takes effect, and a ping
  interval short enough to survive a proxy. Both are push-only; operator
  actions go through the HTTP API where they are authorised and audited
  individually
- api/agents/websocket: command acceptance, progress, results and
  out-of-band instance state changes now reach the operation service. A
  reported state change is resolved from the runtime name against the
  authenticated node rather than trusting the agent's instance id

771 tests passing, clippy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Phase 2 and 3 frontend: replace every phase placeholder with real pages
Some checks failed
Agent CI / Test the agent (pull_request) Failing after 3s
Security / Advisories, licences and sources (pull_request) Failing after 4s
Security / Frontend dependency audit (pull_request) Failing after 38s
Security / No secrets or placeholder credentials committed (pull_request) Failing after 3s
Agent CI / Against a real Incus daemon (pull_request) Has been skipped
Backend CI / Format, lint and test (pull_request) Failing after 2s
Backend CI / Migrations apply to an empty database (pull_request) Failing after 1s
Frontend CI / Typecheck, lint, test and build (pull_request) Failing after 1m34s
Agent CI / The workspace still compiles off Linux (pull_request) Has been cancelled
c6d72fcfcf
All six pages that showed a PhaseNotice now render live data.

Operator
- nodes: fleet list with status computed rather than read from the column, a
  create dialog that shows the registration token exactly once, and committed
  capacity that renders "Unknown" rather than 0% when a node has reported
  nothing
- instances: global list with a drift filter, where drift is recomputed
  client-side against the same rule the Rust side uses so the badge always
  matches the two values beside it
- operations: the work log, with a faster refetch than the other pages
  because an operator watching it during an incident is watching for movement
- overview: fleet health and committed capacity, labelled "committed" so
  nobody mistakes allocation for consumption

Customer
- instances: power actions with per-attempt idempotency keys, controls
  disabled rather than hidden so rows do not jump under the cursor, and no
  node, pool or runtime name anywhere — the DTO has nowhere to put them
- dashboard: one server-assembled request rather than four, with activity
  descriptions the server wrote for a customer

Also adds a useDebounced hook so search boxes issue one request rather than
one per keystroke.

Type-checks, lints and builds clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Luxxy merged commit a0c883a7c8 into main 2026-08-01 22:01:58 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Luxxy/beegoon!1
No description provided.