Skip to content

Tier 2: streamed TA-bot delivery endpoint (team-bound links + fetch budget) #171

Description

@razam-sherwani

Dependency: READY — start now. No open blockers. Blocks cs3600_2026#42 (the T2 bundle loader can't be built until this serves bots). Reuses existing storage/HMAC/auth machinery; the new parts are the artifact entity, team-bound links, and the fetch-budget ledger.

Tier 2 — streamed TA-bot delivery

The delivery half of the three-tier architecture (OBFUSCATION_PITCH.md §5 B / §6 Increment 1b). An authenticated, rate-limited endpoint that serves a pre-built compiled TA-bot to a student per session, team-bound and metered, so practice runs on the student's machine (zero server compute) without leaving a permanent copy on disk.

Framing — read before building: this tier is deterrence + toil reduction + metered acquisition, not protection. Code that runs in a process the student controls is recoverable regardless of how it was delivered (debugger, or an AI behavioural-cloning loop against PlayerAgent.play). The protection guarantee comes only from the T3 server-side tier (#160). Do not describe this endpoint as protecting strategy.

Build (nothing here exists to reuse as-is)

  1. TA-bot artifact entity + migration — competition-scoped, admin-published, versioned, owning a FileRecord by FK. Model it on GameMatchFile (that is where visibility/authz live; FileRecord is a dumb blob row). No artifact/release concept exists in the codebase today.
  2. Team-bound signed links. HmacService.sign currently signs only "GET\n"+uuid+"\n"+exp — a transferable bearer token — and /files/** is permitAll with no download accounting. Extend the signed payload with the team UUID and serve T2 artifacts through a dedicated authenticated route that can count downloads, rather than the anonymous /files/**.
  3. Per-team-per-window fetch budget. No such table or pattern exists (the only limiter is the in-flight concurrency gauge in PrivateGameMatchController). Use the same atomic upsert as the T3 scrim budget:
    INSERT INTO ta_bot_fetch_usage (team_id, artifact, window_start, count) VALUES (…, 1)
    ON CONFLICT (team_id, artifact, window_start)
    DO UPDATE SET count = ta_bot_fetch_usage.count + 1 WHERE ta_bot_fetch_usage.count < :cap
    RETURNING count;
    Return Retry-After on rejection; the row doubles as the attribution ledger (§8) and telemetry.
  4. Config for which bots are T2-eligible per competition — Competition.settings jsonb or an entity flag.

Reuse

LocalStorageService.store(compress=true, maxBytes) + getDownloadLink; StorageController byte-serving (note: no HTTP Range support today — for multi-MB over flaky networks consider UrlResource via loadAsResource); @AuthenticationPrincipal User + playerService.getPlayer + teamService.isMember + 403/404 idiom.

No turnstile

Consistent with #157 removing it from the sibling match endpoint; the fetch budget is the abuse control.

CI feed

CI already builds per-platform artifacts (~0.3–1 MB each). Start with manual admin upload into the artifact entity (same flow as submissions); automate later.

Blocks the bundle-side loader (cs3600_2026). Depends on nothing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    High PriorityenhancementNew feature or requestreadyNo open blockers - can be started now

    Type

    No type

    Fields

    Stage

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions