Web terminal: open a shell on a site from the dashboard
CI / test (push) Successful in 3m51s
Release / release (push) Successful in 6m28s

Hammerhead signs in to the site's Bamboo SSH server through the SSH port an edge
exposes, with a key of its own, and relays the shell to the browser over a
WebSocket (xterm.js, served by Hammerhead). The SSH dialog shows Hammerhead's
public key to add to authorized_keys. Host keys are saved on first use and a
changed one is refused; the terminal opens only from the dashboard's own
origin, is audited, and closes after 30 minutes idle. Errors reach the page as
messages over the socket. An end-to-end test runs a real shell through the
edge, tunnel and Bamboo.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-31 01:02:28 -06:00
1 parent 1c1f2b1d44
commit 66483d8385
14 files changed
+1012 -5

No files matched your search

+2
View File
@@ -18,6 +18,7 @@ func (s *Server) getSettings(w http.ResponseWriter, r *http.Request, p *principa
st := s.st.Settings
st.SMTP.Pass = "" // write-only
st.Sandbar.Token, st.Sandbar.Monitors = "", nil // write-only
st.Terminal = TerminalSettings{} // the private key never leaves the server
writeJSON(w, 200, map[string]any{"settings": st, "smtpPassSet": s.st.Settings.SMTP.Pass != "", "sandbarTokenSet": s.st.Settings.Sandbar.Token != "", "sandbarSync": s.sandbarStatus})
}
@@ -48,6 +49,7 @@ func (s *Server) putSettings(w http.ResponseWriter, r *http.Request, p *principa
in.DashboardURL = s.st.Settings.DashboardURL // pinned by flag/env
}
in.SelfEdgeID = s.st.Settings.SelfEdgeID // not editable here; losing it would make the next start create a second self edge
in.Terminal = s.st.Settings.Terminal // the terminal key and trusted host keys are not editable here either
s.st.Settings = in
s.bump() // edges receive the dashboard URL and ACME settings
s.mu.Unlock()