Connections
The specific places Stream's code actually reaches across product boundaries today, cited to the file.
Boundaries says what Stream doesn't own. This page is narrower: the actual, current places in the code where Stream's own logic reads another product's data. All of them are reads — nothing outside Stream imports any of Stream's own model classes, and no foreign code touches Stream's ten tables directly.
Stream reads Link's shared Media Gallery, by ID
Three call sites — one per scene/overlay surface — resolve a media:<id> reference
against Link's MediaAsset table to render an image or video layer on air:
# app/products/stream/api/admin/stream_ingest.py:488-492
asset = session.scalar(
select(MediaAsset)
.where(MediaAsset.id == int(match.group(1)),
MediaAsset.owner_id == row.owner_id)
.execution_options(skip_tenant=True))The same pattern repeats in stream_scene.py and the public scene_object_public.py —
each one looks the asset up by ID, scoped to the requesting owner, and 404s if it's
missing or belongs to someone else. MediaAsset itself is defined in
app/core/models/engine_presets.py, owned and written by Link's library.py and
background_*.py — Stream never constructs, inserts, or deletes a row there.
No single-item lookup exists yet
Link's own admin API exposes GET /library/media (a full list) but no
GET /library/media/{id}. Stream's three call sites go straight to the ORM instead of
an HTTP call — the only way that changes without adding a new endpoint is if Stream
starts fetching and caching the whole list.
Breaks silently the day Link moves this table to its own schema
Postgres denies cross-schema access by default — there's no gate to "turn on" later,
only the absence of a grant. Today MediaAsset lives in public, readable under
every product's role. The moment Link's own schema-split migration relocates it into
schema link, these three reads start failing permission denied for schema link
for the tease_stream role — not because anything in Stream changed, but because
nobody ever granted tease_stream access to schema link. Fix is a grant in that
direction (GRANT USAGE ON SCHEMA link TO tease_stream plus a table-level SELECT),
applied before or with Link's move — not after it breaks.
Stream reads Account Platform's saved-proxy library, for webcam-platform logins
Connecting a webcam platform (Stripchat, CAM4, BongaCams) through a captured browser session lets the operator route that session through a previously-saved proxy. Three reads, all scoped to the current owner:
# app/products/stream/api/admin/stream_login.py:98
row = session.get(SavedProxy, int(body.savedProxyId))
...
# :174 — same table, resolving a proxy's display name for the login-status card
row = session.get(SavedProxy, int(saved_id))
...
# :178 — the *managed* proxy backing it, for its rotation/location fields
select(ManagedProxy).where(ManagedProxy.saved_proxy_id == int(saved_id))SavedProxy and ManagedProxy are Account Platform's proxy-fleet models. As with the
media gallery, only a list endpoint exists on that side today (GET /saved-proxies,
GET /managed-proxies) — no single-item lookup — so Stream reads the tables directly
rather than calling out.
Same schema-move trap as MediaAsset
These three reads are just as exposed: when Account Platform's own schema-split
migration relocates SavedProxy/ManagedProxy into schema platform, tease_stream
needs an explicit grant on that schema first, or the webcam-platform login flow starts
failing permission denied with no code change on Stream's side. See the identical
note on MediaAsset above — same mechanism, same fix direction.
Stream mirrors Link's landing-page object contract, through one adapter
Scene layers that show landing-page content (a teaser tile, a content deck, a notification banner, the sticky-button widget) reuse Link's own object validators and style rules, so what's built in the landing constructor and what's drawn on air can never silently disagree. The reuse is funneled through exactly one file:
# app/products/stream/core/landing_contract.py
_EXPORTS = frozenset({
"DEFAULT_FONT", "LANDING_FONTS", "LandingUiError",
"STACK_MAX_CARDS", "STACK_MAX_LOCK_LABEL", "STACK_MAX_SUBTITLE", "STACK_MAX_TITLE",
"is_stack_src", "validate_notif_style", "validate_objects", "validate_sticky",
})No other file anywhere in Stream's core/ is allowed to import Link's landing_ui
directly — an import-linter contract (Stream не ходит в core Link мимо адаптера)
enforces it, and the enforcement is real: it's caught a would-be direct import during
this section's own development. Adding a name Stream doesn't yet use means adding it
here first, not at the call site — the whole debt to Link stays visible in one grep.
Nothing reads Stream's own tables from outside
Checked in both directions: no file outside app/products/stream/ imports any of
Stream's ten model classes, and no ForeignKey anywhere in the codebase points at one
of Stream's tables (or vice versa). The only non-ORM exception is a one-shot legacy
SQLite import script that inspects stream_motors' columns by raw PRAGMA — an
offline migration tool an operator runs by hand, not live traffic.
What's next
Admin endpoints — every one of Stream's own internal endpoints: path, method, what it does, and whether it's owner-only.