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:
@@ -29,6 +29,14 @@ type SecretTarget struct {
|
||||
Reload string `yaml:"reload"`
|
||||
}
|
||||
|
||||
// WireGuardConfig holds the mesh client settings (DESIGN-WINDOWS.md §5).
|
||||
type WireGuardConfig struct {
|
||||
// TunnelName is the Windows WireGuard tunnel/service name.
|
||||
TunnelName string `yaml:"tunnel_name"`
|
||||
// Conf is where the pushed peer config is persisted on disk.
|
||||
Conf string `yaml:"conf"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
ServerURL string `yaml:"server_url"`
|
||||
AuthToken string `yaml:"auth_token"`
|
||||
@@ -36,12 +44,37 @@ type Config struct {
|
||||
// the server exchanges it for a per-agent AuthToken (written back to this
|
||||
// file), so it is a bootstrap value, not a long-term credential. Used only
|
||||
// when AuthToken is empty.
|
||||
JoinKey string `yaml:"join_key"`
|
||||
Location string `yaml:"location"`
|
||||
PublicKey string `yaml:"public_key"` // Ed25519 public key for signed commands
|
||||
LdapSocket string `yaml:"ldap_socket"` // local LDAP tunnel socket (DESIGN.md §4)
|
||||
Secrets []SecretTarget `yaml:"secrets"` // secret templates to render (DESIGN.md §5)
|
||||
Capabilities Capabilities `yaml:"capabilities"`
|
||||
JoinKey string `yaml:"join_key"`
|
||||
Location string `yaml:"location"`
|
||||
PublicKey string `yaml:"public_key"` // Ed25519 public key for signed commands
|
||||
LdapSocket string `yaml:"ldap_socket"` // local LDAP tunnel socket (DESIGN.md §4)
|
||||
Secrets []SecretTarget `yaml:"secrets"` // secret templates to render (DESIGN.md §5)
|
||||
Capabilities Capabilities `yaml:"capabilities"`
|
||||
|
||||
// Windows-specific (DESIGN-WINDOWS.md §11).
|
||||
ServiceName string `yaml:"service_name"` // Windows service name
|
||||
DesktopHelper string `yaml:"desktop_helper"` // theta-agent-helper.exe path
|
||||
PublicIPDetect *bool `yaml:"public_ip_detect"` // false disables external lookups (air-gap)
|
||||
WireGuard WireGuardConfig `yaml:"wireguard"`
|
||||
}
|
||||
|
||||
// DetectPublicIP reports whether the agent may perform external public-IP
|
||||
// lookups. Defaults to true; an air-gapped host sets public_ip_detect: false so
|
||||
// the agent never tries to reach ipify/icanhazip/etc.
|
||||
func (c *Config) DetectPublicIP() bool {
|
||||
if c.PublicIPDetect == nil {
|
||||
return true
|
||||
}
|
||||
return *c.PublicIPDetect
|
||||
}
|
||||
|
||||
// ServiceNameOrDefault returns the Windows service name, defaulting to
|
||||
// theta-agent when unset.
|
||||
func (c *Config) ServiceNameOrDefault() string {
|
||||
if c.ServiceName != "" {
|
||||
return c.ServiceName
|
||||
}
|
||||
return "theta-agent"
|
||||
}
|
||||
|
||||
// Credential returns the value to present when connecting: our own token once
|
||||
|
||||
Reference in New Issue
Block a user