Files
theta-agent/CHANGELOG.md
wmantly 7d36318885 fix: rebuild the prebuilt linux/amd64 binary for join keys (v1.5.1)
theta-suite's setup.sh installs the committed theta-agent-linux-amd64
rather than building from source, so a stale binary means the fix in this
repo never reaches the host.

The v1.5.0 binary predated join-key support: setup.sh would write a
join_key into agent.yml that the running agent did not understand, and it
would have looped on "close 4001: Unauthorized" -- the same trap the
v1.3.0 heartbeat fix hit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 10:43:51 -04:00

6.3 KiB
Raw Permalink Blame History

Changelog

All notable changes to the theta-agent daemon will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[v1.5.1] - 2026-08-06

Fixed

  • Rebuilt the prebuilt theta-agent-linux-amd64. theta-suite's setup.sh installs that committed binary rather than building from source, so a stale one means the fix in this repo never reaches the host. The v1.5.0 binary predated join-key support: an install would have written a join_key into agent.yml that the running agent did not understand, and it would have looped on close 4001: Unauthorized. (Same trap as the v1.3.0 heartbeat fix.)

[v1.5.0] - 2026-08-06

Join-key enrollment (protocol v1.2.0 §1.1). Installing the agent with one key is now all it takes to add a host.

Added

  • join_key config field. Presented while auth_token is empty. The SSO exchanges it for this agent's own token and the public key it must pin, both delivered in the config frame; the agent writes them into agent.yml and blanks the join key. No value has to be copied between two machines by hand any more.
  • ConfigManager.PersistEnrollment rewrites only the credential lines, line-based rather than a YAML round-trip, so operator comments, the capability matrix and formatting survive. Re-reads the file afterwards, so the new credential is live without a restart, and keeps the file at 0600.
  • Config.Credential() — the agent's own token when it has one, otherwise the join key.
  • The connect URL carries ?hostname=, so a self-enrolling host is named after itself instead of a generated placeholder.
  • install.sh --join-key.

Fixed

  • The agent now refuses to connect (with a clear message and a long back-off) when it has neither an auth_token nor a join_key, rather than repeatedly presenting an empty credential.

[v1.4.0] - 2026-08-05

Implements Protocol v1.2.0. See PROTOCOL.md §1.1, §5.15.3.

Security

  • Fail-closed signature verification. verifySignature returned true when no public_key was configured, logging "skipping signature verification". Combined with an installer that never wrote a public_key, that meant a default install would execute reboot, service_restart, configure_ldap, arbitrary_bash and update_binary unverified from anything that could reach its socket. An agent that cannot verify a high-risk command now refuses it.
  • The token must be issued by the server. The SSO now rejects tokens it did not mint (close code 4001). Agents carrying a token generated by the old browser-side installer will not connect until re-enrolled.

Fixed

  • Canonicalization mismatch broke signatures for most real scripts. Go's encoding/json escapes <, > and & by default; the server's JSON.stringify does not. Any payload containing them — an arbitrary_bash script using > redirection or &&, which is most of them — hashed differently on each side and failed verification. canonicalize() now uses json.Encoder with SetEscapeHTML(false) and trims the encoder's trailing newline.
  • Auth failures no longer hot-loop. A rejected credential was retried every 5 seconds forever, flooding the SSO and its audit log. Close codes 4001/4003/4004 now back off for 5 minutes and log what to do about it.
  • The auth token no longer appears in logs. The connect line logged the full URL, including ?token=.... It now logs only host + path, and the token is URL-escaped.

Added

  • Close-code handling for the SSO's enrollment signals: 4001 unauthorized, 4002 superseded, 4003 revoked, 4004 token rotated.
  • install.sh --public-key <base64>, written into the generated agent.yml. The installer warns loudly when no public key is configured, since such an agent can report telemetry but will refuse every high-risk command.
  • Tests: fail-closed with no key, wrong key, payload tampered after signing, shell metacharacters (>, &&, <) round-tripping, and canonical-form equality with the server. interop_check_test.go verifies a signature produced by the live SSO against the agent's own verifier (skipped unless INTEROP_FIXTURE is set).

Changed

  • Existing tests no longer rely on verification being skipped; high-risk cases now sign with a real test key.
  • agent.yml.example, README.md, INSTALL.md: enrollment is a prerequisite, and public_key is the base64 of the raw 32-byte Ed25519 key — not a PEM body. The previous documented example (MCowBQYDK2VwAyEA...) decodes to 44 bytes and would have been rejected.

[v1.3.0] - 2026-08-04

Fixed

  • Silently ignore heartbeat_ack — the server replies to the agent's own periodic heartbeat with heartbeat_ack. The agent had no case for it, so it fell through to the unknown-command handler, logged Unknown command type: heartbeat_ack every minute, and answered with a spurious error response. Heartbeat acks are fire-and-forget; the agent now ignores them silently.

[v1.2.0] - 2026-08-03

Added

  • Protocol v1.1.0 Compliance: Full alignment with PROTOCOL.md (v1.1.0) specification.
  • Ed25519 Cryptographic Verification: Verification of Ed25519 Base64 signatures for high-risk C2 commands (reboot, service_restart, configure_ldap, arbitrary_bash, update_binary).
  • Enhanced Journal Log Fetcher (fetch_logs): Support for querying service-specific systemd logs (service parameter) with configurable line count (lines parameter).
  • Pure Go Self-Update Engine (update_binary): Replaced shell script execution with pure Go HTTP client fetching, SHA256 verification, atomic file replacement, and clean daemon restart.

Fixed

  • Goroutine Leak Prevention: Added stopCh lifecycle management to terminate background telemetry and heartbeat tickers upon WebSocket disconnection.
  • Dynamic Config Rerenders: Resolved data races when toggling capabilities or reloading agent.yml via reload_config.
  • Config Path Uniformity: Standardized canonical configuration file path across codebase, installer, and documentation to /etc/theta42/agent.yml.

[v0.1.0] - 2026-08-01

Added

  • Initial release of theta-agent Go daemon replacing legacy bash metric scripts.
  • Persistent outbound WebSocket telemetry and local capability matrix enforcement.