┌────────────────────────────┐
│ Browser (Next.js / React) │
│ Mission Desk · Registry · │
│ Dossier · Toolkit · Ledger│
└──────────────┬─────────────┘
│ axios (REST/JSON)
▼
┌──────────────────────────────────────────────────────┐
│ FastAPI backend (port 8000) │
│ ┌─────────────┬────────────┬────────────────────┐ │
│ │ analysis.py │ intelligence│ pdf_export.py │ │
│ │ (LLM + │ (heuristics:│ (ReportLab + QR) │ │
│ │ heuristics) │ twins, │ │ │
│ │ │ graph, geo, │ regulations.py │ │
│ │ │ lang, counter) │ │
│ │ │ │ (citation library) │ │
│ └─────────────┴────────────┴────────────────────┘ │
│ │ │
│ ▼ SQLAlchemy ORM │
└──────────────────────────────────────────────────────┘
│
┌──────────────▼──────────────┐
│ Postgres (pgvector image) │
│ items · audit_events · │
│ toolkit_incidents · │
│ app_settings │
└─────────────────────────────┘Architecture
PDIP — Parliamentary Disinformation Intelligence Platform
A duty-desk console for EU policy and parliamentary offices to intake suspect content, score democratic-impact risk, ground every assessment in named EU regulations, draft counter-narratives, write an immutable audit trail, and optionally publish a DSA-style transparency ledger. This page is the technical reference for teams that want to evaluate, deploy, or extend the platform.
1. Architecture At A Glance
Containerisation
docker-compose.yml runs three services: frontend, backend, and db. Backend falls back to SQLite when DATABASE_URL points at sqlite:///..., which keeps smoke tests and offline demos working.
Transport
The browser talks to the backend with axios over REST/JSON. The backend uses FastAPI response models and SQLAlchemy ORM sessions to keep payloads typed and predictable.
Core backend modules
analysis.py handles prompts and score assembly; intelligence.py handles deterministic dossier intelligence; regulations.py powers citation drawers; pdf_export.py produces the one-page brief.
2. Data Model
items
Every dossier. Stores source text/URL, narrative, entities, claims, three core scores, confidence, headline, language/country, brief, legal flags, recommended actions, policy roadmap, status, and transparency state.
audit_events
Append-only trace of meaningful actions such as dossier creation, completed analysis, escalation, counter-narrative drafting, PDF export, and publication to the transparency ledger.
toolkit_incidents
Manual, non-LLM rapid-response incident logs from the toolkit. Captures the alert card, risk matrix inputs, chosen response, notes, and the saved risk band.
app_settings
Key/value store used for election-mode state, activation timestamp, and SLA duration. Keeps operational toggles separate from dossier data.
Startup runs ensure_items_table_columns() in backend/main.py to add new dossier columns when needed. It is intentionally lightweight and best-effort rather than a full Alembic migration pipeline.
3. Scoring Engine
deepfake_risk
Returned by analysis.analyze_content() as ai_probability, clamped to [0, 1]. Fallback path returns 0.5 when the LLM is unavailable.
coordination_score
Returned by analysis.analyze_content() as coordination_score, clamped to [0, 1]. Fallback path returns 0.2.
disinformation_impact
Primary escalation score. The current backend blends 35% model output with 65% heuristic output, then caps low-signal content so harmless political reporting does not get over-tagged.
confidence
Computed from the spread between the three core signals: 1 − stdev(values), clamped to [0.45, 0.97]. The UI renders this as the +/- band around each meter.
| Signal | Examples | Cap added |
|---|---|---|
| Election terms | election, polling station, parliament, candidate, campaign | up to +0.16 (0.04 each) |
| Democratic-harm verbs | mislead, fraud, rigged, cancelled, moved, boycott, disenfranchise | up to +0.38 (0.12 each) |
| Amplification cues | viral, share now, bot, coordinated, amplif… | up to +0.22 (0.08 each) |
| Synthetic media cues | deepfake, synthetic, voice clone, impersonat… | up to +0.18 (0.07 each) |
- Baseline score starts at 0.02 instead of a heavier default.
- Benign context terms like report, committee, briefing, and interview activate a soft cap.
- If the text is only about elections/politics and lacks harm, amplification, or synthetic cues, impact is capped at 0.22.
- Benign political reporting is further capped at 0.15 where appropriate.
- Final impact is a weighted blend: 35% model + 65% heuristic, not a blind max().
- Low-signal contexts are capped at 0.25 even if the model score is aggressive.
4. Threat Tiers
Electoral emergency
DSA Art. 34-35; Reg. (EU) 2024/900
Severe integrity threat
DSA Art. 34; AI Act Art. 50
Heightened monitoring
DSA Art. 26; GDPR Art. 5
Routine surveillance
GDPR Art. 5
In election mode, the frontend adds a +0.12 lift before tier lookup, and the dashboard queue threshold drops from 0.45 to 0.30.
5. End-To-End Dossier Flow
6. Product Surfaces
Mission Desk
/Hero, KPI strip, live evidence ticker, intake form, escalation queue, EU heatmap, language pills, and the always-on policy framework block.
Case Registry
/itemsFilterable dossier table with search, status filters, risk-tier filters, and links into each dossier.
Dossier
/items/[id]Hero controls, claims/entities, confidence meters, regulatory exposure, coordination graph, narrative twins, audit log, PDF export, counter-narrative drawer, and policy roadmap.
Rapid Toolkit
/toolkitManual alert card, two-minute triage checklist, risk calculator, decision menu, templates, saved incidents, escalation contacts, and AI literacy modules.
Transparency Ledger
/transparencyDSA Article 24-style public log of redacted interventions, legal basis chips, and action summaries.
Architecture
/architectureThis page: a technical blueprint for implementers, reviewers, and deployment owners.
7. Election Mode
- Queue threshold drops from 0.45 to 0.30 in dashboard_stats().
- Frontend risk tiering adds a +0.12 lift before threshold checks.
- Pending dossiers display a live SlaCountdown pill using created_at + slaMinutes.
- The topbar status pill switches to ELECTION MODE LIVE with an alert tone.
- Every toggle writes election_mode.toggled to the audit log with legal basis DSA Art. 35.
election_modetrue | falseelection_mode_activated_atISO timestampelection_mode_sla_minutes30 by default8. Regulation Library And PDF Export
A curated in-repo dict keyed like 2024/1689:50 stores article title, paraphrased excerpt, and source PDF filename. lookup(citation) powers the citation drawers, and parse_flag_to_citation(flag) turns natural-language legal flags into lookup keys.
GET /api/items/{id}/pdf produces a one-page EP-style brief using ReportLab with a header, four metric cells, situation overview, regulatory exposure, recommended action, and a QR code back to the dossier endpoint. If ReportLab is unavailable, a minimal handwritten PDF fallback prevents runtime failure during demos.
9. Audit Log, Transparency, And Demo Replay
Audit log
Every state-changing route calls record_event(item_id, action, detail, actor, legal_basis). The UI maps those keys to human-readable labels and action tones.
Transparency ledger
Publishing requires a completed or escalated dossier, flips transparency_published to true, and emits a DSA Art. 24 audit event. Narratives are redacted to avoid re-amplification.
Demo replay
POST /api/demo/replay seeds three prewritten dossiers: Marin synthetic audio, Marseille polling-station network, and a promoted opposition deepfake. They are intentionally scripted so every major UI feature has data.
10. Important Files
| File | Role |
|---|---|
| backend/main.py | All routes, process_item(), auto-escalation, election mode, dashboard APIs, demo replay, transparency publication. |
| backend/analysis.py | LLM prompts, score augmentation, disinformation heuristic, legal flag enforcement, action generation, brief assembly. |
| backend/intelligence.py | Headline extraction, confidence band, language/country detection, twins, graph, counter-narrative, redaction. |
| backend/regulations.py | Curated citation library and flag-to-citation parsing. |
| backend/pdf_export.py | One-page EP-style PDF brief with QR fallback behavior. |
| backend/governance.py | EU policy and governance context injected into the LLM prompt. |
| backend/rules_engine.py | Parallel/legacy rules checks for deepfake threshold, prohibited patterns, sponsored content, and data minimisation. |
| frontend/components/Layout.js | Persistent rail, topbar, election toggle, quick action launcher. |
| frontend/components/CommandPalette.js | Keyboard-first navigation and operational shortcuts. |
| frontend/lib/risk.js | Thresholds, risk-tier labels, action labels, and event tones. |
| frontend/pages/index.js | Mission Desk orchestration. |
| frontend/pages/items/[id].js | Dossier composition and operator actions. |
11. Why It Is Designed This Way
Impact > origin
Impact > origin. Electoral harm and amplification matter more than synthetic provenance alone.
Conservative low-signal handling
Conservative low-signal handling. The backend now caps topic-only or benign reporting so ordinary political coverage does not drift into monitoring tiers.
Legal grounding is deterministic
Legal grounding is deterministic. The LLM writes prose, but augmenters guarantee AI Act, DSA, GDPR, and political-ads citations are present.
Auditability is a first-class feature
Auditability is a first-class feature. Every operator action and system action writes a reasoned event with legal basis where available.
Offline-friendly heuristics keep the demo explainable
Offline-friendly heuristics keep the demo explainable. Twins, graph, geo, confidence, and redaction all work without vector search or opaque external scoring services.
For the operational view, go back to the Mission Desk. For public reporting behavior, see the Transparency Ledger.