Skip to content

v2.3.0

Choose a tag to compare

@github-actions github-actions released this 12 Jun 07:52

Added

  • markCommandFailed() exit code helpersrc/utils/exitCode.ts centralizes process.exitCode = 1 signaling; adopted across snapshot, domain, maintain, update, audit, evidence, fix, init commands (P141)
  • isolatedKastellEnv Jest helpertests/helpers/isolatedKastellEnv.ts provides per-test KASTELL_DIR isolation with self-registering afterEach cleanup (P141)
  • Atomic *.tmp rename helpersatomicWriteFileSync, fsRetry, secureAppendFileSync for Windows-safe persistence with EPERM/EACCES retry + copy+unlink fallback (P140)
  • MCP startupDiagnostic helpersrc/mcp/startupDiagnostic.ts reports SDK + build identity at MCP boot (P141)
  • fileLock structured returnassessLockState returns owner PID hash + ownerHost="internal" for safe diagnostic surfacing; stale-lock reclaim now re-acquires the lock and runs fn() when removal succeeds (P141)
  • Plugin checks now run in parallel (cap=3, configurable via PLUGIN_AUDIT_PARALLELISM)
  • Aggregate timeout for plugin audit (PLUGIN_AUDIT_TOTAL_TIMEOUT_MS, default 120s)
  • safeToParallel: false opt-out in plugin manifest for plugins with intentional mutating checkCommands
  • File-mtime cache for getServers() and loadLatestAudit(ip) with network FS guard
  • MCP SDK round-trip test for serverCompare schema
  • Plugin tarball smoke test: MCP boot time measurement + empty dir detection

Changed

  • Breaking: Plugin SDK audit checks now require apiVersion: "2" and object-shaped checkCommand: { kind, cmd }. Manifest-level mutates and safeToParallel are removed; mutation intent is declared per check.
  • chunkConcurrent replaces p-limit in fleet.ts (p-limit dependency dropped)
  • serverCompare outputSchema uses discriminatedUnion (dış shape aynı kalır)
  • PluginRegistryEntry is now a discriminated union (loaded/error/disabled)
  • CI workflow: build artifact shared between jobs (~30s saving per run)
  • fileLock reclaim now re-acquires the lock and runs fn() when stale removal succeeds — previously only recorded the reclaim error and gave up (P141 Codex gap)
  • Explicit process.env[PROVIDER_TOKEN] overrides buffered/keychain tokens — prevents stale desktop keychain entries from breaking MCP/container calls (P141 Codex gap)
  • server_info health outputSchema — single-server fields (server, ip, mode, sshReachable, hostKeyMismatch, platformStatus, coolifyUrl, dokployUrl) now part of the schema; results/summary optional for per-server response (P141 Codex gap)

Release-time version bumps remain owned by /release minor; package.json and kastell-plugin/.claude-plugin/plugin.json must be bumped together.

Fixed

  • serverCompare detail mode returns flat checks array (was object — CQS-11 #1)
  • ssh-factories.ts setTimeout leak — worker force-exit (CQS-10 #1)
  • tests/helpers/fsMock.ts factory prevents Linux CI chmodSync mock omission
  • Explicit provider token environment variables now override buffered and keychain credentials, preventing stale desktop keychain entries from breaking MCP/container calls.
  • Single-server server_info health responses now validate against the registered MCP output schema.
  • Windows local state writes — Atomic *.tmp rename persistence now retries transient EPERM/EACCES failures and falls back to copy+unlink safely. Coverage: servers.json, audit history, evidence manifests (MANIFEST.json, SHA256SUMS), audit snapshots, fix history, regression baselines, and metric history. File-lock reclaim and security-log rotation also retry on transient EPERM/EACCES.
  • Status command error path exits 1 via markCommandFailed() — partial failures now propagate non-zero exit code matching other reliability contracts (P141)
  • Audit --json and --ci stdout parse-clean — no log pollution; reserved for a single JSON payload (P141)
  • Windows file-lock diagnostics for persistent EPERM/EACCES — hint-rich surface, replaces "transient" claim (P141)
  • printDiff regression path unconditionally marks process.exitCode = 1 (P141 /simplify)

Internal

  • 175 console.log triage + sweep (5 categories — 0 actionable)
  • 13 slow tests audit (P140 input)
  • createFsMock factory adopted across 32 test files
  • McpServerInternal named type (CQS-05)
  • serverCompare eslint-disable orphan cleanup (CQS-10 #2)

BREAKING

  • server_fix MCP input shape changed: dryRun: boolean removed, replaced by mode: 'dry-run' | 'live' on the apply action branch. CLI users unaffected (--dry-run flag unchanged). MCP consumers must update calls.
  • server_fix MCP output shape changed: non-apply actions (history, rollback, rollback-all, rollback-to) no longer carry a dryRun field. Previously the field held the action name as a string proxy to satisfy a misnamed discriminator; now responses are discriminated by action and dryRun: boolean appears only on apply responses where it semantically belongs.
  • server_secure firewall-status MCP output: rules is now z.array(z.object({port, proto, action, from})) (object array) instead of z.array(z.string()) (string array). SDK probe confirms: MCP SDK strips structuredContent on outputSchema mismatch. Hard-cut BREAKING. (F-020)

Fixed

  • server_secure action audit added as canonical name. secure-audit still accepted (deprecated, removal scheduled for v2.4) (F-011)
  • server_info status summary.running correctly counts running servers when either serverStatus (cloud provider) or platformStatus (Coolify/Dokploy) is "running". Previously only checked platformStatus, missing servers where the cloud reports running but the platform probe fails. (F-024)
  • server_guard status returns success: boolean and logTail: string[] (line array). (F-022)
  • server_logs monitor returns structured metrics.{cpu,mem,disk} objects (bytes for total/used, IEC binary) instead of validation-failing strings. CLI output unchanged. (F-019)
  • server_backup backup-list returns backupCount field (F-021)
  • kastell audit accepts --framework <cis-level1|cis-level2|pci-dss|hipaa> (parity with MCP) (F-016)
  • Keychain decrypt warnings now deduplicate into single line with provider list (CQS-07)
  • Audit --threshold and all early-return paths now correctly set process.exitCode = 1 via AuditError policy (F-015)
  • kastell add --skip-verify now respects --mode coolify|bare flag (F-002)
  • kastell lock --dry-run error message clarified (F-010)
  • kastell fix --dry-run requires --safe with clear error (F-012)
  • kastell fix --history shows informative empty state (F-014)
  • Bash completions synced with command registry (F-025)

Changed

  • MCP server tool registration refactored to iterate ALL_MCP_TOOLS (~340 lines removed)
  • Plugin audit checks now parallelize (max 4 per host) with AbortController-based aggregate timeout (CQS-06)
  • Fixture makeServerRecord helper extracted across 10 fixtures (~100 lines saved)
  • isWindows() helper extracts 6 inline platform checks

Added

  • Regression test for server_plugin list reading from the loaded plugin registry (fix landed in v2.2.0 P134c/d; test prevents future drift, F-018)
  • AuditError class for centralized audit error handling
  • chunkConcurrent helper for bounded parallel work
  • PluginSeverity / FixTier shared types
  • Safety Modes section in README
  • kastell provision alias for init

v2.3 Reliability Contracts

  • Immediate MCP durable registrationserver_provision returns as soon as the provider creates the server and Kastell durably persists the record. readiness.status may be pending; follow with server_info status or server_info health.
  • Verified CLI failures return non-zero — unsupported and failed CLI operations exit with 1; valid empty results and user cancellation exit with 0. Mixed --all failures exit with 1.
  • Parse-clean audit stdoutaudit --json and audit --ci reserve stdout for a single JSON payload.
  • Actionable Windows lock diagnostics — persistent EPERM/EACCES failures on Windows file-lock paths now surface hint-rich diagnostics.
  • MCP SDK round-trip coverageserver_manage add/remove/destroy outputSchemas now have full normalizeObjectSchema + safeParseAsync round-trip tests.

Security

  • Notify webhook SSRF hardening (HIGH-001) — Discord/Slack webhook connections now reject private/reserved IPv4 and IPv6 targets during the actual socket DNS lookup, pin connections to validated public answers, and disable redirects and environment proxies.
  • Reclassified P142 security follow-ups (reviewed 2026-06-12):
    • MEDIUMstarter CLI template skips the optional extra SSH hardening step. Platform cloud-init still configures firewall rules; bare mode also installs fail2ban and unattended-upgrades.
    • MEDIUM — SSH StrictHostKeyChecking=accept-new carries first-connection TOFU risk; strict host-key handling needs separate policies for interactive SSH and newly provisioned servers.
    • LOWdebugLog does not detect raw secret-shaped strings, though current core error-object call sites are collapsed to [object].
    • LOW / operational — CLI safe mode defaults off for trusted local operation; non-TTY automation can still benefit from safer defaults or warnings.
  • Removed from the security backlog — Ubuntu 24.04 provider pinning is a provisioning reliability policy, not a security vulnerability; the previously documented DigitalOcean SSH issue has dedicated cloud-init mitigations.