This guide covers the fundamental building blocks of Mau. Understanding these concepts is essential for building Mau applications.
Mau adopts the Unix philosophy: everything is a file. This radical simplicity has profound implications.
~/.mau/ # Root directory
├── .mau/ # Reserved metadata directory
│ ├── account.pgp # Your encrypted private key
│ ├── alice-FPR.pgp # Friend's public key
│ ├── bob-FPR.pgp # Friend's public key
│ └── coworkers/ # Key groups (keyrings)
│ └── charlie-FPR.pgp
│
├── alice-FPR/ # Your posts (alice = you)
│ ├── hello-world.json.pgp # Post file
│ ├── recipe-lasagna.json.pgp # Another post
│ └── recipe-lasagna.json.pgp.versions/ # Version history
│ └── abc123...xyz.pgp # Old version (SHA256 hash)
│
├── bob-FPR/ # Bob's synced posts
│ ├── comment-on-hello.json.pgp
│ └── status-update.json.pgp
│
└── .charlie-FPR/ # Hidden = unfollowed (kept for history)
└── old-post.json.pgp
User directories are named by fingerprint
<your-fingerprint>/<friend-fingerprint>/Files use .pgp extension
Hidden directories (.prefix) are ignored
..mau/ is reserved
account.pgp = your encrypted private keyVersioning uses subdirectories
<filename>.versions/<sha256-hash>.pgpThis design gives you:
✅ Backup with standard tools: tar czf backup.tar.gz ~/.mau
✅ Restore from backup: Extract and you’re back
✅ Delete everything: rm -rf ~/.mau
✅ Inspect with text tools: ls, grep, find
✅ Network file systems: Store on NAS, Dropbox, etc.
✅ Multiple clients: Terminal app, GUI, web interface—all access the same files
Mau uses JSON-LD (JSON for Linked Data) with Schema.org vocabulary.
@ keys 1{
2 "@context": "https://schema.org",
3 "@type": "SocialMediaPosting",
4 "headline": "Loving peer-to-peer social media!",
5 "articleBody": "Mau gives me full control over my data.",
6 "author": {
7 "@type": "Person",
8 "name": "Alice",
9 "identifier": "abc123...xyz"
10 },
11 "datePublished": "2026-02-27T10:00:00Z"
12}
Common types for social apps:
| Type | Use Case |
|---|---|
SocialMediaPosting |
Status updates, tweets |
Comment |
Replies to posts |
Message |
Private messages |
Recipe |
Cooking instructions |
Review |
Product/place reviews |
Event |
Meetups, conferences |
Article |
Blog posts |
ImageObject |
Photos |
VideoObject |
Videos |
FollowAction |
Follow someone |
LikeAction |
Like a post |
See Schema.org full list for 800+ types.
Reference other content using Mau addresses:
1{
2 "@context": "https://schema.org",
3 "@type": "Comment",
4 "text": "Great post!",
5 "about": "/p2p/alice-FPR/hello-world.json",
6 "author": {
7 "@type": "Person",
8 "name": "Bob"
9 }
10}
Like someone’s post:
1{
2 "@context": "https://schema.org",
3 "@type": "LikeAction",
4 "agent": {
5 "@type": "Person",
6 "identifier": "bob-FPR"
7 },
8 "object": "/p2p/alice-FPR/hello-world.json"
9}
Schema.org supports localization:
1{
2 "@context": "https://schema.org",
3 "@type": "Article",
4 "headline": {
5 "@language": "en",
6 "@value": "Hello World"
7 },
8 "alternativeHeadline": {
9 "@language": "ar",
10 "@value": "مرحبا بالعالم"
11 }
12}
For images/videos, use encodingFormat and contentUrl:
1{
2 "@context": "https://schema.org",
3 "@type": "ImageObject",
4 "contentUrl": "photo.jpg",
5 "encodingFormat": "image/jpeg",
6 "caption": "Sunset in Berlin"
7}
The binary file (photo.jpg) lives next to the JSON file:
alice-FPR/
├── vacation-post.json.pgp
└── vacation-post-photo.jpg.pgp # Referenced as "photo.jpg" in JSON
Your identity in Mau is your PGP public key fingerprint.
5D000B2F2C040A1675B49D7F0C7CB7DC36999D56
└────────── 160 bits (40 hex chars) ──────────┘
Default: Mau now defaults to Ed25519 keys for new accounts (faster, smaller, modern cryptography).
1# Generate a new identity
2gpg --full-generate-key
3
4# Recommended choices:
5# - (9) ECC (sign and encrypt) *default: Curve 25519*
6# - No expiration (or set expiration for security)
7# - Passphrase to protect private key
8
9# Alternative (RSA for compatibility):
10# - (1) RSA and RSA
11# - 4096 bits
12# - No expiration
13
14# Export public key
15gpg --export alice@example.com > ~/.mau/.mau/account.pub.pgp
16
17# Export private key (encrypted with passphrase)
18gpg --export-secret-keys alice@example.com > ~/.mau/.mau/account.pgp
Ed25519 benefits:
Every post is signed to prove:
1# Sign and encrypt a post for public viewing
2echo '{"@type":"SocialMediaPosting",...}' \
3 | gpg --sign --encrypt -r alice-FPR \
4 > hello-world.json.pgp
When others download your post, they:
Public post (anyone can read):
1gpg --encrypt --sign -r alice-FPR < post.json > post.json.pgp
(Encrypted to your own key, signed by you)
Private message (only Bob can read):
1gpg --encrypt --sign -r bob-FPR < message.json > message.json.pgp
Group message (Alice, Bob, and Charlie):
1gpg --encrypt --sign -r alice-FPR -r bob-FPR -r charlie-FPR < group-msg.json > group-msg.json.pgp
When you follow someone:
~/.mau/.mau/<friend-fingerprint>.pgp1gpg --export bob-FPR | gpg --encrypt -r alice-FPR > ~/.mau/.mau/bob-FPR.pgp
Everything in Mau has a hierarchical address:
/p2p/<user-fingerprint>/<filename>
| Address | Meaning |
|---|---|
/p2p/alice-FPR |
List all of Alice’s files |
/p2p/alice-FPR/hello-world.json |
Alice’s “hello-world” post |
/p2p/alice-FPR/recipe-lasagna.json.versions/abc123 |
Old version of Alice’s recipe |
Request: GET /p2p/alice-FPR
Response:
1[
2 {
3 "name": "hello-world.json",
4 "size": 1024,
5 "sha256": "abc123...",
6 "modified": "2026-02-27T10:00:00Z"
7 },
8 {
9 "name": "recipe-lasagna.json",
10 "size": 2048,
11 "sha256": "def456...",
12 "modified": "2026-02-26T15:30:00Z"
13 }
14]
Use If-Modified-Since header:
1GET /p2p/alice-FPR
2If-Modified-Since: Wed, 26 Feb 2026 00:00:00 GMT
Server returns only files modified after that date.
When a file changes:
<filename>.versions/<sha256-hash>.pgp<filename>.pgpRequest old version:
1GET /p2p/alice-FPR/recipe-lasagna.json.versions/abc123
Mau peers run three main modules:
┌──────────────────────────────────────────────┐
│ Mau Peer │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │HTTP Server │ │HTTP Client │ │
│ │(Serve files)│ │(Sync files) │ │
│ └─────────────┘ └──────────────┘ │
│ │
│ ┌────────────────────────────────┐ │
│ │ Peer Discovery │ │
│ │ - Kademlia DHT │ │
│ │ - mDNS (local network) │ │
│ └────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────┐ │
│ │ Storage Layer (Filesystem) │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────────────┘
Exposes your content:
GET /<fingerprint> → File listGET /<fingerprint>/<filename> → Download fileGET /<fingerprint>/<filename>.versions/<hash> → Download versionAuthentication: TLS 1.3 with client certificates (mutual TLS)
Syncs from friends:
.mau/GET /<friend-fpr>?since=<last-sync><friend-fpr>/Broadcasts on LAN:
5D000B2F2C040A1675B49D7F0C7CB7DC36999D56._mau._tcp.local.
Announces:
_mau._tcp)Peers on the same WiFi discover each other instantly.
Distributed hash table for finding peers anywhere:
Simplified Kademlia (Mau doesn’t store arbitrary values):
/kad/ping → “Are you alive?”/kad/find_peer/<fingerprint> → “Where is this person?”Returns:
1[
2 {
3 "fingerprint": "bob-FPR",
4 "addresses": ["192.168.1.100:8080", "bob.example.com:443"]
5 },
6 {
7 "fingerprint": "charlie-FPR",
8 "addresses": ["charlie.example.com:443"]
9 }
10]
For peers behind firewalls:
Alice wants to sync Bob’s posts:
┌─────────┐ ┌─────────┐
│ Alice │ │ Bob │
└────┬────┘ └────┬────┘
│ │
│ 1. Find Bob │
├─────── /kad/find_peer/bob-FPR ─────>│
│<─────── {addresses: [bob.com:443]}──┤
│ │
│ 2. Connect with mutual TLS │
├─────── TLS Handshake ───────────────>│
│ (Alice's cert + Bob's cert) │
│ │
│ 3. Request file list │
├─────── GET /bob-FPR?since=... ─────>│
│<─────── [{name: "new-post.json"}]───┤
│ │
│ 4. Download file │
├─────── GET /bob-FPR/new-post.json ──>│
│<─────── <encrypted content> ─────────┤
│ │
│ 5. Verify & save locally │
│ - Check Bob's signature │
│ - Decrypt │
│ - Save to ~/.mau/bob-FPR/ │
│ │
Now that you understand the core concepts, let’s build something:
👉 Quick Start Guide - Build your first Mau app in 15 minutes
For deeper dives: