feat(windows): platform ops, service wrapper, helper, and air-gap paths
First Windows parity milestone (DESIGN-WINDOWS.md §13 build order item 1).
- Add a PlatformOps abstraction so command dispatch is OS-neutral:
- linuxPlatformOps keeps today's systemctl/journalctl/bash behavior (deliberately
untagged so shared dispatch tests run on Windows CI)
- windowsPlatformOps maps reboot/shutdown to shutdown.exe, service control to
sc.exe (stop+start for restart), fetch_logs to Get-WinEvent, arbitrary_bash to
powershell -EncodedCommand (byte-exact under arbitrary quoting), and declines
configure_ldap (Windows logon goes through OpenCredential)
- Run theta-agent as a Windows service (x/sys/windows/svc): SYSTEM auto-start,
SCM stop/shutdown handling; CLI install-service/remove-service via svc/mgr
- Add theta-agent-helper (session-0 companion): lock/display_off/logout via
user32/wtsapi32, and staged self-update (wait for service stop, swap the
locked exe, sc start)
- Self-update becomes platform-aware: Linux renames over the running binary;
Windows stages .new and hands the swap to the helper (running exe is locked)
- Platform paths: agent.yml and tray.sock under %ProgramData%\Theta42 (the
service runs as SYSTEM while the tray runs as the user, so the per-user temp
dir no longer works for tray IPC); LDAP byte-pump falls back to TCP loopback
- config: service_name, desktop_helper, public_ip_detect (air-gap: skips
external public-IP lookups in telemetry + home monitor), wireguard block
- cli: platform-aware config path + self-update artifact name + service restart
- tests: dispatch tests pin linuxPlatformOps; 0600 mode assertions gated to
POSIX so the suite is green on Windows
Rebuilds all tracked dist binaries (v2.1.0).
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
// Platform paths. Windows has no /etc, /run or /tmp, so the agent's
|
||||
// filesystem touchpoints move under %ProgramData%\Theta42, which SYSTEM (the
|
||||
// service) and authenticated users (the tray) can both reach.
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// windowsDataDir returns %ProgramData%\Theta42.
|
||||
func windowsDataDir() string {
|
||||
pd := os.Getenv("ProgramData")
|
||||
if pd == "" {
|
||||
pd = `C:\ProgramData`
|
||||
}
|
||||
return filepath.Join(pd, "Theta42")
|
||||
}
|
||||
|
||||
// defaultConfigPath returns the platform's agent.yml location.
|
||||
func defaultConfigPath() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(windowsDataDir(), "agent.yml")
|
||||
}
|
||||
return "/etc/theta42/agent.yml"
|
||||
}
|
||||
|
||||
// defaultLdapSocketPath returns the local LDAP byte-pump socket. Windows has no
|
||||
// AF_UNIX in a stable location that both service and clients share, so it
|
||||
// relies on the TCP loopback listener (127.0.0.1:389) that ldapTunnel.start
|
||||
// falls back to.
|
||||
func defaultLdapSocketPath() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return ""
|
||||
}
|
||||
return "/run/theta/ldap.sock"
|
||||
}
|
||||
|
||||
// windowsTraySocketPath returns the tray IPC socket path. It lives in the
|
||||
// shared data dir (not the per-user temp dir) because the daemon runs as the
|
||||
// SYSTEM service while the tray runs as the logged-in user; the installer
|
||||
// grants Users write access to this directory and its children.
|
||||
func windowsTraySocketPath() string {
|
||||
return filepath.Join(windowsDataDir(), "tray.sock")
|
||||
}
|
||||
Reference in New Issue
Block a user