Documentation प्रलेखन

Trinetra is sovereign border & immigration intelligence software for the agencies of Bharat. Operator console at /dashboard/. Public OSINT pipeline (BSF · CBI · Indian Kanoon · GDELT · RSS). Semantic search via Gemini embeddings. Co-indexed in a local Delphi research server for cross-corpus reasoning.

What is Trinetra?

Trinetra is a Palantir-class intelligence platform engineered for India's border & immigration agencies. It ingests only public OSINT — no surveillance data, no Aadhaar cross-reference, no facial recognition. The product is a graph of entities mentioned in lawful public records (BSF press releases, court judgments, named-accused CBI cases, curated news) and the operator workflow that turns those mentions into usable leads.

What it does

  • Daily operational brief — tempo, elevated modus, top districts
  • Entity dossiers with risk score, timeline, geography, connections
  • Semantic RFI — ask the corpus in plain English
  • Watchlist alerts when pinned entities appear in fresh docs
  • Cross-source leads with reasoning + suggested next action
  • Investigator workspace — search, case lens, notes, CSV/JSON export

What it explicitly is not

  • Not a mass-surveillance system
  • Not connected to Aadhaar, CCTNS, NATGRID, or biometric stores
  • Not facial recognition or face-search
  • Not identity-targeting — events are tagged by modus, not community
  • Not a substitute for warranted intercepts or judicial process

Quickstart (local)

Three commands, two terminals.

# 1. install
git clone https://github.com/aayambansal/trinetra.git
cd trinetra/pipeline && python3 -m pip install -r requirements.txt

# 2. set the Gemini key (for semantic embeddings)
echo "GEMINI_API_KEY=your_key_here" >> ../.env

# 3. ingest + extract + resolve + graph + agents + embed
python3 -m trinetra run        # OSINT ingest + classic graph (one shot)
python3 -m trinetra embed      # Gemini embeddings for semantic RFI

In a second terminal:

# API + dashboard
python3 -m trinetra serve
# → http://localhost:8000/  (password: aayam123)

For semantic search via Delphi (optional but recommended):

# requires Docker Desktop
git clone -b hypothetical https://github.com/synthetic-sciences/delphi.git ../delphi
cd ../delphi && cp env.example .env
# edit .env: set SYSTEM_PASSWORD, EMBEDDING_PROVIDER=gemini, GEMINI_API_KEY=...
docker compose --profile dashboard up -d --build

# bootstrap auth + push the corpus
curl -s -X POST http://localhost:8742/api/bootstrap -d '{"password":"<your password>"}'
# copy the returned api_key into trinetra/.env as DELPHI_API_KEY

cd ../trinetra/pipeline && python3 -m trinetra delphi-push

Architecture

┌──────────────────────────────────────────────────────────────────┐
│                       OPERATOR CONSOLE                           │
│   /dashboard/  ·  16 static pages  ·  shared shell + sidebar     │
│   ▼                                                              │
└────────────────────────────┬─────────────────────────────────────┘
                             │
              ┌──────────────┴──────────────┐
              ▼                             ▼
┌─────────────────────────────┐   ┌─────────────────────────────┐
│   TRINETRA API (FastAPI)    │   │   STATIC JSON EXPORTS       │
│   /api/health               │   │   /dashboard/data/*.json    │
│   /api/rfi (structured)     │   │   stats, nodes, edges,      │
│   /api/rfi/semantic         │   │   events, briefs, dossiers, │
│   /api/rfi/delphi           │   │   modus, districts, ...     │
│   /api/similar/{id}         │   │   (148 docs, 152 entities)  │
│   /api/dossier/{id}         │   │                             │
│   /api/audit  ·  /api/run   │   │                             │
└─────────────────────────────┘   └─────────────────────────────┘
              │                             ▲
              ▼                             │
┌────────────────────────────────────────────────────────────────┐
│        PIPELINE (python -m trinetra ...)                       │
│   ingest → extract → resolve → graph → export → agents → embed │
│                                                                │
│   SQLite (documents, mentions, entities, edges, events,        │
│            embeddings, delphi_papers, ingest_runs)             │
└────────────────────────────────────────────────────────────────┘
              │
              ▼
┌────────────────────────────────────────────────────────────────┐
│        DELPHI (synthetic-sciences/delphi · docker)             │
│   PostgreSQL + pgvector  ·  Gemini embeddings                  │
│   /v1/search/papers  ·  /v1/research  ·  cross-encoder rerank  │
│   148 corpus PDFs indexed for research-grade retrieval         │
└────────────────────────────────────────────────────────────────┘

Login

Open localhost:8000/ or the deployed URL. Click LOGIN → password aayam123 → dashboard.

Demo gate, not security. The password check is client-side SHA-256. A deployed build replaces this with server-side auth + role-based access. For production deployments, see Deployment.

Overview & brief

Overview shows live stat tiles (documents, entities, edges, events, mentions) and surfaces today's operational brief above the recent intel feed. The brief panel links to /dashboard/briefs.html for the full readout — 7d/30d/90d tempo, modus elevated above baseline, top districts, fresh entities. Exportable as JSON or Markdown for the morning meeting.

RFI: ask the corpus

The RFI page (Request for Information) runs three search lanes in parallel against your query:

LaneEngineWhat it's good at
StructuredRegex parser → SQL filterExact modus, district, source, time window. Sub-millisecond.
SemanticGemini 3072-dim embeddings, brute-force cosineConcept search. "Find documents about cross-border infiltration networks" returns the right docs even when the literal phrase isn't there. Sub-second.
Delphipgvector + cross-encoder rerankResearch-grade retrieval over chunked PDFs. Slower (~6s) but more precise for long-form questions.

Example queries the parser handles:

Entity dossiers

Every resolved entity has a dossier: /dashboard/dossier.html?id=<N>. A dossier is one screen with everything an analyst would write up by hand:

The risk score weighs: cross-source presence × network degree × time span × mention count. Institutional names (Modi, Shah, etc.) are filtered out of the operational lists.

Alerts & watchlist

Watchlist stores entities you've pinned (★ button on any dossier or graph node). Alerts shows fresh hits on watched entities, plus derived signals: elevated modus, novel districts, cross-source leads. Notes per entity persist locally and export with the watchlist.

Case lens

Cases is the investigator workspace. Search across every document. Click a result → drawer with all source docs, neighbours, notes, watch toggle, JSON/CSV export. FIND SIMILAR queries the semantic index for similar documents — a "more like this" surface that BSF analysts use to follow a thread.

Network · map · timeline

Network renders the entity graph in Cytoscape — kind filter, search, inspector. Map plots geocoded events on a dark CARTO base, modus-coloured pins. Timeline stacks monthly events by modus.

Pipeline

# Stages, individually
python3 -m trinetra ingest                # all enabled sources
python3 -m trinetra ingest indian_kanoon  # one source
python3 -m trinetra extract --force       # re-extract mentions + events
python3 -m trinetra resolve               # rebuild entities
python3 -m trinetra graph                 # rebuild edges
python3 -m trinetra export                # write JSON for dashboard
python3 -m trinetra analyze               # repeat-persons / hubs / anomalies
python3 -m trinetra agents                # brief / dossier / modus / district / cross-source / RFI
python3 -m trinetra embed                 # Gemini embeddings
python3 -m trinetra delphi-push           # push corpus to Delphi
python3 -m trinetra status                # counts + last runs
python3 -m trinetra run                   # everything end-to-end

Agents

Six deterministic agents — no LLM hallucination — each producing a JSON artifact:

AgentOutputPage
briefToday's operational readout/dashboard/briefs.html
dossierPer-entity dossier (1 file each)/dashboard/dossier.html?id=N
cross_sourceMulti-source leads with reasoning/dashboard/alerts.html
modusPer-modus situation reports/dashboard/modus.html
districtPer-district situation reports/dashboard/districts.html
rfiNL query parser + preset answers/dashboard/rfi.html

API reference

Trinetra API runs on :8000 when you python -m trinetra serve.

MethodPathPurpose
GET/api/healthCounts + last ingest timestamp
POST/api/rfiStructured RFI: {"query": "...", "limit": 60}
GET/api/rfi/parse?q=…Show the parser output without running the query
POST/api/rfi/semanticGemini semantic search: {"query": "...", "k": 25}
POST/api/rfi/delphiDelphi paper search (proxy): {"query": "...", "k": 15}
GET/api/similar/{id}?k=8Documents similar to id
GET/api/dossier/{id}Live entity dossier
GET/api/auditServer-side audit ring (last 500)
POST/api/runAdmin pipeline rerun (header X-Admin-Token: aayam123)

Delphi integration

Delphi is synthetic-sciences/delphi running locally in Docker. It plays two roles:

  1. Corpus context server. Every Trinetra document is rendered as a one-page PDF and uploaded via POST /v1/papers/upload. Delphi chunks, embeds (Gemini), and exposes them at POST /v1/search/papers and the higher-level POST /v1/research endpoint.
  2. MCP server for AI agents. Once configured, Claude Code / Cursor / Windsurf can call Delphi to semantically search the Trinetra corpus from inside any AI chat.

The Trinetra RFI page calls Delphi via /api/rfi/delphi alongside the local Gemini-cosine engine; results are shown side by side.

Repo indexing (developer). Delphi can index the Trinetra repo itself for AI-agent context. Since this repo is private, add a GitHub PAT via Delphi's settings (localhost:3000 · API Keys) before calling POST /v1/repositories/index.

Adding sources

Each source is a Python module under pipeline/trinetra/sources/:

def fetch(client: HttpClient, cfg: Config) -> Iterable[Document]:
    ...
    yield Document(source="my_new", title=..., body=..., url=..., published_at=...)

Register it in sources/__init__.py and add a section to config.toml with whatever knobs it needs. Add a relevance filter (regex) to keep noise out. Re-run python -m trinetra ingest my_new.

Deployment

Local-first is the current focus. For production:

CLI reference

trinetra ingest [source]      run ingest for one or all enabled sources
trinetra extract [--force]    extract mentions + events
trinetra resolve              resolve mentions into entities
trinetra graph                build the edge graph
trinetra export               write JSON for the dashboard
trinetra analyze              produce analysis reports
trinetra agents               run all reasoning agents
trinetra embed [--force]      compute Gemini embeddings for every document
trinetra delphi-push          push corpus to running Delphi instance
trinetra serve [--port 8000]  run the FastAPI backend
trinetra run                  full end-to-end pipeline
trinetra status               DB counts + recent runs

Config

pipeline/config.toml is the single config file. Sections:

.env at the repo root holds secrets (gitignored):

GEMINI_API_KEY=AIza...
DELPHI_API_URL=http://localhost:8742
DELPHI_API_KEY=synsc_...

Data model

TableRowsPurpose
documents148Every ingested doc, SHA-deduped
mentions1,236Raw extracted spans (person / phone / vehicle / etc.)
entities152Resolved canonical entities
mention_entityMention → entity link table
edges1,089Typed, weighted, evidence-tracked relations
events56Geocoded events (document × location × modus × date)
embeddings136Gemini 3072-d vectors, float32 blob
delphi_papers138Cross-ref: document_id → Delphi paper_id
ingest_runsper runAudit log of ingest cycles

Troubleshooting

Dashboard shows "T is not defined" or blank

Hard-reload (Cmd-Shift-R / Ctrl-F5). The browser is caching a stale app.js.

RFI "Delphi unreachable"

The Delphi container isn't running. cd ../delphi && docker compose --profile dashboard up -d.

Embeddings stage fails with "GEMINI_API_KEY not set"

Add the key to .env at the repo root, not inside pipeline/.

Delphi 429 rate-limit on bulk push

Bump SYNSC_RATE_LIMIT_INDEX in Delphi's .env and docker compose up -d --force-recreate api worker.

Ingest doc count low

PIB is disabled by default (its WAF rejects identified crawlers). The four working sources (BSF, CBI, GDELT, Indian Kanoon, RSS) carry the corpus. To enable more: add DRI / ED / NCB credentials via their respective registration flows, then add a source module.