Under the hood

Two processes, one of them offline.

The first process never runs the page’s code. The second runs nothing else, and the operating system takes its network away before the window opens.

Internetapi.example.com:443any other hostthe kernel refusesFirst processhas the networkreads the tagsshows the popupthe proxySecond processin the OS sandbox, offlinepage.pappwindow.pappits programssame sandbox, same limitspapp-netYour diskgranted folderskeys and logins: hidden
  1. The first process

    Has the network and never runs the page. It reads the tags from the file on disk before any script runs, shows the popup, and runs the proxy for papp-net.

  2. The sandbox

    It starts the second process in the OS sandbox: Seatbelt on macOS, bwrap --unshare-net on Linux. The kernel refuses sockets and DNS to it and to every program it starts.

  3. The offline check

    Before it opens the window, the sandboxed process proves it is offline: on macOS it must fail to bind a loopback port, on Linux its network must hold only lo. If it cannot, it refuses. No flag skips this.

  4. The page

    Its only way to files is window.papp, which checks every path against the grants, links resolved. The OS also limits writes to the granted folders, for the page and every program it runs.

The tags

A page asks in its head, before it runs.

One value per <meta>, read from the file on disk. A running page cannot add one, and an unknown tag stops it from opening. With no tags, a page sees its own folder and nothing else.

The head of report.papp
<meta name="papp-read"  content="~/logs">
<meta name="papp-write" content="./reports/*.md">
<meta name="papp-net"   content="api.example.com:443">
TagAllowsFor exampleIn the popup
Files
papp-readSee a folder and its subfolders.~/logsplain
papp-writeSee, change and delete files there.~/notes/*.mdyellow
Network
papp-netSend and receive data with one server, through the proxy.api.example.com:443plain
papp-socketTalk to the program behind one socket file./var/run/docker.sockred
Programs
papp-runRun one program. It can read every file but keys and logins.gitred
papp-envGive its programs one variable.AWS_PROFILEplain
Devices
papp-useUse one of notifications, sound, the clipboard, location, the camera, the microphone or the screen.camerayellow
papp-shortcutAnswer a key combination in every app.CommandOrControl+Shift+Nyellow
Everything
papp-nodeFull Node: any file, any program, as you. Only for pages you trust.fullred

Out of reach

What stays shut, whatever the tags say.

  1. Keys and logins

    The OS hides ~/.ssh, ~/.gnupg, ~/.aws, cloud and GitHub logins, saved passwords and browser profiles from the page and every program. Only a tag that starts inside one opens it, and that line is red.

  2. Your local network

    The proxy looks up names itself and refuses any that lead to this computer or your network: the router, a VPN such as Tailscale, cloud metadata. Every request through it, allowed or refused, goes to egress.log.

  3. Other pages

    The window shows only the page: a link does not navigate and a frame does not load another file. Each page keeps its own storage, and on Linux it sees only its own processes.

  4. Apargon itself

    Its one network call is the update check, at most once a day, and you can turn it off. No telemetry, no crash reports, nothing that runs at login.

Where it stops

What Apargon does not enforce.

These are the gaps that matter most. SECURITY.md lists every one.

Reading is checked by Apargon, not the OS.
Chromium’s own sandbox cannot start inside the OS one. A page that exploits a Chromium bug could read any file but keys and logins. It would still have no network beyond its hosts, and the same write limits.
A program sees more than its page.
A papp-run program can read every file but keys and logins, and takes any arguments. That is why its line is always red.
Full Node runs as you.
A papp-node page has no network of its own, but it can launch an app that does, or write files that run later. Trust it as you would a script.
Only browsers mark downloads.
git clone, curl and USB sticks leave no mark, so Apargon treats such a page as yours: no site in the popup, and no popup at all if it has no tags.
Your own pages follow their path.
An approval of a page made here covers its path, not its content. An edit that asks for nothing new opens without a popup.
It is not a virtual machine.
A hard guarantee that nothing leaves needs a VM with no network card, no shared folders and no shared clipboard.

Tested in the real app

pnpm test runs every check in the real app, on macOS and Linux, and fails if one fails: the offline refusal, the proxy’s refusals, the hidden keys.

Build the app you wish existed.

Completely free and open source, under the MIT license. Read every line on GitHub.

Download for macOS

Also for Intel Mac · Linux (.deb)

Download for Linux

Also for macOS · Intel Mac