# moonshinerd/contraria architecture

> Architecture diagram of the GitHub repository moonshinerd/contraria, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-29). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/moonshinerd/contraria
- Repository: https://github.com/moonshinerd/contraria

## Overview

ContrarIA is an AI-assisted misinformation analysis service centered on Bluesky posts. Its main workflow collects posts, assesses account authenticity, verifies factual claims against retrieved evidence, applies triage/intervention rules, and records an auditable decision. The API exposes on-demand analysis and decision access; a background worker handles ongoing collection and processing. The README also describes Socratic engagement and bot classification. Some subsystem wiring is documented rather than shown in sampled source, so uncertain calls are omitted.

Main entry points: [`api/app/services/pipeline.py`](https://github.com/moonshinerd/contraria/blob/main/api/app/services/pipeline.py) (Analysis pipeline), [`api/app/services/verification.py`](https://github.com/moonshinerd/contraria/blob/main/api/app/services/verification.py) (Verification), [`api/app/jobs/triage_pipeline.py`](https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/triage_pipeline.py) (Triage pipeline), [`api/app/db/base.py`](https://github.com/moonshinerd/contraria/blob/main/api/app/db/base.py) (PostgreSQL)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_entry["Entry and orchestration"]
  node_http_api["HTTP API<br/>[main.py]"]
  node_decisions_router["Decision routes<br/>[decisions.py]"]
  node_worker["Background worker<br/>[worker.py]"]
  node_collector["Post collectors<br/>[collector.py]"]
  node_feed_ingestor["Fact-check feeds"]
  node_pipeline["Analysis pipeline<br/>[pipeline.py]"]
  node_triage["Triage pipeline<br/>[triage_pipeline.py]"]
end

subgraph group_analysis["Analysis and verification"]
  node_bot_scoring["Bot scoring<br/>[bot_scoring.py]"]
  node_classifier["Bot classifier<br/>[fake_news_tfidf.py]"]
  node_claim_planner["Claim planner"]
  node_verification["Verification<br/>[verification.py]"]
  node_self_rag["Evidence reasoning<br/>[self_rag.py]"]
  node_debate["Verdict debate<br/>[debate.py]"]
  node_crc["Confidence calibration<br/>[crc.py]"]
end

subgraph group_evidence["Evidence and models"]
  node_evidence_clients["Evidence adapters"]
  node_llm["LLM client<br/>[litellm_model.py]"]
end

subgraph group_state["State and decisions"]
  node_post_repo["Post repository<br/>[posts.py]"]
  node_fact_repo["Fact article repository<br/>[fact_articles.py]"]
  node_decision_log[("Decision log<br/>[decisions.py]")]
  node_database[("PostgreSQL<br/>[base.py]")]
end

subgraph group_action["Intervention and moderation"]
  node_intervention["Intervention service<br/>[intervention.py]"]
  node_bluesky_client["Bluesky adapter<br/>[bluesky_client.py]"]
  node_ozone_client["Ozone adapter<br/>[ozone_client.py]"]
end

node_user(("API consumer"))
node_bluesky(("Bluesky"))
node_evidence_sources(("Evidence services"))

node_user -->|"requests"| node_http_api
node_http_api -->|"routes"| node_decisions_router
node_decisions_router -->|"analyzes post"| node_pipeline
node_worker -->|"starts collection"| node_collector
node_collector -->|"stores posts"| node_post_repo
node_worker -->|"analyzes candidates"| node_pipeline
node_worker -->|"runs ingestion"| node_feed_ingestor
node_feed_ingestor -->|"stores articles"| node_fact_repo
node_pipeline -->|"gets profile"| node_bluesky_client
node_pipeline -->|"assesses account"| node_bot_scoring
node_bot_scoring -->|"scores account"| node_classifier
node_pipeline -->|"verifies post"| node_verification
node_verification -->|"plans checks"| node_claim_planner
node_verification -->|"checks evidence"| node_self_rag
node_self_rag -->|"retrieves evidence"| node_evidence_clients
node_self_rag -->|"evaluates evidence"| node_llm
node_verification -->|"forms verdict"| node_debate
node_verification -->|"calibrates confidence"| node_crc
node_pipeline -->|"executes action"| node_intervention
node_intervention -->|"posts response"| node_bluesky_client
node_pipeline -.->|"emits label"| node_ozone_client
node_bluesky_client -->|"reads and writes"| node_bluesky
node_ozone_client -.->|"submits labels"| node_bluesky
node_evidence_clients -->|"queries sources"| node_evidence_sources
node_pipeline -->|"records decision"| node_decision_log
node_triage -->|"loads candidates"| node_post_repo
node_triage -->|"assesses account"| node_bot_scoring
node_triage -->|"verifies post"| node_verification
node_triage -->|"executes action"| node_intervention
node_post_repo -->|"reads and writes"| node_database
node_fact_repo -->|"reads and writes"| node_database
node_decision_log -->|"persists"| node_database
node_llm -->|"records usage"| node_database

click node_http_api "https://github.com/moonshinerd/contraria/blob/main/api/app/main.py"
click node_decisions_router "https://github.com/moonshinerd/contraria/blob/main/api/app/routers/v1/decisions.py"
click node_worker "https://github.com/moonshinerd/contraria/blob/main/api/app/worker.py"
click node_collector "https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/collector.py"
click node_feed_ingestor "https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/ingest_fact_articles.py"
click node_pipeline "https://github.com/moonshinerd/contraria/blob/main/api/app/services/pipeline.py"
click node_triage "https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/triage_pipeline.py"
click node_bot_scoring "https://github.com/moonshinerd/contraria/blob/main/api/app/services/bot_scoring.py"
click node_classifier "https://github.com/moonshinerd/contraria/blob/main/api/app/models/classifiers/fake_news_tfidf.py"
click node_claim_planner "https://github.com/moonshinerd/contraria/blob/main/api/app/services/claim_verification.py"
click node_verification "https://github.com/moonshinerd/contraria/blob/main/api/app/services/verification.py"
click node_self_rag "https://github.com/moonshinerd/contraria/blob/main/api/app/services/self_rag.py"
click node_debate "https://github.com/moonshinerd/contraria/blob/main/api/app/services/debate.py"
click node_crc "https://github.com/moonshinerd/contraria/blob/main/api/app/services/crc.py"
click node_evidence_clients "https://github.com/moonshinerd/contraria/tree/main/api/app/clients/evidence"
click node_llm "https://github.com/moonshinerd/contraria/blob/main/api/app/models/llm/litellm_model.py"
click node_post_repo "https://github.com/moonshinerd/contraria/blob/main/api/app/repositories/posts.py"
click node_fact_repo "https://github.com/moonshinerd/contraria/blob/main/api/app/repositories/fact_articles.py"
click node_decision_log "https://github.com/moonshinerd/contraria/blob/main/api/app/db/orm/decisions.py"
click node_database "https://github.com/moonshinerd/contraria/blob/main/api/app/db/base.py"
click node_intervention "https://github.com/moonshinerd/contraria/blob/main/api/app/services/intervention.py"
click node_bluesky_client "https://github.com/moonshinerd/contraria/blob/main/api/app/clients/bluesky_client.py"
click node_ozone_client "https://github.com/moonshinerd/contraria/blob/main/api/app/clients/ozone_client.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_http_api,node_decisions_router,node_worker,node_collector,node_feed_ingestor,node_pipeline,node_triage toneBlue
class node_bot_scoring,node_classifier,node_claim_planner,node_verification,node_self_rag,node_debate,node_crc toneAmber
class node_evidence_clients,node_llm,node_user toneMint
class node_post_repo,node_fact_repo,node_decision_log,node_database toneRose
class node_intervention,node_bluesky_client,node_ozone_client,node_bluesky,node_evidence_sources toneIndigo
```

## Components

### Entry and orchestration

- [HTTP API](https://github.com/moonshinerd/contraria/blob/main/api/app/main.py): `api/app/main.py`
- [Decision routes](https://github.com/moonshinerd/contraria/blob/main/api/app/routers/v1/decisions.py): `api/app/routers/v1/decisions.py`
- [Background worker](https://github.com/moonshinerd/contraria/blob/main/api/app/worker.py): `api/app/worker.py`
- [Post collectors](https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/collector.py): `api/app/jobs/collector.py`
- [Fact-check feeds](https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/ingest_fact_articles.py): `api/app/jobs/ingest_fact_articles.py`
- [Analysis pipeline](https://github.com/moonshinerd/contraria/blob/main/api/app/services/pipeline.py): `api/app/services/pipeline.py`
- [Triage pipeline](https://github.com/moonshinerd/contraria/blob/main/api/app/jobs/triage_pipeline.py): `api/app/jobs/triage_pipeline.py`

### Analysis and verification

- [Bot scoring](https://github.com/moonshinerd/contraria/blob/main/api/app/services/bot_scoring.py): `api/app/services/bot_scoring.py`
- [Bot classifier](https://github.com/moonshinerd/contraria/blob/main/api/app/models/classifiers/fake_news_tfidf.py): `api/app/models/classifiers/fake_news_tfidf.py`
- [Claim planner](https://github.com/moonshinerd/contraria/blob/main/api/app/services/claim_verification.py): `api/app/services/claim_verification.py`
- [Verification](https://github.com/moonshinerd/contraria/blob/main/api/app/services/verification.py): `api/app/services/verification.py`
- [Evidence reasoning](https://github.com/moonshinerd/contraria/blob/main/api/app/services/self_rag.py): `api/app/services/self_rag.py`
- [Verdict debate](https://github.com/moonshinerd/contraria/blob/main/api/app/services/debate.py): `api/app/services/debate.py`
- [Confidence calibration](https://github.com/moonshinerd/contraria/blob/main/api/app/services/crc.py): `api/app/services/crc.py`

### Evidence and models

- [Evidence adapters](https://github.com/moonshinerd/contraria/tree/main/api/app/clients/evidence): `api/app/clients/evidence`
- [LLM client](https://github.com/moonshinerd/contraria/blob/main/api/app/models/llm/litellm_model.py): `api/app/models/llm/litellm_model.py`

### State and decisions

- [Post repository](https://github.com/moonshinerd/contraria/blob/main/api/app/repositories/posts.py): `api/app/repositories/posts.py`
- [Fact article repository](https://github.com/moonshinerd/contraria/blob/main/api/app/repositories/fact_articles.py): `api/app/repositories/fact_articles.py`
- [Decision log](https://github.com/moonshinerd/contraria/blob/main/api/app/db/orm/decisions.py): `api/app/db/orm/decisions.py`
- [PostgreSQL](https://github.com/moonshinerd/contraria/blob/main/api/app/db/base.py): `api/app/db/base.py`

### Intervention and moderation

- [Intervention service](https://github.com/moonshinerd/contraria/blob/main/api/app/services/intervention.py): `api/app/services/intervention.py`
- [Bluesky adapter](https://github.com/moonshinerd/contraria/blob/main/api/app/clients/bluesky_client.py): `api/app/clients/bluesky_client.py`
- [Ozone adapter](https://github.com/moonshinerd/contraria/blob/main/api/app/clients/ozone_client.py): `api/app/clients/ozone_client.py`

### Other components

- **API consumer**
- **Bluesky**
- **Evidence services**

## Connections

- API consumer → HTTP API: requests
- HTTP API → Decision routes: routes
- Decision routes → Analysis pipeline: analyzes post
- Background worker → Post collectors: starts collection
- Post collectors → Post repository: stores posts
- Background worker → Analysis pipeline: analyzes candidates
- Background worker → Fact-check feeds: runs ingestion
- Fact-check feeds → Fact article repository: stores articles
- Analysis pipeline → Bluesky adapter: gets profile
- Analysis pipeline → Bot scoring: assesses account
- Bot scoring → Bot classifier: scores account
- Analysis pipeline → Verification: verifies post
- Verification → Claim planner: plans checks
- Verification → Evidence reasoning: checks evidence
- Evidence reasoning → Evidence adapters: retrieves evidence
- Evidence reasoning → LLM client: evaluates evidence
- Verification → Verdict debate: forms verdict
- Verification → Confidence calibration: calibrates confidence
- Analysis pipeline → Intervention service: executes action
- Intervention service → Bluesky adapter: posts response
- Analysis pipeline → Ozone adapter: emits label
- Bluesky adapter → Bluesky: reads and writes
- Ozone adapter → Bluesky: submits labels
- Evidence adapters → Evidence services: queries sources
- Analysis pipeline → Decision log: records decision
- Triage pipeline → Post repository: loads candidates
- Triage pipeline → Bot scoring: assesses account
- Triage pipeline → Verification: verifies post
- Triage pipeline → Intervention service: executes action
- Post repository → PostgreSQL: reads and writes
- Fact article repository → PostgreSQL: reads and writes
- Decision log → PostgreSQL: persists
- LLM client → PostgreSQL: records usage
