Commit Graph
13 Commits
Author SHA1 Message Date
Juan DenisandClaude Fable 5 e93faac226 feat(servers): store the agent's self-reported footprint dirs
The agent (serverkit-agent bbaf4e2) now sends install_dir and
config_dir in system_info. Panel side:

- servers.agent_install_dir + agent_config_dir columns (migration 047,
  idempotent guard like its siblings) with the same coalesce contract
  as the other system_info fields — a transient payload gap never
  wipes a stored value
- exposed in Server.to_dict and /servers/available; TargetPicker
  passes them on the agent target
- the File Manager rail prefers the reported config dir over the
  installer convention (backslash-normalized for Windows agents;
  agent-logs link derives <config_dir>/logs)
- proving test: full system_info -> DB -> available round trip +
  coalesce survival

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 22:36:34 -04:00
Juan DenisandClaude Opus 4.8 f47d0e1428 feat: Phase 1 foundation — status aggregator, API scopes, onboarding, template magic vars
Four foundational enhancements toward the deploy-code developer experience:

- Container status aggregator (C7): new ContainerStatusService applies a
  deterministic priority hierarchy (degraded > restarting > running:unhealthy >
  starting > running:healthy > exited > unknown) over an app's containers,
  cached with a short TTL. Exposed at /api/v1/status and pushed over a new
  container_status Socket.IO channel; Applications page now renders an
  aggregated ContainerStatusPill with sub-status tooltip.

- API token scopes + sensitive data masking (C10): require_scope() decorator
  enforces fine-grained scopes for API-key requests (pass-through for JWT),
  a canonical SCOPES catalog at /api/v1/api-keys/scopes, and a reusable
  sensitive_data_filter (mask_sensitive) honoring secrets:read. Frontend gains
  a grouped ApiKeyScopesModal picker and per-key scope pills.

- Server onboarding state machine (C8): new onboarding lifecycle
  (pending -> validating -> installing_prerequisites -> installing_docker ->
  pairing_agent -> ready/failed) with ServerOnboardingService, a
  server_onboarding_logs table, servers.onboarding_* columns, REST endpoints,
  and an OnboardingWizard surfaced on the server detail overview.

- Declarative template catalog (C5): documented catalog schema and
  magic-variable resolution (SERVICE_PASSWORD_/USER_/FQDN_/URL_/BASE64_*)
  layered onto the existing YAML template loader without changing behavior for
  templates that don't use them; GET /api/v1/templates/catalog/schema.

Migration 029 adds the onboarding table + columns. 60 new tests pass; frontend
builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 18:23:47 -04:00
Juan DenisandClaude Opus 4.8 299a004cdf feat(workspaces): workspace_id scoping foundation + safe backfill (#33)
The cross-cutting backbone the rest of the #33 agency-scale milestone needs,
landed as a contained, narrow-only foundation rather than a 36-route rewrite.

- Nullable workspace_id FK on applications + servers (Alembic 015 off 014) with
  an idempotent raw-SQL backfill: create a Default workspace, attach every
  workspace-less row, and make every existing user a member so nobody loses
  visibility once scoping is activated. Also creates the workspace_id indexes
  that upgraded installs would otherwise lack.
- Centralized scope helper in WorkspaceService: ensure_default_workspace,
  resolve_workspace_id (validates a requested workspace; membership-checked with
  admin bypass; rejects deactivated accounts on the workspace path), and
  scope_query.
- Opt-in + narrow-only: a request filters by workspace only when it carries an
  X-Workspace-Id header / ?workspace_id=. scope_query INTERSECTS the workspace
  filter with the existing per-user ownership filter, so it can only narrow
  visibility, never broaden it — with no context the behavior is byte-identical
  to before (admin->all, owner->own, servers global). This closes the footgun
  where the backfill would otherwise let a non-admin enumerate all resources via
  the Default workspace.
- Wired into apps.py + servers.py (list + create): replaces the duplicated
  ownership branch and stamps new resources with the active-or-default workspace.

Deferred to follow-on slices: broad route adoption, per-site ACL, the
workspace<->User role reconciliation, server-side branding, and a frontend
workspace selector.

Verified: 10 new pytest tests (scope-narrowing, a can't-broaden-access
regression, deactivated-user gate, raw-SQL backfill against real SQLite +
idempotency), full suite 120 passed, a real end-to-end alembic upgrade to 015 on
a fresh DB (columns + indexes), and a 2-lens adversarial review (3 findings, all
fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 03:36:24 -04:00
Juan DenisandClaude Opus 4.8 749152c09e fix(agent): fail-closed auth + heartbeat reaper reconnect race
Two defects in the agent control plane found during the deep-dive review.

1. verify_agent_auth() failed OPEN on a missing secret (critical). The HMAC
   check was wrapped in `if api_secret:`, so when get_api_secret() returned
   None (NULL ciphertext, or a decryption error it swallows) the signature
   block was skipped and execution fell through to "authenticated" — reducing
   agent auth to agent_id + api_key_prefix, both non-secret observable values.
   Now mandatory: no decryptable secret => log + track_auth_attempt(False) =>
   return None. Also added Server.get_pending_api_secret() and select the
   secret matching whichever prefix matched, so signatures actually verify
   during a key-rotation window instead of hitting the old bypass.

2. Heartbeat reaper evicted freshly-reconnected agents (TOCTOU race).
   _check_heartbeats snapshotted dead server_ids under the lock then released
   it; _handle_agent_timeout popped unconditionally. An agent that reconnected
   in that window (new ConnectedAgent, new socket_id) got clobbered and flipped
   offline right after reconnecting. The snapshot now carries socket_id, and
   _handle_agent_timeout re-validates under the lock (same socket AND still
   stale) before evicting. Extracted HEARTBEAT_TIMEOUT so the scan and the
   re-check can't drift; added the missing rollback on error.

Adds tests/test_agent_registry_security.py (5 tests). Full suite: 80 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 14:38:30 -04:00
Juan DenisandClaude Opus 4.7 6e5bccda01 Permission expansion + Go probe + persisted system info fallback
Three wins exposed by the redeployed panel showing real data:

1. Permission expansion now covers every namespace, not just docker
   and system. Extend _expand_permission_scope so any action with a
   read-style verb (list, list_installed, list_units, status, info,
   inspect, search, logs, stats, available, current) accepts a
   `<ns>:read` or `<ns>:<sub>:read` candidate. Mutating verbs add a
   `<ns>:write` candidate. This unblocks docker:image:list,
   system:info, packages:list_installed, systemd:list_units, and the
   pyenv read paths for users on read-style profiles.

2. Go runtime probe was using `--version` (a flag), but Go takes
   `version` as a subcommand. The agent's runtime detector tries
   each form and falls back gracefully; Go is now correctly
   identified by `go version` and the panel shows a real version
   string instead of the "go flag provided but not defined" error
   text.

3. SystemInfo gains Platform + CPUModel fields (pushed alongside
   the existing hostname/cores/memory/disk). OverviewTab now reads
   from the persisted server row first and falls back to the
   synchronous getRemoteSystemInfo response — so CPU and memory
   render even when the system:info action is permission-denied or
   the agent is offline.

   metrics.GetSystemInfo also gets a runtime.NumCPU() fallback so
   CPU cores never reports 0 on Windows hosts where gopsutil's WMI
   queries flake.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 06:34:20 -04:00
Juan DenisandClaude Opus 4.7 d528d27296 Bypass per-server ACL for control-plane actions; add diagnostic logs
The Refresh button on the System Status card was returning 403
because the per-server permissions allowlist (e.g. 'docker_manager'
profile) doesn't include 'agent:recapabilities' and the scope
expander has no rule for the agent: namespace. The action is purely
panel-driven discovery — it doesn't mutate the host — so it
shouldn't be subject to the same ACL as install/exec actions.

- Add Server.CONTROL_PLANE_ACTIONS bypass set: agent:recapabilities,
  agent:update. has_permission() returns True for these unconditionally;
  JWT auth at the API layer is what gates access.
- /poll endpoint now logs the keys it received in metrics / system_info
  / capabilities so we can debug "panel says N/A" failure modes from
  the panel logs without instrumenting the agent.
- Agent's sendCapabilities now logs at INFO with sudo mode + capability
  count + runtime count so the agent.log shows whether the periodic
  resend is firing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 06:17:31 -04:00
Juan DenisandClaude Opus 4.7 32bf2b8081 Cache agent capabilities to DB and surface them in System Status card
The agent reports a rich capability payload (sudo mode, runtime
managers, detected runtimes, allowed paths, systemd JSON support)
that the panel was discarding once the WebSocket disconnected. The
Overview tab also gave no visibility into what the agent had
discovered, which left users guessing why a feature tab was missing
or a Docker call returned empty.

Backend:
- Server model gains seven nullable columns: cached_capabilities,
  cached_runtimes, cached_runtime_managers, cached_allowed_paths,
  cached_sudo, cached_systemd_json, capabilities_at. Migration 005
  is idempotent (checks existing columns before adding) so it's safe
  on already-bootstrapped databases.
- agent_registry.update_capabilities() now persists a snapshot to the
  Server row whenever the agent reports state. Wrapped in try/except
  so a DB hiccup doesn't drop the in-memory update.
- /servers/<id> falls back to the cached snapshot when the agent is
  offline and exposes capabilities_stale + capabilities_at so the UI
  can badge stale data.

Frontend:
- New SystemStatusCard renders sudo mode, package manager, runtime
  managers, detected runtime versions, capability map (as colored
  chips), and file-access roots. "Stale" badge + relative timestamp
  + Refresh button (calls agent:recapabilities).
- DockerTab now distinguishes "agent online but no docker daemon"
  from "no containers found", with concrete remediation hints. Load
  errors surface as a banner instead of being swallowed to console.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 05:16:42 -04:00
Juan Denis 8ea0586d88 Agent key migration, DB fix, UI tabs/page cleanup
Add robust credential handling and migration for agent encryption keys: surface LoadError on startup, log clear guidance when creds fail to decrypt, try legacy (pre-1.6.14) machine key fallback and transparently re-encrypt under the new host-stable key. Fix machine-key derivation (make host-stable instead of user-stable) and encapsulate decryption/migration helpers.

Fix SQLite autoincrement issue: change ServerMetrics.id from BigInteger to Integer so SQLite will autoincrement rows and metrics are recorded.

Ensure background heartbeat checker can touch the DB by capturing the Flask app in AgentRegistry and entering an app_context in the heartbeat thread (with a test-friendly fallback for when no app is available).

Frontend UI: simplify Tabs component classes and update pages to use a unified .page-container wrapper; replace many manual tab button implementations with Tabs/TabsList/TabsTrigger and add improved tab styles (disabled/focus states). Misc: add docs/PLAN_AGENT_FLEET.md to .gitignore.
2026-05-01 00:27:37 -04:00
Juan Denis 313f1a8f6b Remote Docker remove, normalize API & permissions
Add end-to-end support for removing remote Docker networks and volumes and normalize remote API responses. Server: new Flask endpoints to DELETE remote networks/volumes and helpers to compute external base/ws URLs and v1 API paths. Agent: register new handler docker:network:remove and Client.RemoveNetwork implementation. Backend service: RemoteDockerService.remove_network uses agent registry for remote servers and falls back to local DockerService. Models: expand Server permission logic to map agent actions to profile/legacy scopes (DOCKER_READ_ACTIONS, scope expansion and wildcard matching) and add unit tests covering permission mappings. Frontend: handle agent-backed response shapes (unwrapRemoteData, normalizeListResponse), call new removeRemoteNetwork/removeRemoteVolume API methods, update download/install URLs to /api/v1, and tweak several UI texts. Install scripts: change config/log directories, pass --config when registering/starting agent, and update systemd/service/unit and Windows service install paths. README: small wording updates about fleet/remote deployment and monitoring.
2026-04-29 00:11:46 -04:00
Juan Denis bd8d2e09f6 Add security, logging, DB hardening & SCSS refactor
Broad refactor and hardening across backend and frontend:

- Standardize API error responses by removing legacy `success` flags and returning concise error payloads.
- Replace inline admin decorators with centralized middleware import (admin_required) in DB and Docker APIs.
- DatabaseService: add safer MySQL execution helpers and secure user-creation flow (hex-encoding, env-pwd usage) and improve pg drop to validate identifiers.
- Models: add eager-loading (subquery) for relationships and add indexes to frequently queried columns to reduce N+1 and improve performance.
- DockerService: add structured logging and error messages for better observability when Docker commands fail.
- Config: tighten ProductionConfig cookie settings (secure, httponly, samesite).
- Frontend: migrate many .less styles to .scss, add new security UI components and API client modules, update package metadata accordingly.
- Misc: add APP_IMPROVEMENTS.md to .gitignore.

These changes improve security, reliability, observability, and frontend styling consistency.
2026-03-23 22:00:36 -04:00
Juan Denis d9dfc4d611 Add agent fleet management, discovery & updates
Introduce full agent fleet management and discovery features across backend, agent, and frontend. Backend: add AgentVersion and AgentRollout models, extend Server/ServerGroup/ServerCommand/AgentSession with fleet, queue and latency fields; implement AgentFleetService (bulk upgrades, staged rollouts, queued commands, retries, diagnostics) and DiscoveryService (UDP broadcast scan); add new servers API endpoints for fleet operations. Agent: add UDP discovery responder, agent:update handler to trigger updater, and wire updater.UpdateTo to install specific versions; extend protocol messages with discovery and agent:update types. Registry: deliver queued commands on agent connect and track heartbeat latency. Frontend: add AgentFleet page and route, plus API client updates. Also add SECURITY_AUDIT.md to .gitignore. These changes enable discovery, staged rollouts, offline command queuing with retries, and fleet health/diagnostics.
2026-03-23 01:46:41 -04:00
Juan Denis bca7e29b57 Implement security features: key rotation, IP allowlist, anomaly detection
Adds API key rotation, IP allowlisting, anomaly detection, and replay protection to the ServerKit agent system. Backend changes include new endpoints, models, and services for security alerts, nonce tracking, and anomaly detection, as well as updates to the server model and agent authentication logic. The agent now supports credential rotation and replay protection via nonces. Frontend updates add UI and API methods for managing allowed IPs, viewing security alerts, and rotating API keys. Documentation and environment setup instructions are included.
2026-01-22 18:11:22 -05:00
Juan Denis 009fb12435 Add multi-server management and agent gateway
Introduces backend models, API endpoints, and services for remote server and agent management, including WebSocket agent gateway, server CRUD, metrics, command history, and remote Docker operations. Updates frontend to support server listing, detail pages, and related UI components and styles.
2026-01-21 22:47:42 -05:00