mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 15:31:38 +02:00
A targeted tip is addressed to a Smirk handle rather than to whoever holds a share URL. The v3 port shipped public-only and the schema said so: there was no recipient column anywhere, so create_social_tip rejected is_public=false outright and the two inbox endpoints returned an empty list unconditionally. This is the missing half. The migration adds recipient_user_id plus two CHECKs that keep each kind of tip well formed: a targeted tip must name its recipient, and must carry the ciphertext, because with no URL fragment that ciphertext is the only way to claim it. Without the second one a draft could be created that looks healthy and is permanently unclaimable. Both are satisfied by every existing row, which is public, so nothing is backfilled or rewritten. is_public is derived from the recipient in the INSERT rather than passed alongside it. Two independent sources for "who may claim this" is how a tip ends up addressed to one person and claimable by anyone. Claiming was already guarded by is_public = TRUE, so no targeted tip could be claimed at all; that becomes "public, or the caller is the recipient". The claimable query repeats exactly the conditions the claim call enforces, so the inbox cannot show a Claim button the backend then refuses. It is off by default and separately from FEATURE_TIPS, because it is a distinct surface: an inbox, and a claim authorised by identity rather than by holding a secret. It also needs the migration to have run, so an instance must opt in rather than acquire it by upgrading. The capability is advertised only when tipping is also on, since a targeted tip is still a tip, and recipient resolution checks the flag before anything else: a backend that cannot serve this must refuse, never quietly fall back to creating a public tip from a targeted request, which would hand a tip meant for one person to anyone with the URL. The config fixture moves to Config::test_default so a test of what a flag does can live next to the flag.