helmd hub: central server på Pi5 + agent-uplink + helmd ctl + web-login
All checks were successful
helmd-image / build (push) Successful in 3m22s
helmd-release / build-release (push) Successful in 4m28s

Hub-läge i samma binär (doc/hub-plan.md): agenter ansluter utåt med
delad agent-nyckel (hub-sektion i config.json), web-/CLI-klienter
loggar in (lokala konton, pbkdf2; Authenticator-interface för LDAP
senare) och styr alla agenters sessioner via proxy + merged SSE.
Webbklienten autodetekterar hub/local via /api/mode. Nya kommandon:
helmd hub [setpass|users|agentkey], helmd ctl (login/agents/sessions/
prompt/screen/answer/events …). Enhetstester + scripts/run-hub-smoke.sh
(15-stegs e2e: hub + agent + ctl).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011KikHkfCiC3yELbsMN8fT9
This commit is contained in:
2026-08-06 23:19:18 +02:00
parent d0b57e8224
commit 77682e91b5
17 changed files with 2411 additions and 37 deletions

View File

@@ -18,6 +18,17 @@ type NtfyConfig struct {
NotifyOnQuestion bool `json:"notify_on_question"`
}
// HubUplink pekar ut vilken hub agenten ska ansluta till. Anslutningen
// sker utåt (agenten ringer hubben) utan konto — hubben litar på den
// delade agent-nyckeln (hämtas med `helmd hub agentkey` på hubben
// eller efter inloggning i webben/CLI:n).
type HubUplink struct {
URL string `json:"url"` // t.ex. http://192.168.0.19:8790
Name string `json:"name"` // agentens namn i hubben (tomt = hostname)
Key string `json:"key"` // hubbens agent_key
Enabled bool `json:"enabled"`
}
type Config struct {
Listen string `json:"listen"`
Token string `json:"token"`
@@ -30,6 +41,7 @@ type Config struct {
Cols int `json:"cols"`
Rows int `json:"rows"`
Ntfy NtfyConfig `json:"ntfy"`
Hub HubUplink `json:"hub"`
path string
}
@@ -53,6 +65,11 @@ func defaultConfig() *Config {
Enabled: true,
NotifyOnQuestion: true,
},
Hub: HubUplink{
URL: "",
Name: hostname(),
Enabled: false,
},
}
}
@@ -107,6 +124,14 @@ func (c *Config) Save() error {
return os.WriteFile(c.path, append(data, '\n'), 0o600)
}
func hostname() string {
h, err := os.Hostname()
if err != nil || h == "" {
return "agent"
}
return h
}
func newToken() string {
b := make([]byte, 24)
if _, err := rand.Read(b); err != nil {