[Hosting] Rightsholder notices and takedown: report, review, remove, and keep the record #555

Closed
opened 2026-09-08 05:56:18 +00:00 by claude-bot · 1 comment
Contributor

Raised by the owner on 2026-09-08 alongside the content-pack importer (#553): Quest Board will be offered as a hosted service, and people will load content they have obtained. A host needs a way for rightsholders to tell it that content infringes, a way to act on that, and a record of having done so.

What the software must provide

For the host (instance admin):

  • Record a notice: who sent it (name, organisation, contact), what they claim, which content (pack ids, record ids, or a description), when it arrived, and its status (received, reviewing, actioned, rejected, counter-noticed).
  • Act on it across every scope: disable or remove an instance pack, a campaign pack in any campaign, or individual records, with the notice id as the reason. Disabling hides content immediately and reversibly; removal is final. Both write to the audit log.
  • Tell the person who imported the content what happened and why (an in-app notice on the pack, and the notification path the app already has).
  • See who imported what, when, and under which affirmation version (the importer already records this, #553), so a repeat-uploader pattern is visible.

For a rightsholder:

  • A published way to reach the host. Phase 1 is an address the operator sets in Admin → Settings and shows on the licences page; a public reporting form is a later step, because an unauthenticated form is a spam surface and the address suffices to start.

For the importer (the GM or admin who loaded the content):

  • The affirmation at import (#553) states that the operator may remove content on a rightsholder's request; the removal notice points back to it.

What the software must not do

Decide the law. The app records, hides, removes and notifies; whether a notice is valid, whether a counter-notice is accepted, and what the repeat-infringer policy is, are the operator's decisions and belong in the operator's terms, not in code. The docs should say so, and point at the mechanisms.

Data

content_notices: id, reporter (jsonb: name, organisation, contact), claim (text), targets (jsonb: pack ids, record ids, free text), status, received_at, reviewed_by, actioned_at, actions (jsonb: what was disabled or removed, with ids), notes, counter_notice (jsonb, nullable), created_at, updated_at. Admin-only in every direction.

Acceptance

  • An admin can record a notice and move it through its statuses.
  • From a notice, an admin can disable, enable, or remove any pack or record on the instance regardless of scope, and the audit log shows it with the notice id.
  • The importer of affected content is told, with the reason and a pointer to the affirmation they made.
  • The licences page shows the operator's contact for rightsholder notices when one is set.
  • docs/OPERATIONS.md gains a section on handling a notice, stating plainly what the software does and what the operator decides.

Depends on #553 phase 1 (packs, records, affirmation, audit). Milestone candidate: v4.6.0 Release Readiness, since it is part of being safe to put in front of other people.

Raised by the owner on 2026-09-08 alongside the content-pack importer (#553): Quest Board will be offered as a hosted service, and people will load content they have obtained. A host needs a way for rightsholders to tell it that content infringes, a way to act on that, and a record of having done so. ## What the software must provide **For the host (instance admin):** - Record a notice: who sent it (name, organisation, contact), what they claim, which content (pack ids, record ids, or a description), when it arrived, and its status (`received`, `reviewing`, `actioned`, `rejected`, `counter-noticed`). - Act on it across every scope: disable or remove an **instance** pack, a **campaign** pack in any campaign, or individual records, with the notice id as the reason. Disabling hides content immediately and reversibly; removal is final. Both write to the audit log. - Tell the person who imported the content what happened and why (an in-app notice on the pack, and the notification path the app already has). - See who imported what, when, and under which affirmation version (the importer already records this, #553), so a repeat-uploader pattern is visible. **For a rightsholder:** - A published way to reach the host. Phase 1 is an address the operator sets in Admin → Settings and shows on the licences page; a public reporting form is a later step, because an unauthenticated form is a spam surface and the address suffices to start. **For the importer (the GM or admin who loaded the content):** - The affirmation at import (#553) states that the operator may remove content on a rightsholder's request; the removal notice points back to it. ## What the software must not do Decide the law. The app records, hides, removes and notifies; whether a notice is valid, whether a counter-notice is accepted, and what the repeat-infringer policy is, are the operator's decisions and belong in the operator's terms, not in code. The docs should say so, and point at the mechanisms. ## Data `content_notices`: id, reporter (jsonb: name, organisation, contact), claim (text), targets (jsonb: pack ids, record ids, free text), status, received_at, reviewed_by, actioned_at, actions (jsonb: what was disabled or removed, with ids), notes, counter_notice (jsonb, nullable), created_at, updated_at. Admin-only in every direction. ## Acceptance - [ ] An admin can record a notice and move it through its statuses. - [ ] From a notice, an admin can disable, enable, or remove any pack or record on the instance regardless of scope, and the audit log shows it with the notice id. - [ ] The importer of affected content is told, with the reason and a pointer to the affirmation they made. - [ ] The licences page shows the operator's contact for rightsholder notices when one is set. - [ ] `docs/OPERATIONS.md` gains a section on handling a notice, stating plainly what the software does and what the operator decides. Depends on #553 phase 1 (packs, records, affirmation, audit). Milestone candidate: v4.6.0 Release Readiness, since it is part of being safe to put in front of other people.
Author
Contributor

Shipped in #561 (merged as 073690d).

What landed

  • content_notices table (migration 2d3e4f5a6b7c, raw SQL, no foreign keys so the record of why content went outlives the content).
  • Admin endpoints to record a notice, review it (status, notes, counter-notice, reviewer), and act on it in one place: disable/enable or remove a pack loaded for the whole instance or in any campaign, or remove individual records inside either. Disabling is immediate and reversible and drops the content from every search on the next query; removal is final. A batch of actions is all-or-nothing.
  • One operator-set contact address for notices, readable by any signed-in user so a licences page can print it.
  • The importer is told on the pack itself: every action that leaves a pack standing writes status_detail naming the notice and quoting the affirmation version that importer accepted at import, and every action goes to the audit log with the notice id, importer and affirmation version. Removing a pack outright leaves no row to carry that message, so docs/OPERATIONS.md recommends disable-then-remove.

Decisions

  • Quest Board decides nothing: no automatic takedown, no status moved by an action, no counter-notice reinstating anything, no repeat-infringer logic. The operator records what they chose and the software applies it.
  • counter-noticed keeps the hyphen from this issue's contract; it is a stored literal, never an identifier.
  • There is no per-user notification path in the app (the notification backends are session-scheduling shaped and the attention feed is campaign-scoped), so the pack row and the audit log are the channel for now.

Follow-ups

  • A notices screen in the admin area, and a public licences/contact page that prints the contact, belong to the v4.4.0 UI work; the endpoints are ready for both (docs/API.md → "Rightsholder Notices").
  • Notifying an importer whose pack was removed outright needs a user-level notification path that does not exist yet.
Shipped in #561 (merged as 073690d). **What landed** - `content_notices` table (migration `2d3e4f5a6b7c`, raw SQL, no foreign keys so the record of why content went outlives the content). - Admin endpoints to record a notice, review it (status, notes, counter-notice, reviewer), and act on it in one place: disable/enable or remove a pack loaded for the whole instance or in any campaign, or remove individual records inside either. Disabling is immediate and reversible and drops the content from every search on the next query; removal is final. A batch of actions is all-or-nothing. - One operator-set contact address for notices, readable by any signed-in user so a licences page can print it. - The importer is told on the pack itself: every action that leaves a pack standing writes `status_detail` naming the notice and quoting the affirmation version *that importer* accepted at import, and every action goes to the audit log with the notice id, importer and affirmation version. Removing a pack outright leaves no row to carry that message, so `docs/OPERATIONS.md` recommends disable-then-remove. **Decisions** - Quest Board decides nothing: no automatic takedown, no status moved by an action, no counter-notice reinstating anything, no repeat-infringer logic. The operator records what they chose and the software applies it. - `counter-noticed` keeps the hyphen from this issue's contract; it is a stored literal, never an identifier. - There is no per-user notification path in the app (the notification backends are session-scheduling shaped and the attention feed is campaign-scoped), so the pack row and the audit log are the channel for now. **Follow-ups** - A notices screen in the admin area, and a public licences/contact page that prints the contact, belong to the v4.4.0 UI work; the endpoints are ready for both (`docs/API.md` → "Rightsholder Notices"). - Notifying an importer whose pack was removed outright needs a user-level notification path that does not exist yet.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#555
No description provided.