Commit Graph

129 Commits

Author SHA1 Message Date
m17hr1l
00cd8ca252 db: NullPool + WAL + busy_timeout — fixes QueuePool exhaustion under federation+classify load 2026-06-07 11:57:07 +02:00
m17hr1l
77e4cb6ab9 deploy-all: redirect deploy.sh stdin from /dev/null so loop doesn't drop hosts 2026-06-07 02:06:45 +02:00
m17hr1l
9ba4cd2189 merge topology: per-peer container view in federation network detail panel 2026-06-07 01:59:23 +02:00
m17hr1l
155d6eaaf9 stage-topo-d topology-export: CLI fed-topology + SW v10 2026-06-07 01:58:27 +02:00
m17hr1l
d998be276b stage-topo-c topology-export: federation network panel renders peer containers 2026-06-07 01:57:29 +02:00
m17hr1l
367f17a013 stage-topo-b topology-export: /federation/topology endpoint + CORS cache 2026-06-07 01:56:09 +02:00
m17hr1l
a8216d00ef stage-topo-a topology-export: sanitized public docker snapshot module + tests 2026-06-07 01:55:49 +02:00
m17hr1l
8587e079bb federation/network: fetch peer's own /federation/explore/data on click + render their self-view inline (their peers, vouches in/out, transitive, translog head) 2026-06-07 01:21:58 +02:00
m17hr1l
cef3bcb1ed merge explore: public transparent federation explorer with cross-jump 2026-06-07 01:19:56 +02:00
m17hr1l
9ab3271bc8 stage-exp-f explore: tests 2026-06-07 01:17:11 +02:00
m17hr1l
c2bd68e246 stage-exp-e explore: link from home + info endpoint 2026-06-07 01:16:32 +02:00
m17hr1l
587fd07d38 stage-exp-d explore: JS — cross-jump navigation + verify button 2026-06-07 01:16:02 +02:00
m17hr1l
ca6ba83950 stage-exp-c explore: HTML template + landing layout 2026-06-07 01:13:51 +02:00
m17hr1l
a10203d8f1 stage-exp-b explore: public routes + CORS on existing public endpoints 2026-06-07 01:12:25 +02:00
m17hr1l
56466c334d stage-exp-a explore: public payload builder + tests 2026-06-07 01:11:17 +02:00
m17hr1l
351e16c3ce inference: openai-compatible mode + bearer auth (for api.neuronetz.ai etc.) 2026-06-07 01:09:19 +02:00
m17hr1l
2c7f71eff8 deploy: scripts/deploy-all.sh + hosts.example for multi-node federation rollouts 2026-06-07 01:03:31 +02:00
m17hr1l
925bf76a0b merge network-detail: rich detail panel, corroboration edges, 24h timeline, search 2026-06-07 01:01:42 +02:00
m17hr1l
0d9baef4c8 stage-netd-f network detail: tests for admin enrichment (stats/corroboration/timeline) 2026-06-07 01:00:39 +02:00
m17hr1l
980cf74b76 stage-netd-d cockpit SW: bump CACHE_VERSION to psyc-v7 for network detail CSS+JS 2026-06-07 00:57:53 +02:00
m17hr1l
70b6af6a35 stage-netd-c network detail: rich detail panel + hover tooltips + search/intensity + timeline JS 2026-06-07 00:57:49 +02:00
m17hr1l
15749e050e stage-netd-b network detail: corroboration edges + timeline strip (CSS + template) 2026-06-07 00:57:44 +02:00
m17hr1l
c6c5d3b2ea stage-netd-a network detail: enrich peer stats (signals/severity/vouches/quorum) 2026-06-07 00:52:41 +02:00
m17hr1l
e33c5b41f5 merge network-view: federation graph per node with vouches + signal flow 2026-06-07 00:43:13 +02:00
m17hr1l
865be2e239 stage-net-f network view: tests 2026-06-07 00:42:11 +02:00
m17hr1l
ff44e9e450 stage-net-e network view: CLI fed-network command 2026-06-07 00:40:43 +02:00
m17hr1l
5950d34deb stage-net-d network view: cockpit page + JS force-directed graph 2026-06-07 00:40:13 +02:00
m17hr1l
5ff6d80333 stage-net-c network view: transitive fetcher + admin data endpoint 2026-06-07 00:37:32 +02:00
m17hr1l
6dcaae39c3 stage-net-b network view: public endpoint + signed payload 2026-06-07 00:37:12 +02:00
m17hr1l
fbad78a611 stage-net-a network view: data model + local view builder 2026-06-07 00:36:29 +02:00
m17hr1l
77533eccb1 ui: widen content to 1600px + force long URLs/hashes to wrap (fixes 4K/1920 overflow) 2026-06-06 21:29:16 +02:00
m17hr1l
3e737d61b3 stage-34 wire admin nav: federation hub links to discovery + SW v4 2026-06-06 21:17:57 +02:00
m17hr1l
a53aacfdd8 merge auto-response: severity/quorum/local-only gated execution
# Conflicts:
#	src/psyc/db.py
2026-06-06 21:17:20 +02:00
m17hr1l
53ba537ce8 merge vouching+translog: web-of-trust + signed merkle audit log
# Conflicts:
#	src/psyc/_federation_cli.py
#	src/psyc/cockpit/federation_routes.py
2026-06-06 21:15:11 +02:00
m17hr1l
726117b19b merge discovery: DNS-SD walker + public peers endpoint 2026-06-06 21:13:29 +02:00
m17hr1l
c5472b3134 stage-auto-e pulse: tests for auto-response gating
Cover the auto-fire decision matrix:
- _severity_rank ordering
- mode != auto-execute → never fires (auto-propose, manual)
- below-threshold action is skipped + audited
- federation case + no quorum → skipped + audited "no quorum"
- federation case + quorum met → fires
- local case + quorum required + local-only on → still fires
- local case + quorum required + local-only off → still fires
- quorum gating disabled → federation cases fire too
- kill switch armed → tick() skips everything
- pulse_audit records both auto-fire and skip rows
- audit_count_since returns the per-action counts the cockpit needs
- config round-trips through pulse_settings

Tests patch federation.is_quorum_met (raising=False so the sibling
agent can ship the real function later without breaking these), and
swap respond.execute_action for a counter so no SOAR sink call escapes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:12:02 +02:00
m17hr1l
f5ca928f92 stage-auto-d pulse: cockpit auto-response state panel + CLI
Cockpit:
- /admin/pulse now renders an "AUTO-RESPONSE STATE" panel above the
  pipeline table — mode badge (traffic-light colored), threshold,
  quorum on/off, local-only on/off, auto-fired-in-24h count, last 5
  audit entries, and a one-form save for threshold + gates.
- POST /admin/pulse/respond-config writes the new gates.

CLI:
- pulse-respond-config [--threshold …] [--quorum/--no-quorum]
                       [--local-only/--no-local-only]
  Args left unset are unchanged; echoes the post-state.
- pulse-respond-status prints mode, gates, and the last 10 audit
  entries.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:11:52 +02:00
m17hr1l
e66c3d3359 stage-auto-c pulse: respond runner with gates + auto-fire path
Replace the propose-only respond runner with a two-phase runner: phase 1
always proposes actions for fresh high-severity cases (unchanged); phase 2
fires when pipeline mode is auto-execute and the action clears all gates.

Gates:
- severity ≥ configured threshold
- if require_quorum is on, federation-sourced cases must hit
  federation.is_quorum_met (wrapped in try/except so we tolerate the
  sibling agent not having shipped that function yet — fallback is
  "no quorum metric → don't auto-fire", the safe default)
- locally-generated cases (no row in federation_signals for their case_id)
  bypass the quorum check
- when local-only is armed, federation-sourced cases never auto-fire
  even with quorum

Every decision (auto-fire, skipped, error) records a pulse_audit row so
the cockpit and CLI can show history. Per-action try/except keeps one
bad action from aborting the whole batch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:11:39 +02:00
m17hr1l
f4148d86a6 stage-vouch-e federation: tests for vouching + quorum gate
test_vouching covers the contract auto-response and other agents will
gate on:

- issue_vouch round-trip (sign + verify under our own pubkey)
- accept_vouch rejects expired vouches
- accept_vouch rejects mismatched signatures
- accept_vouch rejects vouchers whose peers.status != "trusted"
- accept_vouch happy path
- is_vouched needs DISTINCT vouchers (two upserts from one peer == 1)
- is_vouched clears threshold with two distinct trusted vouchers
- is_quorum_met counts only listening-eligible peers (untrusted +
  duplicate rows don't count)
- quorum_config defaults + pulse_settings persistence
- import_signed_feed rejects unknown peer ("not trusted")
- import_signed_feed accepts directly-trusted peer
- import_signed_feed accepts a peer made eligible via two vouches
- import_signed_feed stores vouches embedded in a trusted peer's feed

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:11:18 +02:00
m17hr1l
0e56fa70af stage-vouch-d federation: cockpit pages + CLI + public endpoints
Public JSON endpoints (no auth):
- GET /federation/vouches — our_vouches() so peers can pull our trust
- GET /federation/log — last 100 transparency-log entries
- GET /federation/log/verify — re-walks the chain, returns
  {verified, head_hash} or 409 with {error, head_hash}

Admin pages (TOTP-gated):
- /admin/federation/vouches — issued list, issue form, revoke
  buttons, per-peer quorum-met table
- /admin/federation/log — chain verification status + last 200 entries
- /admin/federation/quorum — config form + per-peer eligibility +
  per-signal-hash distinct-eligible-peer counts

CLI: fed-vouch / fed-unvouch / fed-vouches / fed-quorum-set / fed-log /
fed-log-verify, plus existing fed-* commands untouched.

The base /admin/federation page now links to the three new sub-pages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:11:03 +02:00
m17hr1l
31ec1557ec stage-auto-b pulse: pulse_audit table + history
Add a per-decision audit log so the cockpit + CLI can show what the
auto-response runner did each tick:
- pulse_audit table: id, pipeline, action ('auto-fire'|'skipped'|'error'),
  action_id, case_id, detail, timestamp
- helpers: pulse_audit_record, pulse_audit_recent, pulse_audit_count_since
- indexes on (pipeline, timestamp desc) and on action_id

Also add db.signals_for_case(case_id) — checks the federation_signals
buffer to tell whether a case was peer-sourced. Used by the runner to
decide if a quorum check is required.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:10:38 +02:00
m17hr1l
eadd1aea3b stage-vouch-c federation: import gate + translog hook (stage-trans-b)
import_signed_feed now refuses any feed whose declared fingerprint isn't
peer_is_listening_eligible (directly trusted OR vouched in), returning
Err("peer not trusted: …") before any signal lands.

For every case/IOC it does record, it also appends a "signal" entry to
the transparency log (best-effort — logger warns but doesn't abort
ingest if the append fails). This is the stage-trans-b hook: the
import path is the chokepoint, so attaching the chain there gives
us coverage of every peer-originated signal we've ever accepted.

build_signed_feed now includes our_vouches() in the feed body so vouches
propagate. On import we accept_vouch each one — but only if the embedded
voucher_fingerprint matches the peer we just authenticated, so a peer
can't forge vouches "from" someone else through us.

test_federation: the long-standing round-trip test now first registers
the synthetic peer as trusted so the gate lets it through.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:10:36 +02:00
m17hr1l
234e6d98ba stage-vouch-b federation: vouch sign/verify + quorum API
Add the web-of-trust primitives, all keyed off the existing node keypair:

- Vouch + QuorumConfig Pydantic models
- vouch_payload_bytes — canonical-JSON body that the voucher signs
- issue_vouch / accept_vouch / revoke_vouch / our_vouches / vouches_for
- is_vouched(target, min_vouchers) — counts DISTINCT trusted vouchers,
  ignoring expired vouches and re-using QuorumConfig defaults
- peer_is_listening_eligible(fp) — direct-trust OR vouched-in
- is_quorum_met(signal_hash, k) — distinct listening-eligible peers
  reporting the same hash
- quorum_evidence(signal_hash) — (peer_fp, received_at) tuples for UI
- quorum_config / set_quorum_config — persisted in pulse_settings

accept_vouch is paranoid: rejects expired vouches, vouchers that aren't
currently "trusted" in our peers table, mismatched pubkey-fingerprint
pairs, malformed base64, and Ed25519 verification failures — each with
a short Err reason.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:10:03 +02:00
m17hr1l
0dbeb056c5 stage-auto-a pulse: respond config (threshold/quorum/local-only)
Persisted-key/value helpers for the respond-pipeline auto-fire gates:
- respond_auto_threshold / set_… (Severity, default HIGH)
- respond_require_quorum / set_… (bool, default True)
- respond_local_only / set_…     (bool, default False)
Plus a _severity_rank helper for threshold comparison.

Backing store is the existing pulse_settings table; this commit also adds
generic pulse_setting_get / pulse_setting_set helpers in db.py so future
pulse settings don't each need their own column-pair helper.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:09:46 +02:00
m17hr1l
7a510c7acf stage-trans-a translog: append-only signed merkle chain + tests
translog.append computes
sha256(canonical({prev_hash, entry_type, entry_data, timestamp})) and
writes one row per call; the first entry uses prev_hash = "0"*64.
verify_chain walks rows in id order, re-hashes each, and returns
Err("broken at id=X expected=... got=...") on the first mismatch — so
tampering with either entry_data or prev_hash invalidates every
downstream row. recent / entries_after / head support peer sync and UI.

Tests cover: genesis prev_hash, chained prev_hash, full-chain verify,
tampered-data detection, tampered-prev_hash detection, slicing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:09:32 +02:00
m17hr1l
4a9f6ceb7f stage-vouch-a federation: vouches table + DB helpers
Add `vouches` (voucher_fp, target_fp, issued_at, expires_at, signature)
with unique (voucher, target) and target index, plus `translog` for the
append-only signed merkle chain (id, prev_hash, entry_type, entry_data,
timestamp, entry_hash). Also surface `setting_get` / `setting_set`
helpers on pulse_settings so the quorum config has a place to live.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 21:09:25 +02:00
m17hr1l
ff88aba569 stage-disc-e discovery: tests 2026-06-06 21:08:15 +02:00
m17hr1l
9b49f768ca stage-disc-d discovery: cockpit + CLI 2026-06-06 21:06:39 +02:00
m17hr1l
ddb40ff92c stage-disc-c discovery: pulse pipeline wiring + seeds settings 2026-06-06 21:04:54 +02:00
m17hr1l
6241a21af5 stage-disc-b discovery: peer walker (BFS) 2026-06-06 21:04:17 +02:00