Shell Heuristics · application docs

publisher · enricher-v2 · corpus

Architecture

Service-by-service map of what is actually running today (post wave 5 + 6 cutover, 2026-05-19).

Services (with reference hashes)

Use these 4-hex hashes when filing tickets or design notes — deterministic from the service name and disambiguate across repos.

Service Hash Role Replicas
fraud-publisher [37c5] Scans corpus.db pending rows → fraud.rows envelopes 1
fraud-enricher-v2 [eae9] Consumes fraud.rows; LLM + regex enrichment via gpumon-ingress; writes back via fraud-db-broker 20
fraud-enrich-worker [a1f4] Config-loop harness — runs ENRICH_CONFIGS (CIK, captions, matters, scheme). See enrichment-configs.md 1
fraud-db-broker [66d9] Sole rw SQLite handle on corpus.db; HTTP write proxy. Forward store: ParadeDB 192.168.1.181:5432/postcrimedb (postcrime schema) — migration in progress. 1
fraud-search [0592] SvelteKit UI at sec.atsignhandle.xyz; vector + FTS search 1
fraud-corpus-auditor [0c84] Periodic integrity sweep of corpus.db 0 (idle)
fraud-embedder [5419] Embeddings backfill via doc-skills MCP 0 (idle)
fraud-crawler [b372] Crawls SEC/DOJ press releases and litigation 0 (idle)
fraud-watchdog [2e21] Health monitor for the enrichment fleet 0 (idle)

The canonical view is the SVG diagram embedded at the top of README.html. The text representation that follows is preserved for terminal / accessibility reading.

Two pipelines, one DB

There is one source-of-truth corpus (corpus.db, on node-eighteen, mounted at /var/lib/fraud-data/corpus.db). Writer pipelines flow into it:

  1. Crawler pipelinecrawler/ writes new documents.
  2. Queue enrichment harnessfraud-publisher sweeps pending rows and publishes {doc_id, workflow} envelopes to fraud.rows; fraud-enricher-v2 (20 replicas) consumes them and decorates documents. 18 workflows. See enrichment.md.
  3. Config-loop enrichment harnessfraud-enrich-worker runs the ENRICH_CONFIGS array in an in-process loop (CIK resolution, CourtListener captions, matter grouping, scheme classification). Self-selects via each config's selectorSql — no publisher. See enrichment-configs.md.

The legacy per-workflow stack-enrich-*.yml services (old in-process loops) are scaled to 0 as the queue harness took over their work — but note that fields they once filled (e.g. statute_refs, outcome) only continue to advance if the matching workflow is being published and consumed.

Every write goes through fraud-db-broker (Bun HTTP service on port 3100). It owns the single rw SQLite handle. Everyone else is read-only.

Enrichment coverage

The live /stats page renders the per-workflow populated/done/skipped table at the top. Conceptually it belongs here — the table is "how much of the architecture diagram has actually run end-to-end across the corpus". Each row maps one of the enrichment passes to a column on documents and a row class in workflow_runs. A small mirror of this still lives at the bottom of /stats as a progress-bar summary so the UI carries a "this view is X% populated" hint without forcing readers to leave the page.

The numbers update every time fraud-publisher flushes envelopes and fraud-enricher-v2 ACKs them. Inputs:

  • documents.outcome IS NOT NULL → outcome-detect coverage
  • documents.scheme IS NOT NULL → scheme-classify coverage
  • documents.title_formatted IS NOT NULL → format-title coverage
  • entities row count grouped by kind → entity-kind coverage
  • workflow_runs.status per workflow → done / skipped totals

Data flow (current)

Shell Heuristics data flow — sources → broker → enricher fleet → corpus.db external sources crawler / ingest broker / LLM enrichment fleet queue read-only consumers EDGAR · DOJ · SEC press · litigation · indictments crawled HTML fraud-crawler-* HTTP fetch + parse node-eighteen · idle 0/0 fraud-browser-sidecar puppeteer stealth DOJ render proxy fraud-db-broker /update · /apply/<endpoint> sole rw SQLite handle fraud-publisher sweep documents → federation.work / fraud.rows gpumon-rabbitmq vhost: federation fraud.rows → DLX → fraud.dlq fraud-enricher-v2 prefetch=1 · max_attempts=5 10 workflow passes corpus.db documents · entities · triples node-eighteen · /var/lib/fraud-data fraud-search (UI) SvelteKit · FTS5 + vec sec.atsignhandle.xyz stats-miner per-panel weekly scheduler node-eighteen :3200 · L1/L2 cache fraud-watchdog health sweep · /healthz /stats panel uptime fraud-corpus-auditor integrity sweeps idle until scheduled fraud-publisher (residual) backfill mode runs only on signal fraud-tesseract-sidecar OCR fallback fraud-ocr=true label fanout crawl writes reads pending publish envelope consume * patch via broker writes writes documents reads RO reads RO reads RO scheduled backfill signal OCR ack solid edges: writes through broker · dashed: read-only via NFS or HTTP all writes go through fraud-db-broker /update or /apply/<endpoint>; everyone else is read-only. LLM enrichment calls fan out to gpumon-ingress:4001 (not shown) with x-gpumon-project: fraud-heuristics. fraud-watchdog moved to col 3 so corpus.db can fan out to all three RO consumers with single L-shape edges. * consume = fraud-enricher-v2 consumes the fraud.rows queue (edge label shortened to fit the column gap).
                  EDGAR  ─────────────────────┐
                  fraud-scan (node-eighteen)  │
                                              ▼
                                  flagged/*.ndjson  (separate corpus)

  DOJ / SEC sites ──▶ fraud-crawler-* ──▶ fraud-db-broker ──▶ corpus.db
                              (writes)        (single rw)        │
                                                                 │ readonly
                                                                 ▼
                            ┌─────────────────────────────────────────────────┐
                            │ fraud-publisher    (wave 5, node-eighteen)      │
                            │   sweeps documents where                        │
                            │     workflow.selectSql() matches                │
                            │     AND no terminal workflow_runs row exists    │
                            │   publishes {doc_id, workflow} envelope to      │
                            │     federation.work / fraud.rows                │
                            │   flips queued_at / queued_workflow / envelope_id│
                            └─────────────────────────────────────────────────┘
                                                 │
                                                 ▼ AMQP (federation broker on node-eleven)
                                       gpumon-rabbitmq (vhost: federation)
                                                 │  fraud.rows  ──▶ fraud.dlq
                                                 ▼
                            ┌─────────────────────────────────────────────────┐
                            │ fraud-enricher-v2  (wave 6, node-eighteen)      │
                            │   prefetch=1, max_attempts=5                    │
                            │   loads WorkflowDef, calls process(doc)         │
                            │   POSTs patch to broker /update or              │
                            │     /apply/<endpoint>                           │
                            │   writes workflow_runs row (status, attempts)   │
                            │   LLM/OCR calls go to gpumon-ingress:4001       │
                            │     (NIM → LAN GPU overflow → failover;         │
                            │      no direct Ollama/Haiku calls)              │
                            └─────────────────────────────────────────────────┘
                                                 │
                                                 ▼ rw via broker
                                              corpus.db

  search-ui  (Cloudflare → postcrime.atsignhandle.xyz) ──▶ corpus.db (RO)
  stats-miner  (node-eighteen :3200)                ──▶ corpus.db (RO)
  watchdog                                          ──▶ corpus.db (RO)

Running services (node-eleven swarm, 2026-05-19)

Service Replicas Role
fraud-publisher 1 Wave-5 dispatcher (see services/fraud-publisher/). Gates which workflows are swept via PUBLISHER_WORKFLOWS.
fraud-enricher-v2 20 Wave-6 consumer (see services/fraud-enricher-v2/). Pinned to node-eighteen, which has ~1 GB free + 0 swap — do not scale up further here (OOM risk).
fraud-enrich-worker 1 Config-loop harness (see enrich-worker/). ENRICH_CONFIGS. mem 1.5 GB (CIK map).
fraud-db-broker 1 Single rw on corpus.db. Pinned to node-eighteen.
fraud-crawler-doj-sdny / fraud-crawler-sec-pr / fraud-crawler-sec-lr 1 each Source crawlers.
fraud-processor-{html,pdf,vision,triples} 1 each Old in-process enrichment loops.
fraud-enrich-* (titles, entities, summarize, case-facts, ...) mostly 0/0 Legacy per-workflow loops, scaled down as enricher-v2 takes their work.
fraud-tesseract-sidecar spread on fraud-ocr=true OCR fallback.
fraud-browser-sidecar 1 Puppeteer stealth proxy for DOJ.
fraud-search 1 SvelteKit on node-eighteen:7850.
fraud-stats-miner 1 Read-only JSON API for /stats.
edgar-agent-bypass, edgar-rm-expander, edgar-clusterer, ... mixed EDGAR fingerprint side. Sibling pipeline, separate code base (edgar-fraud-scan).

Live list:

ssh rooot@node-eleven 'docker service ls | grep -E "fraud|edgar"'

The federation queue (waves 5–6)

Piece Value
AMQP host gpumon-rabbitmq:5672 (swarm overlay gpumon-net), vhost federation
Exchange federation.work (durable, direct)
Routing key fraud.rows (or fraud.rows.<workflow> if FEDERATION_WORKFLOW_KEYED=true)
Queue fraud.rows (classic, durable, x-dead-letter-exchange → federation.dlx)
DLQ fraud.dlq
Envelope { envelope_id, idempotency_key, doc_id, workflow, attempt, attribution, created_at } (see shared/federation-queue-client.ts:PublishEnvelope)
Confirms confirm-channel publishes from the producer side.
Retry producer retries publish; consumer NACK+republish with x-attempt+1 up to WORKER_MAX_ATTEMPTS (default 5), then NACK with requeue=false → DLQ.

Why node-eighteen

corpus.db lives on node-eighteen's local SSD. Every primary fraud-* service pins to node.hostname == node-eighteen. Do not trust node.labels.fraud_corpus == true — that label doesn't actually exist on any swarm node despite some stack files referring to it (see _overnight-questions.md Q6). The canonical answer is hostname pinning.

Exception: fraud-processor-triples pins to node-eleven because that's where the four pool-fraud-triples GPU backends live with line-of-sight; fraud-tesseract-sidecar fans out to any fraud-ocr=true node.

Image distribution

There is no Docker registry. After rebuilding a fraud service:

docker save fraud-enricher-v2:latest | ssh rooot@node-eighteen docker load
ssh rooot@node-eleven 'docker service update --force fraud_fraud-enricher-v2'

You must docker save | ssh ... docker load to every node the task can be scheduled on, then --force the service update. See docker/README.md and _overnight-questions.md Q10 ("image digests aren't enough — verify routes after broker rebuild").

Public routing

https://postcrime.atsignhandle.xyz → Cloudflare → node-eighteen:7850 (fraud_fraud-search).

GAP: there is no public surface for fraud-enricher-v2 health or queue depth. RabbitMQ management UI is reachable inside the cluster but not exposed via Cloudflare.

Licensing & Consulting

This platform — the crawler/enrichment pipeline, the anomaly-detection heuristics, the scheme-detection rule banks, and the enriched corpus — is research software under active development. It is provided as-is, with no warranty and no claim of fitness for any legal, financial, or investment purpose. Nothing surfaced here is advice; outputs are probabilistic signals, not findings of wrongdoing.

Licensing and consulting are available for serious inquiries. Typical engagements:

  • Source / data licensing — access to the enriched corpus, the heuristic and scheme-rule definitions, or the per-source crawlers under a commercial license.
  • Detection consulting — applying the precrime heuristics to a specific filer universe, custom rule banks, or integration with an existing surveillance/compliance stack.
  • Pipeline build-out — standing up the SEC/DOJ ingest → enrichment → anomaly-scoring stack against another jurisdiction or document corpus.

Serious inquiries only. Reach out through the contact channel listed on the landing page; please include the use case, the data scope, and whether the engagement is licensing or consulting.