cautpartener / docs / ROADMAP.md
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# Caut Partener — Roadmap & state

Orchestrator state document. Updated at every milestone boundary. A checklist item is
ticked ONLY when fully implemented, e2e-verified, and review-cleared — stubs never count.

## Milestone status

| Milestone | Status | Notes |
|---|---|---|
| M0 Scaffold + provisioning | **done** 2026-08-22 | live at https://cautpartener.ro (CID bafybeiduro…6sxm); review findings fixed; Playwright harness deferred to M1 (with mailbox-stub e2e) |
| M1 Identity + multi-device | **done** 2026-08-22 | v0.1.1 live (CID bafybeif2wi…swfe); 12-assertion e2e green; review: 4 majors fixed (settings forward-compat bag, gap honesty, full-log fold, Safari ITP honesty) |
| M2 Contacts + groups | **done** 2026-08-22 | v0.1.2 live (CID bafybeias3…xfym); 20-assertion e2e; review: sheet-dismiss deadlock, TOFU-any-binding, name-in-cert, ambiguity refusal, prompt cap, fold enforcement — all fixed |
| M3 RoomHub + chat (text/image/voice) | **done** 2026-08-22 | v0.1.3 live (CID bafybeifnm…s4ou); 5 e2e suites; review: ginvite hidden-reader hole closed, decode-bomb bound, media unpinning, probe caching |
| M4 Profiles + comments + contact requests | **done** 2026-08-22 | v0.1.4 live (CID bafybeid3w…kody); review: paging convention fixed against empirically-verified node semantics, CBOR canonicality check, avatar decode bound |
| M5 Posts + audience + feed (Instagram UX) | **done** 2026-08-23 | v0.1.5 live (CID bafybeidhu…tf7y); 8 suites 140+ assertions; review: block-verify-at-ingest, rekey convergence, handle leaks, TOFU-rekey, deleteGroup republish — all fixed |
| M6 Polish + source publishing | **done** 2026-08-23 | v0.1.6; source live + publicly clonable at cod-sursa.cautpartener.ro (CID bafybeid6w…hwze); 9 e2e suites, 126 assertions; anonymity gate green |
| P1 Activities | **done** 2026-08-23 | activity+join docs on the existing feed rooms (LWW edit/cancel, joins survive edits); contacts-only = implicit auto-maintained group (constant gid `toate-contactele`); Activități feed section, composer, permalink #/a/…, profile strip; 10th e2e suite (16 assertions) green; anonymity grep clean |
| P2 Matching | **done** 2026-08-23 | pool cards (`cp-card\|v1` in unstriped poolRoom, LWW per (pub,pool), retire via same slot) + likes as anonymous 0x12 sealed dead-drops into the target's inbox (wrapbox, AAD-bound; payload names NO sender); mutual = token intersection (mutualToken §2); Potriviri segment in the Feed tab (card stack, my-card editor, anonymous liked-count); 11th e2e suite matching.e2e.mjs green |
| P3 Match-chat wiring | **done** 2026-08-23 | match room secret = pubLo party's like roomSecret (both sides derive the SAME room, no contact needed); `m~<pub>` conversations in Conversații with ❤️ badge; in-chat "Adaugă în contacte" (card control ops + mandatory fingerprint sheets both ways, TOFU rules); second-device match recovery via mlike home-log sync + inbox re-poll — e2e-verified |
| P4 Audio/video calls | **done** 2026-08-23 | vendored chat's lib/media.ts VERBATIM (media-only RTCPeerConnections, perfect negotiation, sealed SDP; `SignalPayload` restored verbatim into lib/protocol.ts — board's fork had dropped it with media.ts); signaling over the room's `RoomCrypto.roomTopic` gossip (kMsg/kSig Sealer discipline — §5d) because the hub has no per-room Net; 1:1 pair+match only, co-presence-gated buttons, caller-appended `{t:"call", missed\|ended}` markers; 12th e2e suite calls.e2e.mjs (fake media devices + dev relay) green — ALL 12 suites green; anonymity gate green |

## Provisioned infrastructure (2026-08-22)

- p2p app `cautpartener` — node p2.ap2p.ro; state active.
  - Branded endpoint: **signal.cautpartener.ro** (cert issued; clients use ONLY this host).
  - Relay peer id: `12D3KooWKqgi6jXB9yrrXJ6zC1iNodRNJDHNrssKD1xrYeGzshcV`
  - Multiaddr: `/dns4/signal.cautpartener.ro/tcp/443/tls/ws/p2p/12D3KooWKqgi6jXB9yrrXJ6zC1iNodRNJDHNrssKD1xrYeGzshcV`
  - Mailbox: enabled, open mint, **ttl 168 h (server ceiling — 720 requested, clamped)**,
    512 MB, 64 KB/message. Client reads actual values from `/.well-known/ap2p` at boot.
  - Origins: https://cautpartener.ro, *.cautpartener.ro, localhost:5173, localhost:4173.
- IPNS + gateway domains created (serve=true): `cautpartener.ro`, `cod-sursa.cautpartener.ro`.
- Deploy convention: build → `ird ipfs add client/dist` → verify CID URL → publish IPNS
  name for cautpartener.ro → unpin previous root.
- Git: repo-local identity `cautpartener <cautpartener@noreply.local>`; identity patterns
  in untracked `~/.config/cod-sursa/idpat` (file exists on this machine).

## Decisions log

- 2026-08-22: Mailbox TTL server ceiling is 168 h; accepted clamp (still 7× the default).
- 2026-08-22: Two-plane architecture per approved plan (private = sueta/board E2E rooms;
  public = plaintext-signed dag-cbor docs over mailbox, envelope prefixes 0x10/0x11/0x12).
- 2026-08-22: M0 review: 2 major (router URIError crash on malformed hash; phantom
  `multiformats` dep) + minors (descriptor double-fetch race → collapsed to single fetch
  as sole authority; global guarded controllerchange reload; Android pinch-zoom restored;
  bump-version validation; RO/EN header toggle added). All fixed before first commit.
- 2026-08-22: Vendored lib README references signal.ardegazu.ro in examples — acceptable:
  the project openly builds on the public sueta/board stack; upstream credited in README.
- 2026-08-22: App layer stops calling lib's resolveRelayConfig (which can't surface
  ttl_hours); boot does ONE descriptor fetch feeding status + RuntimeLimits + RelayState.
- 2026-08-22: Real mailbox `before=` semantics empirically verified against the live node
  (empty before = OLDEST window; first backward page must use before=max-uint64). Caught
  before ship — would have made all public resolution read stale pages on rooms >100 msgs.
- 2026-08-22: verifyDoc requires canonical-form byte equality of the re-encoded envelope
  (dag-cbor accepts permuted keys on decode) — restores CID as a stable dedup key.
- 2026-08-22: §6 foreign-doc re-seeding deliberately deferred to M5 (needs the feed's
  raw-envelope cache; profile cache stores parsed docs).
- 2026-08-23: P1 "contacts only" implemented as a REAL group with the constant gid
  `toate-contactele`, auto-maintained on contact add/remove (constant gid ⇒ one LWW slot
  ⇒ device convergence for free; gkey races settle via the existing §3b machinery).
  Honest difference from a hand-made group stated in-app: membership follows the contact
  book, later contacts get full history, removed contacts keep what they hold.
- 2026-08-23: P1 join docs bind to (org, aid) — NOT an activity cid — so joins survive
  LWW activity edits; they carry `shard` (like likes) for the room-binding receiver rule.
  Activity permalinks carry the shard (`#/a/<shard>/<org>/<aid>`) because org+aid alone
  cannot derive a mailbox room.
- 2026-08-23: P1 review backlog item (pre-existing M5b): a second owner device with a stale fold rekeying concurrently can grant a just-removed member the winning epoch; converge() re-grants but does not re-rekey on remotely-folded removals — revisit with P2. (Still open after P2/P3 — matching touches none of the audience machinery; carry to P4.)
- 2026-08-23: P2 pool rooms are UNstriped (one room per pool): cards LWW-collapse to ≤1 per
  author and browse runs under the standard page budget, so striping buys nothing at v1 scale.
  Cards carry `name` + §3 cert IN the doc (delta over the original sketch row, which omitted
  name — verifyContactCert needs the literal name, and likers need a display name).
- 2026-08-23: P2 likes are 0x12 wrapbox dead-drops whose payload names NO sender — anonymity
  until mutual is cryptographic, not cosmetic: identity emerges only from the receiver's OWN
  sent-like (token intersection). Honest limits (dictionary-testing by the target, deposit
  timing, no undo) documented in §7 + in-app (aboutLimMatching + mtHonestNote).
- 2026-08-23: P3 match room secret = the roomSecret from the like sent by the pubLo party
  (both sides hold both payloads at mutual time ⇒ deterministic pick, no negotiation). Match
  conversations use conv ids `m~<pub>` so a later contact upgrade never collides with the
  pair room; contact upgrade = in-chat card control ops + mandatory fingerprint sheets (TOFU).
- 2026-08-23: P2 mlike/mcard ride the home log (§4) so devices share sent-likes and card
  sources; the RECEIVED-like store is deliberately device-local (re-derivable by re-polling
  the inbox — e2e-verified on a fresh seed import).

- 2026-08-23: P4 signaling channel: chat's lib runs one Net per room (directed
  `/sueta/2/call-sig/1.0` streams between session PeerIds that double as room-crypto ids);
  our hub is ONE shared node for all rooms with neither per-room network identity nor
  directed-stream plumbing — so call signaling rides the room's gossipsub topic
  (`RoomCrypto.roomTopic`, the lib's own "ephemeral traffic (presence, calls)" derivation)
  carrying the lib's sealed envelopes unchanged (ctl = kMsg, SDP/ICE = kSig from inside the
  vendored media.ts). Plaintext from/to pids are routing hints only — the AADs bind them.
- 2026-08-23: P4 vendoring: lib/media.ts copied byte-identical from chat (shasum-verified);
  its `SignalPayload` import restored verbatim into lib/protocol.ts (board's fork dropped
  the type together with media.ts; the vendored README already documents protocol.ts as
  carrying "signal payloads"). lib/net.ts + lib/turn.ts were already byte-identical to
  chat's. The Net-typed adapter crosses with a cast in core/calls.ts (Net is nominal via
  private fields) — the same cast pattern the hub blesses for app-plane log ops.
- 2026-08-23: P4 widens M3b "lazy live" deliberately: being CALLABLE requires the mesh, so
  CallService starts it when ≥1 pair/match conversation exists (hidden-suspend battery
  valve unchanged). Call buttons gate on subscribersOf(call topic) — honest co-presence.
- 2026-08-23: P4 bug found by e2e and fixed: accept() cleared the ring timers only AFTER
  getUserMedia resolved — a permission prompt (or slow device init) could lose the race to
  the ring timeout, tearing the call down mid-accept (on iOS a prompt can outlast the 45 s
  ring). Timers now stop the moment the user accepts. Also: sealed SDP frames arriving
  before the local media pc exists are buffered and flushed (a dropped first offer can
  deadlock perfect negotiation), and finished callIds are remembered so a late in-flight
  invite cannot re-ring a declined call.
- 2026-08-23: P4 review-backlog carry-over (pre-existing M5b, still open — calls touch no
  audience machinery): a second owner device with a stale fold rekeying concurrently can
  grant a just-removed member the winning epoch; converge() re-grants but does not re-rekey
  on remotely-folded removals.

## Requirements coverage checklist

### V1
- [x] Create profile; identity portable across multiple devices (seed export/import + QR; home-log sync) — M1+M4 (done 2026-08-22)
- [x] Only the owner can change their profile (owner-signed LWW docs; forged docs dropped by verification) — M4 (done 2026-08-22; forged-doc e2e)
- [x] Profile readable by anyone from its link, even with owner offline (HTTPS mailbox replay) — M4 (done 2026-08-22; cold-load e2e + >100-msg paging regression)
- [x] Contact book of verified identities (invite link/QR, in-person fingerprint compare, creq/cack from profile) — M2, M4 (done 2026-08-22)
- [x] Each contact carries their cautpartener.ro profile link (`#/u/<pub>`) — M2 (done 2026-08-22)
- [x] Contacts groupable; one contact in multiple groups — M2 (done 2026-08-22; overlapping membership e2e-verified)
- [x] Restrict profile parts to public/contacts/groups (cryptographic: sealed sections under group epoch keys) — M5 (done 2026-08-23; rekey cascade + convergence e2e)
- [x] Restrict posts to public/contacts/groups (same mechanism; text-only in v1, stated in-app) — M5 (done 2026-08-23)
- [x] Comments on user profiles (anyone; owner moderation via mod doc; non-retraction documented) — M4 (done 2026-08-22)
- [x] Public posts with images and ideas, browsable feed (shards, stripes, re-seeding, live-topic valve) — M5 (done 2026-08-23)
- [x] Instagram-like image UX: profile photo grid, full-bleed feed cards, multi-image carousel, lightbox, double-tap likes, thumbnail-first loading — M5 (done 2026-08-23)
- [x] Private E2E 1:1 chat between contacts (pairwise DH rooms, offline delivery via mailbox) — M3 (done 2026-08-22)
- [x] Group chat (minted-secret rooms, ≤12 members) — M3 (done 2026-08-22; member-authorized invites)
- [x] Image messages in chats (E2E-sealed thumbnail+display blobs, inline render, works offline via mailbox) — M3 (done 2026-08-22; EXIF-strip + decode bounds e2e-verified)
- [x] Voice messages in chats (MediaRecorder Opus/WebM + iOS AAC/MP4 fallback, E2E-sealed, tap-to-play, works offline via mailbox) — M3 (done 2026-08-22)
- [x] RO + EN i18n, RO default, full parity enforced by types — done 2026-08-23 (M6 audit: zero bypasses; 304-key parity type-enforced)
- [x] 100% PWA: installable, offline shell, update banner, maskable icons — done 2026-08-23 (update-banner e2e incl. real SW controllerchange reload)
- [x] iOS-correct: safe-areas, viewport-fit=cover, apple meta/icons, A2HS instructions, keyboard-safe composer — done 2026-08-23 (layout audited every milestone; simulator visual pass green on iPhone 16 Pro; NOTE: fully interactive device pass pending the user running `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer` for native simulator driving)
- [x] Identity survives iOS storage eviction: storage.persist(), forced seed backup before identity "created", wiped-storage recovery screen — M1 (done 2026-08-22; ITP total-eviction limitation documented + onboarding import hint + persist-fail warning)
- [x] Live at https://cautpartener.ro via IPFS/IPNS/DNSLink — done; re-published at every milestone (v0.1.6 current)
- [x] Full source published anonymously at https://cod-sursa.cautpartener.ro — done 2026-08-23 (public `git clone` round-trip verified; npm ci in clone exit 0; browse/ viewer live)
- [x] All commits anonymous (repo-local identity, gate green, NO AI trailers — history rewritten per user) — done, verified via cat-file --batch-all-objects
- [x] Honest-limits documentation in-app (RO/EN #/despre) + docs/PROTOCOL.md §7 — done 2026-08-23

### Post-V1 (continue in-session while context allows; order: activities → matching → match-chat → calls)
- [x] Activities with time ranges, joinable, public or group/contacts-restricted — activity + join docs riding the feed shard rooms (kind-filtered; no new derivations); LWW edit/cancel per (pub,aid); joins LWW per (pub,org,aid) so they survive edits; group-restricted = §5b sealed desc/loc with the title public; "contacts only" = implicit auto-maintained group; expired activities age out of re-seeding — P1 (done 2026-08-23; activities.e2e.mjs)
- [x] Tinder/Bumble matching per activity category, likes hidden until mutual — `cp-card|v1` pool cards in poolRoom(category|county) with §3 certs + retire-via-LWW; likes = anonymous 0x12 wrapbox dead-drops into the target's inbox (token = mutualToken §2, sender unnamed on the wire); swipe stack + my-card editor + anonymous liked-count in the Feed tab's Potriviri segment; skips local-only, no undo (stated in-app) — P2 (done 2026-08-23; matching.e2e.mjs incl. wire-level anonymity + junk-0x12 assertions)
- [x] Match → private P2P conversation (room secret carried inside the likes: pubLo party's roomSecret wins — both sides derive the SAME E2E room with NO contact exchange); match chats in Conversații with ❤️ badge + optional in-chat contact upgrade (card ops + fingerprint sheets, TOFU); second device re-derives match + conversation from mlike sync + inbox re-poll — P3 (done 2026-08-23)
- [x] Audio/video calls (port chat's media.ts: separate media-only PCs, perfect negotiation) — 1:1 over pair + match rooms; sealed offer/answer/ICE on the room's call topic (§5d), ICE/TURN via lib/turn.ts (branded endpoint); ring/accept/decline/hangup control ops with own-device filtering; in-call UI (PiP, mute, camera, flip, duration, safe-areas) + WebAudio ringtone; missed/ended markers in the timeline; group calls out of scope v1 (stated in-app, §7) — P4 (done 2026-08-23; calls.e2e.mjs: audio+video connect, decline, missed, timers, both-sides teardown)

static mirror of HEAD · about · clone: git clone https://cod-sursa.cautpartener.ro/cautpartener.git