Watch
1
0
Fork
You've already forked RedFlag
0
Commit graph

471 commits

Author SHA1 Message Date
Fimeg
aee87c476d refactor: StatusBadge + SeverityBadge primitives — one way to render state
Extract the repeated cn('badge', getStatusColor/getSeverityColor) span
into primitives; port Updates, Agents, Docker, LiveOperations,
PackageDetail, AgentUpdatesEnhanced. Docker's local colour maps deleted —
its image-lifecycle statuses join the central getStatusColor map, and its
severity palette aligns with the app-wide one. AgentUpdatesEnhanced's
divergent local severity palette replaced by the shared map.
2026-06-11 08:05:58 -04:00
Fimeg
7269d25823 fix: route audit misclassified inlined AuthMiddleware — server refused boot
The compiler inlines middleware.AuthMiddleware across packages, renaming
its closure to "<caller>.AuthMiddleware.func1" — the package-qualified
match ("middleware.AuthMiddleware") never hit, every agent-JWT route
was flagged as naked, and AuditAndExit refused boot (122 container
restarts overnight). Classify on bare names, web before agent since
WebAuthMiddleware contains AuthMiddleware. Regression test now uses the
real production middleware instead of a same-package fake.
2026-06-11 07:59:13 -04:00
Fimeg
e4afe1f605 feat: helper trust-file self-validation (SEC-021)
The privileged executor no longer trusts its keyring, agent_id, or
replay-guard state by content alone. Before any read, each trust input
and its immediate parent must be root-owned, not group/other-writable,
and not a symlink — otherwise deny with EXIT_TRUST_PATH (26), fail
closed. This makes the gate self-defending: a packaging or installer
mistake that leaves a trust path writable can no longer be parlayed
into key injection, token rebinding, or replay-record clearing.

An agent_id file that exists but fails validation is a denial, never a
fall-through to the next location. The replay dir is validated after
ensure-exists so a pre-planted attacker-owned dir is refused, not
adopted.
2026-06-11 07:55:34 -04:00
Fimeg
6c3461cdf9 feat: rate limiter startup grace penalty (SEC-004)
The limiter is in-memory; a restart clears all counters, so an attacker
who can force one gets a fresh budget. For 60s after boot every limit
runs at half its configured budget (min 1), making a restart strictly
worse for the attacker while per-key limits keep reconnecting agents
comfortable. Restart semantics documented in OPERATIONS.md §6.
2026-06-11 04:29:50 -04:00
Fimeg
6ac937bfe5 refactor: unified backoff policy with failure classes (BUG-014)
classifyFailure is now the single source of truth for which polling
failures are terminal (ErrRefreshTokenInvalid, ErrMachineMismatch —
wrapped or bare) vs transient. delayForFailure picks the curve: flat
10-minute delay for terminal states awaiting operator intervention,
jittered exponential (calculateBackoff) for everything else. The
terminalBackoff bool is gone from the polling loop. Task file said
to delete itself when this landed — done.
2026-06-11 04:27:44 -04:00
Fimeg
1c4b363375 fix: registration token moves from install URL to X-Registration-Token header (SEC-002)
A token in the query string leaks to shell history, process lists, and
server access logs. The install endpoint now reads X-Registration-Token;
a query-string token is refused with guidance and is never echoed back
or logged. Server-built and web-UI install commands updated (curl -H,
irm -Headers).
2026-06-11 04:25:32 -04:00
Fimeg
0abff08a9d fix: clear selected_version on transition to installed
A version pinned at approval (selected_version) survived the install,
so the next scan compared against the stale pin and plain Approve became
a no-op after the first install. Clear the pin and its metadata source
marker inside the transition tx once the package reaches installed;
subsequent scans track latest available again.
2026-06-11 04:20:20 -04:00
Fimeg
d223c4608a fix: docker/winget VerifyHash fail closed — no silent skip when a hash is registered
Both previously logged hash_verification_skipped and returned nil when the
server had registered an expected hash the installer cannot verify. Now:
empty expected hash errors (consistent with dnf/apt), and a registered hash
without an implemented verifier blocks the install instead of pretending.
No behavior change today — the server only registers hashes for npm/pypi,
and handlers skip VerifyHash on empty hash — this closes the latent path.
2026-06-11 04:20:20 -04:00
Fimeg
c33b62489e feat: boot-time route audit — ETHOS #2 enforced structurally
Walk every Gin radix tree at startup, classify each route's handler chain
(agent JWT / web JWT / metrics token), and refuse to boot if any route
lacks auth and is not on the explicit public allowlist. Adding a path to
PublicPathSet is a reviewable act.

Covers param routes, nested groups, and per-method trees (tested).
2026-06-11 04:20:08 -04:00
Fimeg
71cf60b66c refactor: unify Docker container images + runtime state into single table 2026-06-11 02:28:24 -04:00
Fimeg
f07e4be94c fix: docker ports — replace 0.0.0.0 with * (standard docker ps notation) 2026-06-11 02:18:13 -04:00
Fimeg
175d3dfd90 refactor: port Docker page to SortableTable — unify two container tables
Container images table (per-agent-group) and runtime container state table
both use SortableTable with shared sort state. ~120 lines of raw table markup
removed. Stacks grid unchanged — card layout is the right shape for that.
2026-06-11 02:13:53 -04:00
Fimeg
5a284f1370 fix: show View Update link when package name is empty; cross-link Agent+Update in command history
ChatTimeline: when updateLink exists but packageName is falsy, show 'View Update'
instead of hiding the link entirely. Updates: clickable Agent/Update links in
command history rows (UI-AGENT-UPDATE-CROSSLINKING).
2026-06-11 02:01:44 -04:00
Fimeg
8811ec9100 refactor: centralize polling intervals — replace hardcoded ms with POLL.* constants
Every hook that polled had its own magic number. POLL tiers (LIVE/DASHBOARD/
DETAIL/OVERVIEW/STATIC/HEALTH) declared once in polling.ts, consumed everywhere.
Mechanical change, no behavior shift.
2026-06-11 02:01:44 -04:00
Fimeg
488cca2dd5 refactor: extract SortableTable primitive from Agents page
Column-driven table component under primitives/ — sortable headers, checkbox
selection, empty/loading states, pagination slot. Agents.tsx rewired as
proof-of-integration; ~220 lines of raw table markup collapsed into column
definitions + <SortableTable />. Same output, same behavior.
2026-06-11 02:01:43 -04:00
Fimeg
c71fc093db refactor: vulnerability → security advisory terminology
Rename CVE/vulnerability language to advisory/threat/fix across the stack:
- Dashboard: installed_cve_count→open_threat_count, security_update_count→available_fix_count
- Update detail: Known Vulnerabilities→Security Advisories
- AdvisoryType() helper for human-readable advisory ID prefixes
- clearVulnsOnInstall on installed transition with per-advisory security event logging
- StatsHandler takes checkInInterval for online/offline threshold
- AttentionPanel re-keyed on open-threats / available-fixes
2026-06-11 02:01:43 -04:00
Fimeg
5683bc15a4 feat: Wazuh queue-socket event emitter (INTEG-001)
Outbound-only: no listener, no control surface — RedFlag's journal is
authoritative, Wazuh is a best-effort mirror. DGRAM to the local agent's
queue socket, ECS-formatted, rule IDs mapped from security event types.
Opt-in via REDFLAG_WAZUH_ENABLED=true; disabled = socket never opened.

- Sink interface on SecurityLogger (mirror after journal write)
- Write deadline + one reconnect, then drop-count with rate-limited log
- 3 tests: frame/ECS shape, absent-socket non-blocking, unknown→generic
2026-06-11 02:01:43 -04:00
Fimeg
aff48376b9 v0.2.8.0 — changelog + version lockstep bump 2026-06-11 02:01:43 -04:00
Fimeg
8b1884eadc fix: nil logger on Windows scan path, rpmEVRAhead equal-version false positive, desktop-self replay token burned before install
- NewOrchestrator initialises a log-only TeeLogger (nil buffer) so the
  Windows service path never carries a nil logger into executeScan
- rpmEVRAhead returns false when all epoch/version/release components
  compare equal; adds test cases for "0:2.0-1" vs "2.0-1" normalisation
- processDesktopSelfToken splits replayCheckAndRecordAgent into
  replayCheckAgent (before install) + recordAgentTokenConsumed (after
  successful install) so a transient install failure does not permanently
  consume the token
- Comments on helperSelfStagingPath (Go) and DEFAULT_HELPER_SELF_SOURCE
  (Rust) name each other as the cross-language counterpart
2026-06-11 02:01:43 -04:00
Fimeg
2da1e92fe9 feat: setup accepts operator-supplied signing keypair + validation
Extract serverSetupRequest type and resolveSetupSigningKeys(): when no keys
are provided the server generates a fresh Ed25519 pair (existing behaviour);
when a private key is provided it is validated and the public key derived from
it (public key may be omitted or supplied for cross-check). Mismatched pairs
are rejected 400. Remove configure-secrets route from welcome-mode router
(was only usable with Docker socket mounted, unreachable in that mode).
Add inferPublicURL() helper to fill publicURL from X-Forwarded-* headers when
the operator omits it. pq.QuoteLiteral() used for password in ALTER USER.
Tests: generate-when-missing, use-provided-pair, reject-mismatched-pair.
2026-06-11 02:01:43 -04:00
Fimeg
821bc00099 feat: self-update capability tokens (agent, helper, desktop)
Add AllowedSelfUpdatePackageTypes and allowedCapabilityPackageType() so the
consumer accepts redflag_agent_self / redflag_helper_self / redflag_desktop_self
tokens without putting them in the package-manager allowlist. ProcessToken
dispatches self-update types to dedicated processAgentSelfToken,
processHelperSelfToken, processDesktopSelfToken handlers. ArtifactDownloader
interface extracted; inferred from reporter when available. Tests added for
allowlist invariants, stageClosureArtifact (local file path), and
installDesktopBinary (backup-and-replace).
2026-06-11 02:01:43 -04:00
Fimeg
c1040f413b feat: wire TeeLogger through agent loop for structured dual-output logging
Replace fmt.Printf / log.Printf calls in loop, config, crypto, and security
logger with TeeLogger.Info/Warning/Error. Buffer is persisted to events_buffer.json
and flushed to the server after each check-in. CRITICAL security events still
write to disk immediately then also enter the buffer for server delivery.
SecurityLogger.GetBatch() no longer falls through on CRITICAL before buffering.
2026-06-11 02:01:43 -04:00
Fimeg
244d9091ee feat: process explorer — on-demand /proc scanning with full osquery parity
Agent-side: reads /proc for all PIDs with 25+ fields (identity, resources,
state, disk I/O, elevation) plus related data on drill-down (open files,
sockets, pipes, env keys, memory map, namespaces, listening ports). Pure
/proc reads, no subprocess spawns.

Server-side: dedicated tables (agent_process_snapshots, agent_processes,
agent_process_related) with JSONB for related data. On-demand scan via
scan_processes command, last-10-snapshot retention. Four endpoints:
report, get latest, get detail, trigger scan.

UI: new Processes tab in agent detail with sortable/filterable table,
search by name/cmdline, state/user filters, and ProcessDetailModal with
tabs for Overview, Network, Files, Environment, Memory, Namespaces.
2026-06-11 02:01:43 -04:00
Fimeg
469d61c0dc ui: command primitives extracted; agent page split-button sizing restored
CommandCard and CommandStatusBadge pulled out of Agents.tsx; restart
host split button back to flush edges with stretched trigger.
2026-06-11 02:01:43 -04:00
Fimeg
b9e083c4f7 test+fix: supply-chain consumer/OSV coverage; local-approve and update handler work
OSV fail-closed paths, safeTokenFilename traversal, bind check,
disallowed package types covered in agent/internal/supplychain.
Artifact hash resolution and dnf scanner test fixes. Server update
handlers extended for the local-approve flow with OSV version test
coverage.
2026-06-11 02:01:42 -04:00
Fimeg
4896fb6856 ci: Gitea Actions pipeline — release gate, embedded UI build, guided release script
ci.yml: vet, race tests, clippy, full web build, AI-attribution and
action-pin enforcement. release.yml: gate job verifies tag against
versions.go/docker-compose/Cargo/CHANGELOG, forward-only and on public,
before anything builds; web UI staged into the embed path (gitignored
dist made a bare go build ship an empty dashboard); binaries and docker
image must self-report the tag; release created via Gitea's own API.
scripts/release.sh is the operator path: checks runner, secret, branch,
versions, changelog — asks before every mutation, watches the run after.
bump-version.sh gains current-version display, dirty-tree warning,
duplicate check, changelog check, confirmation. build-secure-agent.sh
retired (bare go build, no version injection, single Makefile caller).
2026-06-11 02:01:42 -04:00
Fimeg
2f3363cbce feat: post-upgrade attestation — new binary proves the swap took
Old binary drops a marker (command_id, from/to) once the swap is
committed, on both the helper path and the legacy path. New binary
checks it at startup: running >= target just clears the marker
(check-in confirm still owns success); short of target means the swap
failed or rolled back, so it files a failed update_agent report under
the original command_id and the server clears is_updating right away
instead of sitting out the stuck-update timeout. Marker survives
failed reports for retry, drops on 409 or after 24h.
2026-06-11 02:01:42 -04:00
Fimeg
1fa76bf665 feat: System Information grid primitives — auto-placing 2-column grid 2026-06-11 02:01:42 -04:00
Fimeg
2084019be7 fix: grant CAP_SYS_PTRACE on agent binary for display/process discovery
The agent runs as redflag-agent user and cannot read /proc/[pid]/environ
from the logged-in user's session processes (owner-only permissions).
Without this, discoverSessionDisplay() finds nothing — screenshot fails
on Wayland because XDG_RUNTIME_DIR/WAYLAND_DISPLAY are never discovered.

- Installer: setcap cap_sys_ptrace=eip after placing the binary
- Helper (agent self-update): restore setcap after atomic replace,
  since rename() creates a new inode and strips file capabilities

Non-fatal if setcap is unavailable — agent degrades gracefully,
just loses screenshot and per-process telemetry.
2026-06-11 02:01:42 -04:00
Fimeg
411f25b1b7 v0.2.7.0 2026-06-11 02:01:42 -04:00
Fimeg
a0f6b821ce feat: top processes primitive — cross-platform /proc-native collection
GetTopProcesses(limit) reads /proc/[pid]/stat and /proc/[pid]/status
directly on Linux — no subprocess spawn, ~30ms for 5 processes.
Windows uses tasklist CSV, macOS uses ps aux.

Wired into reportSystemInfo via metadata[top_processes] — flows
through the existing merge path, no server schema change needed.
UI already reads agent.metadata.top_processes and renders the table.

Added tickCount to the polling loop for future N-tick throttling
(heartbeat-level reporting cadence).

Test output confirms real data:
  1. Isolated Web Co (pid=10319) cpu=4.5% mem=3.4%
  2. firefox (pid=4090) cpu=3.2% mem=4.6%
  3. qs (pid=3611) cpu=1.6% mem=1.7%
2026-06-10 11:06:15 -04:00
Fimeg
33669419ca feat: Wayland screenshot support — session-aware tool selection
Discover XDG_SESSION_TYPE from /proc to route screenshot capture:
- X11: scrot → magick import → import
- Wayland: grim (wlroots) → gnome-screenshot (GNOME) → spectacle (KDE) → magick fallback
- Unknown: try all tools

Session detection reads DISPLAY, WAYLAND_DISPLAY, XDG_RUNTIME_DIR,
XDG_SESSION_TYPE from active user /proc environ. Infers session type
from WAYLAND_DISPLAY when XDG_SESSION_TYPE is unset.
2026-06-10 10:34:46 -04:00
Fimeg
aeff8a67a8 fix: desktop build uses Node 20 stage; tray installs + autostarts by default 2026-06-10 09:45:24 -04:00
Fimeg
e8b7742422 feat: standalone local authority — helper mint mode + local approval flow
FEAT-003 core (design: RAF/security/06-standalone-authority.md, approved
2026-06-10). On a host with no fleet server, the trust boundary preserved is
root-vs-unprivileged: a root-owned 0600 Ed25519 key signs capability tokens
via a new privileged helper invocation; redflag-local membership lets you
request a mint, never perform one.

Helper gains the mint subcommand: validates forward-only ops, mintable-type
allowlist (no agent-self), host agent-id bind, closure shape (64-hex sha256
fail-closed), hard-coded 15-minute gate-evidence freshness with future-dating
rejection, override-reason requirement for vulnerable/unreachable/overridden
verdicts, duplicate request_id dedupe, journal-before-emission. --init-key /
--retire-key manage the authority lifecycle (retire = the fleet-join swap).
Deny taxonomy 22-25. Round-trip test proves a minted token passes the execute
path's own verification and parses as the wire CapabilityToken.

Agent gains POST /v1/actions/approve-update (single-flight, 409/503 mapping):
fleet-mode refusal, dnf/apt dry-run closure resolve + hash pin (no pin, no
mint), best-effort OSV.dev closure check with honest verdicts (unreachable is
never silent-clear), mint via sudo systemd-run mirroring the execute grant,
then the unchanged verify+execute path. Provisioning script sets up the
journal dir (root:redflag-local 2750 setgid), mint request dir, key init, and
the pinned mint sudoers line.
2026-06-10 09:04:06 -04:00
Fimeg
1241c1ef01 feat: embed web UI in server binary + local trigger-scan write endpoint
Server becomes self-contained: web/dist embedded via go:embed
(server/internal/webui), SPA served from the binary with JSON-404 guard on
/api paths, nginx web container removed from compose (31336 now maps to the
server). Clean checkouts without the UI copy build API-only.

Agent local API gains its first write endpoint, POST /v1/actions/trigger-scan
(FEAT-002 write path): group-ACL authorized, single-flight, 202/409/503
semantics. Registered agents run the same HandleScanUpdates path as a signed
scan command (empty command_id, no ack tracking); standalone agents scan
through the orchestrator into the local read model only. Also repairs
localapi tests left uncompilable by the desktop-provider parameter.
2026-06-10 08:38:18 -04:00
Fimeg
73c1e0ef21 fix: build errors + move platform info under screenshot card
- Fix premature </div> in Docker.tsx and History.tsx (TS1005/TS1128)
- Fix unused imports/vars (noUnusedLocals): AgentStorage, AgentUpdatesModal,
  ErrorBoundary, useAgentUpdate, useHeartbeat, Dashboard, Updates
- Move Platform/Distribution/Architecture under screenshot card in Agents page
- Move Top Processes into right stats column
2026-06-08 22:37:31 -04:00
Fimeg
6b28498d84 fix: bump docker-compose version to 0.2.6.9 2026-06-08 19:55:20 -04:00
Fimeg
c3d84ec7f9 feat: UI primitives + agent version bump
- PageState, Modal, Pagination, StatCard primitives
- Migrated Dashboard, Updates, LiveOperations, Agents, Docker, TokenManagement
- AgentUpdate, AgentUpdatesModal now use Modal primitive
- Version bump to 0.2.6.9
2026-06-08 19:40:06 -04:00
Fimeg
7cbf174652 feat: unified event timeline, desktop bundling, UI primitives
Unified timeline:
- client_errors bridge to system_events (component='client')
- Admin action audit middleware on /admin/* routes
- History page filters: type, severity dropdowns
- ChatTimeline accepts externalType/externalSeverity props

Desktop app bundling:
- Docker: Tauri builder stage (Rust + Node + webkit2gtk)
- Server signs desktop binary at startup, serves via /api/v1/desktop/:arch
- Install script downloads + verifies desktop binary (Step 7c)
- Agent spawns desktop as child process, monitors + restarts on crash
- Desktop config: enabled, max_restarts, restart_delay_sec
- Session detection: /proc environ scan (Linux), query session (Windows)
- Desktop health: POST /v1/desktop every 30s from tray app
- /v1/status includes desktop running/pid/enabled state

UI primitives:
- Modal, PageState, Pagination, StatCard components
- Dashboard, Updates, Agents pages refactored to use primitives
- Novell aesthetic preserved throughout
2026-06-08 19:09:58 -04:00
Fimeg
c0a717ab26 fix: README, .env.example, ErrorBoundary, client-logger, HEALTHCHECK, Docker hygiene
- README: version v0.2.6.8, corrected stale gate claim, updated changelog
- .env.example: merged two competing files into one, deleted bootstrap duplicate
- ErrorBoundary: new component wrapping app, prevents white-screen crashes
- Layout sidebar: version display from /api/health, Docs link to GitHub
- client-logger: debug/trace logger gated behind localStorage.redflag_debug=1,
  routes through existing /logs/client-error server endpoint (ETHOS #1)
- All web console.log calls rerouted through client-logger instead of deleted
- Server health endpoint returns version field
- Server accepts client_debug/client_trace in error_type validation
- Dockerfiles: pinned alpine:latest->3.21, nginx:alpine->1.27-alpine,
  added HEALTHCHECK directives
- docker-compose: healthcheck blocks for server and web services
- .dockerignore: created to slim Docker build context
2026-06-08 18:23:39 -04:00
Fimeg
80e719acc9 fix: screenshot square inside System Information card; display discovery
UI: move screenshot/Sunshine square into the System Information card
header (top-right, w-48 aspect-video) instead of a standalone block
above the card. Same click logic, smaller size to fit the header row.

Agent: captureScreenLinux now discovers DISPLAY/WAYLAND_DISPLAY/
XDG_RUNTIME_DIR from /proc environ entries so the service (which
doesn't inherit display vars from systemd) can reach the session.
Tool priority: scrot → grim → magick import → import.
Adds bytes/strconv/strings imports for discoverSessionDisplayEnv.
2026-06-08 18:02:33 -04:00
Fimeg
99246ecf33 fix: add RGBA icon for Tauri desktop build (128x128 placeholder) 2026-06-08 17:33:52 -04:00
Fimeg
ab6fc48925 feat: v0.2.6.8 — dark/light tray app theme, version bump
LocalAgentApp: dark default with ☀/☾ toggle; palette-driven inline
styles (DARK/LIGHT objects) so both modes work without Tailwind dark
mode configuration. Dense Novell-style layout: status strip, section
headers with red accent bar, monospace identifiers, dot indicators per
scanner status. No Tailwind class changes — web/package-lock.json
untouched.

Version bump 0.2.6.7 → 0.2.6.8.
2026-06-08 17:22:01 -04:00
Fimeg
ffffe9b956 feat: FEAT-002 local agent API, desktop tray spine, screenshot handler
Slices 1-3 of the local agent IPC surface:
- Read model (local_status.go, loop wired): update counts, scanner status,
  check-in state, token receipt counts — no token material exposed
- Local IPC (localapi/): Unix socket (group=redflag-local, 0660) + Windows
  named pipe (SDDL: LocalSystem/Admins/RedFlagLocal); five read-only endpoints
- `redflag-agent -local-status` CLI probe of the local API surface
- Screenshot capture handler (screenshot.go, dispatch wired)
- Tauri desktop spine (desktop/): tray icon, left-click window, local IPC reader
- Desktop React entry (web/src/desktop/LocalAgentApp.tsx, index.desktop.html,
  vite.desktop.config.ts)
- Installer group provisioning: linux.sh creates redflag-local, sets
  SupplementaryGroups; windows.ps1 creates RedFlagLocal security group
- Server-side: screenshot receipt handler on agents, updates handler additions
- web/package.json: @tauri-apps/api + tauri CLI dev dep added
2026-06-08 17:08:58 -04:00
Fimeg
b0e2a77528 fix: pin installed version instead of clearing on install
After successful install (confirm_dependencies or capability receipt),
pin the installed version as selected_version instead of clearing it.
This prevents subsequent scans from silently advancing to a newer
unapproved version. The pin is the enforcement mechanism.
2026-06-08 16:07:33 -04:00
Fimeg
7d089ab5d2 v0.2.6.7 — GATE-006 D, version row OSV check
- evaluateSupplyChainHold checks version row osv_status for target
  version, closing hole where we OSV-check one version but install
  another.
- confirm_dependencies uses TransitionByPackageFrom with source-state
  guard (StatusInstalling → Installed/Failed).
- Clears pinned version on successful install.
- Version bump to v0.2.6.7.
2026-06-08 16:01:05 -04:00
Fimeg
14cd0884b4 fix: consolidate heartbeat, lower dashboard polling
- useHeartbeatStatus reads from agent metadata (single source of
  truth) instead of separate endpoint (eliminated split-brain).
- Online/offline threshold fixed to 10min to match server (was 15min).
- Dashboard polling lowered from 30s to 15s.
- Toggle feedback: invalidate agent queries immediately, clear
  loading state after 2s.
- Removed command-table fallback from GetHeartbeatStatus endpoint,
  dead helper functions, and unused GetRecentHeartbeatCommands.
2026-06-08 16:01:01 -04:00
Fimeg
9c8e08e079 fix: migration loop caused by phantom config_v5_migration
Three layered causes:
1. config_v5_migration had no executor phase — structurally impossible
   to mark complete.
2. StateManager.loadConfig unmarshaled into typed config.Config, but
   install template writes version as JSON number while Config types
   it as string — migration completion never persisted to disk.
3. readConfigVersion only parsed float64, so normalized string read 0
   and re-triggered.

Fix: StateManager is map-based (immune to field-type drift). Executor
has real config_v5 phase (bump + mark complete). parseConfigVersion
accepts both number and string. validateMigration uses MkdirAll then
stat (fixes false 'state dir not found' on Windows).
2026-06-08 16:00:56 -04:00
Fimeg
c2d683d63b fix: Windows agent — service, logging, local state, CPU parse
Service:
- Build complete LoopContext in Windows service runAgent (was missing
  ReceiptTracker, ConfirmedTracker, scanners, circuit breakers,
  kernel enforcer — nil ReceiptTracker caused immediate panic).
- Wire process logger so log.Printf writes to agent.log on Windows.
- Parse CPU info JSON from PowerShell fallback (wmic absent on
  modern Windows; old code tried CSV parse on JSON output).

Local state cache:
- Agent writes status, scan results, capability token state to
  local disk cache. Enables local observability without a server
  round-trip. Handlers expose /local/state, /local/status endpoints.
- Record agent status (online/backoff) and capability token metrics
  to local cache during polling loop.
2026-06-08 16:00:47 -04:00
Fimeg
f83f5a0960 fix: Windows install path — ACL repair, host/port, CRLF
- Install script repairs config ACL before detection (unreadable
  config.json caused false 'not installed' and 409 re-registration).
- Config permissions use stable SIDs (SYSTEM, LOCAL SERVICE,
  Administrators) instead of localized account names.
- Install URL preserves port for non-localhost hosts.
- Server bind address vs reachable host distinction fixed.
- Windows install script served as CRLF.
- Installer tolerates missing Ed25519 verifier.
2026-06-08 16:00:01 -04:00