Skip to content

feat(channels): add audited owner recovery#2948

Draft
Jreevo wants to merge 3 commits into
block:mainfrom
Jreevo:quinn/2928-channel-owner-recovery
Draft

feat(channels): add audited owner recovery#2948
Jreevo wants to merge 3 commits into
block:mainfrom
Jreevo:quinn/2928-channel-owner-recovery

Conversation

@Jreevo

@Jreevo Jreevo commented Jul 26, 2026

Copy link
Copy Markdown

Summary

This draft implements a reviewable proposal for #2928 without widening ordinary channel-role management:

  • adds a dedicated protected kind 9038 recovery command across the SDK, CLI, relay, Tauri, and Desktop
  • applies one narrow predicate under database locks: every active human channel owner must have durable self archive consent naming the same existing human member as replacement
  • requires an active, known human community owner as signer and a current, known-human target member of both the same community and channel
  • rejects admins, agents, archived/deactivated or unknown identities, bots, former community members, cross-tenant targets, active channel admins, and elevated agents
  • promotes the target only; it does not remove, demote, archive, delete, recreate, or re-tenant anything
  • commits the signed request, promotion, immutable audit record, and delivery outbox atomically
  • makes only the exact tenant-scoped, durably linked relay audit event immutable under both NIP-29 kind 9005 and NIP-09 kind 5
  • keeps unlinked marker-shaped events from older pods deletable during a rolling upgrade
  • includes desired-state schema parity, public-ingest regression coverage, operator/user/event/config documentation, and public API docs

Maintainer security decision required

This is intentionally a draft proposal, not an assertion that the authorization policy is approved.

Proposed predicate:

all_current_human_owners_self_archived_for_target_v1

Every current human owner must already have a durable archive row with:

  • consent_path = self
  • actor = owner
  • replaced_by = nominated target

Any active human channel admin or owner/admin agent blocks recovery. The signer must independently be the active, known human community owner.

This does not recover a lost or deleted key that never recorded self-consent, so it does not solve the incident that motivated #2928. Maintainers must approve, revise, or reject this predicate and the pre-storage command architecture before the PR can be considered merge-ready.

Data and audit guarantees

  • channel, then sorted identity locks serialize membership/archive races without cross-tenant access
  • the target role transition is member|guest -> owner only
  • request replay is cryptographically idempotent and must match the committed audit
  • tenant-leading keys and composite foreign keys preserve community isolation
  • the immutable database audit cannot be updated or deleted
  • audit delivery atomically stores the relay event and links its exact event ID in the tenant-scoped outbox; relay-key rotation does not weaken immutability
  • UUID, messages, threads, all memberships and agent roles, canvas, and workflow bindings are preservation-tested

Validation

Exact head: c0d444d3a88370f60d5147aaba4f1b893d877acc

Passed on that exact head:

  • repository-pinned cargo fmt --all -- --check and workspace Clippy with warnings denied
  • just test-unit: buzz-core, buzz-auth, buzz-db lint/unit, conformance, and push gateway
  • all 15 Postgres owner-recovery transaction, concurrency, preservation, former-member, race, and denial tests
  • migrated Postgres + Redis relay handler test
  • public NIP-98 ingest test proving marker forgery rejection, rolling-upgrade lookalike deletion, exact audit linkage, kind 9005 and kind 5 immutability, visibility, and persistence
  • desired-state schema/schema.sql bootstrap and embedded relay migration bootstrap
  • converged outbox query: 6 total, 6 delivered, 0 pending, 6 exact-ID-linked
  • touched Rust package Clippy and all touched-package tests except the separately isolated mesh race noted below
  • complete CLI suite: 250 passed
  • Desktop helper suite: 3,515 passed; production build passed
  • Tauri format, Clippy, check, 1,637 library tests, 3 mixer diagnostics, and doc tests
  • web checks and build
  • mobile format, analyzer, file-size checks, and 585 tests (1 skipped)
  • owner-recovery Playwright suite: 5 passed, with screenshot review at 1280x720
  • two independent clean-context security/repository reviews; no remaining implementation blocker found

Known validation and governance blockers:

  • repository just ci stops at the unchanged Desktop file-size guard: desktop/src-tauri/src/managed_agents/runtime.rs is reported as 2,220 lines against a 2,216 limit. This branch does not modify that file. All preceding pinned formatting, workspace Clippy, and Biome checks passed; later CI recipes were run individually and passed.
  • the full parallel touched-package command consistently hits the unchanged api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo 504 race. That exact test passes in isolation, and the full touched-package suite passes when only that separately validated test is skipped.
  • GitHub CI and Docker workflows are action_required for the fork head and have not executed. A maintainer must authorize them.
  • maintainer approval of the security predicate and bespoke pre-storage execution path is still required.

The PR remains draft. No merge or deployment is authorized.

Refs #2928.

Jreevo added 3 commits July 25, 2026 22:21
Co-authored-by: Jreevo <jerry.ytp@gmail.com>
Signed-off-by: Jreevo <jerry.ytp@gmail.com>
Co-authored-by: Jreevo <jerry.ytp@gmail.com>
Signed-off-by: Jreevo <jerry.ytp@gmail.com>
Co-authored-by: Jreevo <jerry.ytp@gmail.com>
Signed-off-by: Jreevo <jerry.ytp@gmail.com>
@Jreevo

Jreevo commented Jul 26, 2026

Copy link
Copy Markdown
Author

Rolling-deployment remediation is pushed at exact head 1da7c7b99b46a06d9c52a82d8a7ea8eb49bac111.

What changed:

  • Migration 0025 and schema/schema.sql now install tenant-scoped database triggers that reject soft or hard deletion of an events row whose exact (community_id, id) is durably linked as channel_owner_recovery_outbox.audit_event_id. Current application checks remain defense in depth.
  • A Postgres regression creates and links a real signed recovery audit, calls the legacy public soft_delete_event_and_update_thread(..., None, None) primitive, and verifies both that primitive and direct hard deletion fail closed while the event remains visible.
  • The recovery-worker warning now records the failure through the structured error = %error tracing field.
  • Migration/desired-schema parity tests and operator rollback documentation cover both event-deletion triggers.

Exact-head validation:

  • Fresh embedded-migration schema: all 16 Postgres recovery tests passed.
  • Fresh desired-state schema: the legacy unconditional deletion regression passed.
  • Postgres+Redis recovery handler: 1/1 passed.
  • Public NIP-98 recovery flow against a local relay: 1/1 passed, including both deletion protocols and retained audit visibility.
  • Full buzz-db, buzz-relay, buzz-cli, unit/conformance/gateway, Desktop, Tauri, web, and mobile suites/checks/builds passed; mobile completed 585 tests with 1 skipped.
  • All five owner-recovery Playwright scenarios passed after an E2E build; the dialog screenshot was reviewed.
  • just ci still stops only at the unchanged baseline desktop/src-tauri/src/managed_agents/runtime.rs size gate (2220 lines versus 2216), after format, workspace Clippy, and Desktop Biome pass.
  • Two independent clean-context reviews found no implementation or standards blocker in this remediation.

The PR remains draft. Exact-head GitHub CI and Docker runs are currently action_required; DCO is green. Maintainer approval of the recovery predicate and pre-storage architecture remains required. No merge or deployment occurred.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant