Skip to content

Repository files navigation

Popy

Popy is a macOS-first local trust broker for files arriving from browsers and AI coding agents. Its job is narrow: keep policy-sensitive raw bytes inert until the user approves them, while making ordinary downloads and safe inspection feel normal.

What it does now

Popy has two policies:

  • Standard (default): ordinary documents, images, archives, and media save normally. Literal native binaries and scripts are held for approval.
  • Strict: every raw file is held. Fixed safe views such as sanitized text or sandbox-rendered images remain available without raw access.

There is no filesystem watcher. Popy protects explicit ingress through the browser extension, popy CLI, MCP server, and agent adapters. It does not claim OS-wide interception of unrelated programs.

For an agent, the useful interface is intentionally small:

  • popy_open(url) fetches, verifies, and returns a safe view in one visible tool call. It never returns a raw path.
  • popy_save(url, to) saves immediately under Standard when the artifact is not executable; otherwise it returns approval_required.
  • popy_approve(id, to) is the explicit raw trust transition.

The old popy_fetch and popy_read_text MCP names remain available for one compatibility release only when POPY_ENABLE_LEGACY_MCP=1.

Architecture

flowchart LR
    B["Chromium extension"] -->|"bounded JSON"| N["popy-native"]
    N --> D["popyd trust broker"]
    A["Codex / Claude / Cursor"] --> M["3-tool MCP adapter"]
    M --> C["popy CLI"]
    C --> S["sealed stage + signed sidecars"]
    D --> S
    D --> L["SQLite artifact ledger + policy"]
    B --> O["browser OPFS"]
    B --> L
    U["browser dashboard"] -->|"approve / delete"| D
Loading

popyd owns shared policy and artifact lifecycle. The native host transports control JSON only, never file bytes. Browser fallback files land under Downloads/popy-quarantine, are explicitly adopted by the broker, inspected by content, and either materialized or held.

The local stage remains mode-restricted and indexer-excluded. Artifacts have SHA-256 metadata and HMAC-signed sidecars. Raw reads re-verify identity, size, signature, and content hash. PDF/image derivation stays in the restricted popy-render child.

Build and install

Browser extension:

npm install
npm run build

Load dist/ as an unpacked Chromium extension and note its extension ID.

Native components on macOS:

brew install cmake curl sqlite3
cmake -S popyd -B popyd/build -DCMAKE_BUILD_TYPE=Release
cmake --build popyd/build -j
cmake --install popyd/build --prefix "$HOME/.local"
sh "$HOME/.local/share/popy/dist/install-macos.sh" EXTENSION_ID "$HOME/.local"

The last command registers the per-user launch agent and native-messaging host for Chrome, Chromium, Brave, and Edge. The manifest is scoped to the supplied extension ID.

Linux still builds the CLI/broker and includes a systemd user unit, but the integrated installer and browser bridge are currently macOS-first.

Use it

Normal browser use needs no new ritual. Under Standard, ordinary downloads are saved automatically. If a binary or script arrives, Popy shows it in the dashboard with its hash, detected type, origin, and destination; approve or delete it there.

CLI:

popy save https://example.com/report.pdf --to ~/Downloads/report.pdf
popy save https://example.com/tool.sh --to ~/Downloads/tool.sh
popy approve ARTIFACT_ID --to ~/Downloads/tool.sh
popy policy --set strict
popy status --json

Lower-level fetch, read, verify, release, and resign commands remain for compatibility and debugging. New integrations should use save, approve, and the three MCP tools.

Agent setup

Codex uses Popy through its MCP server:

codex mcp add popy -- python3 "$HOME/.local/share/popy/mcp/server.py"

Also copy the rules from $HOME/.local/share/popy/dist/AGENTS.popy.md into the applicable AGENTS.md so shell downloads are routed through the MCP interface.

Claude Code and Cursor adapter snippets are installed under $HOME/.local/share/popy/dist/. Their pre-shell hook denies or rewrites direct, simple downloader commands; complex downloader invocations fail closed with a clear popy save instruction.

Trust boundary

Popy is not antivirus and does not decide that a file is benign. It controls when raw bytes acquire a normal executable or handler-visible filename.

It protects:

  • browser downloads captured by the extension;
  • agent downloads made through Popy's MCP tools or guarded adapters;
  • verified reads and releases from the private stage;
  • explicit browser fallback adoption.

It does not protect:

  • arbitrary programs that bypass every Popy entry point;
  • malicious content after the user explicitly approves raw access;
  • browser or kernel compromise;
  • remote URLs merely mentioned in text but never fetched through Popy.

Origin allowlists no longer bypass byte-derived policy. Direct native-messaging access is restricted by Chromium's extension-origin manifest and the broker's same-user Unix socket.

Project map

src/                       Chromium extension
  background/              capture and lifecycle routing
  offscreen/               streaming validation, hashing, OPFS
  dashboard/               shared approval surface
  lib/native/              bounded native-broker client
popyd/
  src/broker/              SQLite ledger, policy, JSON service
  src/cli/                 popy commands
  src/store/               sealed stage, signed sidecars, release
  src/render/              restricted safe derivation child
  mcp/server.py            popy_open / popy_save / popy_approve
  dist/                    service, native host, and agent adapters

See docs/ARCHITECTURE.md for component-level details.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages