Repository navigation
Security defaults
codingncaffeine edited this page Nov 6, 2026
·
13 revisions
No telemetry. No sponsored tiles. No default-search auction. No account requirement. No cloud AI. No feature-removal churn. Every setting lives in a human-readable file, and every feature can be turned off.
| Default | What it means |
|---|---|
| Third-party cookies blocked | The cookie jar stores and sends a cookie only for the site you are on. A request a page makes to another site carries none of that other site's cookies and stores none. |
| HTTPS-first | A typed host is tried over https first. http is tried once, only when the connection itself fails — never after a certificate error. |
| A certificate error stops the load | An expired, self-signed, wrong-host, untrusted-root or revoked certificate lands on an error page that says what is wrong; the badssl.com matrix is refused. For a host that has asked for HTTPS only there is no way past it, as in every browser; going on behind a warning for any other host is not written yet (Not written yet). |
Referrer policy strict-origin-when-cross-origin |
A cross-site request tells the other site which origin you came from, never the full URL. |
| Search goes only where you choose | Nothing you type is sent to a search engine you did not choose. Choosing one is not written yet: until it is, words typed in the address bar open an internal page. |
| A download never runs by itself | A download is saved under a fresh name in the Downloads folder and marked with the mark of the web (the NTFS Zone.Identifier stream, with the source and referrer URLs). Nothing runs; opening one from the browser at your word is not written yet. |
| One network choke point | Every request goes through one fetch(). tools/egress-check.sh fails CI if a socket is opened anywhere else. |
| An honest user agent |
Mozilla/5.0 (<platform>) Sashfold/<version> — no build number, locale or hardware detail, the same for every user of a platform. See Networking. |
| Content blocking from your own lists | Every request is judged, before it leaves, by the lists in the blocklists folder: filter lists in the Adblock Plus syntax (drop EasyList into blocklists/filters/) refuse what pages request, and their element-hiding rules (##, with the sites they name and the #@# exceptions) hide what gets through, as a stylesheet added to the page; lists of phishing and malware sites (blocklists/nefarious/, as hosts files, one host or URL per line, or the same syntax) keep the shell off those sites as pages too, with a page that names the list and the line and offers no way through. Safe-Browsing-class containment with zero cloud lookups: the lists are files of yours, read at start, and nothing about a visit leaves the machine. Nothing ships in the folder. |
| Containers keep sites apart | A tab opened in a container (Ctrl+Shift+N, see The shell) has a cookie jar and a web storage of its own: a site you are signed into in one container is a stranger in every other, and nothing a page stores in one can be read from another. The cache is shared. |
| A page's Content Security Policy is enforced | The Content-Security-Policy headers a page arrives with, and the <meta http-equiv> policies in its head, judge everything the page does: every fetch — scripts, stylesheets, pictures, fonts, fetch() and XMLHttpRequest — before it leaves and again on every redirect hop; every inline script, event handler attribute, <style> and style="" attribute, by 'unsafe-inline', a nonce or a hash ('unsafe-hashes' for the attributes, 'strict-dynamic' as the specification has it); eval, Function and a timer's string by 'unsafe-eval'; a form's target by form-action; a sandbox without allow-scripts runs nothing; upgrade-insecure-requests rewrites http to https. A report-only policy refuses nothing. Every violation is a line on the page's console, once, and no report ever leaves the machine: report-uri and report-to are read and left alone. |
| A page decides whether it may be framed | A document fetched into an <iframe> or a <frame> is shown only where it allows: by its own policy's frame-ancestors, matched against every document the frame is inside, or — when no enforced policy of its says anything about framing — by X-Frame-Options (DENY; SAMEORIGIN held against the whole chain; values at odds refuse). The page's frame-src decides whether an iframe's or a frame's document is fetched at all, and its object-src an <object>'s or an <embed>'s, the framed document's own policy governs everything it fetches, and an srcdoc or data: document keeps the policy of the document it is in. A page may show itself in a frame once, but not again inside that; frames go ten deep; and a page's frames are drawn within a budget of a hundred frames and 64 megapixels. A frame's document runs its scripts in a realm of its own. A window of another origin is reached only through a WindowProxy that shows the few members HTML lets it (postMessage, location to navigate it, close, closed, frames, parent, top), and its document never; a sandbox attribute takes scripts, the frame's origin and navigating the top away from what it frames unless its tokens give them back, and document.domain with no way back. |
| A host that asks for HTTPS only gets nothing in the clear |
Strict-Transport-Security is honored: once a host has said, over TLS, that it is HTTPS-only, every later request to it — a typed http:// address, a link, a redirect, a WebSocket — goes out as https before a connection is opened. The list is the profile's (hsts.txt); there is no built-in list, so the first visit to a host is as its address was written. |
| The browser cannot start a program (Linux) | Nothing in Sashfold starts another program, so on Linux the right to is given up for good before a byte is read from anywhere: a system-call filter, on every thread, refuses execve and with it the calls a browser has no use for and an intruder does — tracing or reading another process, mounting, loading kernel code, changing namespaces. A page that takes the engine over cannot launch anything from it. |
| The browser writes only to its own folders (Linux) | The window and --render keep their writes to the profile, the downloads folder and what the command line named. Everything else you own can be read as before and no longer written, made, renamed or removed: a page that takes the engine over cannot plant a line in a shell's start-up file or change another program's settings. Reading your files and reaching the network are not yet held back — that needs each site in a process of its own, which is not written. SASHFOLD_CONFINE=0 turns both off. |
| Notifications are asked for, per site | A site shows nothing on your desktop until you allow it. Its question appears in a bar under the toolbar only when the page asks while you have just clicked or pressed a key on it — never as a page loads — and Allow, Block or closing the bar is remembered for that site alone (notifications.txt in the profile; the main menu's Notifications row changes it or takes it back). A notice names the site that sent it before anything the page wrote, and the page's text is shown as text. A frame of the page's own origin asks and shows through the page; a frame of another origin, a sandboxed frame and a page that did not arrive over HTTPS are refused, as Chrome and Firefox refuse them. The bar answers from the keyboard too (Alt+Shift+A, then the arrows, Enter, Escape). A press brings the page's tab and the window forward unless the page's click handler says not to. Linux (the desktop's notification service) and Windows (its toasts); macOS answers "denied". |
| Updates are announced, checked against a built-in key, and can be turned off | Once a day the browser asks the project's releases page which release is the latest — one request with no cookies and no referrer — and says so in the main menu when it is newer than this build. A build downloaded from the releases page (Linux and Windows) then fetches that release, takes it only if its list of checksums is signed by the release key built into the browser and the download's SHA-256 is the one listed, and puts it in place the next time it starts, keeping the program it replaced beside the new one; the check is made again at that start, from the files on disk. The new build is then watched through its first start: if it ends before its window has been up ten seconds, the program before it is put back and takes the start, and that release is not fetched again — the reader is never left without a browser by an update. A distribution's package only says a newer release exists. A new profile is not asked about on its first day, and --no-update-check turns all of it off. |
See The Pledge: nothing is vendored, so there is no supply chain; the OS-shipped TLS on Windows and macOS is patched by the OS; and the import table of every release binary is checked against an allowlist in CI.
Getting started
What works today
Measured
Design decisions
Testing and tools