Commit graph

7 commits

Author SHA1 Message Date
Scott Reimers
0f5147a31c feat(fof-layer2): wire types — WrapSlot, FoFCommentGating, CommentPermission::FriendsOfFriends
Adds the on-wire shapes for FoF Mode 2 comment-gating per
docs/fof-spec/layer-2-mode2-fof-comments.md:

- WrapSlot: per-V_x slot with 2B prefilter_tag + 48B read_ciphertext
  + 48B sign_ciphertext (sealed CEK + sealed priv_x_seed). 98 bytes
  total per slot. Receiver trial-decrypts via prefilter match.

- FoFCommentGating: author-published gating block embedded in
  Post.fof_gating. Carries slot_binder_nonce (32B random; replaces
  spec's circular "post_id in HKDF info"), pub_post_set (1:1 with
  wrap_slots, includes dummy pubkeys), wrap_slots, and revocation_list
  (initially empty; revocation diffs accumulate on the BlobHeader copy).

- RevocationEntry: author-signed entry triggering retroactive comment
  delete + pub_post_set removal on every file-holder that receives it.

- CommentPermission gains FriendsOfFriends variant. Existing match arm
  in connection.rs handle-incoming-diff path is extended with a
  "drop pending CDN four-check verification" stub (full verify in a
  later slice).

- InlineComment extended with three optional fields:
    pub_x_index: index into parent post's pub_post_set
    group_sig: 64B ed25519 sig under priv_x
    encrypted_payload: ChaCha20-Poly1305 ciphertext under CEK_comments
  All #[serde(default)] for back-compat. Old comments deserialize
  cleanly with None.

- Post gains optional fof_gating field for the author-signed snapshot
  at publish time. PostId = BLAKE3(Post) covers it, so any tampering
  is detectable. Mutations (revocation, access-grant) arrive later as
  diffs against the local BlobHeader copy.

All 21 existing Post construction sites + 4 existing InlineComment
sites updated via perl -0pe sweeps to pass None for the new fields.
Full test suite: 134/134 pass (4 new slot crypto + 130 existing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:39:46 -04:00
Scott Reimers
d990da5bda Fix: imported DMs silently hidden from Messages tab
Two bugs ganging up:

1) Import ignored the intent field. `ExportedPost.intent` was always
   serialized on export but the import path hardcoded every encrypted
   post to `VisibilityIntent::Friends` (import.rs:308-311), discarding
   whatever `ep.intent` said. DMs got misfiled as Friends.

2) The Messages tab filter only surfaces posts whose `intentKind` is
   `direct` (or `unknown` with the right visibility shape). Posts with
   `intentKind = friends` skip the filter — DMs became invisible after
   an "everything" import, even though the rows were in the DB and the
   per-persona decrypt loop would have resolved them to plaintext.

Fixes:

- `parse_exported_intent(raw, vis)` in import.rs: parses the Debug-format
  intent string the export writes, handling Public / Friends / Circle /
  Direct / Control / Profile / Announcement / GroupKeyDistribute. For
  `Direct`, recovers the recipient list from `PostVisibility::Encrypted`
  since the Debug format for `Vec<NodeId>` isn't cleanly parseable.
- Heuristic fallback when the export carries no intent (pre-v0.6.1
  source DBs, where the intent column wasn't populated): Encrypted
  posts with <=3 recipients are classified as `Direct`, larger
  recipient lists stay `Friends`. DMs typically wrap to 1-2 people;
  Friends wraps to every public follow.
- `StagedImport.posts` tuple grows an `intent` slot; the store step
  uses the parsed/inferred intent instead of the hardcoded default.

One-time startup migration:
- Sweeps existing posts where `visibility_intent = "Friends"` and
  visibility is Encrypted with <=3 recipients; rewrites to Direct.
  Guarded by `mig_import_dm_fixup_v1` settings key so it runs once
  per DB. Handles already-imported corrupt state so users don't need
  to re-import.

Tests: 124 / 124 core tests pass.
2026-04-23 08:11:11 -04:00
Scott Reimers
7e1e1dd738 Platform: Reset wipe, empty name, Android browse + backup-off, import as personas
Reset All Data:
- Sentinel now written at the app-level data_dir instead of the
  active identity's subdir. On Android the subdir path was never
  checked at startup, so reset silently did nothing.
- On detection, wipe EVERYTHING under the app data_dir: identity.key,
  itsgoin.db + WAL + SHM, blobs, all identity subdirs. Next launch
  is truly fresh — new network key, new posting key, no prior data.

First-run name:
- Display name is optional. Blank submits as anonymous.
- First-run modal + profile overlay placeholder updated to say
  "Display name (optional)".

Android file picker:
- pick_file on Android now uses tauri-plugin-android-fs'
  show_open_file_dialog (Storage Access Framework OPEN_DOCUMENT).
  Read the picked URI's bytes, stage them in the app's private cache
  as a timestamped file, return the staged path so existing
  import_* code can read it as a regular filesystem path.
- Zip filter passes application/zip + application/octet-stream (some
  file providers report the latter for .zip).

Android auto-backup off:
- AndroidManifest: allowBackup="false", fullBackupContent="false",
  dataExtractionRules pointing at new data_extraction_rules.xml
- New data_extraction_rules.xml excludes all domains from both
  cloud-backup and device-transfer. Prior default (allowBackup=true)
  silently replicated identity.key to Google Drive for any user with
  cloud backup on — which effectively published the root secret to
  a third party without asking. Users who want off-device backup use
  Settings -> Export (explicit zip they control).

Import as personas:
- New import_as_personas function in core/import.rs + new
  import_as_personas_cmd Tauri IPC.
- Reads identity.key from the bundle and adds it to posting_identities
  as a persona. Also reads posting_identities.json (v0.6+ bundles)
  and adds each entry. Dedupes by node_id.
- Posts stay AS-AUTHORED — original post_id, original author,
  original signatures, original wrapped_key recipients. No
  re-encryption. Content encrypted to any of the imported keys
  becomes decryptable because we now hold the secrets.
- Blobs, follows, profiles copied across.
- If current device has <=1 posting identity (the fresh-install one)
  and the bundle brings more, auto-switch the default to the first
  imported persona. Covers first-run-then-import flow cleanly.

Import wizard UI:
- New default option: "Restore as personas" — posts keep original
  authors; source's keys become personas you can post as.
- Old "Merge with decryption key" retained as "Consolidate under
  current default persona (requires source key)" for the case where
  a user intentionally abandons a persona.
- "Public posts only" and "Add as separate identity" retained.

deploy.sh made executable (chmod +x tracked).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:40:21 -04:00
Scott Reimers
ce4b989b17 Phase 4 (0.6.3-beta): posting-key / network-key split (plumbing)
Decouple the signing identity from the network identity. This phase
ships the plumbing only — every device still has exactly one posting
identity, copied from the network key on first 0.6.3 launch so all
existing signed content keeps verifying. Phase 5 builds the
multi-persona UX on top.

Types:
- New PostingIdentity struct: { node_id, secret_seed, display_name,
  created_at }

Storage:
- New posting_identities(node_id, secret_seed, display_name,
  created_at) table
- Methods: upsert / get / list / delete posting identities;
  get/set default posting id (stored in settings)
- seed_posting_identity_from_network: idempotent migration inserts
  the network key as the single posting identity and sets it default
  on first 0.6.3 launch

Node:
- default_posting_id + default_posting_secret fields populated on
  startup via the migration
- All content signing / encryption / key wrapping now uses
  default_posting_secret; the old Node.secret_seed field is gone
  (iroh holds the network secret internally)
- author field on all locally-created content is now
  default_posting_id (equal to node_id for upgraders until Phase 5
  introduces separate personas)
- Auto-follow-self covers both network_id and default_posting_id
  (same in 0.6.3, may diverge in 0.6.4+)

Export/import:
- Bundle now includes posting_identities.json in
  IdentityOnly / PostsWithIdentity / Everything scopes
- restore_posting_identities(zip, storage) reads and upserts on
  import

Smoke-tested:
- Fresh 0.6.3 install: posting_identities seeded from network key;
  default set; new post's author = default_posting_id = network_id
- Two-node pull sync: B pulls A's post, signature verifies across
  the wire
- 111 core tests pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:38:12 -04:00
Scott Reimers
cba30a1bb3 AppImage video fix, proper import posts, first-run chooser, file pickers
- bundleMediaFramework: true — bundles full GStreamer plugin set in AppImage.
  Fixes WebKit hang on video/audio (missing appsink/autoaudiosink plugins).
  MSDK/VA plugins removed post-build to avoid Ubuntu DMA assertion crash.
- Import creates complete posts: BlobHeader, VisibilityIntent, pinned blobs,
  self-follow. Imported posts now indistinguishable from locally created ones.
- First-run chooser: Start Fresh or Import Identity on fresh install only.
  Profile setup shown for existing identities without display name.
- File pickers: native Browse buttons on export (folder) and import (ZIP)
  via tauri-plugin-dialog.
- Export path: relative paths resolved against home dir.
- Lightbox close: only on overlay/image click, not inner form content.
- Growth loop skips self as N2 candidate.
- Node shutdown on identity switch (prevents zombie background tasks).
- media-src CSP includes asset protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:49:52 -04:00
Scott Reimers
97dc83f9f1 v0.5.0-beta: merge-with-key import, prior_author provenance, beta versioning
Merge-with-key: decrypt exported posts using original identity seed, re-create
under current identity with prior_author in BlobHeader for provenance tracking.
Download page restructured with stable (v0.4.4) + beta (v0.5.0-beta) sections.
Version bumped across all crates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:47:24 -04:00
Scott Reimers
8ef32e6df6 Export/Import: ZIP export with scope selection, import with public post merge
Export (export.rs): ZIP archive with auto-chunking at 4GB. Four scopes:
identity only, posts only, posts+identity, everything (posts+key+follows+
profiles+settings). Includes blobs. Manifest JSON tracks metadata.

Import (import.rs): Read ZIP summary without importing (preview).
Import public posts into current identity with new PostIds + original
timestamps. Import as new identity (creates identity subdir from key).
Uses spawn_blocking for ZIP I/O to avoid Send issues with ZipArchive.

Tauri IPC: export_data, import_summary, import_public_posts,
import_as_new_identity commands. IdentityManager.base_dir() getter.

Frontend: Export wizard lightbox with scope radio buttons + output dir.
Import wizard with ZIP path, preview summary, action selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 20:56:03 -04:00