TEASEDocs
ProductsStream

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.

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.

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.

On this page