---
last_verified: 2026-05-30
verified_version: 0.1.34
owner: frontend
freshness_days: 30
---

# UI / Layout Design — pvtcoms

Reconciled from Codex + Gemini + research (Signal/SimpleX/Briar/Threema, 2024–2026). For high-threat-first users
(BUILD_PLAN positioning) but kept **simple** so it can broaden later.

## The single most important principle
**Trust-first, and honest.** The app's job is to make two things unmistakable at all times: *who you're really talking to*
(verification state) and *what actually happened to your message* (real delivery state). Everything else is minimal.

## Design decisions (and the debate behind them)
- **Advisory, NOT blocking** (Signal's hard-won lesson, rejected pure mandatory-approval): a key/SAS change shows a
  non-blocking banner + a **persistent separator that can't be scrolled past**; hard-block only for contacts the user
  explicitly verified. → We do **not** adopt Gemini's "security brutalism" (hostile hazard-stripe backgrounds) — research
  shows it backfires; but we keep trust state *prominent and calm*.
- **Familiar structure, distinctive identity.** "Original navigation" is a footgun — under duress users rely on muscle
  memory. So: keep the familiar chat-list + conversation structure (low cognitive load), and get distinctiveness from a
  **custom visual identity**, not weird nav.
- **Honest delivery states, never fake ticks.** For this threat model a false "Delivered/Read" is a *safety* bug.
- **No dark patterns** (the threat model makes them harmful): no read receipts/last-seen/online by default, no streaks,
  no growth nags, no address-book contact discovery, content-light notifications ("New message").
- **Accessibility from v1**: screen-reader labels on trust + delivery states, high-contrast theme, scalable type, large tap
  targets for SAS comparison and panic.

## Visual identity (distinctive yet minimal)
Dark-first with a **true-black OLED** option · **one custom typeface** · a single restrained **accent colour that means
"trusted/secure"** (used *only* for verified state and primary actions) · custom **empty-state + onboarding illustrations**
(the empty chat list is the branding moment) · **flat, non-gradient** message containers, generous spacing. Material 3 /
Jetpack Compose on Android with a matching token-based theme on Tauri so both feel like one product.

## Trust badge (one calm, consistent indicator everywhere)
`○ Unverified` (neutral grey) · `● Verified` (accent) · `△ Key changed — re-verify` (warning). Always visible in the chat
list row and the conversation header; screen-reader announces it.

## Information architecture (minimal: 3 tabs + a clear "+")
```
Bottom nav:   [ Chats ]   [ Safety ]   [ Settings ]        FAB: ( + )  → New invite / Scan
```
`Safety` is a first-class tab (not buried in settings): verification centre, security-event timeline (replaces the
"stories/status" noise other apps have), and the panic/wipe action.

## Screen wireframes (v1)

**Chats** — trust state visible per row; honest last-state; no presence/unread-gamification.
```
┌────────────────────────────────┐
│ pvtcoms            [verified ▾] │  ← filter: All / Verified only
├────────────────────────────────┤
│ ● Marta            12:04        │
│   You: see you then  ✓ Delivered│
│ ○ unverified contact   Tue      │
│   △ Verify before trusting      │
│ ● Dad              Mon          │
│   In mailbox (not yet collected)│
├────────────────────────────────┤
│  Chats    Safety    Settings    │
│                         ( + )   │
└────────────────────────────────┘
```

**Conversation** — persistent trust header; honest delivery states; one-tap verify if unverified.
```
┌────────────────────────────────┐
│ ‹  ○ unverified contact         │
│    ⚠ Not verified — Verify now ›│  ← non-blocking advisory bar
├────────────────────────────────┤
│        Hi — is this you?        │
│                    Encrypting…  │
│                  Sending via Tor│
│  ─── ⚠ key changed · re-verify ─│  ← persistent separator (can't scroll past)
│  yes, it's me                   │
│                                 │
├────────────────────────────────┤
│  [ type a message… ]      ( ↑ ) │
└────────────────────────────────┘
```

**New invite (the share feature)** — QR + short string + share sheet; honest "anyone with this can connect until used/expires".
```
┌────────────────────────────────┐
│ ‹  Invite someone               │
├────────────────────────────────┤
│      ▛▀▀▌ ▖▘▌  ▐▞▖ QR ▌         │
│      ▌▞▖▌ ▝▖▌  ▌▘▞▌  ▌          │
│   Single-use · expires in 24h   │
│                                 │
│  pvtcoms.org/i/x…#secret        │
│  [ Copy ]  [ Share… ]  [ Scan ] │
│                                 │
│  Anyone with this link can      │
│  connect until it's used or     │
│  expires. Verify them after.    │
└────────────────────────────────┘
```

**Verify (SAS)** — big, readable, identical both ends; in-person scan auto-verifies, remote = compare out-of-band.
```
┌────────────────────────────────┐
│ ‹  Verify Marta                 │
├────────────────────────────────┤
│  Compare these on a call or in  │
│  person. They must match.       │
│                                 │
│   🦊  anchor  river  ember      │
│   maple  comet  ▢ … (emoji+word)│
│                                 │
│  [ Scan their code in person ]  │  ← only path that auto-marks Verified
│  [ They match — mark verified ] │  ← weaker, explicit, honest
│  [ They DON'T match ]  (danger) │
└────────────────────────────────┘
```

**Safety tab** — verification centre + honest security-event log + panic.
```
┌────────────────────────────────┐
│ Safety                          │
├────────────────────────────────┤
│ Verify contacts          2 ›    │
│ Security events:                │
│  • Invite redeemed (Marta) Tue  │
│  • Key changed (unknown)   Mon  │
│ ──────────────────────────────  │
│ App lock / passcode        ›    │
│ Self-destruct passcode     ›    │  ← distinct PIN; wipes DB on entry
│ ⚠ Panic: clear local data       │  ← states plainly what is destroyed
└────────────────────────────────┘
```

## Duress / panic (done safely)
- A **self-destruct passcode** distinct from the unlock passcode that silently wipes the local DB + keys (SimpleX pattern).
- Optional **PanicKit/Ripple** responder for hardware/system triggers — but **require authenticated intent** so no other app
  can trigger a wipe (explicitly avoid Briar's issue #907 DoS). The wipe screen states plainly: local DB + keys are
  destroyed; messages already sent to peers cannot be.

## Deferred to v2+ (not in v1 UI)
Group UI, voice/video notes, multi-device pairing screen, per-contact transport-profile UI, themes/customisation,
introduction-by-mutual-contact. Keep v1 to: Chats, Conversation, Invite/Scan, Verify, Safety, Settings.
