docs(stalker): reconcile the Stalker docs after the API-compatibility series (#1375)

Nine PRs landed between 2026-08-01 and 2026-08-04 in parallel worktrees, each
editing its own section of docs/architecture/stalker-portal.md and CLAUDE.md.
Sections that were correct when written disagreed with each other, or with
master, afterwards. Every claim here was verified against the code.

Corrected in stalker-portal.md: routes listed without the /workspace prefix;
"simple portals carry only the mac= cookie" (every request goes through the
shared identity builder — but the direct branch forwards no serial, so no
SN/__cfduid either, while playback headers are NOT mode-gated); a facade
introduced as "three modules" above a list of five; the pre-#1370 "blank
fields are not generated" opening; an ambiguous stalker-identity.utils.ts
citation (two files share the name); two of the three surfaces that apply the
scoped header override; a bare {status: 1} now being a refusal; and the
session-state fields #1354 added to the backup exclusion list (mirrored in
playlist-backup-restore.md).

CLAUDE.md had no entry at all for portal mode / endpoint discovery / lazy
repair — the largest change of the series; added one. Its session-facade list
was missing two modules and status 1 still read as plain "blocked".

Mock server: documented the /stalker, /stream/gated and marketing-poster
routes and the HOST variable; replaced the global POST /reset guidance with
the real per-MAC isolation contract (OWNED_MACS, the sibling 00:1A:79:5F:*
range, mode: 'serial'); added get_main_info; refreshed the project tree; fixed
a broken anchor; and corrected MOCK_PORT, which moves the client side only —
nothing maps it to the server's PORT.

The repo skill's "keep Stalker request rules in Stalker data access" no longer
holds: the wire-format, identity, portal-mode and auth-failure contracts live
in shared/interfaces because the Electron main process cannot import renderer
libs.

Also fixes four stale code comments carrying the same claims, including
"Single choke point for Stalker API calls" — four callers deliberately go
direct, and only fetchViaProfile() wires repair itself.

Docs and comments only; no executable change. No release note (no user-visible
behavior); no-release-note label applied for the libs/** paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 authored and GitHub committed 2026-08-06 17:49:49 +02:00
1 parent 53c318bac7
commit 5e4f2ca3dd
11 files changed
+319 -75

No files matched your search

@@ -52,8 +52,16 @@ export const STALKER_SERIAL_NUMBER = LEGACY_DEFAULT_STALKER_SERIAL;
/**
* Service to manage Stalker portal session tokens.
* Handles handshake authentication for full stalker portals (/stalker_portal/c URLs).
* Persists tokens during session and handles re-authentication on auth failures.
*
* Handles handshake authentication for playlists in FULL portal mode. Mode is
* a persisted, behavior-observed fact read through
* `isFullStalkerPortalPlaylist()` — never a URL substring: since endpoint
* discovery, a token-enforcing `portal.php` panel is a full portal and a
* `server/load.php` endpoint that answers without a token is a simple one.
*
* Tokens are cached in-run and written back to the playlist row, so a session
* survives a restart; both are tagged with the identity fingerprint they were
* negotiated for. Re-authenticates on auth failures.
*/
@Injectable({
providedIn: 'root',
@@ -123,13 +123,36 @@ async function dispatchStalkerRequest<T>(
}
/**
* Single choke point for Stalker API calls. On top of the mode routing it
* hooks the lazy portal repair: when a request fails in a way only a wrong
* persisted endpoint/mode produces (plain-text `Authorization failed.`
* bodies on token-less requests, HTTP 404 on a vanished endpoint, terminal
* handshake failures), the repair service re-probes the portal once per
* session and — only when a different configuration is PROVEN to work —
* the request is retried against it. Healthy portals never probe.
* Choke point for Stalker catalog, content and playback calls. On top of the
* mode routing it hooks the lazy portal repair: when a request fails in a way
* only a wrong persisted endpoint/mode produces (plain-text
* `Authorization failed.` bodies on token-less requests, HTTP 404 on a
* vanished endpoint, terminal handshake failures), the repair service
* re-probes the portal once per session and — only when a different
* configuration is PROVEN to work — the request is retried against it.
* Healthy portals never probe.
*
* Four callers deliberately issue `STALKER_REQUEST` themselves, because each
* runs BELOW or BEFORE what this routes on:
*
* - `StalkerAuthApi` — `handshake`/`get_profile`/`do_auth` are what the
* full-portal branch here is implemented in terms of, so routing them back
* through it would recurse.
* - `StalkerPortalDiscoveryService` — probes precede the mode they determine.
* - `StalkerAccountInfoService.fetchViaProfile()` — a profile request, so it
* takes the same exemption as the auth layer.
* - `StreamResolverService`, for a collection item carrying its own portal
* coordinates with no playlist row — there is no meta to route or repair
* with. Its row-backed branch does come through here.
*
* The exemption is from the ROUTING, not from the repair hooked above — but
* only `fetchViaProfile()` wires `StalkerPortalRepairService` itself.
* Discovery is what repair drives, the row-less branch has no playlist to
* repair, and the auth layer needs nothing: a terminal handshake failure
* propagates out of the full-portal branch below and is caught here, which is
* why it is one of the triggers listed above.
*
* Anything new that is not auth or discovery belongs here.
*/
export async function executeStalkerRequest<T>(
deps: StalkerRequestDeps,
@@ -19,9 +19,10 @@ function getHeaderValue(
* Single owner of the scoped Electron request-header override for built-in
* playback. There is exactly one scoped override slot in the main process, so
* every surface that plays a stream inline goes through this service:
* `WebPlayerViewComponent` for the web video players, and the Stalker live
* layout for the dedicated radio audio player, which never mounts a
* `WebPlayerViewComponent` at all.
* `WebPlayerViewComponent` for the web video players, plus the two surfaces
* that render `AudioPlayerComponent` for radio and therefore never mount a
* `WebPlayerViewComponent` at all — the Stalker live layout, and the unified
* live tab behind the Favorites / Recently Viewed collection routes.
*
* `apply()` extracts the full header set from the resolved playback —
* including the portal Cookie/Authorization that auth-gated streams require —