madal

Security

How madal protects what you publish, and where that protection ends. Last updated 16 October 2026.

The short version

madal encrypts a page on your machine before it's uploaded. The server stores ciphertext and never receives a key. A link carries its key after the #, which browsers don't send to servers. A page shared with people by email carries no key at all: the key is sealed to each reader's public key, and only their devices can open it.

Only you can unlock your data. Your master password and the Secret Key in your Emergency Kit are the only things that unlock your keys. If you lose either one, nobody can get your data back, madal included.

What the server sees, and what it doesn't

It can't see:

It does see, because it needs them to work:

It uses these to run madal and for nothing else.

How a page is encrypted

Each page gets its own 256-bit key from a secure random source. Every version is sealed with AES-256-GCM under a fresh nonce, and the page's id and version number are bound in as associated data, so the server can't pass one version off as another or swap one page's content into another link.

K     = 32 random bytes
link  = https://madal.facets.cloud/a/<id>#base64url(K)
blob  = "MDL1" || nonce(12) || AES-256-GCM(K, nonce, page, aad = "madal/v1/<id>/<version>")

With a password, the key becomes HKDF-SHA256(K, salt = PBKDF2-SHA256(password, 600,000 iterations)). The password is mixed into the 256-bit key and doesn't replace it, so someone holding the ciphertext can't guess the password offline, and someone holding a leaked link still needs the password.

The browser decrypts with WebCrypto, using the same format as the CLI. Test vectors produced by the Go code are decrypted by the browser code in CI, so the two can't drift apart.

Sharing with people and teams

Your keys and the vault

Your private keys are stored on the server inside a vault, sealed with a key derived from two secrets, as 1Password does: your master password, and a 128-bit Secret Key made on your device that the server never receives.

unlock = PBKDF2-SHA256(master password, 600,000 iterations)
         XOR HKDF-SHA256(Secret Key, salt = account id)

A stolen copy of the server's data can't be cracked by guessing your master password, because each guess would also need the Secret Key's 128 random bits. Signing in with Google, GitHub or an email link proves who you are. Only the master password and Secret Key together unlock your keys, and setup gives you an Emergency Kit with the Secret Key to keep.

Who can open a page

Every page has one of four kinds of access. You can move a page between named people and a link later with madal share <id> --access only-people or --access link. Once someone has been removed and the key has changed, one link can no longer open every version, so going back to a link means publishing the page again. A public page can't be switched either: publish it again instead.

Sharing with someone who doesn't use madal yet gives them an invite link. It carries a secret the server never sees, and it works only for an account signed in with the invited email, so a forwarded invite link opens nothing. Once they open it, it works like any page shared by name.

Removing a person or team takes effect at once: the server stops serving them the page, and the next version uses a new key they never receive, even if they kept the old one. What they opened before, they may have saved.

How long a page lasts

If you lose your keys

What to share with madal

Good fits: reports, dashboards, plans, reviews and notes you'd otherwise send in chat or email, where you want the server not to be able to read them and the link to stop working when you say so.

Poor fits:

Pages run in a sandbox

Agent-written HTML is untrusted code. It runs in an iframe on a separate domain, madal.facetsusercontent.cloud, with sandbox set and without allow-same-origin. A page can run scripts and make network requests, which is how it loads libraries from CDNs; that also means a script on the page can send what the page shows anywhere. It can't read cookies or storage, submit forms, navigate the page around it, or reach the app or your keys. The decryption key never enters the frame: the app decrypts and hands only the HTML over. Links a page opens in a new tab load as ordinary pages, outside the sandbox. The sign-in cookie is HttpOnly, Secure, SameSite=Strict and sent only to madal.facets.cloud.

Limits you should know

Check it yourself

Reporting a vulnerability

Please don't post security issues in public. Report them privately on GitHub: github.com/Facets-cloud/madal-releases/security. Only the maintainer sees the report. Include what you found and how to reproduce it.

Privacy · Home