Spun out of #47c (Archive folder).
Why
47c ships Archive/Delete split for the Inbox folder. Archive is currently one-way: we stash a local copy and remove the message from the inbox contract. Move-back-to-Inbox is disabled because the contract has no way to re-insert an owner-authored message without an AFT token.
Scope
Add a new variant to `UpdateInbox` in `contracts/inbox/src/lib.rs`:
```rust
OwnerInsert {
signature: Signature, // owner ML-DSA-65 over Vec
messages: Vec,
}
```
Behaviour: contract verifies `signature` against the inbox owner's public key (already in `InboxParams.pub_key`); on success, calls the existing message-insert path without the AFT `add_message` token check. Messages keep their original `token_assignment` so summary diff / dedupe stays consistent.
UI side:
- Wire `Unarchive` button in the Archive folder to re-PUT the message via the new variant.
- Drop the local `archived` row once the contract UPDATE acks.
- Re-enable any test currently gated on the disabled Unarchive button.
Coordination
Contract change → new code hash → `published-contract/contract-id.txt` flips. Schedule the merge against an empty Sent / Archive deployment so existing users on the old contract id aren't stranded.
Acceptance
- Archived messages can be moved back to Inbox without an AFT token.
- The owner ML-DSA signature is the only path that bypasses the AFT check; non-owner attempts still fail (regression test on the contract).
- `published-contract/contract-id.txt` is bumped, and the migration plan is documented in `AGENTS.md`.
Spun out of #47c (Archive folder).
Why
47c ships Archive/Delete split for the Inbox folder. Archive is currently one-way: we stash a local copy and remove the message from the inbox contract. Move-back-to-Inbox is disabled because the contract has no way to re-insert an owner-authored message without an AFT token.
Scope
Add a new variant to `UpdateInbox` in `contracts/inbox/src/lib.rs`:
```rust
OwnerInsert {
signature: Signature, // owner ML-DSA-65 over Vec
messages: Vec,
}
```
Behaviour: contract verifies `signature` against the inbox owner's public key (already in `InboxParams.pub_key`); on success, calls the existing message-insert path without the AFT `add_message` token check. Messages keep their original `token_assignment` so summary diff / dedupe stays consistent.
UI side:
Coordination
Contract change → new code hash → `published-contract/contract-id.txt` flips. Schedule the merge against an empty Sent / Archive deployment so existing users on the old contract id aren't stranded.
Acceptance