The citation & source list endpoints gated only on can_view_tree, so a non-member on a public/unlisted/site_members tree could enumerate citations and sources tied to a redacted living person — leaking that they exist and have sourced facts (and possibly their name via a source title). #46 closed this for events/media/names/relationships but not citations/sources.
Now citation_service.list_citations and source_service.{list_sources,get_source} delegate non-member reads to public_view_service (same pattern as #46):
citations shown only when the cited fact resolves to FULL-visibility person(s) — covers person_id, name_id, event_id (person or both-partner), and relationship_id (both-partner) targets.
sources shown only when they back ≥1 visible citation; a withheld source 404s.
Tests cover all four citation target types, source withholding, the 404, and member-sees-all.
2. On-demand tree purge
Owners can permanently delete a soft-deleted tree now instead of waiting out the 30-day auto-purge. POST /trees/{id}/purge (owner-only): tree must be in the trash + retype its name to confirm. Media objects are deleted from storage, then one DELETE on trees cascades all tree data via the tree_id ON DELETE CASCADE; the audit entry survives (tree_id SET NULL). Frontend gains a Delete forever button on the Recently-deleted list. No migration.
Suite: 102 passing. (Your three June-7 trashed trees can now be purged from /trees once this deploys, rather than waiting for July 7.)
### 1. Privacy fix (NN#2/NN#3) — citation/source redaction
The citation & source list endpoints gated only on `can_view_tree`, so a non-member on a public/unlisted/`site_members` tree could enumerate citations and sources tied to a **redacted living person** — leaking that they exist and have sourced facts (and possibly their name via a source title). #46 closed this for events/media/names/relationships but not citations/sources.
Now `citation_service.list_citations` and `source_service.{list_sources,get_source}` delegate non-member reads to `public_view_service` (same pattern as #46):
- **citations** shown only when the cited fact resolves to FULL-visibility person(s) — covers `person_id`, `name_id`, `event_id` (person or both-partner), and `relationship_id` (both-partner) targets.
- **sources** shown only when they back ≥1 visible citation; a withheld source 404s.
Tests cover all four citation target types, source withholding, the 404, and member-sees-all.
### 2. On-demand tree purge
Owners can permanently delete a soft-deleted tree now instead of waiting out the 30-day auto-purge. `POST /trees/{id}/purge` (owner-only): tree must be in the trash + retype its name to confirm. Media objects are deleted from storage, then one `DELETE` on `trees` cascades all tree data via the `tree_id ON DELETE CASCADE`; the audit entry survives (`tree_id SET NULL`). Frontend gains a **Delete forever** button on the Recently-deleted list. **No migration.**
Suite: **102 passing**. (Your three June-7 trashed trees can now be purged from /trees once this deploys, rather than waiting for July 7.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two changes.
1. Privacy fix (NN#2/NN#3) — the citation and source list endpoints gated only
on can_view_tree, so a non-member on a public/unlisted/site_members tree could
enumerate citations and sources tied to a redacted living person, leaking that
the person exists and has sourced facts (and possibly their name via a source
title). #46 closed this for events/media/names/relationships but not
citations/sources. Now citation_service.list_citations and
source_service.{list_sources,get_source} delegate non-member reads to
public_view_service, mirroring the #46 pattern:
- citations: shown only when the cited fact resolves to FULL-visibility
person(s) — covers the person_id, name_id, event_id (person or both-partner),
and relationship_id (both-partner) target paths.
- sources: shown only when they back at least one visible citation; a withheld
source 404s (don't reveal it exists).
Tests cover all four citation target types + source withholding + member-sees-all.
2. On-demand tree purge — owners can permanently delete a soft-deleted tree now
instead of waiting out the 30-day auto-purge window. POST /trees/{id}/purge
(owner-only): the tree must already be in the trash, and the caller retypes its
name to confirm. Media objects are deleted from storage, then a single
DELETE on trees cascades all tree-owned rows via the tree_id ON DELETE CASCADE;
the audit entry survives (tree_id SET NULL). Frontend adds a "Delete forever"
button to the Recently-deleted list. No migration.
Suite: 102 passing.
Signed-off-by: Justin Paul <[email protected]>
justin
merged commit 265f5f4e7a into main2026-06-10 22:39:16 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
1. Privacy fix (NN#2/NN#3) — citation/source redaction
The citation & source list endpoints gated only on
can_view_tree, so a non-member on a public/unlisted/site_memberstree could enumerate citations and sources tied to a redacted living person — leaking that they exist and have sourced facts (and possibly their name via a source title). #46 closed this for events/media/names/relationships but not citations/sources.Now
citation_service.list_citationsandsource_service.{list_sources,get_source}delegate non-member reads topublic_view_service(same pattern as #46):person_id,name_id,event_id(person or both-partner), andrelationship_id(both-partner) targets.Tests cover all four citation target types, source withholding, the 404, and member-sees-all.
2. On-demand tree purge
Owners can permanently delete a soft-deleted tree now instead of waiting out the 30-day auto-purge.
POST /trees/{id}/purge(owner-only): tree must be in the trash + retype its name to confirm. Media objects are deleted from storage, then oneDELETEontreescascades all tree data via thetree_id ON DELETE CASCADE; the audit entry survives (tree_id SET NULL). Frontend gains a Delete forever button on the Recently-deleted list. No migration.Suite: 102 passing. (Your three June-7 trashed trees can now be purged from /trees once this deploys, rather than waiting for July 7.)
🤖 Generated with Claude Code