Commit Graph

262 Commits

Author SHA1 Message Date
cf548517b7 docs(tor-plan): Task 8 also adds reapply-when-armed (edits apply without disarm/arm cycle) 2026-07-09 08:24:58 +02:00
4ccfe4a331 docs(tor-plan): add Task 8 obfs4 bridges; fold bridge API/webui into Tasks 4/7; bump live-e2e to Task 9 2026-07-09 07:54:51 +02:00
e983520604 docs(tor-spec): add feature 6 — obfs4 bridges (Niveau-1 anti-censorship, entry-side) 2026-07-09 07:53:53 +02:00
e9f1c0706d docs(tor-plan): Tor UI lives in /toolbox/#tor tab; name toolbox api module (secubox_toolbox/api.py) 2026-07-09 07:41:50 +02:00
f2ed362015 docs: Tor enhancement Phase-1 implementation plan (8 tasks, SDD) 2026-07-09 07:34:27 +02:00
1d298b622b docs: Tor enhancement design (country-exit, Tor-VPN per-client, emancipate .onion, persist, auto .onion DNS) 2026-07-09 07:28:46 +02:00
fe10cf5dcd docs(openclaw): tighten target regex in plan (reject leading-dash flag-injection) 2026-07-09 05:55:37 +02:00
32a2907201 docs: openclaw LXC scanner implementation plan (8 tasks, SDD) 2026-07-09 05:16:55 +02:00
eb15e8d2ac docs: openclaw spec — correct LXC path (/data/lxc) + free IP (.41, .30 taken by matrix) 2026-07-09 05:11:56 +02:00
a670cda9e6 docs: openclaw dedicated-LXC OSINT+scanner design 2026-07-09 05:08:47 +02:00
d041f12a05 docs: c3box kernel migration design (6.12.85 + one-shot boot guard)
Some checks are pending
License Headers / check (push) Waiting to run
2026-07-08 13:15:31 +02:00
372dc677c3 docs: plan addendum — real /storage via nextcloudctl (fold b715 #429) (ref #429) 2026-07-08 07:22:10 +02:00
5096b5f015 docs: implementation plan — Nextcloud dashboard real data + manageable users (ref #429) 2026-07-08 07:20:17 +02:00
c1885c9646 docs: spec — Nextcloud dashboard real data + manageable users (ref #429) 2026-07-08 07:13:39 +02:00
CyberMind
62e8631f2b
feat(sentinel): C2/botnet auto-learning — high-precision, report-only (Closes #826) (#827)
* docs: spec — Sentinel C2/botnet auto-learning (high-precision, report-only) (ref #823)

* docs: implementation plan — Sentinel C2/botnet auto-learning (ref #823)

* feat(sentinel): C2 autolearn false-positive allowlist gate (ref #826)

* fix(sentinel): serialize c2allow Add + robust atomicWriteFile (relative-path + temp cleanup) (ref #826)

* feat(sentinel): C2 autolearn corroborating signals (rare/non-browser-JA/DGA) (ref #826)

* feat(sentinel): C2 autolearn candidate lifecycle + persistence (ref #826)

* fix(sentinel): c2cand deep-copy snapshots (persist/snapshot/record race) + evict order + device cap (ref #826)

* feat(sentinel): C2 autolearn orchestrator — learned set + report-only verdicts (ref #826)

* feat(sentinel): wire C2 learner into daemon + /c2 endpoints + seeded allowlist (ref #826)

* fix(sentinel): make /c2/allow writable in packaged deploy (RW path + ownership) + sanitize Add (ref #826)

POST /c2/allow could never write in production: sbx-sentinel.service runs
User=secubox-toolbox under ProtectSystem=strict + ReadOnlyPaths=/etc/secubox,
and the seeded c2-allow.txt ships root:root. Fix both halves: nest
ReadWritePaths=/etc/secubox/sentinel under the existing ReadOnlyPaths=/etc/secubox
in the unit, and chown/chmod 0750 the sentinel/ subdir + file (never the shared
/etc/secubox parent) in postinst's configure branch, fail-safe.

Also: fix the /c2/allow doc comment (form/query only, not JSON — r.FormValue
never parses a body), and sanitize C2Allow.Add against newline injection now
that host is network-reachable.

* feat(toolbox): WebUI C2 appris sub-view + Ignorer allowlist proxy (ref #826)

* fix(toolbox): C2 view XSS (data-host not onclick) + normalize candidate signal maps (ref #826)

* fix(sentinel): bound c2 beaconing/learned maps + drop promoted from candidates + reject bare-TLD allow (ref #826)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-07 07:46:19 +02:00
CyberMind
916cb91b72
feat(toolbox): activate Sentinel + surface compromise detections (WebUI tab · kbin report · PDF) — ref #823 (#825)
Some checks are pending
License Headers / check (push) Waiting to run
* feat(toolbox): arcade-HUD transparency banner redesign + responsive close-X fix

Redesign both MITM transparency-banner renderers into a C3BOX arcade-HUD
"scoreboard" skin and fix the phone bug where the close X dropped off-screen.

Structural responsive fix (both renderers): three flex clusters — LEFT rank
pinned (flex:0 0 auto), MIDDLE evidence flex:1 1 auto;min-width:0;overflow-x:auto
(scrolls instead of pushing), RIGHT report+close pinned (flex:0 0 auto) so the
close X is always visible; box-sizing:border-box added.

bundle.py _BANNER_CORE (primary R3 renderer): still built entirely via the mk()
DOM API (no innerHTML — Trusted-Types/strict-CSP invariant kept). Tier-accent
palette (r0 gold..r4 pink) drives border/glow/rank chip/level switch; evidence +
Tor/Ad-Guard toggles + live sbx-trk/sbx-ck counts become neon pills; one scoped
prefers-reduced-motion <style> for sheen/hover. All ids, toggles, csp proof,
un-hideable styles, SPA re-assert preserved.

inject_banner.py _banner_html_dynamic (secondary addon renderer): same look +
three-cluster fix; keeps grade shield + geo pills, _LEVEL_THEME, _GUARD, NCR
ascii-encodability, gondwana-mitm-banner id, JS-path close moved to right cluster.

Tests updated to the new structure; invariants still asserted (no innerHTML,
ids, toggles, ascii-encodability, dismiss control in right cluster).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(nginx): revalidate luci-static assets (Cache-Control no-cache) so webui updates aren't cached stale

* docs: spec — activate Sentinel + surface on WebUI tab, kbin report, PDF (ref #823)

* docs: implementation plan — Sentinel activation + 3 ToolBoX surfaces (ref #823)

* feat(sentinel): add mac filter to /verdicts status endpoint (ref #823)

* feat(toolbox): sentinel_link — fail-safe daemon fetch + compromise assess (ref #823)

* fix(toolbox): harden sentinel_link int coercions to never raise (ref #823)

* feat(toolbox): /admin/sentinel/{stats,verdicts} fleet proxy routes (ref #823)

* fix(toolbox): guard /admin/sentinel/stats against non-numeric daemon values (ref #823)

* feat(toolbox): WebUI Sentinelle fleet tab (evaluation + detections) (ref #823)

* feat(toolbox): fold per-device Sentinel detections into build_report_data (ref #823)

* feat(toolbox): kbin report per-device Compromission tab (ref #823)

* feat(toolbox): wire per-device Sentinel data into live HTML report render (ref #823)

* fix(toolbox): report-live tab must render when report var is absent (ref #823)

* feat(toolbox): PDF + text-fallback Sentinel compromise section (ref #823)

* docs: GPT demo prompt for kbin Sentinel augmentation (ref #823)

* fix(toolbox): sentinel never-raise on non-dict daemon rows + guard ts render (ref #823)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 17:39:42 +02:00
CyberMind
afd8f6eb83
feat(sentinel): sbxmitm Sentinel — inline+async exploit/malware/spyware detection engine (#823) (#824)
Some checks are pending
License Headers / check (push) Waiting to run
* docs(sentinel): design — inline IOC gate + async Go analyzer + spyware packs (ref #823)

* docs(sentinel): implementation plan — 12 tasks (ref #823)

* feat(sentinel): IOC model + IOCSet matchers (ref #823)

* feat(sentinel): pack loader + live-overlay merge + hot-reload (ref #823)

Add internal/sentinel/pack.go: Pack (JSON base/overlay content bundle),
LoadBasePack, MergePacks (base + N overlays, override by Type+Value via
IOCSet's per-type maps), and Loader (NewLoader/Set/MaybeReload/YaraRules).

Loader mirrors cmd/sbxmitm/policy.go's LoadPolicy/maybeReload mtime-based
hot-reload pattern, adapted for a directory of a DYNAMIC *.json file set
(glob-based) rather than policy.go's fixed Target list. Fail-safe: a
corrupt overlay pack is skipped+logged, never wipes the base or panics;
MaybeReload is a cheap stat-only no-op when nothing on disk changed;
Set()/YaraRules() read via atomic.Pointer so a reload swap never torn-reads
a concurrent hot-path lookup.

Adds JSON tags to Task 1's IOC struct (type/value/class/severity/source/
action) so packs round-trip through encoding/json.

* fix(sentinel): pack loader — fail-closed on base-wipe + throttle MaybeReload + url_regex override + yara dedup (ref #823)

Review found 1 Critical + 2 Important + 1 Minor in Task 2 (pack loader):

- Critical: a vanished/degraded base pack directory (deleted dir, mount
  blip, mid-dpkg-upgrade window) could silently empty the live detection
  set on the next MaybeReload. loaderState now tracks baseCount; a reload
  that would drop base IOCs from >0 to 0 is refused (prior state kept,
  warning logged) instead of swapped in.
- Important: MaybeReload() is called per-flow by Task 3's inline gate: add
  a 5s default throttle (mirrors cmd/sbxmitm/policy.go's reloadThrottle)
  so a hot-path call is a pure no-op until the window elapses.
- Important: url_regex IOCs previously only appended, so a live overlay
  could never override a base pack's URL rule (base always matched
  first). Add urlIndex (pattern -> slice index) so re-adding the same
  pattern replaces the entry in place, keeping deterministic match order.
- Minor: YaraRules() now dedups identical rule bodies across base+overlay
  packs instead of emitting duplicates.

go test ./internal/sentinel/: 21/21 PASS. go vet/gofmt/go build clean.

* feat(sentinel): inline gate matcher (fail-open, highest-severity) (ref #823)

* feat(sentinel): bbolt verdict store (pure-Go, WAL-free, TTL prune) (ref #823)

* feat(sentinel): action scorer — heuristic/low-confidence forced report-only (ref #823)

* feat(sentinel): bounded fire-and-forget mirror channel (drop-with-count) (ref #823)

* fix(sentinel): mirror Emit always copies body — no aliasing of caller's hot-path buffer (ref #823)

* feat(sbxmitm): inline Sentinel gate — neutralize high-confidence, mirror rest, fail-open (ref #823)

Wire internal/sentinel's inline IOC gate into the shared mitmPipeline response
path (covers both the transparent R3 and CONNECT accept paths without drift):
build a FlowMeta from Host/URL/ClientIP/MacHash after up.Do, call the gate, and
on a HIGH-CONFIDENCE known-infra hit neutralize inline — block/sinkhole serve a
Sentinel block page instead of the upstream response, strip serves the response
with an emptied body. Everything else (miss, heuristic, low-confidence, report)
is mirrored bounded fire-and-forget to the async analyzer and proceeds unchanged.

Fail-open everywhere: SENTINEL_ENABLED unset/false, a pack-load failure, a nil
hook, or a gate panic all degrade to a transparent passthrough — a Sentinel
setup problem can never break browsing. With the engine off, behavior is
byte-identical to today. Env: SENTINEL_ENABLED / SENTINEL_PACK_DIR /
SENTINEL_OVERLAY_DIR / SENTINEL_MIRROR_SOCK.

Hot path (BenchmarkSentinelInspectMiss, -benchmem): 101.0 ns/op, 0 B/op,
0 allocs/op — one throttled gate Match + a non-blocking mirror Emit; no YARA or
heavy analysis inline.

go test ./cmd/sbxmitm/... -race → ok (all pre-existing + 7 new green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(sentinel): sbx-sentinel async daemon scaffold + store wiring (ref #823)

* feat(sentinel): YARA engine (cgo, build-tagged) + no-cgo stub (ref #823)

Add internal/sentinel/yara.go (cgo && yara) wrapping
github.com/hillu/go-yara/v4 for compiled-rule body scanning, and
yara_stub.go (!yara) as the no-op default: NewYaraEngine always
succeeds, Scan/Analyze always return nil. Both expose identical
signatures (YaraEngine, NewYaraEngine, Scan, Analyze, Close) so
sbx-sentinel/sbxmitm can wire a YaraEngine unconditionally.

yara_test.go (untagged) proves the default build works with YARA
disabled. yara_real_test.go (yara tag) compiles a trivial EICAR rule
and exercises the real libyara-backed engine — split into its own
file since Go build constraints are file-scoped and can't mix a
tagged and untagged test in one file.

go-yara is now an indirect go.mod requirement and vendored, but is
only reachable via the yara build tag: default `go build ./...` and
`CGO_ENABLED=0 go build ./...` both stay clean, no libyara/cgo
required. `-tags yara` needs libyara-dev + a C toolchain (documented
in yara.go's header).

* fix(sentinel): YARA verdict confidence 90 (>= threshold) so a confirmed malware match actually strips, not report-only (ref #823)

* feat(sentinel): behavioral engine — beaconing + zero-click heuristics (report-only) (ref #823)

* fix(sentinel): tighten one-time-link heuristic — exclude file-extension + base64 shapes (cut zero-click FPs) (ref #823)

* feat(sentinel): spyware indicator engine + commercial-spyware base pack (MVT/CitizenLab) (ref #823)

* fix(sentinel): base-pack declared-block IOCs severity>=85 so they actually block + general guard test (ref #823)

Three shipped base-pack IOCs declared action:"block" but severity:80,
below scorer.go's HighConfidenceThreshold=85 — FinalizeAction silently
downgrades any Confidence below that threshold to report-only, so these
entries never actually blocked despite being declared as known-bad:

- packs/base/malware.json: ip 198.51.100.23 (class malware)
- packs/base/malware.json: cert_sha1 ...01234567 (class trojan)
- packs/base/botnet.json: ja3 deadbeef... (class botnet_c2)

Bumped all three to severity 90 so they survive FinalizeAction and
actually block, matching every other declared-block entry in the base
packs.

Added TestBasePackDeclaredBlockIsHighSeverity, a general guard over
every base pack file/entry: any IOC with an auto-neutralize action
(block/strip/sinkhole) must carry Severity >= HighConfidenceThreshold.
This generalizes the narrower TestBasePackKnownInfraIsHighSeverity
(spyware classes only) so a future pack entry in any class can't ship
a "block" that silently never blocks. Confirmed the new test fails red
against the pre-fix JSON (all 3 offenders reported) and passes green
after the severity bump.

* feat(sentinel): proposal/solution reporter — RenderReport(Verdict) (ref #823)

* feat(sentinel): wire real analyzers into sbx-sentinel + read-only status HTTP surface (ref #823)

* feat(sentinel): live threat-feed → overlay pack fetcher (fail-safe, atomic) (ref #823)

* feat(sentinel): live feed overlay + reporter + daemon analyzer wiring + packaging (ref #823)

* fix(sentinel): feeds fetcher — add curl --fail so HTTP 4xx/5xx is treated as a fetch failure (ref #823)

* fix(sbxmitm): drop mis-wired ClientIP from inline Sentinel FlowMeta (source IP must not match destination-C2 IP IOCs) (ref #823)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 15:53:21 +02:00
CyberMind
e79dfc8368
feat(frigate): Frigate NVR Foundation — podman-in-LXC on amd64 + /api/v1/frigate shim (#821) (#822)
Some checks are pending
License Headers / check (push) Waiting to run
* docs(frigate): Foundation design — LXC podman on amd64 + /api/v1/frigate shim + WAF-fronted (ref #821)

* docs(frigate): Foundation implementation plan — 7 tasks (ref #821)

* feat(frigate): package scaffold — control/rules/changelog + placeholder (ref #821)

* feat(frigate): idempotent LXC + podman provisioning (mirror photoprism) (ref #821)

* feat(frigate): config example + reconcile example path/db-path/image-override (ref #821)

Task 3: conf/frigate.config.yml.example — OpenVINO CPU detector, go2rtc
demo source (boots with no real camera), record/snapshots retention,
mqtt.enabled: false, commented real-camera block.

Task 2 review reconciliations:
- debian/rules now ships the example to the canonical path
  /usr/share/secubox/frigate/frigate.config.yml.example, matching what
  install-lxc.sh's install_config() actually reads (was wrongly shipped
  to /etc/secubox/frigate.config.yml.example).
- config example adds database.path: /media/frigate/db/frigate.db so
  Frigate's SQLite DB lands on /data/frigate (bind-mounted), not the
  Frigate default /config/frigate.db.
- install-lxc.sh templates the installed frigate.service unit's image
  tag from $IMAGE via sed after install -D (idempotent, fail-safe via
  grep -qF check), so SECUBOX_FRIGATE_IMAGE overrides actually take
  effect instead of silently running the hardcoded :0.14.1. Also drops
  the unused `openssl` from require_cmds (dead dep from photoprism).

* feat(frigate): /api/v1/frigate shim — status/cameras/events/storage/stats, JWT, double-cached, fail-safe (ref #821)

* fix(frigate): shim robustness — ImportError-only JWT fallback, cache /storage, bound /events, single /api/stats fetch, + fail-safe tests (ref #821)

* feat(frigate): host shim service + disk-pressure guard + frigatectl (ref #821)

* fix(frigate): diskguard survives missing/unmounted /data (was silent exit 1) + regression test (ref #821)

* feat(frigate): nginx shim route + menu.d + documented WAF exposure & sidebar stats (ref #821)

* feat(frigate): postinst/prerm (provision LXC, data-preserving) + build verification (ref #821)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-06 11:53:20 +02:00
CyberMind
418126c541
feat: SecuBox Media Buffer Phase 1 — dashcam capture + replay + metatag lifecycle (#814)
* docs(media-buffer): design spec — dashcam capture + replay + metatag lifecycle (ref #812)

* docs(media-buffer): phase-1 implementation plan (capture+janitor+API+DPI tab) (ref #812)

* feat(sbxmitm): media buffer store + metatag writer (ref #812)

* fix(sbxmitm): media-buffer review — 4096 atomic-append cap + *string buffer_ref (ref #812)

Task 1 review (Important): mediaBufferLineMax 8192→4096 to match mediacatch.go's
PIPE_BUF atomic-append guarantee across 4 workers; BufferRef string→*string so the
janitor (Task 3) can flip it to JSON null on eviction.

* feat(sbxmitm): tee media up/download bodies into the buffer (ref #812)

Wire the R4 media buffer tees into mitmPipeline: the download hook wraps
resp.Body and the upload path wraps req.Body via teeReadCloser, capturing
whole-file media into the rolling /data buffer. Add the mbuf field + flags
(--media-buffer default OFF, --media-buffer-root, --media-buffer-per-object).

Design upgrade over the plan sketch: ObjectWriter.Write is now NON-BLOCKING.
Instead of io.TeeReader driving a synchronous os.File.Write on the client read
path (which would add latency / could stall large media on ARM eMMC), Write
copies each chunk into a bounded channel and returns len(p),nil immediately; a
background goroutine started at Capture drains the channel to disk. A full
channel drops the chunk and flags truncated — never blocks, never errors the
proxied flow (design 4.2/7). Close signals the drainer, waits for it to flush
queued chunks + close the file, then appends the metatag; it is idempotent,
deadlock-free (sole closer of ch) and safe if Write is never called or races
Close.

* fix(sbxmitm): media download tee — defer closure so w.Close runs (ref #812)

defer resp.Body.Close() bound the ORIGINAL upstream body at the defer
statement, before the media-buffer tee reassigned resp.Body — so the
tee's Close() (and w.Close, which flushes the sink/metatag/drain
goroutine) never ran on any download. Wrap the close in a closure so
it reads resp.Body at return time instead.

Adds TestTeeDeferOrderingMatchesMainGo, which replicates main.go's
exact defer-then-reassign ordering and fails against the old plain
defer form (verified manually, then restored the fix).

Also: drop the dead write-only ObjectWriter.dropped field (truncated
already covers it), document Capture's reserved contentLen param, and
note that ObjectWriter.Close is allowed to block (post-stream
finalisation), unlike Write.

* feat(sbxmitm): media buffer janitor — time + LRU eviction, metatag survives (ref #812)

SweepOnce evicts a session's bytes (RemoveAll, idempotent) once
first_ts+retentionSecs elapses, then falls back to oldest-first LRU
eviction if the buffer root (media bytes only, log excluded) still
exceeds sizeCeilBytes. Eviction never rewrites the append-only
metatag log in place — it appends a corrected line with the same id
(expired:true, buffer_ref:null); readers dedup by id, last line wins,
so the flip is durable and safe across the 4 concurrent sbxmitm
worker processes each running their own RunJanitor (idempotent
RemoveAll + harmless duplicate flip lines).

RunJanitor ticks SweepOnce every 30s (test-overridable period) until
its context is cancelled; wired at sbxmitm startup as a
process-lifetime goroutine when --media-buffer is enabled.

NewMediaBuffer's signature is unchanged; retentionSecs (1200),
sizeCeilBytes (24 GiB) and nowFn now default inside the constructor.

* feat(core): media-buffer metatag reader (dedup by id, fail-empty) (ref #812)

* test(core): media-buffer reader — tail-read truncation + malformed-field regressions (ref #812)

* chore(#812): keep SDD scratch reports out of the feature diff (restore to master)

* feat(dpi): media-buffer list/replay/thumb API (admin/owner, audited, 410-on-evict, traversal-safe) (ref #812)

* harden(dpi): media API — \Z-anchored id regex + role short-circuit security note (ref #812)

* feat(sbxmitm): media-buffer retention/size-ceil flags + /data tmpfiles + opt-in worker env toggle (ref #812)

- main.go: --media-buffer-retention / --media-buffer-size-ceil flags, applied
  onto the already-constructed mbuf's unexported retentionSecs/sizeCeilBytes
  fields (NewMediaBuffer signature unchanged).
- tmpfiles/zz-secubox-media-buffer.conf: pre-creates /data/secubox (0755) and
  /data/secubox/media-buffer (0750 secubox:secubox), installed via the
  package's existing manual tmpfiles.d mechanism in debian/rules.
- secubox-toolbox-ng-worker@.service: EnvironmentFile=-/etc/secubox/toolbox-ng.env
  + trailing $SBX_MEDIA_BUFFER_ARGS on ExecStart so an operator can opt in
  (SBX_MEDIA_BUFFER_ARGS=--media-buffer) without editing the shipped unit;
  default stays OFF. Added ReadWritePaths=/data/secubox/media-buffer.

* fix(sbxmitm): media-buffer dir owned by writer (secubox-toolbox) + setgid group secubox; changelog 0.1.27 (ref #812)

Task 6 review Critical: buffer dir was 0750 secubox:secubox but sbxmitm workers run
as secubox-toolbox (not in group secubox) → EACCES on every capture, silently no-op'd
(errors swallowed by design). Fix: 2750 secubox-toolbox:secubox — writer owns, dpi
reader (secubox) is group, setgid propagates group secubox to created objects so the
reader can read them. Important: add debian/changelog 0.1.27 stanza (version bump).

* feat(dpi): Media tab — live capture gallery with replay/download, escaped fields (ref #812)

Adds a Media Buffer card to the DPI dashboard (packages/secubox-dpi/www/dpi/index.html)
that lists sbxmitm's live media-buffer captures via GET /api/v1/dpi/media/buffer.

- Auth: dedicated apiAuthed() reads the canonical `sbx_token` localStorage key
  and sends it as a Bearer token (media/buffer requires JWT, unlike the rest
  of this page's endpoints which currently ride the SSO-lite session cookie);
  401 redirects to /login.html, matching sibling dashboards (e.g. crowdsec).
- Security: host/url/mac_hash are attacker-influenceable (captured live
  traffic). Every card is built via createElement + textContent (or the
  `.title` IDL property, never HTML-parsed) — no innerHTML string
  concatenation of these fields — so a captured value like
  `<img onerror=...>` renders as inert text. Replay/download URLs are built
  with encodeURIComponent(id) even though the id is hex-validated server-side.
- UI: kind emoji (video/audio/manifest/file), short device id (shortDev,
  reused from the existing #785 helpers), ⬆/⬇ direction, human size
  (formatBytes) and age (agoStr) per card; expired captures grey out and show
  "métatag seul (expiré)" with no action links.
- Refresh: wired into the existing refreshAll() plus its own ~15s interval.

Verified with a Playwright harness stubbing fetch (incl. an XSS payload in
host/url) against a local copy of the page: no console/page errors, XSS did
not fire, no <img>/<script> element materialized from the captured strings,
expired-card gating confirmed.

* fix(sbxmitm): media buffer — upload tee needs a real body, download tee 200-only (whole-branch review); drop dead item.ts (ref #812)

* docs(media-buffer): Phase 2 plan — HLS reassembly via read-time URL join (ref #812)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-04 18:15:58 +02:00
4979631138 docs(plan): mesh-federate toolbox exclusion lists — 5-task TDD plan (#806)
Some checks are pending
License Headers / check (push) Waiting to run
2026-07-04 17:06:45 +02:00
25ce4693a2 docs(spec): mesh-federate toolbox exclusion lists (#806) 2026-07-04 17:01:16 +02:00
CyberMind
10e1bc8ff4
PeerTube WebUI: admin reset-password + version check/upgrade (#800)
Some checks are pending
License Headers / check (push) Waiting to run
* docs(spec): PeerTube WebUI admin ops — reset-password + version check/upgrade (ref #798)

* docs(plan): PeerTube WebUI admin ops implementation plan (ref #798)

* feat(peertube): API ops spool plumbing + require_admin (ref #798)

* feat(peertube): spool→root ops mechanism (process-ops + path/service) (ref #798)

* feat(peertube): reset admin password button (lockout-safe, spool→root) (ref #798)

* fix(peertube): admin-secret chown to real secubox user + WebUI error field (ref #798)

* feat(peertube): version check (installed vs latest GitHub release) (ref #798)

* feat(peertube): one-click upgrade with pre-backup + rollback (spool→root) (ref #798)

* fix(peertube): require_admin store lookup + op-name align + result group-readable (whole-branch review, ref #798)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-04 10:12:08 +02:00
CyberMind
1bbd237346
Merge pull request #795 from CyberMind-FR/feature/793-per-vhost-exposure-switch-localhost-lan
Some checks are pending
License Headers / check (push) Waiting to run
Per-vhost exposure switch: localhost/LAN/WAN + mesh/Tor (Closes #793)
2026-07-04 07:38:00 +02:00
a1ea1b38f3 docs(wiki): drop decommissioned Authelia SSO from Modules catalog (128→127)
secubox-authelia (SSO IdP) was decommissioned (#64768978, package removed, gate
now permissive no-op) but still listed in the Modules wiki. Removed from
generate-docs.py MODULES dict + regenerated MODULES/CATEGORIES (EN/FR/DE/ZH).
2026-07-04 07:00:50 +02:00
CyberMind
c3616cc266
PDF report: rich character sheet + route parity + real Quêtes (#794)
* docs(spec): rich PDF character sheet + persona/dpi/media route parity (ref #790)

* docs(plan): rich PDF character sheet + route parity implementation plan (ref #790)

* feat(toolbox): rich PDF character sheet — pips, on/off inventory, quests (ref #790)

* feat(toolbox): factor _enrich_report_data + apply to token/admin PDF routes (ref #790)

* feat(toolbox): populate Quêtes/menaces from raw DPI alerts (dest/detail) — HTML + PDF (closes #792, ref #790)

_dpi_stats now exposes alerts_raw (kind/service/dst/detail) alongside the donut
'alerts' ({label,count}). The persona Quêtes section (PDF _persona_block + HTML
.nr card) reads alerts_raw so each threat shows its destination + detail instead
of a dangling em-dash; donut 'alerts' kept for the DPI-Exfil tab; graceful
fallback to the donut label (no dangling dash) when alerts_raw is absent.

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-04 06:31:30 +02:00
09b369f269 docs(plan): per-vhost exposure switch 8-task TDD plan (ref #793) 2026-07-04 06:29:54 +02:00
bc4fd90eb4 docs(spec): per-vhost exposure switch (localhost/LAN/WAN + mesh/Tor) design (ref #793) 2026-07-04 06:24:58 +02:00
CyberMind
45f8397efd
Rapport kbin fidèle (PDF donut-grids + media types) + WebUI DPI + incident hardening (#787)
Some checks are pending
License Headers / check (push) Waiting to run
* docs(spec): rapport kbin fidèle + media types + WebUI DPI (ref #785)

* docs(plan): rapport kbin media types implementation plan (ref #785)

* feat(core): shared media-catch JSONL aggregator (ref #785)

* fix(core): guard media-catch aggregate against malformed fields + full SPDX header (ref #785)

* feat(toolbox): _media_stats + wire media_exfil into report routes (ref #785)

* feat(toolbox): PDF DPI-exfil/overall donut-grids + media-type block (ref #785)

Replaces the text-bullet DPI/EXFIL section (per-device + overall) with
_pdf_donut_grid 4-donut grids and adds a new "TYPES DE MEDIAS CAPTES"
section (kinds/content-types donut grid + top-hosts emoji table), for
parity with the HTML report. Also guards the PDF footer's italic
set_font against DejaVu-Oblique not being registered (fonts-dejavu-core
ships Regular+Bold only) — a pre-existing latent crash on any full
render_pdf() call, uncovered because no prior test exercised the whole
function end-to-end.

* fix(toolbox): per-grid captions so network/media donuts aren't mislabeled as device stats (ref #785)

* feat(toolbox): media-type cards in report web page (me + overall) (ref #785)

* fix(toolbox): close media card divs so Overall tab + footer aren't nested in DPI pane (ref #785)

* feat(dpi): WebUI card — services by category (bytes) (ref #785)

* feat(dpi): /media_types endpoint (MIME media-catch, fail-empty) (ref #785)

* feat(dpi): WebUI card — MIME media types + refreshAll wiring (ref #785)

* chore: gitignore SDD run scratch + drop stray task report (ref #785)

* perf(core): bounded tail-read for media-catch + import-in-try for uniform fail-empty (ref #785)

* fix(dpi): backport board-live async control endpoints (drift reconciliation, ref #785)

The board ran a manually-deployed main.py with status/restart/start/stop/logs/
interface_list/tc_status/remove_mirred as 'async def' — never committed to git.
Backported so the next .deb install doesn't revert them. Signature-only change,
verified identical to the live-validated deployed file.

* fix(toolbox): PDF render must not wedge the event loop (#785 incident)

Heavier #785 report PDFs (more matplotlib donut-grids, ~9s render) + the WAF
504-page auto-retry storm pegged the single uvicorn worker and 504'd the whole
toolbox vhost. Fixes:
- run render off the event loop (threadpool) so HTML report / landing stay live
- serialize renders through one asyncio lock (pyplot is not thread-safe; also
  bounds CPU so retries can't render in parallel)
- short per-device PDF cache with a double-checked lock: a retry storm now
  triggers exactly ONE render, all other hits return cached bytes instantly
- persistent MPLCONFIGDIR (systemd drop-in + postinst dir) so matplotlib builds
  its font cache once instead of on every process start

Validated live on gk2: cold render caches, 8-request retry storm all served
from cache (~0.1s), /landing 0.012s throughout.

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-03 12:27:28 +02:00
CyberMind
ff8ba36ff0
feat(proxypac): WPAD/PAC auto-config routing to active mesh services (Closes #784) (#788)
Some checks are pending
License Headers / check (push) Waiting to run
* docs(spec): secubox-proxypac WPAD/PAC mesh-service routing design (ref #784)

* docs(plan): secubox-proxypac 11-task TDD implementation plan (ref #784)

* feat(annuaire): optional byte-stable pac descriptor on ServiceOffer (ref #784)

* feat(proxypac): PAC template + fail-open directive builder (ref #784)

* feat(proxypac): rules.d parsing + precedence compose (ref #784)

* feat(proxypac): p2p /services catalog reader → routing rules (ref #784)

* feat(proxypac): generator + atomic fail-safe write + CLI (ref #784)

* fix(proxypac): shadow file is <pac>.shadow not <base>.shadow (ref #784)

* feat(proxypac): WebUI API override CRUD + candidates + audit (ref #784)

* fix(proxypac): regen honors RULES_DIR + reject whitespace in override host (ref #784)

* feat(proxypac): nginx PAC/WPAD serving (LAN-only) + seed rule (ref #784)

* fix(proxypac): gate /proxy.pac to LAN/mesh (never public) (ref #784)

* feat(proxypac): regen path+timer units + DHCP 252 WPAD advertise (ref #784)

* feat(proxypac): WebUI override panel + menu entry (ref #784)

* fix(proxypac): correct menu schema (name/category) + escape panel output (ref #784)

* feat(proxypac): debian packaging (ref #784)

* fix(proxypac): drop duplicate debian/compat + correct secubox user creation (ref #784)

* docs(proxypac): README + deploy notes (ref #784)

* fix(proxypac): apply final-review follow-ups (ref #784)

- compose: overrides now last-file-wins per host, so an operator's later
  50-webui.rules override beats a shipped 00-onion.rules seed for the same glob
  (explicit policy > defaults); cross-source override>service>toolbox unchanged.
  New test test_compose_override_last_file_wins.
- packaging: declare Depends: secubox-hub — the /proxy.pac allow/deny gate matches
  $remote_addr, which behind HAProxy is 127.0.0.1 unless secubox-hub's real_ip
  (X-Forwarded-For) rewrite is loaded; without it the PAC would be world-readable.
  nginx/proxypac.conf comments the dependency; test asserts it in control.
- README: document last-file-wins precedence + the real_ip/secubox-hub dependency.

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
2026-07-03 11:57:26 +02:00
CyberMind
90a323e6e8
secubox-p2p: Kademlia DHT + federation health-checks + hierarchical master-link (#774) (#775)
Some checks are pending
License Headers / check (push) Waiting to run
* feat(p2p): DHT, Federation, MasterLink evolutions for P2P-EVO-2026-07-001

Implements three major features for secubox-p2p:

1. DHT (Distributed Hash Table) based on Kademlia algorithm
   - Peer discovery across different subnets
   - Resilience to temporary node failures
   - Scalability for large number of nodes
   - REST API endpoints for DHT management

2. Service Federation
   - Service registration and discovery
   - Multi-version service management
   - Automatic health checks
   - DHT integration for service propagation

3. Master-Link Hierarchical Topology
   - Master/satellite/leaf hierarchical structure
   - Automatic master election and failover
   - Heartbeat monitoring
   - Routing policy management
   - OPAD security integration

New files:
- api/dht.py: Kademlia DHT implementation
- api/federation.py: Service federation system
- api/masterlink.py: Hierarchical topology manager
- api/main_evolutions.py: Integrated FastAPI server
- tests/test_dht.py: Unit tests for DHT

Issue: P2P-EVO-2026-07-001
Worktree: secubox-p2p-evolutions

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>

* revert(p2p): remove non-integrating DHT/federation/masterlink code, keep requirements

The DHT/federation/masterlink modules + main_evolutions.py + test_dht.py did not
integrate with the module: tests imported 'secubox.p2p.api.*' (the package uses
'from api import ...' via conftest sys.path) so nothing even collected, and the
modules reinvented standalone aiohttp servers instead of building on the existing
WireGuard mesh (mesh.py) + annuaire-backed registry (registry.py/annuaire_client.py).
Removing them to restart on the real base. The requirements doc
(.github/ISSUES/2026-07-P2P-EVOLUTIONS.md) is kept as the spec source.

* docs(p2p): point the local evolutions note to real issue #774

The '.github/ISSUES/2026-07-P2P-EVOLUTIONS.md' was a local file with a fabricated
id, not a tracked issue. Replace its contents with a pointer to the real GitHub
issue #774 (Kademlia DHT + hierarchical master-link + federation health-checks),
framed against the already-done federation (#766) and directory (#768) work and
the master-link/auto-enrollment issue (#762).

* docs(spec): p2p Kademlia DHT + master-link + federation health-checks (#774)

Clean redesign on the real base (mesh.py + registry/annuaire_client) after
reverting the non-integrating attempt. Custom minimal Kademlia (pure asyncio
UDP, JSON wire, signed reachability records), federation health-checks layered
over the annuaire federation (#766) publishing status via the DHT, and a
deterministic-election master-link (term-based failover, aligns #762). All
feature-flagged off by default (OPAD opt-in), from api import convention, TDD
per module. Refs #774.

* docs(plan): p2p DHT/federation/master-link TDD implementation plan (#774)

17 bite-sized TDD tasks across 3 phases (DHT 1-9, federation health-checks
10-13, master-link 14-16, finalization 17). Each task = failing test -> minimal
impl -> pass -> commit, on the real base (from api import ...). Crypto (Ed25519
sign/verify) and UDP transport are module-level seams injected in unit tests so
the Kademlia primitives, health debounce, and election/term logic are tested
without real sockets. Refs #774.

* feat(p2p): DHT scaffold — node id + xor distance (#774)

* feat(p2p): DHT k-bucket with LRU (#774)

Add DHTNode contact + DHTBucket (k-bucket with LRU ordering).

- DHTNode: dataclass with node_id, did, endpoint, last_seen
- DHTBucket: Kademlia k-bucket with OrderedDict LRU management
  - add(node) → bool (True if stored/refreshed, False if full)
  - refresh moves node to tail (most-recent)
  - oldest() returns head node
  - remove(node_id) and nodes property for list access

All Task 1 tests + new Task 2 tests passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(p2p): DHT routing table + closest-N (#774)

* feat(p2p): DHT signed reachability records + verify (#774)

Implement canonical_record, sign_record, verify_record functions with
crypto SEAMS (_did_from_pubkey, _verify_sig, _sign_sig) for testing.
Tests use monkeypatching; real crypto wired in Task 8.

Canonical records are deterministic sorted JSON. verify_record checks
for sig field, DID validity via wg_pubkey, and cryptographic signature.

* feat(p2p): DHT JSON UDP codec + hardening (#774)

* feat(p2p): DHTNetwork store+dispatch (transport-injected) (#774)

* feat(p2p): DHT iterative find_node/find_value + announce (#774)

Add asyncio-based iterative Kademlia lookup on top of the injected
DHTNetwork transport from Task 6:

- pending: dict[rpc_id, Future] on DHTNetwork, resolved by handle_message
  when a reply (pong/nodes/value/ok) arrives with a matching rpc_id.
- _rpc(node, msg): send + await the matching future with RPC_TIMEOUT,
  returns None on timeout.
- iterative_find(target_id, mode): alpha-parallel iterative lookup,
  dedups queried nodes, merges discovered contacts into the shortlist
  (dedup by node_id, skip self), terminates on no-improvement-in-a-round
  or exhausted candidates; mode="value" short-circuits on first verified
  record.
- find_peer(did) / announce(): DID resolution and self-record
  publication (sign, cache locally, push to the k closest nodes found).

Tests (tests/test_dht.py, +2 async, pytest-asyncio strict mode):
- in-process UDP router delivering via call_soon (never inline) so
  awaited RPC futures resolve on a later loop turn;
- find_peer resolves a record announced by a node reachable only
  through a bootstrap intermediary;
- dedup: a node introduced twice into the shortlist (via two different
  repliers) is queried exactly once.

All 16 tests green (14 prior + 2 new).

* fix(p2p): harden DHT iterative lookup — skip malformed contacts, cap shortlist, tolerate rpc exceptions (#774)

* feat(p2p): DHT record schema (id+wg keys) + real Ed25519 sign/verify (#774)

* feat(p2p): DHT UDP transport + routing persistence + bootstrap (#774)

* feat(p2p): mount DHT — endpoints + startup guard + [dht] config (#774)

Task 9: wire the finished Kademlia DHT (api/dht.py) into the FastAPI
app as a feature-flagged background service, fully backward compatible.

- api/mesh.py: load_p2p_config() now also returns a `dht` sub-dict
  (enabled/port/bootstrap/announce/announce_interval/rps) built from an
  optional [dht] toml section, with defaults when absent.
- api/main.py: @app.on_event("startup") _dht_startup creates+binds a
  DHTNetwork only when [dht].enabled is true; any failure (missing
  identity, bind error, ...) is caught and leaves app.state.dht = None
  so the app always starts. _dht_shutdown persists the routing table
  and closes the UDP transport.
- New endpoints (bare paths, nginx adds /api/v1/p2p prefix): GET
  /dht/peers (routing snapshot), POST /dht/announce (JWT-protected,
  audit-logged to /var/log/secubox/p2p-audit.log), GET /dht/find/{did}.

Tests: tests/test_dht_wiring.py (config defaults/overrides + disabled
TestClient smoke). All 22 existing DHT tests + 3 new tests green (74
total in the package).

* feat(p2p): federation HealthStore + debounce (#774)

* feat(p2p): federation HealthChecker sweep (#774)

* feat(p2p): federation real probe + registry services (#774)

* fix(p2p): bound TCP-probe close, declare aiohttp dep, guard non-dict service (#774)

* feat(p2p): federation health publish-via-DHT + endpoints + [federation] config (#774)

* fix(p2p): wire federation probe_timeout + harden sweep/health-TTL tests (#774)

* feat(p2p): master-link elect() + term store (#774)

* feat(p2p): master-link state machine + failover (#774)

* fix(p2p): master-link equal-term tie-break (no zero-master window) + defensive peers (#774)

* feat(p2p): master-link signed heartbeats + UDP transport + tick loop (#774)

* feat(p2p): mount master-link — endpoints + startup guard + [masterlink] config (#774)

- masterlink.MasterLink.request_promotion(): on-demand promotion attempt
  (bump term, run election, become MASTER + heartbeat on win, else CANDIDATE)
- mesh.load_p2p_config(): add [masterlink] section defaults/overrides
  (enabled, role_preference, priority, heartbeat_interval, election_timeout,
  port, peer_addrs) alongside existing [dht]/[federation]
- main.py: _masterlink_startup/_masterlink_shutdown mirror the DHT/federation
  defensive startup pattern (feature-flagged, never breaks app startup);
  GET /masterlink/topology (public) + POST /masterlink/promote (JWT, audited
  to p2p-audit.log)

* fix(p2p): master-link request_promotion records winner on loss + strengthen test (#774)

* docs(p2p): document DHT/federation/master-link evolutions (#774)

Add an "Evolutions (#774)" section to packages/secubox-p2p/README.md
covering the DHT (api/dht.py), federation health-checks (api/federation.py)
and hierarchical master-link (api/masterlink.py) subsystems: what each does,
their API endpoints, the real p2p.toml config defaults from api/mesh.py,
and the audit log. All three remain OFF by default / OPAD opt-in. Docs only,
no behavior change. Append a matching dated entry to .claude/HISTORY.md.

* fix(p2p): final-review fixes — audit-log path, master-link peers_fn, wg_pubkey, handle_message hardening (#774)

* fix(p2p): implement mesh visualization tab — load on activation + master-link roles (#774)

The Mesh tab canvas was never rendered (the tab-switch handler didn't call
initMesh, and drawing into a hidden tab gave clientWidth 0 -> blank canvas).
Now: initMesh fires when the Mesh tab is activated, retries if the canvas has
no layout yet, and enriches the star graph with the live master-link state
(role/term/master + DHT peer count from /masterlink/topology + /dht/peers) —
center node coloured gold=MASTER / cyan=SATELLITE, peers by online status.

* docs(p2p): poster GPT + roadmap des évolutions DHT/federation/master-link; maj WIP/HISTORY/TODO (ref #774)

---------

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 06:59:35 +02:00
550e3d3482 docs(spec): toolbox cert-mismatch warn-and-allow (no 502) design
Some checks are pending
License Headers / check (push) Waiting to run
sbxmitm classifies a non-validating upstream cert (expired/self-signed/
hostname-mismatch/third-party) without ever hard-failing, keeps the site
accessible under the R3 CA, injects a per-site dismissible identity-warning
banner (keyed on cert fingerprint, re-alerts if it changes), logs a
cert_anomaly SOC event, and notifies only on the third-party (active
impersonation) class. Replaces the current 502 on upstream cert failure.
2026-07-02 10:04:49 +02:00
520ff18bf9 docs(notes): Gondwana trust-substrate poster research-note — vulgarized sketch + image-gen prompt + roadmap 2026-07-01 15:15:44 +02:00
1608f7cdea docs(macro): M2 implementation plan — macro subsystem + tor-exit (3 packages) 2026-07-01 07:15:35 +02:00
a8291fa554 docs(macro): M2 design spec — macro subsystem + tor-exit reference kind (secubox-macro)
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-30 17:15:44 +02:00
599aa550b1 docs(p2p): implementation plan — Service Registry as live annuaire catalog view (M1) 2026-06-30 12:19:27 +02:00
8471a4df03 Merge branch 'feature/768-gondwana-p1-directory-bridge-secubox-p2p'
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-30 11:57:02 +02:00
489ff1184f docs(p2p): design spec — p2p Service Registry as live view of annuaire catalog (M1) + deferred macro subsystem 2026-06-30 11:41:54 +02:00
9b3c1e91fe feat(annuaire): directory primitives — NodeRecord + ConfigBlob models (ref #768)
First slice of gondwana P1: make the annuaire the distributed directory.
Adds Op.NODE_PUBLISH/CONFIG_PUBLISH/CONFIG_REVOKE and the NodeRecord (signed
mesh peer registry, public wg key only) + ConfigBlob (signed, versioned,
single-writer config distribution) models — modelled on ServiceOffer.
Carries the consolidated clone/distribute/emancipate roadmap doc.

TDD: tests/test_directory.py (9 tests); full annuaire suite 136 green.
2026-06-30 09:22:37 +02:00
021710f01c feat(annuaire): service model (ServiceOffer/Subscription, ApprovalMode, ops) + design plan 2026-06-30 08:07:10 +02:00
3e4d431bd9 docs(annuaire): Annuaire·Miroir — federated self-certifying trust-substrate design
Some checks are pending
License Headers / check (push) Waiting to run
The decentralized trust directory hinging MirrorNet (below) <-> Gondwana
(above): two mirrored faces + the third (audited reconciliation is what is
authoritative). Reuses did:plc self-certifying identity (secubox-identity),
extends the live master-link flow with four signed-log verbs (AUTO-ADD /
INVITE / PROPOSAL / EMANCIPATE), a new BLAKE2b-chained SQLite-WAL audit log +
can() resolver, GK·HAM NIZK non-revoked-membership proofs, coordinate-free
jurisdiction tags + PSI. Honest substrate: an append-only LOG, not a
blockchain; staged monotone founder-emancipation; every ungranted guarantee
paired with its detection mechanism. Pydantic v2 model + FastAPI/SQLite-WAL
reference code (syntax-checked). Fulfils gondwana spec §8 distributed directory.
2026-06-30 06:27:09 +02:00
a9f349a57d docs(appstore): SecuBox App Store + Module Composer architecture sketch + GPT prompt
Catalog over the 128 secubox.yaml manifests; granular control
(module/component/navbar/appearance); profiles (4R atomic apply); P2P-mirrored
apt repo + federated catalog; multi-service mesh agents. Includes a complete
GPT architectural-research-notes prompt (§10).
2026-06-29 13:09:15 +02:00
7206350c34 docs(gondwana): fix P1 plan version bump 1.7.2->1.7.6 (1.7.5 already shipped)
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-29 10:38:39 +02:00
29ac8c311c docs(gondwana): Phase 1 implementation plan (mesh substrate)
10-task TDD plan: api/mesh.py pure logic (collision guard, p2p.toml loader,
master-assigned IP, wg.conf parse/render/adopt, DDNS name), wire into
api/main.py (defaults 10.100.0.0/24->10.10.0.0/24, 51820->51822), root
sbx-mesh-up provisioner, packaging (1.7.2, wireguard-tools dep), then
zero-disruption cutover on gk2 (key adoption preserves c3box handshake)
and amd64 enrollment as .3 with verification.
2026-06-29 10:38:10 +02:00
74959276b6 docs(gondwana): rendezvous-as-role + per-node DDNS + distributed directory
Fold two design refinements into the roadmap without expanding Phase 1:
- Rendezvous is a role any node can hold (gk2 active now, not hardwired);
  each node carries a DDNS name <boxname>.secubox.in as part of identity;
  registry is local-first/replicable. Phase 1 = forward-compat only;
  availability-based floating failover is Phase 4.
- Cross-cutting distributed directory: shared state (peers/services/threats/
  names) migrates to a replicated DNS-structured append-only ledger
  (did:plc / Chain of Hamiltonians) underpinning Phases 2-4.
- Phase 4: per-node auto-registration -> <service>.<boxname>.secubox.in
  vhosts, DNS to gk2 public IP, Host-routed over wg-mesh.
2026-06-29 10:33:19 +02:00
d61d585f91 docs(gondwana): master_endpoint configurable (DDNS-ready), pinned to public IP
Satellite-facing endpoint is free-form host:port — accepts a DDNS hostname
(WireGuard re-resolves per handshake, survives WAN IP changes) or a literal
IP. Current deployment pins 82.67.100.75:51822; DDNS is a later one-line
config swap.
2026-06-29 10:29:13 +02:00
3fa951017b docs(gondwana): Phase 1 mesh substrate design spec
Multi-site WireGuard substrate + node identity for the gondwana mesh:
adopt secubox-p2p as the single mesh owner, fix the 10.100.0.0/24 ->
10.10.0.0/24 subnet collision with br-lxc, gk2 = public rendezvous (UDP
51822), hub-and-spoke routing, master-assigned addressing, persistent
per-node identity for the Phase 2 ZKP layer. Zero-disruption cutover via
private-key adoption. Phases 2-4 (ZKP enrollment, signed protection
sharing, service mirroring) scoped out for their own cycles.
2026-06-29 10:27:36 +02:00
bdafced25a docs(netstats): implementation plan — 11 TDD tasks (ref #758) 2026-06-27 10:54:47 +02:00
5b283b6bff docs(netstats): design spec — nft-based network stats dashboard (ref #758) 2026-06-27 10:14:23 +02:00
fb90349670 docs(sbxmitm): plan — SW revalidation nudge (ref #757) 2026-06-27 09:40:32 +02:00
3e8b3e80fd docs(toolbox): implementation plan — #ads aggregate MITM stats (ref #755) 2026-06-27 08:54:57 +02:00