privacy-threat-oracle
Skill0 starsChanged 4 months agoMITView on GitHub
Deterministic, rule-based privacy decision engine — evaluates a proposed action (which identity/compartment it's taken from, who can see it, what sensitive content it contains) against a threat model of adversary classes and identity compartments, and reports a recommendation (proceed / proceed_with_modification / decline) with a stated residual risk and reason. Its five-class content-sensitivity vocabulary (direct_pii, secret, metadata, inference_cue, stylometric) matches privacy-linter's own leak_class values for the first three, so privacy-linter's --json output plugs in directly as the content-sensitivity input via --from-linter-json; stylometric matches style-obfuscator's --emit-findings bridge instead, and inference_cue is a reserved class neither tool currently produces (see "How this works" for the full breakdown). Runs entirely locally, no model or network call — a rule table over a fixed schema, not an LLM judgment call. Use when the user wants to check whether sharing/posting something crosses an identity-compartment boundary, asks "should I post this under my real name," "is this safe to share publicly," "does this leak across my compartments," or wants a structured second opinion before a disclosure decision. Triggers on "check this against my threat model," "will this cross compartments," "is this safe to post/share," "privacy decision check," and the script oracle.py. Does not parse free-text proposed actions (see "What's NOT built here") or replace privacy-linter's own content detection — this is the decision layer on top of it, not a second scanner.
In this repository19 other files it gives its agents.
- agent-evalSkill
- agent-redteamSkill
- broadcastSkill
- deid-reid-harnessSkill
- fiction-workshopSkill
- issue-reconcilerSkill
- privacy-linterSkill
- repo-pincerSkill
- research-ledgerSkill
- review-and-planSkill
- style-obfuscatorSkill
- wiki-governorSkill
- wiki-librarianSkill
- wiki-operatorSkill
- wiki-privacy-auditSkill
- wiki-synthesizerSkill
- wiki-teacherSkill
- wiki-warehouseSkill
- my-skill-nameSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 62
Plug in privacy-linter directly: instead of naming content classes by hand:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Privacy Threat Oracle
The governance layer of the Privacy OS spine (Projects/Privacy OS - Threat-Model
Decision Engine in the source vault): a living threat model — adversary classes,
identity compartments, cost-to-defend tiers — encoded as data, plus a deterministic
decision layer over it. Where privacy-linter answers "does this content contain a
leak," this answers "given who this identity is and who'd see it, should this
disclosure happen at all."
Why this is deterministic-only, not model-based
Unlike privacy-linter's own still-unbuilt inference-cue class (privacy-linter's
SKILL.md names it as deliberately out of scope, blocked on a local-model decision that
hasn't been made — nothing in this repo produces it yet), this project's core design
doesn't have the same wall: v1 evaluates already-structured facts about a
proposed action (which compartment, which exposure, which content classes) against a
fixed rule table — not raw disclosure content run past an external model. See
${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md for why the rule table is a ranked ordering rather than
a weighted scoring function (the source project's own Open Question #2), and why
structured-only input (not free text) is the deliberate v1 scope (Open Question #3).
How this works
python ${CLAUDE_PLUGIN_ROOT}/scripts/oracle.py \
--source-compartment personal --target-compartment public_professional \
--target-exposure public_internet --content-class direct_pii- Compartment violation check — does the target compartment fall outside what the
source compartment may reference? All three compartments (
public_professional,personal,sensitive_research) only reference themselves, per${CLAUDE_PLUGIN_ROOT}/references/threat-model.json— full mutual isolation is the confirmed policy, not a v1 placeholder (see${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's Step 1). - Exposed adversaries — resolved from
--target-exposurevia the same file'starget_exposure_map(e.g.public_internetreaches data brokers, criminals, employer, corporations, civil discovery, state actors, and autonomous adversarial AI agents — automated tools that scrape and correlate public content across compartments at machine scale, without needing a human to bother). Any exposed adversary flaggedout_of_scopeinthreat-model.json(currently just the state actor — see${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's "Out-of-scope adversaries" section) still counts towardadversary_cost_tierand the recommendation like any other adversary, but is also surfaced separately inout_of_scope_adversaries_exposedand noted inreason— a flag this tool can't meaningfully help you defend against, called out rather than silently folded into the same bucket as one you can. - Sensitivity tier — the highest tier among
--content-classvalues present (direct_pii/secret= high,metadata/inference_cue/stylometric= medium). - Recommendation — the rule table in
${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md, applied top to bottom: compartment violation + high sensitivity →decline; compartment violation alone →proceed_with_modification; high sensitivity reaching a high-cost-to-defend adversary →decline; and so on down to a cleanproceed. - Reversibility —
--reversibledowngrades the recommendation one step (decline→proceed_with_modification,proceed_with_modification→proceed; never skips straight to an unqualifiedproceedon a compartment-violation-plus-high-sensitivity case — see${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's Step 4), modeling that a retractable action carries genuinely lower cost. Plug in privacy-linter directly instead of naming content classes by hand:
python ../privacy-linter/scripts/scan_diff.py --file draft_post.txt --json | \ python ${CLAUDE_PLUGIN_ROOT}/scripts/oracle.py --source-compartment personal --target-compartment personal \ --target-exposure public_internet --from-linter-json -This is the "wire oracle to Pre-Disclosure Linter for severity context" integration the source project named as a milestone —
--from-linter-jsonreads ascan_diff.py --jsonpayload (or any list of{"leak_class": ...}objects) and folds its finding classes into the sensitivity calculation, deduped. If the input can't be read (malformed JSON, wrong shape), the oracle fails closed, not open: it warns on stderr, setslinter_json_parse_error: true, and forces the sensitivity floor tohighrather than silently falling back to "nothing sensitive found."Where each of the five content classes actually comes from — this only matters once, but matters precisely, since two of the five aren't what the earlier framing implied:
direct_pii,secret,metadata— confirmed (grep) as the onlyleak_classvaluesprivacy-linter'sscan_diff.pyever emits.--from-linter-jsongenuinely plugs in directly for these three.stylometric— not produced byprivacy-linter. It'sstyle-obfuscator's--emit-findingsbridge that emits it (scripts/fingerprint.py), a separate skill with its own invocation. Pipingprivacy-linter's output alone will never produce this class.inference_cue— produced by nothing in this repo yet.privacy-linter's ownSKILL.mdnames it explicitly as out of scope, blocked on a local-model decision that hasn't been made. It's accepted as a--content-classvalue here so the rule table andthreat-model.json's sensitivity tiers are ready for it, not because anything currently populates it automatically.
--jsonfor machine-readable output; default is a short human-readable report.
This exits 0 by default — decline is the rule table's most severe label, not an
automatically enforced block. --block-on {proceed_with_modification,decline} exits 1
instead, for a caller that wants the exit code to reflect the verdict (mirrors
privacy-linter's own --block-on). See ${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's closing
note for why this is opt-in rather than a default-behavior change: nothing invokes
oracle.py unattended today, unlike privacy-linter's git hook.
What's NOT built here
- Free-text proposed-action parsing. The original design's input was a sentence
("I want to post this to my public GitHub"); turning that into the five structured
fields this script needs is a judgment call, not pattern-matching. Claude can do that
translation conversationally before calling the script — a reasonable division of
labor — but the script itself only accepts already-structured input. Not a silent
scope cut: see
${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's Inputs section for the reasoning. - A calibration/validation pass against real expert judgment (source project's Open Question #5). The rule table is reasoned from the vault's existing threat-modeling concept pages, not yet tested against real proposed-action scenarios.
- Anything that actually calls
oracle.pyunattended.--block-on(see above) only matters once some scripted or automated context invokes this and checks its exit code — none does yet, unlikeprivacy-linter's git hook. - Live editing of the vault's threat model.
${CLAUDE_PLUGIN_ROOT}/references/threat-model.jsonis the executable copy of the adversary/compartment schema already documented inKnowledge/AI/privacy-threat-modeling.mdand the project page; keeping the two in sync by hand is the current state, the same relationshipprivacy-linter'sreferences/leak-taxonomy.mdhas to its ownscan_diff.pypatterns. - Any presence at the actual point of disclosure. This oracle,
privacy-linter, andstyle-obfuscatorall operate on something that has already touched disk — a file, a git diff, stdin. None of them have any presence at the moment content is actually composed: a GitHub PR description typed directly into the web UI, a LinkedIn post editor, a comment box. A PR description drafted in the browser and pasted straight into agh/web submit is invisible to this entire stack, no matter how hardened any one tool is — this is a structural property of the architecture (local, file-based, explicitly user-invoked), not a gap any single tool's hardening closes. It's also, notably, not really the threat this stack was built against: theautonomous_ai_agentadversary's primary threat (above) is correlating content that's already public, not intercepting a draft pre-publish — this gap is closer to "did a human remember to run the check before hitting submit" than a detection problem. Considered and explicitly deferred rather than built: a browser extension hooking known compose boxes (real per-site DOM maintenance burden, and an extension with page-read access is itself a new privacy-sensitive surface — ironic for this toolkit) and a clipboard monitor (categorically more invasive than anything else here, which only ever acts on something the user explicitly named). A possible future direction, also not designed or scoped yet: a local LLM agent that sits closer to the point of disclosure and can actually read/flag a draft as it's composed, rather than requiring it to be staged to a file first — noted here as an idea worth revisiting, not a commitment.
Pairing
- privacy-linter —
--from-linter-jsonconsumes its--jsonoutput directly; 3 of the 5 content classes (direct_pii/secret/metadata) are genuinely its vocabulary, reused rather than reinvented — see "How this works" step 6 for the other two. - style-obfuscator — the actual source of the
stylometriccontent class, via its--emit-findingsbridge (notprivacy-linter, despite the similar-looking--json-payload shape both tools produce). Not currently wired together as a documented pipeline the wayprivacy-linteris — pipingfingerprint.py --emit-findingsoutput into--from-linter-jsonworks today (the bridge targets the same{"leak_class": ...}shape), but nothing here shows that command yet. - wiki-privacy-audit — a natural next integration (not built): running the oracle over a vault-wide audit's findings the same way it already runs over a single linter scan.
Files
| Path | What it is |
scripts/oracle.py | The decision engine — rule table, linter-JSON bridge, CLI |
scripts/test_oracle.py | Unit + CLI test suite (stdlib unittest) |
references/threat-model.json | The adversary/compartment schema data (executable copy of the vault's threat-modeling pages) |
references/decision-rubric.md | The rule table itself, with rationale for every branch |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

