psyc demo now closes with cockpit links pointing at the Worker Mesh and reports whether the live model server is up. README rewritten to current state — Worker Mesh, inference server, model-in-operation, the three services, accurate code layout. Adds docs/demo.md, a one-page run-sheet. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
183 lines
7.0 KiB
Markdown
183 lines
7.0 KiB
Markdown
<p align="center">
|
|
<img src="src/psyc/cockpit/static/psyc-logo.png" alt="psyc" width="320">
|
|
</p>
|
|
|
|
# psyc
|
|
|
|
> Validate the signal, protect the evidence, route only what each destination is
|
|
> authorized to receive, and prove every external action through an immutable ledger.
|
|
|
|
Defensive cyber-threat-intelligence routing & evidence-sealing platform — a
|
|
small-worker mesh that ingests public threat feeds, classifies and seals cases,
|
|
routes them to the right destinations under TLP policy, and proves every action
|
|
through an append-only ledger. Started as a 48h hackathon (2026-05); grown into
|
|
a working platform with a fine-tuned model in operation.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```text
|
|
Sensors
|
|
→ Scoutline fetch + parse public feeds, emit normalized cases [built]
|
|
→ Proofline validate indicators, score confidence [planned]
|
|
→ Mapline resolve hosting country / jurisdiction [built]
|
|
→ Classifyline severity, TLP, incident type, internal class [built]
|
|
→ Sealine authority-sealed evidence encryption [built]
|
|
→ Routeline pick destinations under policy, build payloads [built]
|
|
→ Courier submit to destinations, collect receipts [built]
|
|
→ Ledgerline immutable audit of every submission + blocked route [built]
|
|
→ Publishline sanitized public intelligence after mitigation [planned]
|
|
→ Trainline lawful intel → LoRA datasets + QLoRA training [built]
|
|
→ Cockpit operator UI (FastAPI + Jinja) [built]
|
|
```
|
|
|
|
Each `-line` is a stage in a small-worker mesh; each worker does one narrow job
|
|
and passes a normalized `Case` object onward. Rules drive the deterministic
|
|
work; a fine-tuned model handles judgment (see Training). Humans approve
|
|
anything sensitive before it leaves the platform.
|
|
|
|
Full design: [`docs/dossier.md`](docs/dossier.md) · style: [`docs/style.md`](docs/style.md) · demo run-sheet: [`docs/demo.md`](docs/demo.md)
|
|
|
|
---
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
python3 -m virtualenv .venv
|
|
.venv/bin/pip install -e .
|
|
|
|
.venv/bin/psyc init # create the sqlite db
|
|
.venv/bin/psyc fetch-all # ingest URLhaus + CISA KEV + Feodo Tracker
|
|
.venv/bin/psyc demo # run one case through the whole pipeline
|
|
```
|
|
|
|
The platform runs as up to three services (each in its own terminal):
|
|
|
|
```bash
|
|
.venv/bin/psyc serve --port 8767 # operator cockpit → http://127.0.0.1:8767
|
|
.venv/bin/psyc mock-cert --port 8770 # stand-in CERT / abuse-API receiver
|
|
|
|
# optional, needs an NVIDIA GPU — puts the live model behind the Classifier bot:
|
|
docker run --gpus all --rm -p 8771:8771 --entrypoint python \
|
|
-v $(pwd)/data:/data -v $(pwd)/scripts:/scripts \
|
|
psyc-trainer /scripts/serve_model.py --adapter /data/adapters/psyc-v4/final
|
|
```
|
|
|
|
---
|
|
|
|
## Cockpit
|
|
|
|
`http://127.0.0.1:8767` — five views:
|
|
|
|
| View | Path | Shows |
|
|
|---|---|---|
|
|
| Case Queue | `/cases` | every ingested case, severity + TLP badges |
|
|
| Case detail | `/cases/{id}` | classification, observables, sealed package, routes, per-case ledger |
|
|
| Worker Mesh | `/cases/{id}/journey` | animated 7-bot replay of the case's path; the Classifier bot shows the live model's verdict |
|
|
| Ledger | `/ledger` | immutable audit feed |
|
|
| Trainline | `/train` | datasets + trained adapters with loss charts |
|
|
|
|
---
|
|
|
|
## Code layout
|
|
|
|
```
|
|
src/psyc/
|
|
models.py normalized Case object + enums (Pydantic)
|
|
db.py SQLAlchemy Core — cases + ledger tables
|
|
result.py Ok / Err / Result[T, E]
|
|
log.py structlog configuration
|
|
cli.py flat Typer CLI
|
|
mock_cert.py stand-in CERT / abuse-API receiver
|
|
lines/ one file per worker line
|
|
scout.py multi-source fetch + signalize (URLhaus, CISA KEV, Feodo)
|
|
classify.py severity / TLP / incident type / internal class
|
|
map.py GeoResolver — host IP → country
|
|
seal.py PyNaCl sealed-box evidence encryption
|
|
route.py destination matrix + policy gates
|
|
courier.py HTTP submission + payload building
|
|
ledger.py append-only audit
|
|
train.py JSONL dataset builders + quality gate
|
|
cockpit/ FastAPI + Jinja operator UI
|
|
app.py routes
|
|
journey.py Worker Mesh / case-journey assembly
|
|
inference.py client for the live model server
|
|
templates/ static/
|
|
|
|
scripts/
|
|
train_qlora.py unsloth QLoRA fine-tune
|
|
eval_adapter.py adapter evaluation
|
|
serve_model.py inference server (FastAPI, runs in the CUDA container)
|
|
|
|
docs/
|
|
dossier.md style.md demo.md archive/
|
|
```
|
|
|
|
---
|
|
|
|
## Training & the live model (Trainline + QLoRA)
|
|
|
|
`psyc train-build-all` emits Alpaca-style JSONL datasets under
|
|
`data/datasets/<task>-v<n>.jsonl` for four defensive tasks — `ioc_extraction`,
|
|
`severity_classification`, `routing_decision`, `tlp_assignment`. QualityGate
|
|
drops TLP:RED, restricted-source, empty, and credential-leak rows.
|
|
|
|
Fine-tune Qwen3.5-4B with QLoRA in the CUDA container:
|
|
|
|
```bash
|
|
docker build -t psyc-trainer -f Dockerfile.train .
|
|
|
|
docker run --gpus all --rm --entrypoint python \
|
|
-v $(pwd)/data:/data -v $(pwd)/scripts:/scripts \
|
|
psyc-trainer /scripts/train_qlora.py \
|
|
--dataset /data/datasets/ioc_extraction-v4.jsonl \
|
|
--dataset /data/datasets/severity_classification-v4.jsonl \
|
|
--dataset /data/datasets/routing_decision-v4.jsonl \
|
|
--dataset /data/datasets/tlp_assignment-v4.jsonl \
|
|
--output /data/adapters/psyc-v4
|
|
```
|
|
|
|
Defaults target a 24 GB GPU (3090/4090): `unsloth/Qwen3.5-4B` at 4-bit, LoRA
|
|
r=16, bf16, 3 epochs. Output: `data/adapters/<run>/final/` + `training_meta.json`.
|
|
Evaluate with `scripts/eval_adapter.py`; the `/train` cockpit page shows every
|
|
dataset and adapter with its loss curve.
|
|
|
|
`scripts/serve_model.py` loads an adapter and serves `/infer` over HTTP. When
|
|
it's running, the cockpit's **Classifier bot** shows the live model's severity
|
|
verdict beside the rule's — and degrades to rules-only if the server is down.
|
|
|
|
---
|
|
|
|
## Style
|
|
|
|
All code follows [`docs/style.md`](docs/style.md) — a 12-fold guide: `Optional[X]`
|
|
/ `List[X]` from `typing`, `Field(default_factory=...)`, `Result[T, E]` for
|
|
expected failures, `class X(str, Enum)`, structlog `area.action` events,
|
|
SQLAlchemy Core (no ORM), flat hyphenated Typer commands.
|
|
|
|
---
|
|
|
|
## Scope
|
|
|
|
**Lawful, white-hat defensive operations only.** psyc routes intelligence to
|
|
victims, CERT/CSIRTs, sector ISACs, provider/registrar abuse desks, and trusted
|
|
CTI communities. It will **not** amplify stolen data, expose victims
|
|
prematurely, interact with criminal actors, distribute exploitation content, or
|
|
submit evidence beyond a destination's max TLP. Boundaries: `docs/dossier.md`
|
|
§5, §10, §16.
|
|
|
|
---
|
|
|
|
## Status
|
|
|
|
Working platform. Built: Scoutline (URLhaus + CISA KEV + Feodo Tracker) →
|
|
Classifyline → Mapline → Sealine → Routeline → Courier → Ledgerline → Trainline,
|
|
the FastAPI cockpit (five views incl. the animated Worker Mesh), and a
|
|
fine-tuned Qwen3.5-4B (psyc-v4) served live behind the Classifier bot.
|
|
Not yet built: Proofline (confidence scoring), Publishline (public advisories).
|
|
|
|
## License
|
|
|
|
Unset. Choose before any external release.
|