CaseLinker is a project designed to group and visualize statistical and contextual information from cases involving crimes against children and child sexual exploitation & abuse (CSEA).
Try the latest version online: https://caselinker.up.railway.app/
The live release includes all features and a processed case corpus from publicly available ICAC / NCMEC / DOJ / State Attorneys General press materials. The corpus holds 7,426 cases across 56 ingestion sources. Live counts and per-source coverage are on the in-app Sources page. These reports summarize investigations, arrests, and prosecutions, redacted for public release. No PII was processed; all data was already in the public domain. No installation required — just open the link in your browser.
-
Report #1: CaseLinker: An Open-Source System for Cross-Case Analysis of Internet Crimes Against Children Reports - Initial technical report documenting the prototype architecture, deterministic extraction pipeline, and evaluation baseline on 47 cases.
-
Report #2: Interpretable ML Approaches for Analyzing Internet Crimes Against Children Reports - Second report covering NER integration, dataset expansion to 207 cases, and emerging patterns from the expanded dataset including the distributed network of 215 law enforcement organizations.
-
Report #3: 5 Sources, 500 Cases, and Scaling Considerations - Third report covering the addition of 3 new sources, facet-tree search, and data utility.
-
Report #4: Framework for Retrospective Analysis and Case Studies of Internet Crimes Against Children Across U.S. Task Forces - Fourth report establishing a replicable and extensible case study methodology: four-era framework (2010-2026), stratified sampling across eras, a five-dimension case study structure, and the legal and ethical grounding for the analysis.
-
Report #5: Painting the Landscape of Internet Crimes Against Children with Interpretive Tooling - Fifth and final report: a ten-page visual briefing on sixteen years of U.S. ICAC enforcement, presenting the extraction and analysis framework, 20 case studies across four eras, and aggregate findings across 5,000+ cases.
This project was motivated by challenges I encountered with understanding child exploitation cases, including:
- Fragmented data sources: Cases are scattered across numerous organizations, states, and agencies
- Cross-case analysis: Identifying patterns, similarities, and connections between cases becomes challenging without a unified system, even when cases share common characteristics such as abuse patterns, platforms, or perpetrator demographics
- Limitations in trend analysis: Analyzing the evolution of child exploitation, the mediums in which it occurs, and recurring case topics
- Emotional impact: The challenge of repeatedly reading and processing highly disturbing case material
CaseLinker aims to address these challenges by serving as a tool for case analysis, enabling researchers, law enforcement, and advocacy organizations to better understand the landscape of child exploitation.
- Feature extraction: Robust extraction of information from cases and explainability for clusters and analysis
- Clustering and linking: Cases based on shared characteristics such as victim context, platforms used, and law enforcement actions
- Visualization: With particular attention to tasteful presentation of case content, and pattern analysis across investigations
CaseLinker follows a modular, layered architecture:
- Ingestion Layer: Handles PDF data sources with text extraction and validation
- Processing Layer: Extracts features, assigns comparison values, and fills case schema
- Storage Layer: PostgreSQL (production) / SQLite (local development) - Database-agnostic implementation
- Clustering & Analysis Layer: Case comparison, similarity detection, automated grouping, triage, and insights generation
- Visualization Layer: Interactive web dashboards (platform harm dashboard, lifecycle state machines, facet tree search, analysis views)
No installation required. Visit the live deployment:
- Live Application: https://caselinker.up.railway.app
The live version includes all features and a processed case corpus. Created for quick testing and demonstrations.
# Clone the repository
git clone https://github.com/mrinaalr/CaseLinker.git
cd CaseLinker
# Run setup script (creates venv and installs dependencies)
./setup.sh
# Activate virtual environment
source .venv/bin/activate
# Start the main application
python3 run/main.pyThen open your browser to:
- Home: http://localhost:8000/
- Visualizations: http://localhost:8000/visualization
- Advanced Analysis: http://localhost:8000/analysis
- Clusters: http://localhost:8000/clusters
- Stats: http://localhost:8000/stats
- Search: http://localhost:8000/search
- Query: http://localhost:8000/query
- Lifecycle: http://localhost:8000/lifecycle
- Triage: http://localhost:8000/triage
- Patterns (Phase 2): http://localhost:8000/patterns
- Tech Landscape: http://localhost:8000/tech-landscape
- LLM: http://localhost:8000/llm
- Data Sources: http://localhost:8000/sources
- Data Audit: http://localhost:8000/audit
- Look Under the Hood: http://localhost:8000/under-the-hood
- Case Studies: http://localhost:8000/case-studies
- API Documentation: http://localhost:8000/docs
Database:
- Production (Railway): PostgreSQL with encrypted connections and managed backups
- Local Development: SQLite (
caselinker.db) - created automatically when running locally, initially empty
What a fresh clone includes (works with an empty DB):
| Included | Path | Local use |
|---|---|---|
| Ontology case graphs (~7k+ expressed via CASE/UCO/CAC) | ontology/graph_output/ |
/patterns/graph, /api/ontology/* |
| PACER lifecycle state machines (30 cases) | state_machines/graphs/ |
/lifecycle |
| PACER knowledge graphs (41 investigations, 128 docs, 297 graph, modeled by the CASE-UCO SDK) | ontology/PACER/ (BULK_FOLDER/, ENTICEMENT/, ENTERPRISE/, PRODUCTION/, SEXTORTION/, TRAFFICKING/) |
queryable via CASE-UCO SDK MCP Server |
| L* trajectories / transition matrix | state_machines/data/lstar_all_cases.json |
/api/lifecycle/lstar |
| Case studies (21 across 4 eras) | data/case_studies.json |
/case-studies |
| Triage model bundle | models/triage_bundle.joblib |
/triage, /api/triage-live |
Reproduce the local corpus DB (two options):
- Trusted API export (full production corpus in one shot) — request a
CaseLinker-Keyat mramachandra@umass.edu, then:
source .venv/bin/activate
export CASELINKER_KEY='your-trusted-key'
python3 scripts/run/import_corpus_from_api.py
python3 run/main.py- PDF ingest (build from public data you collect; see Process Your Own PDF Files below):
source .venv/bin/activate
python3 src/main.py "path/to/your/file.pdf"
# or, after placing multiple PDFs in the repo root:
./scripts/run/ingest_all_pdfs.sh
python3 run/main.pyYou can process additional PDFs to add more cases to the database.
CaseLinker processes publicly available case reports and related records for research, analytical, and investigative workflow purposes.
While all source materials are drawn from public reports, content involves descriptions of child exploitation, abuse, or other disturbing criminal case details that may be difficult to read.
Please proceed with awareness of the sensitive nature of the material and use the project responsibly for research, academic, or authorized analytical purposes.
-
src/main.py- CLI Tool for Processing PDFs- Processes PDF files to extract and store cases in the database
- Run this when you want to add new cases from PDF files
- Extracts text, identifies sources, splits cases, extracts features, and stores them
- Pre-computes clusters after storing new cases
-
run/main.py- Main application- Starts the web server that serves the visualization interface and API
- Run this to access the web interface and visualizations
- Uses pre-computes clusters on startup for fast performance
- Provides REST API endpoints for case data, analysis, and statistics
-
caselinker_mcp/server.py— MCP server for agent and LLM analysis- Exposes 37 tools: corpus search, triage, Q1 platform evidence, on-demand
case2caccohort graphs, Turtle export (export_case_graph_ttl), and graph traversal - Read-only; wraps the existing REST API; no database mutation
- Reach out for private mcp.json keys and review
caselinker_mcp/README.md&caselinker_mcp/tool_registry.mdfor setup, auth and the full tool catalog
- Exposes 37 tools: corpus search, triage, Q1 platform evidence, on-demand
Typical use case:
- First, populate the database with processed PDFs or request a trusted API key
- Then, start the web server with
run/main.pyto view and analyze cases - Optionally, connect an MCP client (Cursor, Claude Desktop, or any MCP-compatible host) via stdio, SSE, or Streamable HTTP
The local database starts empty. To populate it with cases, you need to process PDF files you want to ingest.
Visit the Sources page to see links for collecting sources:
- Live Demo Sources Page: https://caselinker.up.railway.app/sources
- Or visit
/sourceswhen running locally: http://localhost:8000/sources
Processed sources include:
- Arizona ICAC (AZICAC): Annual case reports and arrests (AZICAC)
- National Center for Missing & Exploited Children (NCMEC): Case summaries and CyberTipline-related publications
- Georgia Bureau of Investigation (GBI): CEACC / Georgia ICAC press releases
- Idaho Office of Attorney General (Idaho ICAC): ICAC newsroom press releases
- Texas Office of the Attorney General (Texas AG): Cyber Crimes / ICAC-related press releases
- Michigan State Police (Michigan ICAC): MSP newsroom ICAC releases
- Silicon Valley ICAC (SVICAC): Regional "In The News" articles
- Tennessee Bureau of Investigation (TBI ICAC): TBI newsroom ICAC search results
- South Carolina Attorney General (SCAG ICAC): ICAC-tagged news releases
- New York State Police (NEWYORK SP): NYSP newsroom ICAC keyword search
- Illinois Attorney General (ILLINOIS AG): ICAC press release search
- Pennsylvania Office of the Attorney General (PA AG): Child Predator / ICAC-related releases
- New Jersey Office of the Attorney General (NJ AG): ICAC site search
- Washoe County Sheriff's Office (WCSO): Nevada ICAC newsroom search
- Fresno County Sheriff's Office (FRESNO SO): ICAC site search
- Osceola County Sheriff's Office (OSCEOLA SO): ICAC site search
- Las Vegas Metropolitan Police Department (LVMPD): ICAC site search
- San Jose Police Department (SJPD): ICAC / child exploitation press search
- Los Angeles Police Department (LAPD): ICAC news search
- Seattle Police Department (SPD): SPD Blotter ICAC search
- San Diego Police Department (SDPD): City of San Diego ICAC site search
- Colorado Springs Police Department (CSPD): ICAC site search
- Hawaii Department of the Attorney General (HI AG): HICAC media and press
- Cook County State's Attorney (CCSAO): ICAC unit news releases
- South Florida ICAC (SOUTH FLORIDA ICAC): Regional task force news index
- Florida Office of the Attorney General (FL AG): ICAC site search
- Vermont Office of the Attorney General (VT AG): Child-related / ICAC releases
- Rhode Island Office of the Attorney General (RI AG): ICAC site search
- Ohio Attorney General (OHIO AG): ICAC / child-related news search
- Delaware Department of Justice (DE AG): Child Predator Task Force / ICAC releases
- Sedgwick County Sheriff's Office (SEDGWICK SO): Child exploitation press search
- Anchorage Police Department (ANCHORAGE PD): Alaska ICAC-related releases
- Mississippi Attorney General (MS AG): ICAC media releases
- Montana Department of Justice (MT DOJ): Child-related press releases
- New Mexico Attorney General's Office (NM AG): ICAC site search
- North Carolina State Bureau of Investigation (NC SBI): ICAC news search
- Louisiana Office of the Attorney General (LA AG): ICAC news releases
- Utah Attorney General (UT AG): ICAC site search
- Washington State Office of the Attorney General (WA AG): Child-related news search
- Oregon Department of Justice (OREGON DOJ): ICAC site search
- Wyoming Division of Criminal Investigation (WY DCI): ICAC / Computer Crime news
- Iowa Division of Criminal Investigation (IA DCI): ICAC site search
- Arkansas Department of Public Safety (ARKANSAS DPS): ICAC / ASP news search
- Alabama Law Enforcement Agency (ALEA): SBI / ICAC news search
- South Dakota Office of the Attorney General (SD AG): ICAC press releases
- Kentucky State Police (KY SP): News archive ICAC search
- Nebraska State Patrol (NE SP): Child exploitation press search
- U.S. Army Criminal Investigation Division (ARMY CID): ICAC releases
- U.S. Air Force Office of Special Investigations (AF OSI): Child sexual abuse material and exploitation press releases
- U.S. Customs and Border Protection (CBP): Newsroom releases on child sexual exploitation and related border enforcement (site search)
- U.S. Immigration and Customs Enforcement (ICE): HSI child-exploitation press releases
- Naval Criminal Investigative Service (NCIS): Child exploitation and related investigation press releases
- U.S. DOJ CEOS (DOJ CEOS): Child Exploitation and Obscenity Section press releases
- U.S. DOJ CEOS Archives (DOJ ARCHIVES): Archived CEOS criminal press releases (2002-2008)
- U.S. Secret Service (USSS): ICAC-related newsroom press releases (ICAC task forces, CSAM, and child exploitation search results)
- U.S. Marshals Service (US MARSHALS): Press releases on child predators, sexual-assault fugitives, and recovered minors
Once you have PDF files, process them using the CLI tool:
Single PDF:
source .venv/bin/activate
python3 src/main.py "path/to/your/file.pdf"Multiple PDFs:
python3 src/main.py "2011 Cases and Arrests – AZICAC.ORG.pdf" "2020 Reports" "2024-media-coverage-cybertipline-success-stories.pdf" All PDFs under the repo (e.g. after a fresh DB wipe):
./scripts/run/ingest_all_pdfs.sh
# Skip NCMEC/DOJ on first pass if you want state feeds first:
./scripts/run/ingest_all_pdfs.sh --no-aggregateThe system will:
- Extract text from each PDF
- Identify organization name from filename (AZICAC, NCMEC, etc.)
- Batch cases, extract features, assign case IDs
- Store all cases in the local SQLite database
- Pre-compute clusters for fast visualization
Access the Platform Harm Dashboard via the live demo or locally at http://localhost:8000/visualization.
Dashboard features:
- Lifecycle map: platforms placed on six exploitation lanes (distribution, storage, communities, discovery, messaging, production)
- Detail panel: per-platform analysis with four tabs:
- Affordances → what the medium enables
- Misuse Surface → how offenders abuse those properties
- Harm Vectors → victim-facing harm pathways
- Case Evidence → expandable quotes from
q1_evidence.json; links to Audit for full case review
- Filters: search by platform name; narrow by evidence tier
Access Search via the live demo or locally at http://localhost:8000/search
Search provides a facet decision tree over the stored case corpus: the server builds a deterministic partition tree from structured facets (not a precomputed file on disk). The view uses D3.js (SVG) to render cohort nodes and edges. You can limit tree depth, prune which partition dimensions apply and optionally filter allowed values per facet (extracted feature), then click any node (branch or leaf) to list case IDs in that cohort for use elsewhere (e.g. single-case visualization, manual cross-case analysis). Small cohorts (fewer than three cases) have IDs gated behind a demo access key. See src/Storage Layer/facet_tree.py and /api/facet-tree for the partition order and semantics.
Navigate to live demo or run server locally and navigate to http://localhost:8000/analysis.
-
Tag-Based Analysis (Run Advanced Analysis):
- Select one or more tags from categories: Case Topics, Severity Indicators, Platforms & Environments, Investigation Types, Perpetrator Relationships, Perpetrator Status
- Click "Run Advanced Analysis" to find all cases matching ALL selected tags (intersection logic)
- View matching cases with highlighted text showing where tags were found in the raw case data
- See case counts for each selected tag
-
Automated Analysis (Run Automated Analysis):
- Click "Run Automated Analysis" to run the full automated analysis pipeline
- Case Groups: View cases grouped by similarity (platforms, demographics, topics, severity, investigation)
- Top Priority Cases: See cases sorted by priority score (normalized to 5-10 scale) based on:
- Severity indicators (35%): infant, rape, very_young, physical_abuse
- Victim count (30%): Higher scores for multiple victims
- Case type (25%): production, hands_on, possession, online_only
- Severity phrases (15%): dangerous, stated, told, continue, attacked, out_of_control, attracted
- Evidence volume (10%): images, videos, storage size
- Registered sex offender (10%): Repeat offender status
- Automated Insights: View insights about most common platforms, severity distribution, and case topics
- Patterns Detected: See patterns like repeat offenders, relationship patterns, and investigation focus
- Top Keywords: View most frequent keywords extracted from case text
- Expandable Details: Click any box to view raw case data with highlighted priority indicators and detailed explanations of why the analysis prioritized/grouped the case
Access Triage via the live demo or locally at http://localhost:8000/triage. Current implementation uses rule-based priority tiers, ML Classification for triage (random forest or decision tree trained on features from the database with labels derived from deterministic rules), optionally constrained by the same facet-dimension filtering used in Search, and supports paste-in live triage that scores text in memory without writing to the database. For the full triage documentation (rules, bundle paths, APIs, live paste), see triage.md in the repo root.
Phase 2: Patterns (local · live) is an ongoing stage of research for cross-case pattern analysis ontop of the corpus.
Three questions drive Phase 2; each builds on the last. Findings pages: /patterns/questions/q01 (Q1), q02 (Q2), q03 (Q3).
| Question | Focus | Why it matters |
|---|---|---|
| Q1 — Platform harm | What is the platform and what capabilities does it have? How has it been misused in practice, and what surfaces, vectors, and avenues for exploitation does it expose? What specifically about this medium makes it usable for exploitation? Can those properties be generalized into a framework for stress-testing future platforms? | Press releases and online harms research often name platforms but rarely explain what about the medium enabled the offense. Affordance-level analysis turns platform mentions and documented crimes into a transferable framework. Evidence: ontology/q1/ (q1_evidence.json) |
| Q2 — Exploitation lifecycle | What does offending and enforcement look like at scale across offense subsets (familial abuse, grooming, sextortion, production, possession, etc.)? What platforms, methods, and patterns recur within each? | Most ICAC research works in hashing, aggregate counts, or single-case narratives. The lifecycle view, stratified by subset, shows how offending and enforcement unfold across thousands of cases. Evidence: ontology/q2/ (q2_lifecycle.json, q2_evidence.py). |
| Q3 — Kill-chain interventions | Given Q1 and Q2, where can technology, investigation, or enforcement intervene with the most leverage? | Platform mapping and lifecycle analysis exist to answer the operational question: where disruption is most plausible (detection, reporting, warrant execution, prevention). Evidence: ontology/q3/ (q3_interventions.json, q3_evidence.py). |
Rebuild evidence tables locally:
python3 ontology/q1/build_candidates.py && python3 ontology/q1/q1_evidence.py
python3 ontology/q2/build_candidates.py && python3 ontology/q2/q2_evidence.py
python3 ontology/q3/build_candidates.py && python3 ontology/q3/q3_evidence.pyTo answer Q1–Q3 at corpus scale, enforcement narratives cannot stay only in relational tables and regex-derived tags. They must be expressed in a structured, graph-queryable form so patterns can be queried, validated, and compared across cases. CaseLinker already extracts consistent features from each narrative; the ontology pipeline maps those features into a standard investigation vocabulary and builds a validated knowledge graph as the mechanism for cross-case analysis.
What is an ontology: The CAC Ontology (Crimes Against Children Ontology) is a formal vocabulary developed to model the entities of a child-exploitation investigation as typed, related objects: platforms, victims, offenders, investigations, and outcomes. CAC is shepherded by Project VIC International and is built on the Linux Foundation's Cyber Domain Ontology stack (UCO and CASE). CaseLinker case data is being aligned to this vocabulary so graphs can be shared, validated, and queried with the same tools used in forensic and intelligence workflows.
How CaseLinker uses ontologies: A deterministic mapping layer translates each case's extracted features into CAC entities and relationships, emits per-case RDF graphs, validates them, and merges conformant graphs into a queryable knowledge graph.
Pipeline flow:
- CaseLinker case features — already extracted (platforms, topics, investigation signals, prosecution outcomes).
- Mapping layer — deterministic translation to CAC entities and relationships (
ontology/graph_generate.py). - RDF emission — per-case graphs as Turtle and JSON-LD under
ontology/graph_output/. - SHACL validation — only conformant graphs enter the merged corpus.
- SPARQL-queryable corpus — canonical per-case graphs are loaded into Oxigraph (named graph per case; union default graph) and served at
GET|POST /sparql.
Agents can also build cohort graphs on demand via MCP (case2cac → graph_summarize → export_case_graph_ttl), or query the live store with the CASE/UCO SDK execute_sparql_query tool.
Public SPARQL (GET|POST /sparql) — SPARQL 1.1 Query only (SELECT, CONSTRUCT, ASK, DESCRIBE). No auth. Update and SERVICE are rejected. Rate limit is 30/minute per IP. If a query has no outer LIMIT, the proxy injects 1000; outer LIMIT above 10,000 is rejected. Each case is named graph https://caselinker.up.railway.app/resource/case/{case_id}; default-graph patterns see the union. Request formats, namespaces, errors, and more examples: ontology/docs/SPARQL.md.
curl -sS -X POST 'https://caselinker.up.railway.app/sparql' \
-H 'Content-Type: application/sparql-query' \
-H 'Accept: application/sparql-results+json' \
--data-binary @- <<'SPARQL'
PREFIX cac: <https://cacontology.projectvic.org#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?platform ?label (COUNT(DISTINCT ?case) AS ?cases)
WHERE {
?event cac:usesChannel ?platform .
?platform rdfs:label ?label .
?case a cac:CACInvestigation ; cac:hasStep ?event .
}
GROUP BY ?platform ?label
ORDER BY DESC(?cases)
LIMIT 8
SPARQLFull ontology documentation (vocabulary, pipeline, graph pools, Q1–Q3, PACER, code map): ontology/README.md.
References
- Sources Tab: View data sources and access original case reports
- Clusters Tab: View pre-computed clusters and analyze case reports
- Stats Tab: Coverage over the dataset and case distributions
- Tech Landscape: Technology revolver (platforms, investigation tech, anonymization, P2P) by era
- Lifecycle: PACER exploitation lifecycles. Five offense types as CAC ontology state machines
- Query: Custom analysis lab (public APIs)
- LLM: Natural-language queries over case statistics (SQL-backed; rate limited on production)
- Case Studies Tab: Era-organized narrative case studies (
data/case_studies.json) - Audit Tab: Review extracted features case-by-case with interactive highlighting to verify extraction accuracy
CaseLinker/
├── src/
│ ├── Ingestion Layer/ # PDF extraction, source detection, ingest_file
│ ├── Processing Layer/ # batching.py, processing.py, merge_processing.py
│ │ ├── Pattern Processing Layer/ # Regex / rule-based feature extraction
│ │ └── ML Processing Layer/ # NER, semantic concepts, content sanitization
│ ├── Storage Layer/ # SQLite + PostgreSQL storage, facet_tree.py
│ ├── Clustering & Analysis Layer/ # analysis.py, triage.py
│ ├── Visualization Layer/ # Server-side viz helpers
│ └── main.py # CLI: ingest PDFs → process → store
├── run/
│ ├── main.py # FastAPI app: pages + REST API + /sparql
│ ├── sparql_proxy.py # SPARQL parser policy (LIMIT / Update / SERVICE)
│ ├── redis_cache.py # Optional Redis caching (production)
│ └── auth.py # Access gates / keys for sensitive views
├── scripts/
│ ├── rebuild_oxigraph.py # Wholesale Oxigraph reload (N-Quads PUT /store)
│ ├── stats/ # Corpus statistics scripts
│ ├── verify/ # Claims, uniqueness, ICAC TF alignment, triage tests
│ ├── run/ # ingest_all_pdfs.sh, import_corpus_from_api.py, clear_postgres.py, train_triage_model.py
│ └── scraper/ # fetch_source_urls.py, scrape_pdf.py
├── visualization/ # Static HTML (served by run/main.py)
│ ├── assets/ # caselinker-api.js, cover.png
│ ├── home.html # /
│ ├── visualization.html # /visualization
│ ├── search.html # /search (facet tree)
│ ├── analysis.html # /analysis
│ ├── clusters.html # /clusters
│ ├── stats.html # /stats
│ ├── query.html # /query
│ ├── lifecycle.html # /lifecycle
│ ├── triage.html # /triage
│ ├── patterns.html # /patterns
│ ├── patterns-graph.html # /patterns/graph
│ ├── questions/ # /patterns/questions/q01–q03
│ ├── tech-landscape.html # /tech-landscape
│ ├── LLM.html # /llm
│ ├── sources.html # /sources
│ ├── case-studies.html # /case-studies
│ ├── audit.html # /audit
│ └── under-the-hood.html # /under-the-hood
├── ontology/
│ ├── README.md # Ontology docs (vocabulary, pipeline, pools)
│ ├── docs/SPARQL.md # Public SPARQL 1.1 API guide
│ ├── features_to_cac.py # Case features → CAC RDF
│ ├── graph_generate.py # Batch TTL / JSON-LD
│ ├── oxigraph_rebuild.py # Canonical TTL → named-graph N-Quads
│ ├── q1/ q2/ q3/ # Research evidence
│ ├── PACER/ # PACER → lifecycle facts
│ └── graph_output/ # staging + universe/ + big_bang/ + analysis/
├── caselinker_mcp/ # MCP server (37 tools; SSE + Streamable HTTP on Railway)
│ ├── server.py # FastMCP entry point
│ ├── README.md # Hosted auth, Cursor config, graph workflow
│ └── tool_registry.md # Full tool catalog
├── models/ # triage_bundle.joblib (optional; see /triage)
├── data/ # case_studies.json for /case-studies
├── setup.sh
├── requirements.txt # Core deps
├── requirements-ml.txt # Optional ML / NER stack
├── config.py
├── caselinker.db # SQLite (local; created on first ingest)
├── triage.md # Triage rules and model docs
├── Procfile # Railway / Heroku start command
└── Architecture design.md
Each case includes structured features extracted from case narratives:
- Perpetrator: Age(s), registered sex offender status, gender when stated; flags for multi-defendant operations
- Victim: Age(s), age ranges, count when explicitly stated in victim-clause phrasing, gender after validation
- Relationship: Kin and role labels (father, mother, parent, sibling, teacher, coach, stranger, etc.); unknown when not stated
- Platforms: Named apps and surfaces — social, messaging, gaming, file hosting, livestreaming, early-era chat, Gen AI tools when cited — plus generic online, chat, or social media when no product matches
- Technology signals (stored separately from the platform list): investigation tooling (PhotoDNA, hash matching, CyberTipline language), anonymization (Tor, dark web, cryptocurrency), P2P clients
- Prosecution: Charge phrases with counts, booking stage (arrest through sentenced), jail mentions, sentence durations
- Evidence volume: Image, video, storage, and message counts when quantified in text
- Investigation: Non-exclusive types — proactive, reactive, online, undercover, CyberTipline-origin, or unknown; agencies from pattern matching plus NER supplements
- Prior record: Registered status and age at prior arrest when stated
- Case topics: production, possession, distribution, trafficking, csam, ai_csam, sextortion, hands_on vs online_only, family vs stranger, international, multi_state
- Severity indicators: infant, very_young, under_12, sexual_abuse; multiple_perpetrators when several offender ages are extracted
- Severity phrases: dangerous, stated, told, continue, attacked, out_of_control, attracted — used in priority scoring
When the ML stack is enabled, NER adds organizations, locations, dates, and ages, merged with regex output. A victim-age gate drops decoy and headline ages. Semantic sentence scoring stores concept scores on each case; grooming may add a severity tag, and strong possession or AI-generation language may reinforce case topics. Most concept scores are kept for analysis; only selected ones are merged in with main fields.
- Comparison values: Normalized vectors (platforms, demographics, investigation, evidence, topics, severity, date) for similarity and pre-computed clusters
- Agency pathways: Federal vs state/local splits where inferable from narrative context
- Preserved data: Raw narrative, source organization, source URL when present, source publication date from formatted PDFs, timestamps
GET /- Home pageGET /api/cases- Full bulk case export (localhost orCaseLinker-KeyinCASELINKER_TRUSTED_KEYS)GET /api/cases-summaries-chunk- Public paginated summaries (offset,limit≤ 500); UI loads the full timeline via many small responses, not one bulk JSONPOST /api/cases-summaries-by-ids- Public batched summaries (max 500 ids per request) for cluster membership and similar flowsGET /api/cases/{case_id}- Single case (public responses omitraw_data; narrative available ascase_textfor UI drill-down)GET /api/case-count- Total case count (public, rate limited)GET /api/case-ids-by-filter- Case IDs matching filter query params (public, rate limited)GET /api/tags- Distinct tag values across the corpus (public, rate limited)POST /api/tag-threader- Tag co-occurrence threading analysis (public, rate limited)GET /visualization- Platform Harm Dashboard (Q1 affordance-misuse-harm analysis, tiered evidence, manual platform analysis)GET /api/q1/platform-evidence- Q1 platform evidence index or per-platform cohort (platform, optionaltier)GET /search- Facet decision tree over stored cases (D3); prune filters; cohort case IDs via APIGET /query- Custom analysis lab (browser-only JavaScript calling public APIs; see page for examples)GET /lifecycle- Exploitation lifecycle visualization (public HTML; payload embedded server-side)GET /api/lifecycle/cases- Lifecycle JSON (trustedCaseLinker-Keyor localhost; same gate asGET /api/cases)GET /api/lifecycle/lstar- Full L* output (state_machines/data/lstar_all_cases.json; trusted key or localhost)GET /api/lifecycle/canonical- Public JSON for the 5 canonical PACER state-machine cases (no key; rate limited; intended for external embeds)GET /analysis- Advanced case analysis page with tag-based filtering and automated analysisGET /api/facet-tree- Build facet tree JSON (max_depth, optional prune query params)GET /api/facet-distinct- Distinct primary-bucket values per facet (for Search prune UI)POST /api/facet-cohort-members- Case IDs for a facet path (same prune semantics as tree; small cohorts gated)GET /triage- Triage page (rules, model evaluation, corpus model tiers, live paste)GET /patterns- Phase 2: Patterns research documentation pageGET /patterns/graph- Merged CAC ontology graph explorer (compare / Big Bang pools)GET /patterns/questions/{question_id}- Q01–Q03 narrative question pagesGET /mcp/sse- MCP SSE transport (requiresAuthorization: Bearer <MCP_ACCESS_KEY>)GET|POST /mcp-http/- MCP Streamable HTTP transport (same auth)GET|POST /sparql- SPARQL 1.1 Query proxy over the CASE/UCO/CAC case graphs (public, 30/minute; query-only; ontology/docs/SPARQL.md)GET /api/ontology/merged- Merged graph JSON (pool=compare|all|universe|analysis; public, cached)GET /api/ontology/cases- Per-case graph catalog (pool=compare|all|universe|analysis; public metadata:case_id,path,ttl_path)GET /ontology/graph_output/{pool}/{case_id}.jsonld|.ttl- Static per-case CAC graph files (public)GET /ontology/q1/*,/ontology/q2/*,/ontology/q3/*,/ontology/question_data/*- Static Q1–Q3 evidence JSON (public)GET /ontology/q_results.json- Aggregated question results JSON when present (public)POST /api/ontology/cache/warm- Rebuild/warm the merged-graph cache (pool=compare|all|universe|analysis|both)GET /api/triage-eval- Stratified train/test metrics on live cases (same pipeline asscripts/verify/test_triage.py)GET /api/triage-model-corpus- Saved bundle predictions over live DB; optionalfacet_constraintsJSON query param (rate limited)POST /api/triage-live- Classify pasted batch text in memory only; requires bundle; no persistenceGET /sources- Data sources pageGET /case-studies- Case studies reading room (eras + studies fromdata/case_studies.json)GET /api/case-studies- Case study content document (eras, studies, default form URL)GET /api/case-studies/notes/{case_id}- Community notes for a study idPOST /api/case-studies/notes/{case_id}- Append a community note (rate limited)GET /audit- Data audit page for reviewing extracted features case-by-caseGET /api/automated-analysis- Run automated analysis (case grouping, triage, insights)POST /api/return-tagged-cases- Get cases matching selected tags (intersection logic)GET /api/stats- Get case statistics (total cases, extracted features count, sources)GET /api/stats-detailed- Detailed corpus statistics (public, rate limited)GET /api/location-stats- Location/geography aggregates (public, rate limited)GET /api/technology-revolver- Technology-by-era data keyed by era buckets (public, rate limited)GET /api/cluster-groups- Pre-computed similarity cluster groups (public, rate limited)GET /stats- Statistics dashboard pageGET /clusters- Cluster explorer pageGET /tech-landscape- Technology landscape pageGET /under-the-hood- Architecture/methodology pageGET /llm- LLM chat pagePOST /api/llm/chat- LLM chat endpoint (public; per-IP daily cap, exempt for localhost/trusted key)POST /api/cache/clear- Clear server caches (rate limited 10/hour)GET /api- API info root;GET /healthz- health checkGET /docs- Interactive API documentation
- Backend: Python 3, FastAPI, Uvicorn
- Data Processing: Pandas, NumPy
- PDF Processing: pdfplumber
- Database: PostgreSQL (production) / SQLite (local development)
- Production: Railway PostgreSQL with encrypted connections
- Local: SQLite database auto-created on first run
- Visualization: D3.js, HTML/CSS/JavaScript
- ML/NER:
- Stanza primary NER model; optional Transformers/spaCy paths in code
- Semantic concept scoring (grooming, possession, AI-generated CSAM language, and related themes) merged into case topics and severity where thresholds are met
- Supervised triage (experimental): scikit-learn random forest or decision tree; labels from rule-based priority scores;
joblibbundle loaded at inference time
- Architecture: Modular 5-layer design
CaseLinker can be deployed to cloud platforms for public access. The app includes a Procfile for deployment to Railway, Heroku, and similar platforms. The SPARQL proxy reads OXIGRAPH_URL (private Oxigraph base URL). After regenerating case graphs, reload the store with python3 scripts/rebuild_oxigraph.py (wholesale overwrite, not incremental).
- No Sensitive Data: This system contains cases from publicly available sources (ICAC Task Forces Cases and Arrests, NCMEC CyberTipline Success Stories, DOJ CEOS Press Releases, and State Attorneys General's Office Press Releases). These reports are publicly available, summarize investigations, arrests, and case details, and are redacted for public release. All data was already in the public domain. This project received a determination from the University of Massachusetts Amherst Human Research Protection Office (HRPO Determination #7668); this research does not contain private or identifiable information under federal regulations [45 CFR 46.102(f)(1), (2)].
- See
/sourcespage for full disclaimer regarding data usage
Contributors can help by:
- Adding ideas to the README
- Contributing to the architecture design
- Code implementation