Skip to content

Content Lifecycle: Edit, Delete, Hide ​

How posts and comments are edited, deleted and hidden, who may do what, and what happens to everything that references them.

Who may do what ​

One policy module, packages/types/src/content-policy.ts, is enforced by the API and used by the app only to decide which menu actions to show.

ActionPost authorComment authorPost author, on others' commentsAnyone else
Edit post✓ (1 hour, 5 edits)
Delete post✓
Edit comment✓ (1 hour)✗
Delete comment✓✓
Hide / unhide comment✓

Content the viewer can't see (deleted, blocked, someone else's unpublished post) is a 404; visible-but-not-yours is a 403. Deleted content returns code: "POST_DELETED" / "COMMENT_DELETED".

Schema ​

ModelFields
PostdeletedAt, deletedById, editedAt (user edits only — updatedAt also moves on media sync), version (edit count and concurrency token), retainUntil (hold)
CommentdeletedAt, editedAt, version, hiddenAt, hiddenById
MediaremovedAt — removed from its post by an edit
PostRevision / CommentRevisionThe body (and, for posts, media ids and link card) a later edit replaced
ContentAuditLogActor, action, target, author, previous content — written in the same transaction as every edit, delete and hide
Reportsnapshot — the content as it was when reported

Read paths filter through shared fragments: onlyPublishedPosts() (excludes deleted posts for everyone, the author included), liveMedia, countedCommentWhere, hiddenCommentWhere.

Editing ​

Version-checked: the client sends expectedVersion; if another device edited first the API answers 409 with the current copy and the app keeps the user's text. Previous versions are kept as revisions. Posts show "Edited" with a public, text-only history; comments show "Edited" only. Mentions are re-synced (a user is notified only if no earlier version tagged them) and the link card is re-derived. Details: Content API.

Deleting ​

Soft delete, then purge.

  1. At delete: the row gets deletedAt and disappears from every read path (feeds, profile, search, trending, detail, comment lists, socket join). Notifications about it are deleted. Its files are made unreachable immediately by ogun's media-revoke job: private ACL on the original and every variant, a DigitalOcean CDN purge when DO_API_TOKEN/DO_CDN_ENDPOINT_ID are set, and the Mux playback id deleted. post:deleted reaches open screens; the app leaves the post's screen and closes the viewer. DM shares render "This post is unavailable".
  2. Comments: a reply, or a top-level comment without live replies, disappears. A top-level comment with live replies stays as an anonymous placeholder until its last reply goes.
  3. Purge (ogun content-sweep, daily at 04:00 UTC): rows deleted more than 30 days ago are hard-deleted with everything referencing them (purgePostRows/purgeCommentRows in @playpals/db), then their files and Mux assets. Skipped while the post has an open report or retainUntil is in the future. Media removed by an edit is purged on the same schedule.

Moderator removals (admin remove_content) soft-delete the same way with a one-year hold. Admins can hold or release any post from the report page (PUT /admin/posts/:id/retention). Account deletion purges the user's posts and comments synchronously through the same helpers.

What is kept ​

KeptFor
Report rows and their snapshotModeration history; detached from the content at purge
ContentAuditLog1 year; its previous content is redacted 90 days after the content is purged
Held posts (retainUntil)Until the hold ends

Hiding ​

See Moderation.

In the app ​

  • Menus: lib/content-actions.ts builds each menu from the policy; components/moderation/content-action-sheet.tsx draws it everywhere.
  • Cache: every mutation and realtime event writes through lib/post-cache.ts (removePostEverywhere, patchPostEverywhere, patchCommentEverywhere, markCommentDeleted, moveCommentToHidden/FromHidden). Mutations are optimistic with rollback and an error toast (lib/content-errors.ts).
  • Editing: the post composer (components/composer/post-composer.tsx) has an edit mode at /post/edit/[postId]; comments are edited in the existing comment composers via lib/comment-edit-store.ts.

Internal documentation — PlayPalz platform