ProductsLink
Connections
What Link gives its neighbors and what it takes from them — contracts, endpoints, and events that exist in code today.
Only what's backed by real code — no roadmap items. Each row names the file that proves it.
What Link gives
| To | What | Where |
|---|---|---|
| Club | Click and Impression rows — the only writer is Link's click/impression logger; ~40 Club files read them for dashboards, attribution and A/B reporting. | products/link/core/click/log.py, impression_log.py |
| Club | tg_bot_starts rows (Telegram user → click id, routed destination, rule, language) — the exact-match join key Club's Telegram attribution resolves fan origin against. | core/tg/bot.py (store_start) |
| Club | The domain-rename cascade also updates Club's own tables (fan_source, fan_link, traffer_payout, of_campaigns) so a renamed domain doesn't orphan Club's records. | products/link/api/admin/domain_rename_storage.py |
| Platform | Domain.creator_id / hostname, read directly for RBAC, owner search and diagnostics. | core/rbac.py (creator_id_for_domain), owner/diagnostics.py, owner/search.py |
| All products | DomainMigrationMiddleware, mounted globally — any GET/HEAD hitting a renamed host gets redirected regardless of which product's route it targets. | app/main.py |
| Club | fetch_spend_by_key / apply_link_cost (per-link ad spend) — Club's smart-link revenue view adds this to compute ROI without owning spend data itself. | products/link/core/link/spend.py |
What Link takes
| From | What | Where |
|---|---|---|
| Club | Writes directly into Link's SegmentLink table when "dressing" a free-pool spare, bypassing Link's own inventory endpoint. | core/free_pool.py (dress_spare) |
| Club | Computes the money side of the smart-link vitrina/funnel views that Link's HTTP layer exposes — Link's endpoint is a thin wrapper. | core/smartlink_vitrina.py, core/smartlink_funnel.py |
| Club | Writes both Link's unassigned_links and Club's own fan_source canon in a single transaction. No written contract governs this coupling today — treat it as a live but undocumented seam. | scripts/poll_source_links/writes.py |
| Club | Owns the /sources screen end to end (OfCampaign, fan attribution, the transaction join); Link's only presence there is the source_link rule table. | api/admin/source_layout.py |
| Platform | require_workspace_owner / team-scope gates on every panel route; platform_home_owner_id for the env→row fallback in the Telegram gate. | products/link/api/admin/tg_turnstile.py |
| Platform (billing) | Domain purchase creates an invoice and waits for a production webhook to call activate_domain_purchase — not yet wired end to end. | products/link/core/domains/purchase.py |
| Shared core | human_clicks() (frozen bot-exclusion predicate) and the generic bot/datacenter/geo utilities. | core/click/filters.py, core/bot.py, core/dc_ranges.py, core/geo |
Live, undocumented seams
Two connections above exist in code but have no written contract anywhere: the free-pool write
into SegmentLink, and the source-poller's single transaction across unassigned_links and
fan_source. Both are real and both work today — they're listed here as-is, not as an
endorsement of the pattern.
What's next
See Boundaries for the reverse view — what Link deliberately leaves to a neighbor.