Commit Graph

16 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
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
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
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
0f4e089556 feat(annuaire): auto-publish self NodeRecord on startup — 0.2.3 (ref #768) 2026-06-30 12:30:28 +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
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
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
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
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
b9c8b298e9 feat(annuaire): minimal dashboard UI (chain/merkle/log) + navbar visibility (0.1.2) 2026-06-30 07:54:52 +02:00
168b35707b build(annuaire): 0.1.1 (security hardening) 2026-06-30 07:51:30 +02:00
a0d372404c feat(annuaire): four verbs (auto-add/invite/proposal/emancipate) + FastAPI + packaging
- annuaire/verbs.py: auto_add / invite / accept_invite / propose / vote /
  tally / revoke / emancipate — pure service layer, no FastAPI.
  Enforces OBSERVED→MEMBER state machine, EM-MONOTONE invariant,
  one-voice-per-node dedup, bounded cascade revocation, M1/M2/M3 milestone
  gates for emancipate, and founder-anchor safety predicate.
- api/__init__.py + api/main.py: FastAPI on root_path=/api/v1/annuaire,
  Unix socket /run/secubox/annuaire.sock; read endpoints public,
  mutating endpoints JWT-gated; secubox_core import guarded for off-box use.
- tests/test_verbs.py: 24 new tests (97 total). EM-MONOTONE: level-decrease
  and same-level both rejected. Invitation: tampered sig / expired / over-used
  all rejected. Non-MEMBER inviter blocked. One-voice-per-node enforced.
- Packaging: debian/control, changelog (0.1.0-1~bookworm1), rules, postinst,
  prerm, secubox-annuaire.service (NoNewPrivileges=yes, RuntimeDirectoryPreserve=yes),
  secubox.yaml, nginx/annuaire.conf + annuaire-routes.conf, menu.d/575-annuaire.json.
2026-06-30 07:01:17 +02:00