Skip to content

A retired Bot can still be reached from outside: its shared grant survives archive_bot #1801

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: e235ecb932d34d92bfb93b810f64c70a
Branch: dev/4-retired-inbound-grant
Base: 7b8f81e
At: 2026-10-30T05:29:25Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.

Written-by: FlyBot Lead

What a person sees

A Bot that was shared with a friend's Bot and later retired still accepts messages from that friend's Bot: the retired Bot runs a turn, reads the memory it kept, and spends the owner's model budget — with nothing on screen telling the owner it happened. From the friend's side it looks like the Bot is still there.

Where (main 7b8f81e15a803d54a13fe32d2c0bc2119e7f9333, and the same code at 92a4cf26)

  • core/src/host/roster.js:519-540 archiveBot writes retired/hidden, stops the timers and files a failure card — but leaves state.grants untouched; only the delete path clears sharing (roster.js:584 rt.forgetBotSecrets).
  • core/src/host/hooks.js:733-736 send checks that the target exists, not that it is retired.
  • core/src/host/reach.js:323-353 reach() never looks at retiredBot.

Reproduction

  1. grantTo('b', '@bob.bot'); the friend's Bot sends one message → {"action":"allow"}, delivered: 1.
  2. archive_bot Writer → retired: true; the grant survives: grants: ["b"], [{"id":"b:@bob.bot::0","grantee":"@bob.bot"}].
  3. The same message again → {"action":"allow"}, delivered: 2, and it lands as [Message from Bob] <untrusted-message from="Bob"> hello after </…>.

Expected: after retirement the outside sender is refused, the same way message_bot refuses a retired Bot's name ("No Bot named …").

This is not the channel.js send case: that is the owner typing in their own chat, which docs/subsystems/core-host.md:217 explicitly keeps ("opens the chat again"). This one is triggered by an outside party and is invisible to the owner, and the docs do not keep it.

Expected

A retired Bot cannot be reached from outside: the shared grant either stops resolving (the clean fix — clear state.grants[id] in archiveBot and rt.reachChanged(id), symmetric with the delete path) or reach()/send refuses a retired target with the same No Bot named-shaped refusal.

Scope

5-10 lines plus one case in core/test/retired-bot-paths.test.js; regenerated patches/ds-bot/0001/0002. It is a generate face, so the PR queues behind the ones already in flight.

Dedupe (searched 2026-10-30, open + closed)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions