Add scout constellation mode for APK venue persona packs.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Cluster 3+ scout_report hits on the same SSID within 10 minutes; server infers airport/campus/retail venue class and pushes persona spread_policy. Emberwake weather-map merges active scout biomes. Includes agent, server API, and Vitest coverage.
This commit is contained in:
AetherForge
2026-06-07 09:18:58 -07:00
parent 8b14582975
commit bbab38f8e1
60 changed files with 2610 additions and 43 deletions

View File

@@ -81,6 +81,13 @@ type TraceSession struct {
// Client WireGuard keypair — used to build the QR config.
clientPrivKey string
clientPubKey string
// Onion timeline fork/merge — ghost branches explore persona spread/mining on target hop.
TimelineRootID string `json:"timeline_root_id,omitempty"`
TimelineBranches []*TimelineBranch `json:"timeline_branches,omitempty"`
MergedPersona string `json:"merged_persona,omitempty"`
MergedSpreadLane string `json:"merged_spread_lane,omitempty"`
MergedBranchID string `json:"merged_branch_id,omitempty"`
MergedHashrate float64 `json:"merged_hashrate,omitempty"`
}
const (
@@ -122,6 +129,12 @@ type pathTraceSessionPersist struct {
NetworkHints json.RawMessage `json:"network_hints,omitempty"`
ClientPrivKey string `json:"client_priv_key,omitempty"`
ClientPubKey string `json:"client_pub_key,omitempty"`
TimelineRootID string `json:"timeline_root_id,omitempty"`
TimelineBranches []*TimelineBranch `json:"timeline_branches,omitempty"`
MergedPersona string `json:"merged_persona,omitempty"`
MergedSpreadLane string `json:"merged_spread_lane,omitempty"`
MergedBranchID string `json:"merged_branch_id,omitempty"`
MergedHashrate float64 `json:"merged_hashrate,omitempty"`
}
func (h *PathTracerHandler) loadPersistedSessions() {
@@ -163,6 +176,12 @@ func (h *PathTracerHandler) loadPersistedSessions() {
NetworkHints: rec.NetworkHints,
clientPrivKey: rec.ClientPrivKey,
clientPubKey: rec.ClientPubKey,
TimelineRootID: rec.TimelineRootID,
TimelineBranches: rec.TimelineBranches,
MergedPersona: rec.MergedPersona,
MergedSpreadLane: rec.MergedSpreadLane,
MergedBranchID: rec.MergedBranchID,
MergedHashrate: rec.MergedHashrate,
}
}
if len(rows) > 0 {
@@ -189,6 +208,12 @@ func (h *PathTracerHandler) persistSession(sess *TraceSession) {
NetworkHints: sess.NetworkHints,
ClientPrivKey: sess.clientPrivKey,
ClientPubKey: sess.clientPubKey,
TimelineRootID: sess.TimelineRootID,
TimelineBranches: sess.TimelineBranches,
MergedPersona: sess.MergedPersona,
MergedSpreadLane: sess.MergedSpreadLane,
MergedBranchID: sess.MergedBranchID,
MergedHashrate: sess.MergedHashrate,
}
h.mu.Unlock()
raw, err := json.Marshal(rec)
@@ -303,6 +328,7 @@ func (h *PathTracerHandler) Start(w http.ResponseWriter, r *http.Request) {
h.mu.Lock()
h.sessions[sess.ID] = sess
initCanonicalTimeline(sess)
h.mu.Unlock()
h.persistSession(sess)
@@ -345,6 +371,9 @@ func (h *PathTracerHandler) Status(w http.ResponseWriter, r *http.Request) {
if routes := h.spreadRoutesForSession(sess, nil, ""); len(routes) > 0 {
resp["spread_routes"] = routes
}
for k, v := range timelineFieldsForStatus(sess) {
resp[k] = v
}
writeJSON(w, resp)
}