feat(fof-layer4): Tauri commands + Settings "Rotate my vouch key" UI
Three new Tauri commands surface Layer 4 to the frontend:
- rotate_v_me() -> { newEpoch }: generates next V_me epoch +
republishes bio. Old epoch retained in vouch_keys_own; existing
vouchees receive the new key on their next bio-scan.
- cascade_revoke_v_me_epoch(retired_epoch, reason_code) ->
{ postsRevoked }: bulk per-post revocation across every author
post that sealed slots under (self, retired_epoch). Useful as a
follow-up after rotate_v_me when the author wants to actively
cut off comment access on old posts.
- key_burn_post_slot(post_id_hex, slot_index, new_v_x_hex): the
leaked-V_me primitive. Re-seals one slot under a different V_x.
Frontend (Settings → Vouches):
- New "Rotate my vouch key" button + status pill below the Given /
Received lists.
- Confirmation prompt explains the grandfather-by-default semantics:
"old posts remain readable to anyone who held the old key — cascade-
revoke separately if you want to cut off old-content access."
- Wired once per settings-tab activation.
Cascade-revoke and key-burn surfaces aren't visualized yet (require
per-post selection UI); the Tauri commands are available for follow-up
UI work or scripting via the desktop dev console.
Workspace builds clean; 148 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
fdbf97f2d7
commit
ce710a6596
3 changed files with 89 additions and 1 deletions
|
|
@ -1228,6 +1228,57 @@ async fn read_fof_closed_body(
|
|||
node.read_fof_closed_body(&pid).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// FoF Layer 4: V_me lifecycle + cascade + key-burn.
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct VmeRotatedDto {
|
||||
new_epoch: u32,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn rotate_v_me(state: State<'_, AppNode>) -> Result<VmeRotatedDto, String> {
|
||||
let node = get_node(&state).await;
|
||||
let new_epoch = node.rotate_v_me().await.map_err(|e| e.to_string())?;
|
||||
Ok(VmeRotatedDto { new_epoch })
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CascadeRevokeResultDto {
|
||||
posts_revoked: usize,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cascade_revoke_v_me_epoch(
|
||||
state: State<'_, AppNode>,
|
||||
retired_epoch: u32,
|
||||
reason_code: u8,
|
||||
) -> Result<CascadeRevokeResultDto, String> {
|
||||
let node = get_node(&state).await;
|
||||
let n = node.cascade_revoke_v_me_epoch(retired_epoch, reason_code)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(CascadeRevokeResultDto { posts_revoked: n })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn key_burn_post_slot(
|
||||
state: State<'_, AppNode>,
|
||||
post_id_hex: String,
|
||||
slot_index: u32,
|
||||
new_v_x_hex: String,
|
||||
) -> Result<(), String> {
|
||||
let node = get_node(&state).await;
|
||||
let pid = parse_node_id(&post_id_hex)?;
|
||||
let new_v_x_bytes = hex::decode(&new_v_x_hex)
|
||||
.map_err(|e| format!("invalid new_v_x hex: {}", e))?;
|
||||
let new_v_x: [u8; 32] = new_v_x_bytes.as_slice().try_into()
|
||||
.map_err(|_| "new_v_x must be 32 bytes".to_string())?;
|
||||
node.key_burn_post_slot(pid, slot_index, &new_v_x).await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn list_follows(state: State<'_, AppNode>) -> Result<Vec<PeerDto>, String> {
|
||||
let node = get_node(&state).await;
|
||||
|
|
@ -3251,6 +3302,9 @@ pub fn run() {
|
|||
revoke_fof_commenter,
|
||||
create_post_fof_closed,
|
||||
read_fof_closed_body,
|
||||
rotate_v_me,
|
||||
cascade_revoke_v_me_epoch,
|
||||
key_burn_post_slot,
|
||||
list_circles,
|
||||
create_circle,
|
||||
delete_circle,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue