Commit Graph

2419 Commits

Author SHA1 Message Date
fd47aa99b6 fix(annuaire): threatmesh-bridge reads cscli itself (heredoc/stdin clash) — 0.3.2 (ref #768)
Some checks are pending
License Headers / check (push) Waiting to run
2026-07-01 08:37:43 +02:00
dfa0a778c2 Merge M1: p2p Service Registry as live view of annuaire catalog (#769, PR #770)
secubox-p2p 1.8.0 — /services live-merges the federated annuaire catalog with a
thin activation overlay; Auto register all; consumer subscribes as the node.
Reviewed (SDD, Critical XSS + init_dirs regression fixed) + deployed gk2+c3box.
2026-07-01 08:37:39 +02:00
e1ef7c8105 feat(annuaire): threatmesh crowdsec->mesh bridge (the WAF hook) — 0.3.1 (ref #768) 2026-07-01 08:36:32 +02:00
38d8ad4428 feat(annuaire): threatmesh — bidirectional WAF ban federation + nft enforce — 0.3.0 (ref #768) 2026-07-01 08:26:42 +02:00
47cc9c8fd0 fix(appstore): sbx-apt-mesh prepend so mesh IP wins — 0.3.3 (ref #768)
Some checks are pending
License Headers / check (push) Waiting to run
2026-07-01 07:59:12 +02:00
1406a8b0b6 fix(appstore): sbx-apt-mesh via /etc/hosts mesh resolution — 0.3.2 (ref #768) 2026-07-01 07:57:21 +02:00
bad21e38a8 feat(appstore): sbx-apt-mesh — install packages from the mesh repo — 0.3.1 (ref #768) 2026-07-01 07:55:20 +02:00
f5ce4cc6bd feat(exposure): Tor + public egress channels federated into the directory — 1.2.0 (ref #768) 2026-07-01 07:52:20 +02:00
8fdd95aa55 feat(appstore): federated Mesh Catalog over the annuaire directory — 0.3.0 (ref #768) 2026-07-01 07:44:35 +02:00
9ee985bdb1 feat(exposure): real mesh egress channel — emancipate opens port + registers in directory (ref #768) 2026-07-01 07:25:14 +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
3f7b4b43e2 feat(annuaire): hands-off config-apply timer (opt-in scopes) — 0.2.7 (ref #768) 2026-06-30 17:14:44 +02:00
0c9e8c1774 feat(annuaire): P2 config distribution — 4R replica apply + annuairectl publish-config/apply — 0.2.6 (ref #768)
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-30 14:51:46 +02:00
9a447b004d docs(p2p): HISTORY entry for 1.8.0 annuaire-backed Service Registry (#769) 2026-06-30 13:11:57 +02:00
b969b8064d docs(p2p): correct _service_token comment — annuaire gate is is_enabled(), not a session validator (review #769) 2026-06-30 13:11:13 +02:00
383bd527c7 fix(p2p): service token must name a real master user (admin) — annuaire validates session (ref #769) 2026-06-30 13:06:23 +02:00
79078c4c25 fix(p2p): attach service JWT to annuaire subscribe (endpoint is JWT-gated) (ref #769) 2026-06-30 13:04:27 +02:00
6160c01673 release(p2p): 1.8.0 — annuaire-backed Service Registry (ref #769) 2026-06-30 13:00:57 +02:00
e6fab772fc fix(p2p): service_id onclick XSS (encodeURIComponent) + visible catalog-unavailable notice (review #769) 2026-06-30 12:58:59 +02:00
f7139d48e4 feat(p2p): Service Registry live catalog view + Auto register all UI (ref #769)
- Add "Auto register all" button to Services tab section-header
- Replace loadServices() render to consume new backend row shape:
  subscription_state, active, source, provider_label, automatable
- Remove stale svc.status field reference (old single-status field gone)
- Add autoRegisterAll(), requestAccess(), activateService() action fns
- All API-derived strings escaped via escapeHtml; IDs URI-encoded in POST paths
- Notification pattern: console.log/error (consistent with file; no showNotice helper)
2026-06-30 12:53:54 +02:00
8870f19542 docs(p2p): append review-fix report to task-3-report.md (review #769) 2026-06-30 12:50:37 +02:00
0e1c6c2f67 fix(p2p): revert init_dirs PermissionError swallow; monkeypatch init_dirs in tests (review #769) 2026-06-30 12:50:09 +02:00
36ed77c8d8 feat(p2p): /services live merge + auto-register/request/activate endpoints (ref #769)
- Add ACTIVATION_FILE constant to api/main.py
- Import registry and annuaire_client modules
- Replace GET /services with live catalog⨝subscriptions⨝overlay⨝legacy merge
- Add POST /services/auto-register: activates local offers, subscribes to remote
- Add POST /services/{service_id}/request: single-service subscription
- Add POST /services/{service_id}/activate: mark service locally active
- Harden init_dirs() for test monkeypatching (PermissionError guard + patched-path mkdir)
- Add tests/test_services_endpoints.py: 3 tests, all green (32/32 suite)
2026-06-30 12:44:44 +02:00
b0d2506cb8 fix(annuaire): robust satellite-dashboard guard (nginx -t arbitrates) — 0.2.5 (ref #768)
Some checks are pending
License Headers / check (push) Waiting to run
2026-06-30 12:39:33 +02:00
f1b8cb3872 fix(annuaire): auto-wire satellite dashboards via secubox.d API drop-in — 0.2.4 (ref #768) 2026-06-30 12:37:34 +02:00
768154ff25 feat(p2p): annuaire unix-socket client + node-key identity (ref #769)
Adds api/annuaire_client.py — a stdlib-only HTTP client over AF_UNIX
(/run/secubox/annuaire.sock, never the aggregator) with four public
functions: node_identity, get_catalog, get_subscriptions, subscribe.
All calls return (data, error) and never raise into the caller.
did_from_pubkey_hex mirrors annuaire/crypto.did_from_pubkey exactly.

Tests use a real _UnixSocketHTTPServer (BaseServer subclass with
fileno()) for the end-to-end socket test, plus real-missing-socket and
real-key-file tests. 4/4 pass; full suite 29/29.
2026-06-30 12:37:27 +02:00
0f4e089556 feat(annuaire): auto-publish self NodeRecord on startup — 0.2.3 (ref #768) 2026-06-30 12:30:28 +02:00
a3df9ebe6c feat(p2p): pure registry merge + activation overlay (ref #769) 2026-06-30 12:29:33 +02:00
599aa550b1 docs(p2p): implementation plan — Service Registry as live annuaire catalog view (M1) 2026-06-30 12:19:27 +02:00
6f27570f25 fix(annuaire): durable wg-mesh :8799 nft allow on vortex nodes — 0.2.2 (ref #768) 2026-06-30 12:00:57 +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
35d87dfe3c feat(annuaire): integrate P1 directory onto #766 — 0.2.1 (ref #768)
Wires the gondwana P1 directory (NodeRecord/ConfigBlob/full-log federation +
mesh_sync loop) onto #766's trustless bootstrap:
- mesh listener (annuaire-mesh.conf.tpl) now also serves GET /log/export
  (signed, self-certifying, read-only) for cross-node replication.
- mesh_sync pulls /log/export from the #766 listener (wg-mesh-ip:8799), not the
  main :9080.
- node identity reuses #766's genesis()/annuairectl/node.key.
Combined suite 167 green.
2026-06-30 11:45:22 +02:00
e1fb33d668 Merge remote-tracking branch 'origin/feature/766-annuaire-trustless-cross-node-service-fe' into feature/768-gondwana-p1-directory-bridge-secubox-p2p 2026-06-30 11:42:22 +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
af05954590 feat(annuaire): in-process directory replication loop (ref #768)
Slice 5b of gondwana P1: the wg-mesh directory sync. Runs IN-PROCESS in the
annuaire service (the journal owner) rather than an external p2p timer — this
sidesteps both the JWT session-validator (no localhost bypass in require_jwt)
and the single-writer journal race. secubox-p2p stays the peer-list owner
(wg_mesh.json); annuaire only reads it.

- annuaire/mesh_sync.py: read_mesh_peers + sync_once (network injected) — pulls
  each peer's public /log/export over the mesh (nginx 0.0.0.0:9080) and merges
  via import_entries; never raises on an unreachable peer.
- api/main.py: @on_event startup launches the loop (blocking fetch+merge in a
  worker thread); disabled via ANNUAIRE_DIR_SYNC=0.

TDD: +5 tests (converge / unreachable / idempotent / peer-parse); suite 160 green.
2026-06-30 11:36:23 +02:00
e9e02739f6 feat(p2p): [mesh] role placement field (primary|replica|gateway) (ref #768)
Slice 5 (part 1) of gondwana P1. Adds the SERVICE-placement role to p2p.toml,
distinct from [wireguard].role (transport rendezvous). Consumed by the
directory / config-apply / routing layers; single-writer = the 'primary'.
2026-06-30 11:27:58 +02:00
6d4cb2f2e0 docs(annuaire): HISTORY entry for 0.2.0 trustless federation (#766) 2026-06-30 11:27:30 +02:00
2efb4485fd feat(annuaire): directory HTTP endpoints — nodes/config/log-export/pull (ref #768)
Slice 4 of gondwana P1: thin FastAPI layer over the tested directory verbs.
- GET /nodes, GET /config[?scope=]  (public reads)
- GET /log/export  (public; full signed log + author_pubkey for federation —
  distinct from the truncated recent GET /log)
- POST /node/publish, /config/publish, /config/revoke  (JWT)
- POST /log/pull  (JWT; fetch a peer /log/export, import_entries; graceful on
  an offline peer)

TDD: tests/test_api_directory.py (5 TestClient wiring tests); full suite 155 green.
2026-06-30 11:25:28 +02:00
742c5373a3 harden(annuaire): nginx-safe mesh listener render + non-local bind (review #766)
Security-review follow-ups (no exploitable forgery was found; these address
board-wide nginx availability):
- postinst validates the rendered /etc/nginx/conf.d/annuaire-mesh.conf with
  nginx -t and removes it on failure, so a broken render can never persist and
  break a later nginx -t by any other actor (reboot, sibling postinst).
- ship /etc/sysctl.d/30-secubox-nonlocal-bind.conf (ip_nonlocal_bind=1) so
  nginx binds the specific wg-mesh IP even when wg-quick@wg-mesh starts after
  nginx at boot (the board currently has it =0, and already binds specific IPs
  → real bind-failure risk). postinst applies it before rendering the listener.
2026-06-30 11:24:39 +02:00
797a30e30b feat(annuaire): federation gossip — export_entries / import_entries (ref #768)
Slice 3 of gondwana P1: the generic /log replication core. Federation
re-appends each foreign entry's payload+sig into the LOCAL chain (prev/entry
hashes are local), preserving the author's signature. Security:
- dedup by (author, sig) — idempotent re-pulls
- self-certifying binding enforced: did_from_pubkey(author_pubkey) == author
- sig verified over canonical payload BEFORE append
Forged-payload and author-spoofed entries are rejected, never landed.

TDD: +4 adversarial tests; directory suite 23 green, full suite 150 green.
2026-06-30 11:20:50 +02:00
f15c2ff0da feat(annuaire): trustless cross-node service federation + node bootstrap + mesh listener (ref #766)
Self-certifying federation (the core fix):
- ingest_offer enforces did_from_pubkey(pubkey) == offer.provider BEFORE the
  signature check — closes 'bring your own key, claim any provider DID'. The
  sig alone only proves key-ownership; did:plc = sha256(pubkey)[:32] binds the
  key to the DID with no directory and no prior trust.
- _get_offers / GET /services emit self-contained verifiable offers: the stored
  payload omits sig/signer_did and never carried the pubkey (so a stranger
  could not verify at all). Now carry sig + signer_did + provider_pubkey;
  provider_pubkey is transport metadata, stripped before reconstructing the
  extra=forbid ServiceOffer model.
- pull_services uses the carried pubkey → self-certifying ingest.

Node bootstrap:
- genesis() verb: a node self-attests as founding MEMBER (root of trust),
  breaking the invite/join/subscribe chicken-and-egg. Self-cert DID, empty
  invited_by, idempotent. Op.GENESIS (additive, chain-compatible).
- crypto.public_from_private().
- /usr/sbin/annuairectl: init|whoami|status|offer|services|pull. Operates the
  journal directly as secubox (no JWT for one-time bootstrap); key 0600 in
  /etc/secubox/secrets/annuaire/.

Mesh listener:
- nginx/annuaire-mesh.conf.tpl: binds ONLY wg-mesh IPv4:8799, allow
  10.10.0.0/24 + deny all, ONLY GET /api/v1/annuaire/services. postinst renders
  with detected mesh IP (skips if no mesh); postrm cleans up.

tests: +7 federation tests (134 passing). changelog 0.2.0.
2026-06-30 11:19:46 +02:00
875342471d feat(annuaire): directory verbs — publish_node / publish_config / revoke_config (ref #768)
Slice 2 of gondwana P1. Adds the self-certifying directory verbs modelled on
offer_service/ingest_offer:
- publish_node + _get_nodes (signed mesh peer registry)
- publish_config / revoke_config + _get_configs/_get_config (signed, versioned
  config; last-writer-wins by (version, height) so peers converge regardless of
  ingest order; revocation honored only from the publisher)
- ingest_config (federation, verify-before-append)
- can() right config.publish (MEMBER, ungated)

TDD: +10 tests; directory suite 19 green, full annuaire suite 146 green.
2026-06-30 11:18:02 +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
a2ce99d14b fix(yacy): postinst always systemctl restart (deb-systemd-invoke refuses disabled units) + HISTORY
Some checks are pending
License Headers / check (push) Waiting to run
dh_installsystemd's deb-systemd-invoke restart won't start a disabled unit, so
an upgrade on a board where secubox-yacy.service was disabled left
/run/secubox/yacy.sock gone and nginx 502. postinst now does an unconditional
plain systemctl restart (host uvicorn API has no LXC dependency, safe on fresh
installs). Documents the live nginx drift root-cause + Phase-2 detection fix.
2026-06-30 09:14:55 +02:00
946ebe9a7c fix(yacy): complete 1.0.12 changeset — changelog + rules sudoers-install + privilege-free curl probe
Some checks are pending
License Headers / check (push) Waiting to run
ef09b108 shipped the index.html/yacyctl/sudoers but dropped the debian/changelog
1.0.12 block and the rules sudoers-install line, and a later stash-pop layered the
final probe-based yacyctl on top. This reconciles source to the deployed board:
 - debian/changelog: 1.0.12-1~bookworm1 entry
 - debian/rules: install /etc/sudoers.d/secubox-yacy
 - yacyctl: liveness PROBED via curl http://$LXC_IP:$HTTP_PORT/ (no privilege;
   preserves the hardened User=secubox+NNP unit); lxc-info best-effort enrichment
 - sudoers comment: reflects probe-not-introspect rationale (reload still needs root)
2026-06-30 08:23:48 +02:00
ef09b108da feat(yacy): repair webui embed (runtime src from /access) + navbar template migration (1.0.12) 2026-06-30 08:20:15 +02:00
7f74ff25a3 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.claude/HISTORY.md
2026-06-30 08:19:09 +02:00
b49cda5643 Merge branch 'feature/annuaire-services' — annuaire service offers + subscription (v0.1.3) 2026-06-30 08:17:46 +02:00
0d77b64f6b feat(annuaire): service offers + subscription verbs/API/UI + pull federation
- annuaire/verbs.py: offer_service, revoke_offer, subscribe,
  approve_subscription, reject_subscription, ingest_offer,
  subscription_state, _get_offers/_get_offer helpers.
  Security: self-certifying authorship enforced; only offer provider may
  approve/reject/revoke; approve entries from non-providers are ignored.
- api/main.py: GET /services, POST /service/offer,
  POST /service/{id}/revoke, POST /service/{id}/subscribe (returns derived
  state), GET /subscriptions (?mine=/?pending_for=),
  POST /subscription/{id}/approve, POST /subscription/{id}/reject,
  POST /services/pull (federation pull with 5s timeout, never crashes).
- www/annuaire/index.html: Services + My subscriptions panels with
  per-offer Subscribe button, state badges, defensive fetch.
- tests/test_services.py: 28 tests — AUTO→approved, PENDING→approve→APPROVED,
  PENDING→reject→REJECTED, non-provider-approve blocked at call layer and
  defense-in-depth (forged log entry ignored), non-provider revoke blocked,
  provider revoke removes offer + blocks new subscribe, OBSERVED subscribe
  blocked, ingest_offer valid/wrong-key/tampered/forged all tested.
- debian/changelog: bump to 0.1.3-1~bookworm1.
2026-06-30 08:14:32 +02:00