Skip to content

Explore hiding signals instead of hard-deleting them (traceability) #68

Description

@BurningAXE

Problem

Deleting a signal today is a hard delete, performed by the client: _confirmDeleteSignal in lib/src/widgets/signal_details_screen.dart best-effort deletes the Storage photos, batch-deletes the comments subcollection, then deletes the signal document. Nothing survives.

That is at odds with where the case system is heading (Master Specification §4.5 case ownership history, §4.6 case timeline, §18.7 audit and oversight), and it causes concrete problems now:

  • The signal history is destroyed with the signal. The new events subcollection (signal timeline: status/urgency changes with mandatory update notes) is deleted along with everything else, so there is no record that a signal ever existed, or of who changed what.
  • The reporter can delete individual history entries. Firestore rules allow a comment/event delete by the parent signal's reporter — required for the delete cascade to work at all from the client. An audit trail its own subject can prune is not an audit trail.
  • The cascade is client-side and best-effort. If the app is killed mid-delete, subcollection documents are orphaned (Firestore keeps subcollection docs when the parent document is deleted) and nothing ever cleans them up.
  • Moderation has nowhere to go. §18.3 wants "hide post" as a moderator action distinct from deletion, and §4.10 wants expiry/archiving with reduced-quality media rather than erasure.

Idea to explore

Replace hard delete with hiding / soft delete:

  • A hidden (or deletedAt + hiddenBy) field on the signal, with rules excluding hidden signals from client queries, so the document and its history survive.
  • Move the actual destructive path server-side into a deleteSignal callable using an Admin SDK recursive delete, mirroring the existing deleteAccount callable. With deletion no longer client-driven, events can become allow delete: if false and the tamper hole closes.
  • Decide the retention story alongside §4.10 (archive after ~6 months, media deleted after 6–12 months) and the GDPR erasure path — soft delete must not become a way to retain personal data indefinitely.

Questions to settle

  • Who may hide: reporter only, or reporter + moderator/admin once those roles exist?
  • Is a hidden signal still reachable by direct link / share link, or 404?
  • Do profile statistics keep counting a hidden case? (§3.5.1 says stats survive case deletion.)
  • What actually gets purged, and when — photos first, document later?
  • Does the reporter still get a way to truly erase their own content, for GDPR?

Context

Came out of the signal-timeline work (Master Spec §4.6, "Case Timeline"), which introduces the signals/{id}/events subcollection. That change deliberately keeps allow delete: if isParentSignalReporter(...) on events so the existing cascade keeps working, and records the tamper hole as a known gap rather than fixing it — this issue is that fix.

Activity

  1. BurningAXE commented on Aug 22, 2026

    @BurningAXE
    MemberAuthor

    Built on feature/remove-instead-of-delete (unmerged). Worth recording how this landed, because the shape changed from the proposal:

    • The hidden flag was not used. The moderation slice (deployed 2026-08-17→21, after this issue was filed) already answered that question the other way: hiding moves the document to moderationQuarantine, because signals is world-readable and a flag would hide a signal from the app and from nobody else. Removal follows the same shape, into removedSignals.
    • The motivating problem turned out to be statistics, not audit — people reached for Delete to mean "this case is finished". Hiding alone would not have fixed that, because the profile counted signals with a live count(), so it measured signals still visible. That moved to a server-written publicProfiles.signalsPosted.
    • The comments rule was a wider hole than the events one this issue names: there is no author-delete rule at all, so a reporter can delete anyone's comment on their own signal, unaudited.

    Functions, rules and indexes are deployed and device-verified. The tamper-hole half is NOT closed yet — narrowing the three delete rules breaks the old client's cascade, so it waits for the app release and is tracked in #73 along with the other rules-gated items.

  2. BurningAXE commented on Sep 10, 2026

    @BurningAXE
    MemberAuthor

    Done

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