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).detectandevaluateover 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 injectableEmbeddingFnseam plus a local Ollama client that uses only stdliburllib(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; writesresults.json+RESULTS.md. - Real-world loader (
realworld.py). Normalizes a documented intermediate JSONL into thePost/Datasetschema 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)
| File | Size | Uploaded | |
|---|---|---|---|
| oubliette_barbican-0.1.1.tar.gz | 22.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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