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>
This commit is contained in:
Scott Reimers 2026-04-22 17:40:21 -04:00
parent 4a1db1ce7f
commit 7e1e1dd738
7 changed files with 365 additions and 21 deletions

View file

@ -2701,13 +2701,13 @@ async function doSyncAll() {
}
async function doSetupName() {
// Name is optional — users who want to stay anonymous can proceed with a blank field.
const name = setupName.value.trim();
if (!name) return;
setupBtn.disabled = true;
try {
await invoke('set_display_name', { name });
setupOverlay.classList.add('hidden');
toast('Welcome, ' + name + '!');
toast(name ? 'Welcome, ' + name + '!' : 'Welcome!');
loadNodeInfo();
} catch (e) {
toast('Error: ' + e);
@ -3797,9 +3797,10 @@ $('#import-btn').addEventListener('click', () => {
</div>
<div id="import-summary-box" style="display:none;text-align:left;background:#111;border-radius:8px;padding:0.75rem;margin-bottom:0.75rem;font-size:0.75rem"></div>
<div id="import-action-box" style="display:none;text-align:left;margin-bottom:0.75rem">
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="add_identity" /> Add as new identity (requires key in export)</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="import_posts" checked /> Import public posts into current identity</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="merge_key" /> Merge with decryption key (decrypt + re-create)</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="as_personas" checked /> Restore as personas &mdash; posts keep their original authors; source's keys become personas you can post as</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="add_identity" /> Add as a separate identity (own data dir)</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="import_posts" /> Public posts only (no keys imported)</label>
<label class="checkbox-label" style="font-size:0.8rem"><input type="radio" name="import-action" value="merge_key" /> Consolidate under current default persona (requires source key)</label>
<div id="merge-key-input" style="display:none;margin-top:0.4rem">
<input id="import-merge-key" type="text" placeholder="Original identity key (64 hex chars)" maxlength="64" style="width:100%;font-family:monospace;font-size:0.7rem" />
</div>
@ -3866,7 +3867,9 @@ $('#import-btn').addEventListener('click', () => {
overlay.querySelector('#import-go').disabled = true;
try {
let result;
if (action === 'add_identity') {
if (action === 'as_personas') {
result = await invoke('import_as_personas_cmd', { zipPath });
} else if (action === 'add_identity') {
result = await invoke('import_as_new_identity', { zipPath });
} else if (action === 'merge_key') {
const keyHex = overlay.querySelector('#import-merge-key').value.trim();

View file

@ -7,12 +7,12 @@
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- First-run: set display name -->
<!-- First-run: set display name (optional — can be left blank) -->
<div id="setup-overlay" class="overlay hidden">
<div class="overlay-box">
<h2>Welcome to ItsGoin</h2>
<p>Choose a display name to get started.</p>
<input id="setup-name" type="text" placeholder="Your name" maxlength="50" autofocus />
<p>Pick a display name if you want one &mdash; or leave blank to stay anonymous.</p>
<input id="setup-name" type="text" placeholder="Display name (optional)" maxlength="50" autofocus />
<button id="setup-btn" class="btn btn-primary">Continue</button>
</div>
</div>