Metadata-Version: 2.4
Name: yatharth
Version: 0.1.3
Summary: Offline, air-gapped assurance for object-detection datasets and models: poisoning, backdoors, label flips, duplicates, drift, model substitution and tampered inference logs - with evidence, not a score.
Author: Aditya Tipirneni
License: Proprietary
Project-URL: Homepage, https://yatharth.teentechindia.com
Project-URL: Documentation, https://github.com/theusscoder/yatharth/blob/main/docs/GETTING_STARTED.md
Project-URL: Repository, https://github.com/theusscoder/yatharth
Project-URL: Issues, https://github.com/theusscoder/yatharth/issues
Project-URL: Changelog, https://github.com/theusscoder/yatharth/releases
Keywords: dataset,assurance,object-detection,data-poisoning,backdoor,ml-security,audit,air-gapped
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security
Classifier: License :: Other/Proprietary License
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pillow>=10
Requires-Dist: numpy>=1.26
Requires-Dist: pydantic>=2.5
Requires-Dist: pyyaml>=6
Provides-Extra: dinov2
Requires-Dist: torch>=2.1; extra == "dinov2"
Requires-Dist: torchvision>=0.16; extra == "dinov2"
Requires-Dist: transformers>=4.40; extra == "dinov2"
Provides-Extra: index
Requires-Dist: faiss-cpu>=1.7; extra == "index"
Provides-Extra: viz
Requires-Dist: scikit-learn>=1.3; extra == "viz"
Provides-Extra: sign
Requires-Dist: cryptography>=42; extra == "sign"
Provides-Extra: model
Requires-Dist: torch>=2.1; extra == "model"
Requires-Dist: safetensors>=0.4; extra == "model"
Requires-Dist: onnxruntime>=1.17; extra == "model"
Requires-Dist: onnx>=1.15; extra == "model"
Provides-Extra: tui
Requires-Dist: rich>=13; extra == "tui"
Provides-Extra: yolo
Requires-Dist: ultralytics>=8; extra == "yolo"
Provides-Extra: all
Requires-Dist: cryptography>=42; extra == "all"
Requires-Dist: safetensors>=0.4; extra == "all"
Requires-Dist: onnxruntime>=1.17; extra == "all"
Requires-Dist: onnx>=1.15; extra == "all"
Requires-Dist: torch>=2.1; extra == "all"
Requires-Dist: torchvision>=0.16; extra == "all"
Requires-Dist: transformers>=4.40; extra == "all"
Requires-Dist: faiss-cpu>=1.7; extra == "all"
Requires-Dist: scikit-learn>=1.3; extra == "all"
Requires-Dist: rich>=13; extra == "all"

# Yatharth

**Offline assurance for object-detection datasets and the models trained on them.**
SIH 2026 · Problem Statement 26228 · team SPARCX

*Built by **Aditya Tipirneni***

Yatharth takes a contributed detection dataset, and optionally the model trained on it,
and says whether either can be trusted. It checks for:

- poisoned, flipped or planted labels;
- duplicates and flooding;
- train/test leakage;
- foreign or out-of-distribution images;
- trigger patches and backdoored models;
- a substituted model, or a doctored inference pipeline;
- tampered prediction logs;
- drift against a trusted reference.

It runs **fully air-gapped**: no network, no downloads at run time, no CDNs in its HTML.

Every finding carries the measurements that produced it, a reason generated from those
measurements (never free text, never an LLM), and a statement of what the check *cannot*
tell you. Severity and confidence are kept apart. The final decision (accept, review,
quarantine, recalibrate or inconclusive) is set by named claims, never by an average.
A check that could not run is reported **NOT RUN, with the reason**, never counted as passed.

Recorded runs, with their data and models to download: **https://yatharth.teentechindia.com**

**New here? Start with [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md): every command, step by step, from install to your first result.**

---

## Contents

1. [Requirements](#1-requirements)
2. [Install](#2-install)
3. [First run: check the install](#3-first-run-check-the-install)
4. [Assess something: `yatharth run`](#4-assess-something-yatharth-run)
5. [Preparing a submission](#5-preparing-a-submission)
6. [Reading the results](#6-reading-the-results)
7. [Configuration](#7-configuration)
8. [Command reference](#8-command-reference)
9. [The JavaScript package](#9-the-javascript-package)
10. [Tests](#10-tests)
11. [Publishing the site and releasing packages](#11-publishing-the-site-and-releasing-packages)
12. [Repository layout](#12-repository-layout)
13. [Troubleshooting](#13-troubleshooting)

---

## 1. Requirements

| | minimum | used for the recorded runs |
|---|---|---|
| Python | 3.10 (3.11 for `tools/release.py`) | 3.11 |
| OS | Windows, Linux or macOS | Windows 11 |
| RAM | 8 GB | 16 GB |
| GPU | optional: everything runs on the CPU, slowly | NVIDIA RTX 4050 Laptop, 6 GB |
| Disk | ~3 GB with torch, plus your datasets | |

A full assessment of ~1,200 images with a real model and the vision-model witness takes
about 15–25 minutes on the GPU above. Without a GPU, use `--backbone stub` to try things
out; it is fast and records itself as a stub, so it is never mistaken for a real run.

---

## 2. Install

### 2a. From PyPI

```bash
python -m venv .venv
# Windows: .venv\Scripts\activate      Linux/macOS: source .venv/bin/activate
pip install "yatharth[all,tui]"
yatharth --version
```

The install ships the default `policy.yaml` and its calibration measurements, so it runs
without a checkout. Extras:

| extra | adds | needed for |
|---|---|---|
| *(core)* | pillow, numpy, pydantic, pyyaml | ingest, hashing, stub embeddings, every data check |
| `dinov2` | torch, torchvision, transformers | real embeddings (DINOv2-small) |
| `model` | torch, safetensors, onnxruntime, onnx | assessing models (safetensors and ONNX) |
| `sign` | cryptography | Ed25519 signatures on inference logs and ledger heads |
| `tui` | rich | the live terminal UI (`yatharth run`, `yatharth view`) |
| `yolo` | ultralytics | looking *inside* YOLOv8 / YOLO11 models (layer graph only; weights still come only from safetensors) |
| `index` | faiss-cpu | FAISS index (optional; numpy is used without it) |
| `viz` | scikit-learn | the embedding explorer's projections |
| `all` | everything above except `yolo` | |

For the GPU on Windows or Linux, install the CUDA build of torch first
(<https://pytorch.org/get-started/locally/>). To run ONNX models on the GPU, also install
`onnxruntime-gpu` in a version built for the same CUDA as your torch; for CUDA 12 that is
`onnxruntime-gpu==1.22.0`. Asking for `--device cuda` when the GPU cannot be used is an
error, never a silent fall-back to the CPU.

### 2b. From source (development)

```bash
git clone https://github.com/aditya36900/yatharth.git
cd yatharth
python -m venv .venv
.venv\Scripts\activate                    # Linux/macOS: source .venv/bin/activate
pip install -e ".[all,tui]"               # add ,yolo to assess YOLO models white-box
```

Without installing, every command also works as `python yatharth <command>` from the repo
root (`python ps <command>` is the same thing).

### 2c. DINOv2 weights (once, on a machine with internet)

Weights are never downloaded at run time. Fetch them once and copy them in:

```bash
python -c "from transformers import AutoModel, AutoImageProcessor; AutoModel.from_pretrained('facebook/dinov2-small', cache_dir='vendor/dinov2-small'); AutoImageProcessor.from_pretrained('facebook/dinov2-small', cache_dir='vendor/dinov2-small')"
```

Yatharth looks for them at `--weights`, then `$YATHARTH_DINOV2`, then `./vendor/dinov2-small`.

### 2d. Air-gapped install

On a connected machine: `pip download "yatharth[all,tui]" -d wheels/`. Carry `wheels/`
and `vendor/` across, then: `pip install --no-index --find-links wheels/ "yatharth[all,tui]"`.

### 2e. Optional: the semantic witness (local vision-language model)

One check asks a local vision-language model (Qwen3-VL-4B through llama.cpp) whether each
object is what its label says. It is set up once and pinned by hash; see
[WITNESS.md](WITNESS.md). Without it, that check reports NOT RUN, and every other check runs.

---

## 3. First run: check the install

```bash
yatharth doctor                               # Python, GPU, torch, weights, optional extras
yatharth fixtures --out fixtures              # a seeded synthetic dataset (209 images)
yatharth ingest run --archive fixtures/coco --backbone stub
yatharth analyze run --dataset <id printed above>
```

Or run the whole showcase: one hostile submission with every attack planted, assessed in
one go (about a minute):

```bash
yatharth showcase --out showcase
```

---

## 4. Assess something: `yatharth run`

`yatharth run` is the live, interactive way in. You choose what to assess, pick a
submission and options, and watch the real pipeline run: every stage, every check as it
measures, the claims resolving and the decision.

```bash
yatharth run
```

It asks, in order:

1. **What to assess:**
   - **data only**: the images and labels;
   - **model only**: the model file(s), with the images used only as probe inputs;
   - **model + data**: everything, plus which training images taught the model a backdoor.
2. **Which submission.** It searches `corpus/`, `showcase/` and `fixtures-model/`, or you
   give a path.
3. **Options:** backbone (`dinov2` or `stub`), device (`cuda` or `cpu`), whether to ask
   the witness, and whether to compare with the reference set.

Non-interactive, with every choice on the command line:

```bash
yatharth run --mode both --submission corpus/real-attacked-yolo/submission \
             --name yolo-all --backbone dinov2 --device cuda --witness --yes
```

| flag | default | meaning |
|---|---|---|
| `--mode data\|model\|both` | ask | what to assess |
| `--submission PATH` | ask | the submission folder (see §5) |
| `--name NAME` | `<folder>-<mode>` | the run's name |
| `--runs-dir DIR` | `runs` | where run folders are written |
| `--backbone dinov2\|stub` | dinov2 if weights found | the embedding model |
| `--device cuda\|cpu` | cuda if present | where embeddings **and** model inference run |
| `--witness` / `--no-witness` | ask | ask the local vision model |
| `--no-reference` | | skip the drift comparison |
| `--reference-dir PATH` | `../reference` beside the submission | the trusted reference set |
| `--trust PATH` | `../trust` beside the submission | the assessor's trust store folder |
| `--policy FILE` | see §7 | the policy file |
| `--weights DIR` | see §2c | the DINOv2 folder |
| `--witness-root DIR` | `data/datasets` | where `yatharth witness setup` put its config |
| `--search DIR` | corpus, showcase, fixtures-model | where to look for submissions (repeatable) |
| `--yes` | | take defaults, ask nothing |

Which checks each mode runs is not a fixed list: every check declares whether it examines
the data, the model, or both, and the mode selects on that. A check a mode leaves out is
still listed in the report, as NOT RUN with the reason.

---

## 5. Preparing a submission

```
my-case/
  submission/
    coco/                 the dataset: a COCO or YOLO export (train/ valid/ test/)
      contributors.csv    optional: file,contributor - enables per-source analysis
    models/               optional: the model(s) submitted with the data
      detector.safetensors   (white-box: every model check can run)
      detector.onnx          (black-box: behaviour checks only)
    pipeline.json         required with models/: what the submitter says they deploy
    records/              optional: sealed inference logs (records.jsonl + inputs/)
  reference/coco/         optional: a trusted reference set, for drift
  trust/                  optional: the ASSESSOR's trust store, never the submitter's
    trust.json            approved deployments, their fingerprints, authorised node keys
    models/               the assessor's own copies of the approved models
  class_descriptions.json optional: plain-English class meanings for the witness
```

`pipeline.json`:

```json
{
  "model_file": "detector.safetensors",
  "deployment": "yolov8n-v1",
  "preprocess":  {"resize": [256, 256], "channel_order": "RGB", "normalize": "unit"},
  "postprocess": {"score_threshold": 0.25, "max_detections": 1}
}
```

**Model formats.**
- **Safetensors** earns full access (L3): its weights are loaded into an architecture
  Yatharth defines. That is `tinydet`, or with the `[yolo]` extra, YOLOv8 or YOLO11 at
  sizes n/s/m/l/x. The safetensors metadata carries `arch`, `classes` (a JSON list),
  `input`, and optionally `crop_side`.
- **ONNX** earns black-box access (L1).
- **Pickles** (`.pt`, `.pth`, `.pkl`) are never loaded. They are statically inspected by the
  safety gate, and that is all.

**Approving a deployment** (what `weight_integrity`, `model_fingerprint` and inference
re-execution compare against):

```bash
yatharth model register --label yolov8n-v1 --model approved.safetensors
```

`tools/yolo_scenario.py` builds a complete example from two YOLOv8n models: a backdoored
one submitted, a clean twin approved, and two edge nodes' sealed logs.

---

## 6. Reading the results

A run writes `runs/<name>/`:

| file | what it is |
|---|---|
| `<name>-report.html` | the report: every stage, every finding with its evidence (self-contained) |
| `<name>-observe.html` | the pipeline replay: watch each check sweep the files, then the claims resolve |
| `SUMMARY.json` | decision, counts, paths |
| `store/<dataset_id>/analysis/findings.json` | the assessment itself (the `AnalysisReport` contract), which is all any UI reads |
| `store/<dataset_id>/analysis/trace.json` | the digest-pinned trace the replay is drawn from |
| `store/ledger/` | the append-only, hash-chained audit ledger |

In the terminal:

```bash
yatharth view runs/yolo-all                    # the dashboard
yatharth view runs/yolo-all --file IMAGE.jpg   # one file, taken apart
yatharth view --list                           # every run found
```

From JavaScript, see §9. [REPORT_GUIDE.md](REPORT_GUIDE.md) explains how to read the report.

---

## 7. Configuration

**Every threshold lives in `policy.yaml`**, and nothing else judges. The policy in force is
chosen in this order:

1. `--policy FILE`. A named file that does not exist is an error; it is never swapped for
   another.
2. `$YATHARTH_POLICY`.
3. `./policy.yaml`.
4. The copy shipped inside the package.

The report prints the policy's version and digest.

| environment variable | meaning |
|---|---|
| `YATHARTH_ROOT` | the dataset store root (default `data/datasets`); `PRAMANA_ROOT` still works |
| `YATHARTH_POLICY` | the policy file |
| `YATHARTH_DINOV2` | the vendored DINOv2 folder |
| `YATHARTH_THREADS` | cap on CPU threads for ONNX inference (default: up to 8, verified not to change outputs) |
| `YATHARTH_PYTHON` | (JS package) the Python that `runYatharth()` starts |

---

## 8. Command reference

Every command is `yatharth <command>`, or `python yatharth <command>` in a checkout.
The steps are separate and each writes to disk, so any one can be rerun alone.

**Assess**

| command | what it does |
|---|---|
| `run` | live, interactive assessment (§4) |
| `view <run> [--file NAME] [--list]` | a finished run in the terminal |
| `showcase --out DIR` | every attack in one submission, assessed |
| `analyze run --dataset ID [--access L0..L3] [--reference-dataset ID]` | assess an ingested dataset |
| `analyze report --dataset ID` / `analyze observe --dataset ID` | re-render the report / replay |
| `analyze engines` | list every check, its axis, access tier and subject |

**Ingest (facts only: hashes, geometry, embeddings)**

| command | what it does |
|---|---|
| `ingest run --archive PATH --backbone dinov2\|stub [--device cuda]` | the whole ingest pipeline |
| `ingest accept\|parse\|hash\|describe\|embed\|manifest` | one step at a time |
| `ingest models --dataset ID --model FILE [--declare pipeline.json]` | store model artifacts (write-once) |
| `ingest records --dataset ID --records DIR` | store sealed inference logs (write-once) |
| `datasets list` | everything ingested |
| `deaugment --src DIR --dest DIR` | strip Roboflow augmentation copies |
| `report --dataset ID` / `explore --dataset ID --level crop` | ingest diagnostic / embedding explorer |

**Models, provenance, audit**

| command | what it does |
|---|---|
| `model register --label L --model FILE` | approve a deployment (keeps the assessor's copy) |
| `model fingerprint FILE` | behavioural fingerprint on the public challenge set |
| `model scan --dataset ID` | trigger inversion table |
| `provenance keygen\|trust\|seal\|verify\|scenario` | signing keys, trust store, sealed logs |
| `audit init\|show\|head\|verify\|prove\|witness\|replay\|delta` | the audit ledger |
| `memory list\|show\|prior\|revoke\|diff` | assurance memory across datasets |
| `witness setup\|check\|ask` | the local vision-model witness |

**Other**

| command | what it does |
|---|---|
| `doctor` | environment check |
| `fixtures --out DIR [--profile base\|engines\|drift\|trigger\|model\|models]` | seeded synthetic fixtures (`model` trains the model suite) |
| `schema` | regenerate `schemas/*.schema.json` from the contracts |
| `--version` | version |

---

## 9. The JavaScript package

`yatharth-assurance` (npm) reads, queries and checks assessments from Node or a browser.
It never re-judges: it reads the decision, severity and confidence exactly as written.

```bash
npm install yatharth-assurance
npx yatharth-report runs/yolo-all                          # summary
npx yatharth-report runs/yolo-all --type backdoor_trigger  # matching findings, explained
npx yatharth-report runs/yolo-all --check                  # exit 1 if it breaks the contract
npx -p yatharth-assurance yatharth-report runs/yolo-all    # one-off, without installing
```

```js
import { readReport, summarise, findings, explain, checkShape, runYatharth } from "yatharth-assurance";

const report = await readReport("runs/yolo-all");     // or a findings.json path, or a fetched object
const s = summarise(report);                            // decision, counts, NOT RUN checks, claim states
for (const f of findings(report, { severity: "high" })) console.log(explain(f).reason);
await runYatharth(["run", "--mode", "data", "--submission", "my-case/submission", "--yes"]);
```

Its TypeScript types are generated from the same JSON Schemas as the Python contracts
(`python tools/js_contract.py`), so the two cannot drift apart unnoticed.

---

## 10. Tests

```bash
python tools/acceptance.py            # ingest: needs fixtures/ (yatharth fixtures --out fixtures)
python tools/engine_acceptance.py     # every check, against fixtures with planted ground truth
cd packages/js && npm test            # the JavaScript package
```

The engine suite separates ASSERTED checks (pass/fail) from MEASURED ones (printed,
never asserted). It needs `fixtures-engines/` (`yatharth fixtures --out fixtures-engines
--profile engines`) and `fixtures-model/` (`yatharth fixtures --profile model --out
fixtures-model`, about 3 minutes, once). The two FAISS checks in the ingest suite need
the `[index]` extra; without it, 10/12 is the environment, not a regression.

---

## 11. Publishing the site and releasing packages

**The site.** `tools/site_runs.json` lists the runs to publish and their order. Then:

```bash
python tools/site.py --out site --downloads-url https://github.com/<owner>/<repo>/releases/download/v1/
```

**The packages.** Uploading is permanent, so a release is built and proven first:

```bash
python tools/release.py                    # build wheel + sdist + npm tarball, install each cleanly, smoke-test
python tools/release.py --upload testpypi  # then check: pip install -i https://test.pypi.org/simple/ yatharth
python tools/release.py --upload pypi
python tools/release.py --upload npm
```

Credentials come from `~/.pypirc` (twine) and `~/.npmrc` (`npm login`), never from this repo.
Bump `version` in `pyproject.toml`, `services/ingest/pramana_ingest/__init__.py` and
`packages/js/package.json` before each release.

---

## 12. Repository layout

```
yatharth                 launcher: `python yatharth <command>` with no install (also `ps`)
policy.yaml              every threshold in the system
calibration.json         measured confidences (written by tools/harness.py)
packages/schemas/        pramana_schemas - the contracts (pydantic only)
packages/js/             yatharth-assurance - the npm package
schemas/                 JSON Schemas, generated from the contracts (`yatharth schema`)
services/ingest/         pramana_ingest - lane 1: facts (hashes, geometry, embeddings)
services/engines/        pramana_engines - lane 2: 20 checks (evidence), correlation (judgement),
                         claims, ledger, memory, provenance, the report, replay and terminal UIs
tools/                   acceptance suites, scenario builders, site builder, release
```

Further reading:

- [ARCHITECTURE.md](ARCHITECTURE.md): how everything maps to the problem statement.
- [ENGINES.md](ENGINES.md): the checks.
- [GUIDE.md](GUIDE.md): ingest, formats and the manifest.
- [MATH.md](MATH.md): every formula, checked against the code.
- [MEASUREMENT.md](MEASUREMENT.md): what was measured and how.
- [REPORT_GUIDE.md](REPORT_GUIDE.md): reading a report.
- [DASHBOARD_SPEC.md](DASHBOARD_SPEC.md): building a UI on `findings.json`.
- [WITNESS.md](WITNESS.md): the vision-model witness.
- [RUN.md](RUN.md): copy-paste commands.
- [CLAUDE.md](CLAUDE.md): the rules the code keeps.

Git ignores the generated and local folders: `data/`, `corpus/`, `fixtures*/`, `runs/`,
`showcase/`, `site/`, `vendor/`, `scratch/`, `dist/`.

---

## 13. Troubleshooting

| you see | do |
|---|---|
| `'yatharth' is not recognized` | activate the venv, or use `python yatharth ...` from the repo root |
| `ModuleNotFoundError: torch` | `--backbone stub`, or `pip install "yatharth[dinov2]"` |
| `the GPU was asked for, but ...` | the CUDA build of torch or onnxruntime-gpu is missing or mismatched (§2a); run `yatharth doctor`, or use `--device cpu` |
| `... need the architecture definition from ultralytics` | `pip install "yatharth[yolo]"` to look inside YOLO models |
| `no policy at ...` | the `--policy` / `$YATHARTH_POLICY` you named does not exist |
| a check says NOT RUN | read its reason: missing access tier, reference, trust store or extra, never a silent pass |
| `semantic_witness` NOT RUN | the witness is not configured (§2e) |
| acceptance fails after minutes | a real dataset got into `fixtures/`: regenerate with `yatharth fixtures --out fixtures` |
| `Ambiguous dataset format` | the export has both COCO and YOLO files; re-export in one format |

---

Yatharth is proprietary; see `pyproject.toml`. The real datasets in the recorded runs are
credited on the site.

**Aditya Tipirneni** · Yatharth · https://yatharth.teentechindia.com
