agentleFS
HefrockHefrock/agent-skills/skills/privacy-threat-oracle/SKILL.md

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.

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
  1. Distinctive:

    Plug in privacy-linter directly: instead of naming content classes by hand:

    line 62

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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
  1. 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).
  2. Exposed adversaries — resolved from --target-exposure via the same file's target_exposure_map (e.g. public_internet reaches 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 flagged out_of_scope in threat-model.json (currently just the state actor — see ${CLAUDE_PLUGIN_ROOT}/references/decision-rubric.md's "Out-of-scope adversaries" section) still counts toward adversary_cost_tier and the recommendation like any other adversary, but is also surfaced separately in out_of_scope_adversaries_exposed and noted in reason — 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.
  3. Sensitivity tier — the highest tier among --content-class values present (direct_pii/secret = high, metadata/inference_cue/stylometric = medium).
  4. 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 clean proceed.
  5. Reversibility — --reversible downgrades the recommendation one step (decline → proceed_with_modification, proceed_with_modification → proceed; never skips straight to an unqualified proceed on 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.
  6. 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-json reads a scan_diff.py --json payload (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, sets linter_json_parse_error: true, and forces the sensitivity floor to high rather 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 only leak_class values privacy-linter's scan_diff.py ever emits. --from-linter-json genuinely plugs in directly for these three.
    • stylometric — not produced by privacy-linter. It's style-obfuscator's --emit-findings bridge that emits it (scripts/fingerprint.py), a separate skill with its own invocation. Piping privacy-linter's output alone will never produce this class.
    • inference_cue — produced by nothing in this repo yet. privacy-linter's own SKILL.md names it explicitly as out of scope, blocked on a local-model decision that hasn't been made. It's accepted as a --content-class value here so the rule table and threat-model.json's sensitivity tiers are ready for it, not because anything currently populates it automatically.
  7. --json for 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.py unattended. --block-on (see above) only matters once some scripted or automated context invokes this and checks its exit code — none does yet, unlike privacy-linter's git hook.
  • Live editing of the vault's threat model. ${CLAUDE_PLUGIN_ROOT}/references/threat-model.json is the executable copy of the adversary/compartment schema already documented in Knowledge/AI/privacy-threat-modeling.md and the project page; keeping the two in sync by hand is the current state, the same relationship privacy-linter's references/leak-taxonomy.md has to its own scan_diff.py patterns.
  • Any presence at the actual point of disclosure. This oracle, privacy-linter, and style-obfuscator all 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 a gh/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: the autonomous_ai_agent adversary'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-json consumes its --json output 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 stylometric content class, via its --emit-findings bridge (not privacy-linter, despite the similar-looking --json-payload shape both tools produce). Not currently wired together as a documented pipeline the way privacy-linter is — piping fingerprint.py --emit-findings output into --from-linter-json works 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

PathWhat it is
scripts/oracle.pyThe decision engine — rule table, linter-JSON bridge, CLI
scripts/test_oracle.pyUnit + CLI test suite (stdlib unittest)
references/threat-model.jsonThe adversary/compartment schema data (executable copy of the vault's threat-modeling pages)
references/decision-rubric.mdThe rule table itself, with rationale for every branch

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.