Enterprise privacy engine for Windows 11 Recall.
Shield Recall monitors Windows 11's Recall feature in real time, intercepts and redacts sensitive data before it is permanently indexed, gives you a live count of snapshots on disk, and puts a one-click kill switch over the entire Recall subsystem via the Windows Group Policy registry.
- What Shield Recall does
- Architecture overview
- Quick start
- Running in production
- Dashboard reference
- Interpreting the metrics
- Scenario playbook
- Configuration reference
- Project structure
- Testing
- Security notes
- Packaging as an executable
Windows 11 Recall continuously screenshots your desktop and uses on-device AI to make that history searchable. Everything visible on screen — passwords typed into a browser, financial documents, private messages, medical records — can end up permanently stored in a local database that any process running as you can query.
Shield Recall sits between the OS and that database as a zero-trust middleware layer:
| Layer | What it stops |
|---|---|
| Microsoft Presidio NER | SSNs, credit cards, email addresses, phone numbers, person names |
| Credential regex | password=, api_key:, STRIPE_SECRET=, Bearer …, etc. |
| Shannon entropy scanner | High-randomness strings (raw tokens, hex secrets, base64 blobs) |
| Kill switch | Writes the official Group Policy registry DWORD that disables Recall entirely |
All processing is local-only. No data leaves your machine.
┌─────────────────────────────────────────────────────────────┐
│ Windows desktop activity stream │
└───────────────────────────┬─────────────────────────────────┘
│ Win32 / UI Automation
▼
┌─────────────────────────────────────────────────────────────┐
│ Capture worker (background asyncio task) │
│ get_active_window_info() → raw text per window frame │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PrivacyEngine.sanitize_text() │
│ 1. Presidio NER 2. Credential regex │
│ 3. Entropy scan → clean_text + RedactionSummary │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ EncryptedStorage (SQLite WAL, AES-256 ready) │
│ secure_timeline table activity_log table │
└───────────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ FastAPI (127.0.0.1 loopback only) │
│ HTMX dashboard REST JSON API HTMX partials │
└─────────────────────────────────────────────────────────────┘
- Python 3.11+
- Windows 11 (for live Recall integration; the app runs in dev mode on macOS/Linux)
- Administrator rights (only required for the kill switch; the dashboard works without them)
git clone https://github.com/david-spies/shieldrecall.git
cd shield-recall
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS / Linux
pip install -r requirements.txtPresidio requires a spaCy model for named-entity recognition:
python -m spacy download en_core_web_lgcopy .env.example .env # Windows
# cp .env.example .env # macOS / LinuxEdit .env and set a strong 32-character SHIELD_RECALL_KEY. Everything else can stay at its default for a first run.
python main.pyOpen http://127.0.0.1:8000 in your browser.
To unlock the kill switch, run the terminal as Administrator before launching:
# In an elevated PowerShell: python main.py
uvicorn main:app --host 127.0.0.1 --port 8000 --workers 1Single worker only — the in-memory activity feed is not shared across processes.
- Open Task Scheduler → Create Basic Task.
- Trigger: At log on.
- Action: Start a program →
pythonw.exewith argumentsmain.py, start in the project directory. - Check Run with highest privileges to enable the kill switch.
pip install pyinstaller
pyinstaller --uac-admin --onefile --name ShieldRecall main.py--uac-admin embeds a UAC elevation manifest so Windows will always prompt for admin rights on launch, unlocking registry control.
| Badge | Meaning |
|---|---|
| Admin elevated (green) | Process has admin rights; kill switch is functional |
| User mode — registry locked (amber) | No admin rights; kill switch toggle will return an error |
| Recall active (red pulse) | Recall is currently enabled at the OS level |
| Recall shielded (green pulse) | The Group Policy key has been set; Recall is disabled |
| Card | What it shows |
|---|---|
| Recall snapshots on disk | Live file count in %USERPROFILE%\AppData\Local\CoreAIPlatform\CaptureRegions. Auto-refreshes every 2 seconds via HTMX polling. |
| PII items redacted | Cumulative count of PII tokens removed by the engine since the database was last wiped. |
| High-risk captures | Capture events with a risk score ≥ 0.7 — these had multiple high-severity items (SSNs, credit cards, API keys) in a single frame. |
The toggle and the Kill Recall now button do the same thing: they POST to /api/recall/toggle, which reads the current registry value, flips it, and writes it back. The section replaces itself in-place via HTMX hx-swap="outerHTML" so the entire toggle UI reflects the new state without a page reload.
Purge snapshots deletes all .bin/.dat files from the snapshot directory. It does not require admin rights (the files are in your user profile). A confirmation dialog is shown before any deletion.
The search form sends requests to /api/search on every keystroke (debounced 250 ms). Results show:
- The capturing application and window title
- The risk score badge (colour-coded low / medium / high)
- The sanitized text with
[REDACTED_*]tokens highlighted in red
You can filter by application name and minimum risk level simultaneously.
| Count | Interpretation |
|---|---|
| 0 | Recall has never been activated on this machine, or snapshots have been purged |
| 1–80 (green) | Normal operating range for a Recall-enabled system |
| 81–150 (amber) | Elevated — Recall has been running for an extended session; consider purging |
| 150+ (red) | High accumulation — significant visual history on disk; purge and/or disable Recall |
The count updates every 2 seconds. If Recall is disabled via the kill switch, the count will stop growing but existing files remain until you click Purge snapshots.
Each capture event is scored 0.0–1.0 based on what was found:
| Score range | Colour | Meaning |
|---|---|---|
| 0.0 | — | Nothing detected; clean capture |
| 0.01–0.29 | Green | Low-risk PII (person names only) |
| 0.30–0.69 | Amber | Medium risk (emails, phone numbers) |
| 0.70–1.0 | Red | High risk — SSNs, credit cards, API keys, or multiple categories |
A high-risk event means Shield Recall did its job: it caught something serious and redacted it before it hit the database in cleartext. However, a persistent stream of high-risk events from the same application suggests that application is handling sensitive data frequently and warrants closer review.
Entropy measures randomness in a string (bits per character).
| Entropy value | Likely content |
|---|---|
| < 3.5 | Normal English prose |
| 3.5–4.0 | Code, structured data, identifiers |
| 4.0–4.5 | Borderline — may be a short credential |
| > 4.5 | Very likely a password, token, or key |
The default threshold of 4.0 balances sensitivity against false positives. Lower it to catch more; raise it if you see normal identifiers being incorrectly redacted.
The activity feed is colour-coded by severity:
| Colour | Level | Meaning |
|---|---|---|
| Green | INFO | Normal operation (engine start, routine scans) |
| Amber | WARNING | PII found and redacted, or partial purge errors |
| Red | CRITICAL | Kill switch toggle failed (usually missing admin rights) |
Symptom: Snapshot counter jumps from ~40 to 200+ within minutes. Cause: Recall was running during an intensive screen session (video call, document editing, browser research). Response:
- Click Purge snapshots to delete the existing files.
- If you don't want Recall accumulating further: click Kill Recall now (requires admin).
- Check the audit query store for any high-risk captures during that period by filtering
min_risk ≥ 0.7.
Symptom: Multiple risk 0.90 entries in the timeline attributed to 1Password.exe or KeePass.exe.
Cause: Recall is screenshotting the unlocked vault view. Shield Recall is catching and redacting the credentials before they are stored — but the raw screenshot may still exist on disk.
Response:
- Purge snapshots immediately.
- Disable Recall entirely — password managers should never be captured.
- Consider adding your password manager to Windows' exclude-from-Recall list (Settings → Privacy & Security → Recall & Snapshots → Excluded apps).
Symptom: Clicking Kill Recall now shows a red error banner: "Administrator privileges required."
Cause: Shield Recall was launched without elevated rights. The registry path HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsAI requires admin to write.
Response:
- Close the current instance.
- Right-click your terminal / IDE → Run as Administrator.
- Relaunch
python main.py. - The header badge will now show Admin elevated (green).
Symptom: Audit results show [REDACTED_HIGH_ENTROPY_SECRET] replacing what you know is a normal product code or identifier.
Cause: The entropy threshold is too low for your use case.
Response:
- Open
.envand raiseENTROPY_THRESHOLDfrom4.0to4.5or5.0. - Restart the app (
python main.py). - Re-run a test to confirm the identifier now passes through.
Symptom: secure_recall.db is several hundred MB.
Cause: The timeline has accumulated thousands of capture events over days/weeks.
Response:
- In the Danger zone section, click Wipe local timeline DB.
- This only removes the sanitized text log — it does not affect the Recall snapshot directory.
- Alternatively, use the REST API:
curl -X DELETE http://127.0.0.1:8000/api/timeline
Symptom: Unsure whether the registry key was successfully written. Response:
- Open an elevated PowerShell and run:
Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" -Name TurnOffWindowsRecall
- Expected output when shielded:
TurnOffWindowsRecall : 1 - Alternatively, hit the JSON health endpoint:
Look for
curl http://127.0.0.1:8000/health
"recall_active": false.
Symptom: pip install fails or spaCy model download is blocked.
Response:
- On an internet-connected machine:
pip download -r requirements.txt -d ./packages - Copy the
packages/directory to the air-gapped machine. - Install offline:
pip install --no-index --find-links=./packages -r requirements.txt - For the spaCy model, download the
.whlfrom https://github.com/explosion/spacy-models/releases and install it directly:pip install en_core_web_lg-*.whl
All settings can be set in .env or as environment variables. Environment variables take precedence.
| Variable | Default | Description |
|---|---|---|
SHIELD_RECALL_KEY |
ShieldRecall_DefaultKey_32Bytes!! |
32-byte AES encryption key. Change this in production. |
APP_NAME |
ShieldRecall |
Display name in the dashboard header |
DEBUG |
false |
Enables FastAPI debug mode and verbose logging |
LOCAL_DB_PATH |
secure_recall.db |
Path to the SQLite database file |
HOST |
127.0.0.1 |
Bind address. Do not change to 0.0.0.0 — this would expose the dashboard on the network |
PORT |
8000 |
HTTP port |
CAPTURE_INTERVAL_SECONDS |
5 |
Seconds between capture worker iterations |
ENTROPY_THRESHOLD |
4.0 |
Shannon entropy floor for secret detection. Range: 3.5–5.5 |
MAX_ACTIVITY_LOG |
100 |
Maximum in-memory activity entries |
shield-recall/
├── main.py # FastAPI app, routes, capture worker
├── config.py # Pydantic settings (reads .env)
├── pii_engine.py # Presidio + regex + entropy redaction pipeline
├── database.py # SQLite encrypted storage layer
├── os_control.py # Windows registry, snapshot counting, Win32 helpers
├── requirements.txt
├── .env.example # Copy to .env and configure
├── .gitignore
├── templates/
│ └── index.html # Jinja2 + HTMX dashboard template
├── static/
│ ├── css/
│ │ └── dashboard.css
│ └── js/
│ └── dashboard.js
└── tests/
├── test_pii_engine.py
└── test_database.py
# Install pytest (already in dev dependencies)
pip install pytest
# Run all tests
pytest tests/ -v
# Run only PII engine tests
pytest tests/test_pii_engine.py -v
# Run with coverage
pip install pytest-cov
pytest tests/ --cov=. --cov-report=term-missingTests are intentionally split into two files:
test_pii_engine.py— pure Python, no OS dependencies, fasttest_database.py— usestmp_pathfixture for isolated SQLite files, no cleanup needed
Encryption key management
The default key in .env.example is a placeholder. In production, derive the key from Windows Hello / DPAPI so it is tied to the device and the authenticated user, not to a file on disk:
import ctypes, ctypes.wintypes
def get_dpapi_key(label: bytes = b"ShieldRecall") -> bytes:
"""Encrypt the key blob with DPAPI so it is only usable by the current user on this machine."""
# Use CryptProtectData / CryptUnprotectData via ctypes
...Loopback-only binding
The server binds to 127.0.0.1 by default. Never change HOST to 0.0.0.0 — doing so would expose the kill switch and the full redacted audit history to every device on your network.
SQLCipher at-rest encryption
The database schema is ready for SQLCipher. Uncomment the pysqlcipher3 line in requirements.txt, compile it against a local SQLCipher build, and replace the sqlite3 import in database.py with from pysqlcipher3 import dbapi2 as sqlite3. The PRAGMA key= call should use the ENCRYPTION_KEY from settings.
Admin privilege scope Shield Recall only requests elevated privileges for two operations:
- Writing the
TurnOffWindowsRecallregistry DWORD underHKLM - UAC prompt at launch when compiled with
--uac-admin
All other operations (reading the snapshot directory, querying the DB, serving the dashboard) run at standard user privilege level.
pip install pyinstaller
pyinstaller \
--uac-admin \
--onefile \
--name ShieldRecall \
--add-data "templates;templates" \
--add-data "static;static" \
main.pyThe resulting dist/ShieldRecall.exe is fully self-contained. Copy it alongside a .env file to any Windows 11 machine and run it. No Python installation required.
MIT — see LICENSE for details.