VayuPress Docs

VayuPress Threat Model

Version: 1.0.0
Date: 2026-06-12
Author: Security Lead, @johalputt
Review cycle: Annual or after any architectural change


Trust Boundaries

┌─────────────────────────────────────────────────────────────┐
│  INTERNET (untrusted)                                        │
│  ┌──────────┐  ┌──────────┐  ┌────────────┐               │
│  │ Readers  │  │ Authors  │  │ Attackers  │               │
│  └────┬─────┘  └────┬─────┘  └─────┬──────┘               │
│       │              │               │                       │
│  ─────┼──────────────┼───────────────┼────── TrustBoundary1 │
│       ▼              ▼               ▼                       │
│  ┌─────────────────────────────────────────┐               │
│  │  Nginx (TLS termination + WAF headers)  │               │
│  └────────────────────┬────────────────────┘               │
│  ──────────────────────┼────────────────── TrustBoundary2   │
│                        ▼                                     │
│  ┌─────────────────────────────────────────┐               │
│  │  VayuPress Go binary (localhost:8080)   │               │
│  │  - Single process, non-root uid         │               │
│  │  - Read-only filesystem except /data    │               │
│  └───────┬───────────────────┬─────────────┘               │
│  ─────────┼───────────────────┼──────── TrustBoundary3      │
│           ▼                   ▼                              │
│  ┌──────────────┐   ┌──────────────────┐                   │
│  │ SQLite /data │   │ Meilisearch      │                   │
│  │ (rw, WAL)    │   │ localhost:7700   │                   │
│  └──────────────┘   └──────────────────┘                   │
└─────────────────────────────────────────────────────────────┘

TB1 — Internet ↔ Nginx: TLS required, rate limiting via fail2ban, CSP/HSTS enforced
TB2 — Nginx ↔ App: Unix socket or 127.0.0.1 only, no direct public access
TB3 — App ↔ Storage: Filesystem ACLs, SQLite WAL, no network exposure of DB


Entry Points

ID Entry Point Protocol Auth Required Notes
EP-01 GET / (public posts) HTTPS No Rate-limited by nginx
EP-02 POST /api/v1/posts HTTPS API key (Bearer) Authors only
EP-03 POST /api/v1/admin/* HTTPS API key + lockout Admin actions
EP-04 GET /health/* HTTPS No (read-only) Health contracts
EP-05 GET /metrics localhost only No Prometheus; not exposed publicly
EP-06 GET /debug/pprof/* HTTPS API key + rate limit Audit logged
EP-07 POST /webhooks/github HTTPS HMAC-SHA256 Signature verified
EP-08 SSH (port 22) SSH Key auth only Admin server access
EP-09 GET/POST /isso/* HTTPS CSRF token Comment system proxy
EP-10 Meilisearch API localhost only Master key Never exposed publicly

Assets

ID Asset Sensitivity Location Owner
A-01 Published posts Public SQLite /data/vayupress.db VayuPress
A-02 Draft posts Confidential SQLite, author-only Authors
A-03 Admin API key Critical Env var VAYU_API_KEY Operator
A-04 Meilisearch master key Critical Env var VAYU_MEILI_KEY Operator
A-05 TLS private key Critical /etc/letsencrypt/live/ Certbot
A-06 SQLite database High /data/vayupress.db VayuPress
A-07 Backup archives High /data/backups/ VayuPress
A-08 Audit logs High audit_log table (WORM) VayuPress
A-09 User comments Internal Isso SQLite VayuPress
A-10 Search index Internal Meilisearch data dir VayuPress
A-11 Media uploads Internal /data/media/ VayuPress
A-12 Config / env vars Critical /etc/vayupress/ Operator

Threat Actors

ID Actor Motivation Capability Likelihood
TA-01 Script kiddie Defacement, crypto mining Low High
TA-02 Spam bot SEO spam, comment spam Low-Medium High
TA-03 Credential stuffer Account takeover Medium Medium
TA-04 Targeted attacker Data exfiltration, content manipulation High Low
TA-05 Supply chain attacker Backdoor via dependency High Low
TA-06 Malicious author Privilege escalation, data theft Medium Low
TA-07 Rogue maintainer Insider threat High Very Low
TA-08 Nation-state Censorship, surveillance Very High Very Low

Mitigations

Authentication & Authorization

Threat Mitigation Implementation
Credential stuffing (TA-03) Account lockout after 10 failures authFail counter, 15-min lockout
API key theft Key rotation endpoint POST /api/v1/admin/rotate-key
Weak password hashing Argon2id (not bcrypt) argon2.IDKey() in Go
Privilege escalation (TA-06) Role-based: author vs admin Separate API key scopes
Session fixation Bearer tokens, no sessions Stateless JWT-style API keys

Injection Attacks

Threat Mitigation Implementation
SQL injection Parameterized queries only database/sql with ? placeholders
XSS via content CSP nonce per request ADR-0036; Content-Security-Policy: nonce-{uuid}
SSTI (template injection) html/template (auto-escape) Go stdlib; no raw string templates
Command injection No shell exec in hot path exec.Command only in deploy script
Path traversal (media) filepath.Clean + prefix check Media handler validates path within /data/media/

Network Attacks

Threat Mitigation Implementation
SSRF (TA-04) Block link metadata fetches to private IPs 169.254.169.254, 10.x, 172.16.x, 192.168.x blocked
CSRF Per-request CSRF token X-CSRF-Token header checked on state-changing requests
Clickjacking X-Frame-Options: DENY Nginx header
Protocol downgrade HSTS (2yr, includeSubDomains) Strict-Transport-Security: max-age=63072000
DDoS Nginx rate limiting + fail2ban 10 req/s per IP, ban after 20 failures

Supply Chain

Threat Mitigation Implementation
Dependency backdoor (TA-05) govulncheck in CI Scans all transitive deps
Build tampering Reproducible builds CGO_ENABLED=0 go build
Artifact substitution SHA-256 checksums in CHANGELOG Verifiable release artifacts
Dependency drift go mod verify in CI Ensures go.sum integrity
License violation go-licenses check in CI All deps must be approved licenses

Data Protection

Threat Mitigation Implementation
Data exfiltration (TA-04) No external network calls from app Outbound blocked except Let's Encrypt ACME
Backup theft Encrypted offsite backups AES-256 (operator-configured)
Log tampering WORM audit table audit_log insert-only, no UPDATE/DELETE grants
Database corruption WAL mode + nightly integrity check PRAGMA integrity_check in cron
Migration tampering Checksum drift detection ADR-0034; halts startup if drift found

Infrastructure

Threat Mitigation Implementation
Root privilege abuse VayuPress runs as vayupress uid systemd User=vayupress
File write outside data dir Read-only filesystem systemd ProtectSystem=strict, ReadWritePaths=/data
Port scanning UFW default deny Ports 22, 80, 443 only
SSH brute force (TA-01) fail2ban ssh jail 5 failures → 10-min ban
Certificate expiry Auto-renew via certbot cron Daily certbot renew check

Residual Risks

ID Risk Severity Accepted By Rationale
RR-01 Single SQLite writer is a bottleneck Low Architecture Lead Design choice; escape hatch to PG exists
RR-02 Isso comment system is 3rd-party Go Low Security Lead Isolated process, no DB access
RR-03 Let's Encrypt outbound ACME traffic Low Security Lead Required for TLS; ACME pinned to LE CAs
RR-04 Meilisearch has no auth in dev mode Medium Operator Must set master key in production
RR-05 Inbound ActivityPub inbox accepts unsigned requests unless a key resolver is configured Low Security Lead internal/federation now implements RSA-SHA256 HTTP Signature verification (VerifyRequest), with body Digest validation and a ±5 min clock-skew window, enforced on the inbox whenever Server.SetKeyResolver is set. It also caps payloads at 10 MiB and applies replay protection (federation_seen_activities, 7-day TTL), and federation is suspended in quarantined mode. Residual: enforcement is opt-in, so a deployment that exposes the inbox without wiring a resolver remains open — operators federating with untrusted peers must configure a resolver.
RR-06 IndexNow submissions trust the configured key/host Low Operator pingIndexNow is mode-aware (suppressed in read-only/quarantined/maintenance) and journals success/failure/suppression, but the IndexNow key is operator-supplied and not rotated automatically.

Review Log

Date Reviewer Changes
2026-06-12 @johalputt Initial threat model (v1.0)
2026-06-14 @johalputt Added RR-05 (federation HTTP-signature gap) and RR-06 (IndexNow governance) after code audit