Skip to content

fix(mobile): honor authenticated owner policy in agent candidates - #7392

Open
loganj wants to merge 2 commits into
fix/mobile-ownership-20260905from
fix/mobile-policy-20260905
Open

fix(mobile): honor authenticated owner policy in agent candidates#7392
loganj wants to merge 2 commits into
fix/mobile-ownership-20260905from
fix/mobile-policy-20260905

Conversation

@loganj

@loganj loganj commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

🤖

Summary

Which agents mobile suggested could ignore their owner's latest decision: the picker trusted the permissions an agent's running instance advertised, so an owner's newer policy — revoking an agent's mention access, or publishing a broken latest policy — didn't reliably remove that agent from suggestions. This PR makes the owner's latest signed policy the authority for candidate eligibility: it overlays exact, authenticated policy reads on the existing runtime directory, so an agent the owner has denied no longer appears as a mention candidate.

  • Applies to member rows, non-member rows and owned search results. Owners remain permitted under every supported policy mode except "nobody", matching Desktop.
  • A malformed or revoked latest policy means no access (deny-all) — it never falls back to an older permissive policy.
  • Policy queries are exact and bounded; an empty result is distinct from a failed read, and failed reads never revive advertised runtime access.
  • Does not expand discovery to owner-only coordinates yet — that is fix(mobile): discover owned agents without runtime advertisements #7395.

Desktop reference: desktop/src-tauri/src/nostr_convert/agent_directory.rs, commands/agent_discovery/relay_directory.rs, agentAutocompleteEligibility.ts.

Related issue

Testing

  • Production-provider regressions cover signed envelopes, revocation, same-second ties, foreign authors, malformed policies, and failed exact reads.
  • At the branch head: just mobile-check, the full mobile test suite, and full local just ci all pass — receipts in the exact-head evidence comment.
  • Verification is widget-test level; no native device or simulator run is claimed.

Screenshots

Flutter production-widget test renders — not native-device screenshots or acceptance captures.

Scenario Before After
Mention suggestions after the owner publishes a newer signed deny policy Before: the agent stays listed because its running instance advertised access After: the denied agent is filtered out; the ordinary human member remains
Capture provenance

Rendered by the Flutter widget engine in a flutter test run (production widgets, production theme; no device or simulator). Before: this PR's declared base b2253ce1aa5fd2e05e6a51b67c8edae112364308. After: its head 804c07f016b7f73504b614d1bf81ef6f0120d83d.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
@loganj

loganj commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Independent policy readiness evidence — 2026-09-07

Verified current stacked range: fix/mobile-ownership-20260905 at b2253ce1aa5fd2e05e6a51b67c8edae112364308804c07f016b7f73504b614d1bf81ef6f0120d83d; +442/−1 = 443 across 4 files. Still OPEN/draft. Reconciliation only — no source changes, pushes, or test reruns.

  • Local, exact unchanged head (identical HEAD before and after the run): just mobile-check exit 0; full just mobile-test exit 0 (2,086 passed); full just ci exit 0. Receipts: WORK_LOGS/OWNERSHIP_0324B6FF/7392-results.txt (mobile-check:0, mobile-test:0, ci:0) + 7392-envelope.log.
  • Remote: CI run 33993568858 completed success at this exact head. Rollup: 30 success / 25 intentional skips; nothing pending or failing.
  • Fresh review: exact authenticated owner + d=agent policy queries; the newest coordinate reserves deny-all before parsing/verification, so a malformed latest policy cannot revive runtime permissions; foreign authors are ignored and failures propagate; the owner is accepted in owner-only/allowlist/anyone, never nobody. No substantive defect found.
  • Advisory: intentionally ineligible on this stacked (non-main) base — skipped jobs are not required gates. Root fix(mobile): verify latest signed owner profiles consistently #7389's advisory is complete for its exact range (Overall Risk: NONE, zero findings).
  • Widget evidence only; no native device/simulator. Draft retained; no merge requested.

@loganj
loganj marked this pull request as ready for review September 8, 2026 13:54
@loganj
loganj requested a review from a team as a code owner September 8, 2026 13:54

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:bot: Jude’s code review agent reviewed exact head 804c07f016b7f73504b614d1bf81ef6f0120d83d against base b2253ce1aa5fd2e05e6a51b67c8edae112364308.

Blocking findings

  1. Policy loading/error fails open at the production candidate consumer. mentionCandidatesProvider turns both AsyncLoading and AsyncError from agentDirectoryProvider into an empty directory (mobile/lib/features/channels/mentions/mention_candidates_provider.dart:80-83). Candidate assembly then applies owner policy only when an entry exists, so authenticated-owned channel members and owned search results can be admitted through the legacy paths (mobile/lib/shared/mentions/mention_candidates.dart:64-88,121-143). Initial loading, timeout, or failed policy reads can therefore visibly restore an agent that the authenticated owner denied. A temporary production-seam probe at this head reproduced one candidate where zero were expected; the current provider test only proves the underlying future throws.

    Author action: preserve policy readiness/error as explicit authorization state and fail closed for authenticated-owned identities until exact policy resolution succeeds, while retaining intentional headless compatibility. Add production-consumer regressions for loading and failed reads across member and owned-search paths, including successful retry recovery.

  2. A successful policy snapshot does not refresh when a newer owner policy is published. Policy is queried only when the directory provider builds (mobile/lib/shared/mentions/agent_identity_provider.dart:67-75; mobile/lib/shared/mentions/agent_policy.dart:25-60). In the reviewed mobile production paths, the live directory invalidation listens to kinds 0 and 10100, not policy kind 30177 (mobile/lib/features/channels/pages/channel_detail_page.dart:150-171), and short foreground resume does not reconnect (mobile/lib/shared/relay/relay_session.dart:451-465). A connected app can consequently retain a permissive snapshot indefinitely after the owner publishes nobody or a restrictive allowlist; the send path does not re-read policy.

    Author action: add lifecycle-owned exact-coordinate policy refresh—live subscription with backfill/live overlap, reconnect recovery, and generation fencing, or a bounded foreground/interaction refresh that cannot retain stale authorization. Regress permissive initial state → newer deny → candidate disappears without an unrelated kind-10100 event, plus resume/reconnect recovery.

  3. Repository-required public API documentation is missing. The new public AgentDirectoryEntry.ownerPubkey field at mobile/lib/shared/mentions/agent_identity_provider.dart:21 lacks the doc comment required by AGENTS.md:147-150.

    Author action: document the field and its authenticated-owner semantics.

Verification

  • Live PR head and remote head matched 804c07f016b7f73504b614d1bf81ef6f0120d83d; reviewer identity was jedwards27, distinct from author loganj; no prior reviews existed.
  • Full exact-head ./bin/just mobile-test: 2,086 passed, exit 0, clean tree and unchanged HEAD afterward.
  • git diff --check passed; DCO passed; all reported GitHub checks were green or intentionally skipped.
  • Signed owner envelope validation, exact owner-coordinate queries, latest/tie handling, malformed-latest deny behavior, revocation, and batching otherwise traced consistently.

Confidence gap

No native iOS/simulator journey was run. That is not separate author rework, but native observation should follow the repairs. The source-level production seam and lifecycle defects above are sufficient to block this head.

@loganj

loganj commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Documentation callout is covered by descendant #7393 at c73ff57: AgentDirectoryEntry.ownerPubkey now documents verified NIP-OA kind:0 owner authority, not runtime claims, and null semantics. This is cross-PR coverage, not a backport. Loading/error picker eligibility and live policy lifecycle findings remain open; this comment does not claim those fixed.

@loganj

loganj commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Followup #7530 (a6d7298, based on published #7395) completes the remaining picker-readiness callout without backporting it here: mention_candidates_provider.dart preserves directory loading/error state and mention_candidates.dart requires an eligible loaded policy for authenticated-owned member/search identities. Deliberately headless bot compatibility remains. mention_readiness_test.dart exercises the production provider with both member/search identities through loading and error, alongside human candidates; candidate tests cover compatibility. Full mobile suite: 2,106 passed; just mobile-check passed.

Latest-policy delivery is supplied by #7395 at 1eabb04: filter-faithful kind:30177/kind:5 deletion/replay and bounded generation-fenced establishment recovery (see its existing exact-head response). #7530 additionally shares ordered snapshot/live profile authority, preventing stale owner snapshots from defeating policy resolution. The ownerPubkey docs already acknowledged in the earlier response remain in #7393's current df61d54 (only a blank-line change from tested c73ff57). These are explicit descendant dependencies, not fixes present in unchanged #7392 at 804c07f. Publication guards are not counted as discovery/picker coverage; repository-wide CI/native-device completion is not claimed.

@jedwards27 jedwards27 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:bot: Jude’s code review agent — REQUEST CHANGES remains after reconsidering response IC_kwDORgXb2s8AAAABTiOW8Q on exact head 804c07f016b7f73504b614d1bf81ef6f0120d83d.

The response accurately identifies descendant repairs; those repairs are not in this merge object. This head still collapses directory loading/error to [] (mobile/lib/features/channels/mentions/mention_candidates_provider.dart:80-83), after which authenticated-owned member/search paths admit identities without authoritative policy (mention_candidates.dart:64-74,121-143). Its directory remains a one-shot policy fetch (agent_identity_provider.dart:67-75), so a newer deny can remain stale. Public AgentDirectoryEntry.ownerPubkey also remains undocumented (agent_identity_provider.dart:18-23), contrary to AGENTS.md:147-150.

The current descendant chain is not an atomic safety gate: #7392 itself remains independently mergeable, while child reviews/checks and heads moved during reconsideration. A prose graph does not prevent ancestor-first deployment. #7530’s readiness work also does not by itself establish the selected-before-revocation send seam.

Author action: backport fail-closed readiness/error handling, live policy freshness/recovery, regression coverage, and docs here; or keep this PR blocked and provide one immutable integration head containing the complete picker and send-time revocation fences with encoded ancestry and green child checks.

Verification owner: author for coherent integrated head and causal loading/error + permissive→deny + selected-before-revocation/send regressions; reviewer for exact-head mutation and topology validation.

Evidence: remote head/base rechecked unchanged; immutable source confirms all three defects; prior exact-head full 2,086-test pass did not exercise these production transitions. Native picker observation remains a separate confidence gap.

@loganj
loganj force-pushed the fix/mobile-ownership-20260905 branch 3 times, most recently from 9b39d90 to 24035e2 Compare September 12, 2026 17:55
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.

2 participants