Complete project rebrand for better positioning in the press freedom and digital security space. FieldWitness communicates both field deployment and evidence testimony — appropriate for the target audience of journalists, NGOs, and human rights organizations. Rename mapping: - soosef → fieldwitness (package, CLI, all imports) - soosef.stegasoo → fieldwitness.stego - soosef.verisoo → fieldwitness.attest - ~/.soosef/ → ~/.fwmetadata/ (innocuous data dir name) - SOOSEF_DATA_DIR → FIELDWITNESS_DATA_DIR - SoosefConfig → FieldWitnessConfig - SoosefError → FieldWitnessError Also includes: - License switch from MIT to GPL-3.0 - C2PA bridge module (Phase 0-2 MVP): cert.py, export.py, vendor_assertions.py - README repositioned to lead with provenance/federation, stego backgrounded - Threat model skeleton at docs/security/threat-model.md - Planning docs: docs/planning/c2pa-integration.md, docs/planning/gtm-feasibility.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
2.2 KiB
Python
79 lines
2.2 KiB
Python
"""
|
|
Federation blueprint — peer status dashboard and management.
|
|
"""
|
|
|
|
from auth import admin_required, login_required
|
|
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
|
|
|
bp = Blueprint("federation", __name__, url_prefix="/federation")
|
|
|
|
|
|
@bp.route("/")
|
|
@login_required
|
|
def status():
|
|
"""Federation status dashboard."""
|
|
from fieldwitness.attest.peer_store import PeerStore
|
|
|
|
store = PeerStore()
|
|
peers = store.list_peers()
|
|
history = store.get_sync_history(limit=20)
|
|
|
|
# Get local node info
|
|
node_info = {"root": None, "size": 0}
|
|
try:
|
|
from fieldwitness.attest.storage import LocalStorage
|
|
|
|
import fieldwitness.paths as _paths
|
|
|
|
storage = LocalStorage(_paths.ATTESTATIONS_DIR)
|
|
stats = storage.get_stats()
|
|
merkle_log = storage.load_merkle_log()
|
|
node_info = {
|
|
"root": merkle_log.root_hash[:16] + "..." if merkle_log.root_hash else "empty",
|
|
"size": merkle_log.size,
|
|
"record_count": stats.record_count,
|
|
}
|
|
except Exception:
|
|
pass
|
|
|
|
return render_template(
|
|
"federation/status.html",
|
|
peers=peers,
|
|
history=history,
|
|
node_info=node_info,
|
|
)
|
|
|
|
|
|
@bp.route("/peer/add", methods=["POST"])
|
|
@admin_required
|
|
def peer_add():
|
|
"""Add a federation peer."""
|
|
from fieldwitness.attest.peer_store import PeerStore
|
|
|
|
url = request.form.get("url", "").strip()
|
|
fingerprint = request.form.get("fingerprint", "").strip()
|
|
|
|
if not url or not fingerprint:
|
|
flash("URL and fingerprint are required.", "error")
|
|
return redirect(url_for("federation.status"))
|
|
|
|
store = PeerStore()
|
|
store.add_peer(url, fingerprint)
|
|
flash(f"Peer added: {url}", "success")
|
|
return redirect(url_for("federation.status"))
|
|
|
|
|
|
@bp.route("/peer/remove", methods=["POST"])
|
|
@admin_required
|
|
def peer_remove():
|
|
"""Remove a federation peer."""
|
|
from fieldwitness.attest.peer_store import PeerStore
|
|
|
|
url = request.form.get("url", "").strip()
|
|
store = PeerStore()
|
|
if store.remove_peer(url):
|
|
flash(f"Peer removed: {url}", "success")
|
|
else:
|
|
flash(f"Peer not found: {url}", "error")
|
|
return redirect(url_for("federation.status"))
|