Skip to main content
S

ShyPie (dsh-privacy-gate)

siml1169/shypie/plugins/dsh-privacy-gate

Redacts PII in tool-result text before it is logged (error-result metadata is not rewritten), denies read_image and binary/document reads outside allowPaths, provides local privacy_extract, privacy_ocr, privacy_scan and privacy_rehydrate tools (PDF/OCR needs a separately installed local extractor configured through extractCli), and enforces a fail-closed llm/stream egress guard before DSH dispatches any model request.

Install

dsh plugin --profile web add github:siml1169/shypie

This plugin lives in the plugins/dsh-privacy-gate subdirectory of the repository.

README

ShyPie — privacy-first workflow for DeepSeek Harness + Magpie

Keep PII on your machine: documents are converted to text locally, recognised PII is replaced with stable placeholders before it can enter the session log, and anything that cannot be masked locally is blocked, not sent.

This repository is a reference implementation of a two-plugin combo for DeepSeek Harness (DSH) and Magpie:

Componentnpm packageRole
plugins/dsh-privacy-gate@shypie_privacy/dsh-privacy-gateDSH host bundle: redacts tool output and document text at ingestion, provides local privacy_extract / privacy_ocr / privacy_scan / privacy_rehydrate tools, guards binary reads, and enforces a fail-closed llm/stream egress guard
Magpie middleware (in-tree)@magpie-community/middleware-privacy-gateMagpie gateway middleware: a structural egress firewall that rejects content Magpie cannot mask (images, files, encoded blobs) for non-local models
packages/pii-core@shypie_privacy/pii-coreZero-dependency detector + keyed-placeholder library shared by both plugins
packages/local-extract@shypie_privacy/local-extractLocal-only PDF / Office / scan → text CLI with fail-closed blocking

pii-core and local-extract are published on npm at 0.1.0; the DSH bundle is at 0.2.0 with its fail-closed llm/stream egress guard. The Magpie middleware now lives in-tree in magpie-community/plugins (@magpie-community/middleware-privacy-gate); the legacy @shypie_privacy/magpie-privacy-gate package is no longer referenced and is being retired. The registry entry is PR #685; the DSH catalog entry is PR #12308. docs/publish.md has the exact state.

It is a privacy-first workflow, not a guarantee: what it protects, and what it cannot, is documented precisely in docs/design.md and summarised below.

Why two plugins

  1. DSH's outbound request is immutable. It is a deep-frozen fold of the session log (dsh-agent-loop, markAgentLoopRequest), so a plugin cannot rewrite it on the way out the way Magpie does for other agents. Masking must happen at ingestion — before bytes become part of the history that is replayed on every turn.
  2. Magpie's masking is text-only. Its own documentation says "Images and file data are sent as they are. Text inside a screenshot is not masked." A gateway can never mask a PDF page or a screenshot, so the gateway must reject what it cannot mask.

The DSH gate covers what enters the session log; the Magpie gate covers every other agent behind the gateway (Cursor, Copilot, Claude Code, scripts).

Architecture

flowchart TB
  subgraph DSH[DeepSeek Harness]
    direction TB
    U["user text / attachments"] --> G3["Gate 3<br/>user ingress redaction"]
    T["tool call<br/>read / pwsh / MCP"] --> TOOL["tool execution"]
    TOOL --> G1["Gate 1<br/>tool-result text redaction"]
    PDF["PDF / scan / document"] --> EXT["privacy_extract / privacy_ocr<br/>local CLI: pdftotext / OS OCR"]
    EXT --> RED["local redaction"]
    RED --> PH["placeholders"]
    EXT -.->|blocked| ERR["tool error<br/>no data"]
    G3 --> LOG["session log<br/>placeholders only"]
    G1 --> LOG
    PH --> LOG
    LOG --> REQ["LLM request<br/>placeholders only"]
    REQ --> EG{"DSH llm/stream<br/>egress guard"}
    EG -->|reject| BLOCK["turn fails<br/>PRIVACY_EGRESS_BLOCKED"]
  end
  subgraph MAGPIE[Magpie gateway controls]
    direction TB
    MW["privacy-gate middleware<br/>rejects images / files / audio,<br/>encoded blobs, disallowed models"]
    MASK["built-in Privacy text masking"]
    MW --> MASK
  end
  EG -->|pass| GW["Magpie gateway"]
  GW --> MW
  MASK --> LLM["LLM provider"]
  OTHER["other agents<br/>Cursor / Copilot / Claude Code / scripts"] -.-> GW
  LLM --> OUT["model output<br/>placeholders"]
  OUT --> RH["privacy_rehydrate"]
  RH --> ART["local artifact"]
  classDef gate fill:#fff3cd,stroke:#b8860b,color:#000
  classDef reject fill:#f8d7da,stroke:#b02a37,color:#000
  class EG gate
  class BLOCK reject

What it protects

  • Tool and document ingress. Tool-result text is scanned by deterministic detectors before it is appended; PDFs, Office documents and scans are converted to text locally first. Error-result meta is not rewritten and remains a documented local residual.
  • Local conversion only. Text layers use pdftotext; scans use pdftoppm plus a local OCR adapter (on Windows, the built-in Windows.Media.Ocr engine — no install, no network). If no local engine exists the call is blocked (NO_LOCAL_OCR), never sent to a cloud converter.
  • Un-maskable payloads are rejected: base64/hex/data:-encoded documents (contiguous, line- wrapped or dump-formatted), image/file/audio parts to non-local providers, unknown binaries, encrypted PDFs.
  • Round-trip without exposing values. privacy_rehydrate resolves placeholders inside the tool and returns only {path, replaced, kinds}; the model can fill a form without seeing the value.

What it does not do

  • Free-text names are outside deterministic detection (use the word list or a local classifier).
  • Transformed values (reversed, re-encoded) are not detected; zero-width and soft-hyphen tricks are.
  • Detector false negatives are possible; the design is layered (rules → local gateway model → fail-closed blocks) but not complete. Read docs/design.md §12.
  • Local at-rest residues are named, not hidden: DSH records the raw submitted prompt before any plugin seam, and Magpie's masking journal keeps values for 30 days.
  • History predating installation is replayed unchanged — start new sessions after installing.
  • The plugins are not installed by this repository; installation changes profile-wide state and is documented step by step.

Requirements

  • Windows 10/11 (the OCR and path policy are Windows-first; the core libraries are portable).
  • DeepSeek Harness with a configurable plugin profile, and Magpie running its local gateway.
  • pdftotext / pdftoppm / pdfinfo on PATH for PDF text and scan rendering (poppler).
  • Node.js 22+ for the test suite and CLI (the bundled DSH runtime works).
  • Optional: tesseract (an adapter exists but is untested here).

Quick start

The DSH bundle is on npm. The Magpie middleware is being moved in-tree to magpie-community/plugins; until its package publishes, load the folder from this checkout. The legacy @shypie_privacy/magpie-privacy-gate package is retired.

  1. Install the Magpie middleware — magpie plugin add "$repo/plugins/magpie-privacy-gate", or magpie plugin add @magpie-community/middleware-privacy-gate once the in-tree package publishes.
  2. Install the DSH bundle — dsh plugin --profile <name> add @shypie_privacy/dsh-privacy-gate, or through the plugin manager as in docs/install.md.
  3. In Magpie, enable masking under Settings → Privacy (text half of the combo).
  4. Start a new DSH session and run the checks in docs/install.md.

Both plugins are reversible; no profile file is edited by hand. The install names are @magpie-community/middleware-privacy-gate and @shypie_privacy/dsh-privacy-gate; docs/publish.md covers the release state and the two marketplace listings.

Verify

node tests/run-all.mjs

Measured on the reference machine (Windows 11, Node 24, no network):

SuiteResult
pii-core61 tests, 60 pass (1 platform skip); 50,125-char doc with 2,133 hits redacts in 11.5 ms; 300k-digit run 40 ms
local-extract49/49; text PDF, Office, local OCR, and all documented block reasons
dsh-privacy-gate100/100; activation check registers 4 listeners, 1 guard, 4 tools
magpie-privacy-gate58/58 in a node:vm sandbox with only console (proves the moejs constraint)
e2e11/11 — synthetic invoice → local text → placeholders → gateway → mock vendor, with zero plaintext on the wire

The end-to-end suite uses only synthetic fixtures generated at test time. No real document, invoice, identity paper or personal record is present in this repository.

Repository layout

docs/
  design.md               design, threat model, gates, fail-closed matrix, limitations
  install.md              installation and verification guide
  interface-contract.md   frozen API / CLI / tool interfaces
  publish.md              npm publish order + Magpie and DSH marketplace listings
  research/               evidence: DSH seams (path:line), Magpie hook docs (URL + quote),
                          detector catalogue, measured engine matrix, background, security review
packages/
  pii-core/               detectors, keyed placeholders, encrypted vault
  local-extract/          local-only PDF / Office / OCR to text
plugins/
  dsh-privacy-gate/       DeepSeek Harness bundle
  magpie-privacy-gate/    Magpie gateway middleware
marketplace/              submission files; Magpie PR #685 open, DSH catalog PR #12308 open
config/                   example Magpie middleware options
tests/                    end-to-end tests and the one-command runner
LICENSE                   MIT

Documentation

Status

Reference implementation, tested on one Windows machine. Published: pii-core and local-extract at 0.1.0, and the DSH bundle at 0.2.0 (with its egress guard). The Magpie middleware moved in-tree to magpie-community/plugins (PR #685); the legacy @shypie_privacy/magpie-privacy-gate npm package is no longer referenced and is being retired. Tag v0.2.0 and its GitHub release are published; the DSH catalog entry is PR #12308. The live wiring was exercised manually on 2026-11-03: DSH 0.2.1-rc.5 headless with the bundle installed from npm, two-turn DeepSeek and Copilot gpt-6.1-sol sessions through a live Magpie gateway, and a temporary model-allowlist block that the gate rejected. The 0.2.0 llm/stream guard and its stable PRIVACY_EGRESS_BLOCKED code were re-verified on 2026-11-05. OpenAI Responses encrypted_content and Anthropic thinking signature were replayed through Copilot-routed models and accepted (HTTP 200). It is not part of the automated suite; the remaining verification limits are listed in docs/design.md §11.

Security issues: see SECURITY.md.

Related plugins