# visage $ ./visage.com daemon -c config.example.dhall

Email aliases that hide your real address.

a compact alias@domain forwarding server — C11 · typechecked Dhall · cosmocc · DAFSA store

The idea: disposable aliases

// alias@domain → your real inbox, and reply through it

Give out disposable alias@domain addresses that forward to your real inbox — and reply through them too. You never hand out your real address; only disposable aliases. Replies route back through a reply+<token>@yourdomain reverse alias, so the sender only ever sees the alias.

The whole service — daemon and alias store — fits in one small, portable APE binary, configured in typechecked Dhall. The same C core also builds to WebAssembly and runs fully client-side in your browser.

Try it right here — the real C config + alias pipeline, compiled to WebAssembly, runs in your browser. No setup, no server.
Open the Playground →
  • One C codebase — one visage.com APE binary, no VM, no runtime, no database process.
  • DAFSA alias store — aliases live in a minimal acyclic DAFSA sharing prefixes and suffixes; lookups are byte-prefix walks.
  • Reverse-alias reply — reply through a reply+<token> address so your real address stays hidden.

One source, two targets

// native APE · browser wasm
01

Native binary — visage.com

A single self-contained ~2.6 MB polyglot binary — an Actually Portable Executable built with cosmocc. The same file runs on Linux, macOS, Windows, and the BSDs with no VM, no runtime, no recompile.

An SMTP receiver with a typechecked Dhall config, alias + reverse-alias forwarding, STARTTLS relay (optionally cert-verified), DKIM signing, and a durable outbound retry queue.

02

In the browser — visage.wasm

The real C config + alias pipeline compiled to a small .wasm module. It runs 100% client-side — the actual config_load + address resolution, no server, no build step. Your config and addresses never leave the tab.

The live alias resolver →

Feature summary

// the whole service in one table
AreaCapabilities
ConfigTypechecked Dhall record (aliases, domains, relay, limits, storage, DKIM) evaluated at startup — config errors surface before the daemon binds a port.
SMTPHand-rolled RFC 5321 state machine for receiver and relay (HELO/EHLO, MAIL, RCPT, DATA) with full bounds-checking on the wire path.
Storedatalog-dafsa minimal-DAFSA store: fixed-width big-endian keys, symbol interner, WAL + flock, mmap zero-copy reads. Prefix-search lookups, no separate index.
DeliveryDurable outbound retry queue (spooled to disk, bounded retries), STARTTLS / STARTTLS-verify relay, DKIM signing.
ReplyReverse-alias reply routing via reply+<token>@domain — 32-hex-char tokens from /dev/urandom, 30-day TTL.

Compact server, compact store

// ~480 B/alias · sub-linear resolve

Two things stay small as you grow: the server is one ≈2.6 MB APE binary, and its alias store is a minimal DAFSA that shares prefixes and suffixes across every key — on-disk size is ~480 B/alias at 1,000 aliases and ~542 B/alias at 1,000,000, roughly constant across three decades.

Email routing needs exactly two lookups, and both are native DAFSA primitives: forward routing (alias@domain → destinations) and reverse reply routing (reply+<token> → sender) are byte-prefix walks, O(prefix length) regardless of alias count.

The compactness & prefix-search story →

One daemon, end to end

// accept · forward · reply
1

Accept

The SMTP receiver (src/smtp_in.c) parses the envelope, gates on served domains, and resolves each alias@domain case-insensitively against the DAFSA store, falling back to catch-all.

2

Forward

Accepted mail is spooled to disk and handed to the outbound queue (src/smtp_out.c): bounded retries, STARTTLS to the relay, DKIM-signed before it goes out.

3

Reply privately

When you reply, the reply+<token>@yourdomain reverse alias (src/reply.c) routes back to the original sender — the sender only ever sees your alias.

Quickstart

// make · make wasm
git submodule update --init --recursive   # fetch vendor/dhall-c + vendor/datalog-dafsa
make                                      # builds visage.com (APE) + *_check tools
make wasm                                 # → docs/visage.js + docs/visage.wasm
node tests/wasm-smoke.js                  # headless smoke test of the wasm module

The full CLI reference & usage →

Reference pages

// compactness · security · config · cli · playground
  • Compactness — the DAFSA store, prefix search, and the benchmark numbers.
  • Security — the wire-path hardening and the pre-launch review.
  • Config — the typechecked Dhall schema and config.example.dhall.
  • CLI — daemon, config-check, add-alias, rm-alias, log.
  • Playground — the wasm alias resolver, in your browser.