13 KiB
Support and content-removal operations
Status: implemented intake and operator workflow. This document describes the software behavior; it is not a legal opinion and does not establish that a particular law applies to the operator.
Separate queues
Who Need Help deliberately separates three mechanisms:
- An authenticated in-product report targets exactly one request, assignment, message, Activity, or Activity message and follows the existing audited trust-and-safety workflow.
- A support request covers account access, technical issues, safety concerns, moderation appeals, privacy questions, data export, and account deletion.
- A content-removal notice covers a precise item alleged to be illegal or to infringe a right. TAKE IT DOWN notices have a dedicated public form and a separate regime value in the removal queue.
The operator workspace is /support/operations. Support and legal access are
separate database-checked permissions that can be combined on one account;
administrators have both. Each queue supports search, status/type filtering,
assignee filtering, cursor pagination, and assignment only to an active member
of the corresponding team. Every creation and operator decision records an
audit event. The complete role matrix is documented in
docs/staff-operations.md.
Public routes
/support— ordinary support./account/delete— external account-deletion request path suitable for use as the Google Play account-deletion web resource./legal/content-removal— general electronic notice-and-action intake./legal/take-it-down— dedicated intimate-visual-material removal intake.
The request and Activity screens link to the general form with the exact local content URL prefilled. Only same-origin relative paths can be prefilled this way. The server validates one complete HTTP or HTTPS URL per line and accepts at most 20 locations in one notice.
Contact verification and status access
Public support submissions display a reference but never expose the signed
status token in the redirect. They are stored as pending_verification, are not
visible in the operator queue, and do not produce an operator alert. The private
status link is sent to the contact email. Opening it atomically verifies the
address, changes the request to open, makes it visible to authorised staff,
and enqueues at most one metadata-only operator alert when that optional mode is
enabled. Opening the same link again does not send another alert. An authenticated
submission uses the confirmed account email, is verified immediately, and enters
the queue without this extra step. Ongoing requester messages update the staff
workspace through PubSub and do not generate one operator email per message.
Exact repeats of the same unverified public support submission are represented
by one pending row. Its temporary fingerprint is a SHA-256 digest of normalized
form fields; it is cleared on verification and does not replace the original
record or its audit history. A pending support request or an emailed removal
notice that is still unverified after
PUBLIC_CONTACT_VERIFICATION_MAX_AGE_SECONDS is deleted by the maintenance
worker together with its creation audit entry. This verification-lifecycle
cleanup does not delete confirmed cases. A permitted no-contact report of
sexual material involving a minor has no mailbox to verify, enters the urgent
staff queue immediately, and is therefore outside this unverified-contact
cleanup. Retention or erasure of verified and no-contact case records remains
disabled until an applicable policy is approved.
Email-link verification establishes access to the mailbox, not government identity, authority to act for another person, or the truth of the claim. Operators must perform any additional verification appropriate to the requested action. In particular, an account-deletion request must not be fulfilled against another person's account merely because an address was typed into the form.
Identity and contact fields may be omitted from a general report of sexual material involving a minor. Such a report still receives an urgent queue state, but no status email can be sent without a contact address.
TAKE IT DOWN boundary
The dedicated form accepts exact URLs and textual identification only. It does not accept file uploads and explicitly tells the submitter not to reproduce or email the intimate image or video. A submitted notice is marked for urgent review and records a review due time 48 hours after receipt.
The current product does not accept user-uploaded images or videos. The presence of this preparedness workflow therefore does not claim that Who Need Help is a covered platform, that a submitted item satisfies the statutory definition, or that identical-copy detection exists. If media hosting is added, the operator must review the then-current law, implement safe media hashing and identical-copy handling where applicable, and test the entire removal path before enabling uploads.
The primary US text currently requires a clear and conspicuous process and, for a valid request to a covered platform, removal of the depiction plus reasonable efforts concerning known identical copies as soon as possible and no later than 48 hours:
EU electronic notices
The general form captures the elements listed in Article 16 of Regulation (EU) 2022/2065: a reasoned explanation, exact electronic location, submitter name and email subject to the child-sexual-abuse exception, and a good-faith accuracy statement. It sends an acknowledgement when email is available and sends the recorded decision after a verified-contact operator update.
Product implementation alone does not determine the service's legal classification, establishment, target markets, applicable national law, or redress obligations. Those remain launch decisions requiring jurisdiction- specific review.
Email and operator notification
EMAIL_FROM_ADDRESS remains the outbound sender. Optional
SUPPORT_INBOX_ADDRESS has two separate effects:
- outgoing support/removal messages use it as
Reply-To; - it receives a metadata-only alert containing the reference, queue type, and protected operator URL when an authenticated support case is created or a public support contact is verified. Content-removal notification behavior is kept separate because those notices can require prompt legal or safety review.
All support-contact and content-removal confirmation, receipt, decision, and
optional operator-alert messages run through the dedicated Oban mail queue.
Authentication email remains a separate immediate path. A legal operator's
assignment or other internal-only change does not email the submitter; a public
status or decision-text change does. Queue uniqueness coalesces an undelivered
update to the latest persisted case state instead of sending one message for
each rapid edit.
The alert intentionally excludes the free-text report and reported URLs. The full record remains in the protected database queue. If the variable is empty, intake and reporter acknowledgement still work, but no operator inbox alert is sent. The configured inbox must be monitored operationally; the application cannot prove staffing or response availability.
Optional inbound-email intake
SUPPORT_INBOX_ADDRESS is a reply address and operator-notification target;
by itself it does not make a mailbox capable of receiving mail. An optional
Brevo inbound-parse boundary is available at
POST /webhooks/brevo/inbound-support. It remains disabled unless both
SUPPORT_INBOUND_RECIPIENT and SUPPORT_INBOUND_WEBHOOK_TOKEN are set.
The endpoint accepts only requests carrying the configured Bearer token and
only messages addressed to the configured recipient. It stores the provider
MessageId for idempotency, ignores attachment download tokens, and creates
the same pending_verification support record as the public form. The sender
must follow the existing confirmation link before the case becomes visible to
staff. The provider's From field is therefore never treated as proof that the
sender controls that mailbox.
Brevo requires the receiving domain to differ from the sending domain. Its
documented example uses a dedicated subdomain such as reply.example.com, MX
priority 10 to inbound1.sendinblue.com. and priority 20 to
inbound2.sendinblue.com., followed by a secured inbound webhook. For this
project, do not publish a support address until the exact subdomain, Bearer
header, MX records, provider delivery, confirmation email, and reply workflow
have all passed an external test. Provider setup is an external operation and
is not performed merely by enabling these application settings.
- https://developers.brevo.com/docs/inbound-parse-webhooks
- https://developers.brevo.com/docs/secured-webhooks
Anonymous submissions use two distinct private links. The confirmation link is
valid for PUBLIC_CONTACT_VERIFICATION_MAX_AGE_SECONDS (the initial pilot
policy is 24 hours). Confirming it moves the record into the permission-scoped
staff queue and sends a second status link. The status link lifetime is
PUBLIC_CASE_ACCESS_MAX_AGE_SECONDS (the initial pilot policy is one year).
Expired unconfirmed records are removed by the maintenance worker together with
their creation audit entry; confirmed support and legal records are outside this
cleanup. Each deployment can change both values through its own .env without
rebuilding the release.
Account deletion and data export
The web application and Android WebView expose the account-deletion request from
account settings, and the public /account/delete route remains usable after an
app is uninstalled. The workflow verifies the contact and creates an audited
account-lifecycle request.
An authenticated user can download /users/data-export. The JSON export uses
explicit field allow-lists and includes data the account supplied or generated
through its own use of the product. It excludes password hashes, session and
OAuth tokens, push tokens and Web Push keys, and messages written by the other
participant. The response is an attachment with Cache-Control: no-store.
The restricted support workspace can run a read-only deletion preflight for a verified, account-linked deletion case. It reports active help requests, assignments, tracking sessions, activities/memberships, unresolved reports, and open trust signals. It does not change those records or the support case.
Actual erasure/anonymization remains intentionally non-executable because the operator has not selected a jurisdiction-specific retention policy for safety, fraud, disputes, and legal records. An operator must not mark a request resolved until the applicable approved data action has actually been completed and communicated. Before enabling a destructive executor, legal review must define which linked records are erased, anonymized, or retained and for how long; that executor must then be tested against backups and relational constraints.
Google Play's current policy requires both an in-app path and an external web resource when an app allows account creation:
Abuse controls and operational limits
support_request, support_request_ip, content_removal_notice, and
content_removal_notice_ip are supported names in the shared PostgreSQL rate
limiter. Public support and content-removal intake each check both their
normalized account/email scope and the trusted client-IP scope in one database
statement. This prevents a public submitter from evading the network policy by
rotating contact addresses. The initial pilot policy enables both scope types;
operators can replace the complete map through RATE_LIMIT_POLICIES_JSON, and
an explicit {} is reserved for isolated E2E/load runs. The values are product
policy, not a universal abuse or capacity threshold. CSRF protection,
validation, exact URL
limits, contact verification, staff authorization, and audit events apply
regardless.
The software does not provide emergency response. Threats to life or safety are prioritized in the queue, while every public safety screen continues to direct people in immediate danger to local emergency services.
Reproducible browser verification
The isolated browser topology exercises these queues without using a public deployment or real SMTP provider:
./scripts/e2e-run.sh tests/support-legal.spec.ts
The scenario registers a run-scoped requester, creates an isolated administrator, submits authenticated support and both removal regimes, verifies Mailpit messages, processes each permission-scoped queue, and checks requester-visible status and decision delivery. The wrapper removes only its unique Compose project, networks, database volume, and temporary image on success, failure, or interrupt. This test does not establish staffing, legal classification, or production email delivery.