secubox-annuaire (0.3.2-1~bookworm1) bookworm; urgency=medium * feat(#768): threatmesh WAF hook — crowdsec -> mesh bridge. /usr/sbin/sbx-threatmesh-bridge reads local `cscli decisions list` and publishes each NEW ban into the mesh via `annuairectl ban` (TTL = the crowdsec duration; reason = the scenario), idempotent via a state file. A timer runs it ~90s. Net: crowdsec detects on one node -> signed ban federates -> every peer enforces it in the secubox_meshban nft set -> auto-expires. Safe no-op where crowdsec is absent. -- Gerald Kerma Wed, 01 Jul 2026 16:30:00 +0200 secubox-annuaire (0.3.0-1~bookworm1) bookworm; urgency=medium * feat(#768): threatmesh — bidirectional WAF/threat ban federation. - model: BanRecord + Op BAN_PUBLISH/BAN_REVOKE. verbs: publish_ban, revoke_ban, _get_bans (convergent UNION of active bans across publishers, TTL + per-publisher revoke), banned_ips. Bans gossip over the same convergent log as node/config records — a ban on ANY node reaches ALL nodes, no master. - threatmesh.apply_bans: rebuilds a self-contained `inet secubox_meshban` nft table (own table, never touches the main firewall) dropping the union. - annuairectl ban/unban/bans/bans-apply; GET /bans; a bans-apply timer (~60s, enabled by default — empty set is a no-op). - +8 tests; suite 185 green. -- Gerald Kerma Wed, 01 Jul 2026 15:30:00 +0200 secubox-annuaire (0.2.7-1~bookworm1) bookworm; urgency=medium * feat(#768): hands-off config apply — secubox-annuaire-apply.timer. A root oneshot (secubox-annuaire-apply.service) runs `annuairectl apply` every ~2 min for the scopes listed in /etc/secubox/annuaire-apply.conf (ANNUAIRE_APPLY_SCOPES, comma list or *). EMPTY = no-op, so the timer is enabled by default safely; the operator opts in per scope. prerm disables the timer on removal. -- Gerald Kerma Tue, 30 Jun 2026 19:00:00 +0200 secubox-annuaire (0.2.6-1~bookworm1) bookworm; urgency=medium * feat(#768): P2 config distribution — replica-side 4R apply. - annuaire/config_apply.py: apply a signed ConfigBlob (payload carries the config TEXT) to /.toml through a CSPN 4R double-buffer (shadow -> validate content_hash + TOML parse -> atomic swap -> keep .sbx-rollback). Idempotent by version; rejects hash-mismatch / unparseable without touching the live file. Allowlist + single-writer gating (a node never applies a scope it published). - annuairectl: `publish-config` (primary publishes a TOML file as a signed, versioned blob) and `apply` (replica applies allowed blobs, run as root). - +7 tests; suite 177 green. -- Gerald Kerma Tue, 30 Jun 2026 18:30:00 +0200 secubox-annuaire (0.2.5-1~bookworm1) bookworm; urgency=medium * fix(#768): correct the satellite-dashboard guard. The 0.2.4 check `nginx -T | grep secubox-routes.d` matched config-file *comments*, not real includes, so it wrongly removed the API drop-in and re-broke satellite dashboards. Replaced with the robust approach: add the route, then let `nginx -t` arbitrate — drop it back only if it duplicates a location already active in the same (hub) vhost. nginx stays valid either way. -- Gerald Kerma Tue, 30 Jun 2026 17:45:00 +0200 secubox-annuaire (0.2.4-1~bookworm1) bookworm; urgency=medium * fix(#768): satellite Annuaire dashboards. The legacy nginx vhost (sites-available/secubox) includes secubox.d/ but not secubox-routes.d/, so the dashboard's /api/v1/annuaire/ calls fell through to the SPA shell (empty UI on non-hub nodes). postinst now drops the API route into secubox.d/ on those nodes (idempotent, nginx -t validated, reverted on failure); hub nodes whose active config already includes secubox-routes.d are left untouched. -- Gerald Kerma Tue, 30 Jun 2026 17:15:00 +0200 secubox-annuaire (0.2.3-1~bookworm1) bookworm; urgency=medium * feat(#768): each node auto-publishes its own NodeRecord on startup. The annuaire service (journal owner → no JWT, no write race) reads its mesh metadata from secubox-p2p's wg_mesh.json + node.id and publishes a signed NodeRecord into the directory, so /nodes lists the actual peers (not just replicated offers). Idempotent: skipped when the latest record is unchanged. Requires the node identity key from `annuairectl init`; no-op without it. +5 tests (read_self_meta, publish-self idempotency); suite 170 green. -- Gerald Kerma Tue, 30 Jun 2026 16:30:00 +0200 secubox-annuaire (0.2.2-1~bookworm1) bookworm; urgency=medium * fix(#768): durable wg-mesh nft allow for the :8799 federation listener. postinst inserts `iifname wg-mesh ip saddr 10.10.0.0/24 tcp dport 8799 accept` BEFORE the terminal drop in /etc/nftables.conf on vortex-firewall nodes (table inet secubox_filter) so it survives reboot — a rule appended after the drop is dead. Idempotent; guarded to mesh nodes; validated with `nft -c` and reverted on failure. Legacy `inet filter` hubs (broad 10/8 accept) are skipped. Closes the cross-node gap where gk2 could not pull a peer's /log/export after the peer rebooted. -- Gerald Kerma Tue, 30 Jun 2026 15:30:00 +0200 secubox-annuaire (0.2.1-1~bookworm1) bookworm; urgency=medium * feat: gondwana P1 distributed directory, on top of #766 (#768) - model: NodeRecord (signed mesh peer registry, public wg key only) + ConfigBlob (signed, versioned, single-writer config) + Op NODE_PUBLISH / CONFIG_PUBLISH / CONFIG_REVOKE. Coexists with #766's Op.GENESIS. - verbs: publish_node / publish_config / revoke_config + _get_nodes / _get_configs (last-writer-wins by version → convergent across the mesh); ingest_config; export_entries / import_entries generic log federation (dedup by (author,sig), self-certifying binding + sig verified before append — same defense #766 added to ingest_offer, generalized to the whole log). resolver: config.publish right (MEMBER). - api: GET /nodes, /config, /log/export; POST /node/publish, /config/publish, /config/revoke, /log/pull. - mesh_sync: in-process replication loop (the annuaire service owns the journal → no JWT, no write race) pulling each peer's /log/export from the #766 Gondwana mesh listener (wg-mesh-ip:8799). ANNUAIRE_DIR_SYNC=0 disables. secubox-p2p stays the peer-list owner (wg_mesh.json). - nginx/annuaire-mesh.conf.tpl: the mesh listener now also serves GET /log/export (signed, self-certifying, read-only) for replication — not just /services. - node identity reuses #766's genesis()/annuairectl/node.key (no new key path invented). 40 directory/sync tests; full suite 167 green. -- Gerald Kerma Tue, 30 Jun 2026 14:30:00 +0200 secubox-annuaire (0.2.0-1~bookworm1) bookworm; urgency=medium * feat: trustless cross-node service federation (#766) - ingest_offer now enforces the self-certifying binding did_from_pubkey(pubkey) == offer.provider BEFORE checking the signature. Closes the "bring your own key, claim any provider DID" forgery: the sig alone only proves key-ownership, not that the key belongs to the claimed provider. did:plc is sha256(pubkey)[:32], so the check needs no directory and no prior trust in the provider. - _get_offers / GET /services now emit self-contained, verifiable offers: the stored payload omits sig/signer_did (not part of the signed bytes) and never carried the pubkey, so a stranger could not verify at all. Offers now carry sig + signer_did + provider_pubkey (transport metadata, stripped before model reconstruction — ServiceOffer is extra=forbid). - pull_services uses the carried provider_pubkey and feeds the now self-certifying ingest_offer. * feat: node bootstrap — genesis() verb + annuairectl CLI. - genesis(): a node self-attests as a founding MEMBER (root of trust), breaking the invite/join/subscribe chicken-and-egg. Self-certifying DID, empty invited_by (never inflates emancipation plurality), idempotent. - Op.GENESIS added (additive, chain-compatible). - crypto.public_from_private(): derive pubkey from the persisted private key. - /usr/sbin/annuairectl: init | whoami | status | offer | services | pull. Operates the journal directly as the secubox user (no JWT for the one-time privileged bootstrap); node key persisted 0600 in /etc/secubox/secrets/annuaire/node.key. * feat: Gondwana mesh federation listener (postinst-rendered). - nginx/annuaire-mesh.conf.tpl: binds ONLY the node wg-mesh IPv4:8799, allow 10.10.0.0/24 + deny all, exposes ONLY GET /api/v1/annuaire/services. postinst substitutes the detected wg-mesh IP; skips install when no mesh interface exists. postrm removes it on uninstall. * tests: +7 federation tests (genesis member/idempotent, offer enrichment, trustless round-trip pull, forged-pubkey + tampered-payload + bad-hex rejection). 134 passing. * hardening (security review): postinst validates the rendered mesh listener with `nginx -t` and reverts it on failure so a broken render can never persist (board-wide nginx blast radius); ship /etc/sysctl.d/30-secubox-nonlocal-bind.conf (net.ipv4.ip_nonlocal_bind=1) so nginx can bind the wg-mesh IP even if wg-quick@wg-mesh starts after nginx at boot. -- Gerald Kerma Tue, 30 Jun 2026 16:00:00 +0200 secubox-annuaire (0.1.3-1~bookworm1) bookworm; urgency=medium * feat: service offers + subscription verbs, API and UI + pull federation - annuaire/verbs.py: offer_service, revoke_offer, subscribe, approve_subscription, reject_subscription, ingest_offer, subscription_state, _get_offers, _get_offer helpers. - api/main.py: GET /services, POST /service/offer, POST /service/{id}/revoke, POST /service/{id}/subscribe, GET /subscriptions, POST /subscription/{id}/approve, POST /subscription/{id}/reject, POST /services/pull. - www/annuaire/index.html: Services section + My subscriptions panel. - tests/test_services.py: 28 new tests covering all security invariants. -- Gerald KERMA Tue, 30 Jun 2026 12:00:00 +0200 secubox-annuaire (0.1.2-1~bookworm1) bookworm; urgency=medium * ui: minimal shared-template dashboard (chain integrity, merkle root, trust log) at /annuaire/ — also makes the module appear in the navbar. -- Gerald KERMA Tue, 30 Jun 2026 09:30:00 +0200 secubox-annuaire (0.1.1-1~bookworm1) bookworm; urgency=medium * security: enforce self-certifying authorship at read time (an Identity entry only sets state for its own did:author); authorize revocation (only self or the grafting inviter), derive REVOKED from the signed RevocationNotice (no victim-identity forging); staged single-step emancipation with a required founder_did. +7 adversarial tests. -- Gerald KERMA Tue, 30 Jun 2026 09:00:00 +0200 secubox-annuaire (0.1.0-1~bookworm1) bookworm; urgency=medium * Initial release — Annuaire-Miroir trust substrate. - annuaire/verbs.py: auto_add / invite / accept_invite / propose / vote / tally / revoke / emancipate — the four protocols from spec §3. - annuaire/model.py, crypto.py, log.py, resolver.py: BLAKE2b-chained SQLite-WAL journal + Ed25519 signing + can() resolver (pre-existing core). - api/main.py: FastAPI on /run/secubox/annuaire.sock, JWT-gated mutating endpoints; read endpoints public. - Packaging: secubox-annuaire.service (User=secubox, RuntimeDirectoryPreserve=yes, NoNewPrivileges=yes), nginx routes + static conf, menu.d, secubox.yaml. -- Gerald KERMA Mon, 30 Jun 2026 08:00:00 +0200