Skip to main content

BARBICAN — synthetic influence-operation detection

Public defensive detector for coordinated synthetic social-post content. The offensive twin (SPECTRE) is private and federal-only and is not part of this package.

This is a self-contained, zero runtime dependency Python package (standard library only). The content-scanner signals it relies on are vendored into barbican._scanners, so importing barbican never loads any other Oubliette package.

Quickstart

Point it at a file of posts and ask whether anything in it is coordinated:

pip install oubliette-barbican
barbican demo

A ready-made corpus ships inside the package, so there is nothing to source first and nothing to clone — 21 posts, two constructed coordinated pushes and twelve organic controls. It is CC0 text written for the fixture, so it carries no licence encumbrance. See examples/README.md for provenance and for what it does and does not demonstrate. Point detect at your own corpus the same way: barbican detect -i your_posts.jsonl.

Examined 21 posts (embeddings: none).
Thresholds: edge=0.6 coord=0.6 window=3600s

2 coordinated cluster(s):

  [1] 5 posts, score 0.807
      ferry-push-1 eastbank_1 2026-03-02T09:00:00Z
                 The Rivermouth ferry closure is a disaster for working families on th...
      ...

Score is coordination evidence, not proof of inauthenticity: quotation,
syndication and genuine consensus also cluster.

No network by default. The correlator blends an embedding signal with n-gram, temporal and persona signals; with embeddings off the embedder is never consulted, so detect runs on a disconnected host using the standard library alone. --embed ollama opts in to a local model server and buys paraphrase robustness.

Input is JSONL, one post per line. post_id, text, author_id and timestamp are required; campaign_id, label and backend_model are evaluation metadata and optional.

{"post_id": "c1", "author_id": "a1", "timestamp": "2026-01-01T10:00:00Z", "text": "..."}

Exit codes suit pipelines: 0 nothing flagged, 1 coordination found, 2 bad input.

With ground truth available, score the discovered clusters against the known campaigns rather than eyeballing them:

barbican evaluate -i labelled.jsonl -f json

evaluate refuses a corpus with no campaign_id, rather than reporting a number computed against nothing.

What it does

  • Operator CLI (cli.py). detect and evaluate over a JSONL corpus — the analyst-facing surface over the library below.
  • Per-artifact baseline (artifact.py). A deterministic single-surface detector: short-text lexical/statistical features plus two vendored scanner signals (invisible-text hits, AI-generation hits), combined by a linear baseline. Demonstrates — and bounds — what per-post scoring alone can achieve.
  • Coordination correlator (correlator.py). The novelty: pairwise coordination signals (narrative n-gram/embedding similarity, temporal proximity, persona similarity) → thresholded graph → union-find clustering → per-cluster coordination score. Recovers coordinated campaigns that look individually authentic and only become suspicious in aggregate.
  • Embeddings (embed.py). An injectable EmbeddingFn seam plus a local Ollama client that uses only stdlib urllib (no third-party HTTP dependency).
  • Evaluation harness (experiment.py). Baseline, layered (baseline + correlator), and real-world-split conditions with precision/recall/F1 and campaign-level metrics; writes results.json + RESULTS.md.
  • Real-world loader (realworld.py). Normalizes a documented intermediate JSONL into the Post/Dataset schema for a held-out generalization check.

Boundary

The offensive SPECTRE framework and the eval-only corpus generator that imports it (corpus.py in the private monorepo) are deliberately excluded from this package. A regression test (tests/test_barbican_smoke.py) enforces the invariant that import barbican succeeds standalone and pulls in no oubliette_shield module.

Real-world validation

The loader reads a documented intermediate JSONL — one object per line:

{"text": "...", "author_id": "...", "timestamp": "ISO-8601", "label": "io|control", "campaign_id": "... | null"}

label maps io→synthetic, control→authentic; campaign_id is kept for io posts (the coordinated group) and forced to null for controls. Producing that JSONL from a specific licensed release is a one-time local preprocessing step you run — no source dataset is bundled or committed. When the JSONL is absent, run_realworld_split returns status="SKIPPED"; the real-world number is never fabricated.

Suggested held-out corpus: ISI/USC "Labeled Datasets for Research on Information Operations" (Zenodo 10.5281/zenodo.14141549) — labeled IO posts plus matched organic control, per-post schema. Its license is CC BY-NC-ND 4.0 (NonCommercial): usable for research/validation, not for bundling into a shipped product.

Development

pip install -e ".[dev]"
pytest
ruff check src/
ruff format --check src/
mypy --strict src/

License

Apache-2.0. See LICENSE and NOTICE.

Copyright 2026 Oubliette Security LLC.

Metadata

Release files for oubliette-barbican 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for oubliette-barbican 0.1.1
File Size Uploaded
oubliette_barbican-0.1.1.tar.gz 22.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for oubliette-barbican 0.1.1
File Interpreter ABI Platform
oubliette_barbican-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 48.6 kB

Release files / oubliette_barbican-0.1.1.tar.gz

Download URL oubliette_barbican-0.1.1.tar.gz
Size 22.1 kB
Tags Source
SHA-256 checksum
How to use checksums
6aaa0d0a5aac3a6068c00329cd50a8d12e2b4c5bfe14e4e1417192fb18bb2abe
BLAKE2b-256 checksum
How to use checksums
cd20b71caa241e358ddb3de174f0e4b326f23bf96e1a1798fa3502dfe4e0c1ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / oubliette_barbican-0.1.1-py3-none-any.whl

Download URL oubliette_barbican-0.1.1-py3-none-any.whl
Size 26.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28fc9af2e157389249c10b9b82a38fc9d5e2796833e3573f9255b9fbc75bfbaf
BLAKE2b-256 checksum
How to use checksums
f3b9e28191a0f21a4d1c5b540c1540ee5400a752132c796fb0480e4d39807e05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page