# Blaze Handshake — Reference & Milestone Map This is the reference for the **blaze_brain** arc: emulating the EA Blaze backend well enough for FIFA 23 FUT to get past *"FUT is connecting to the EA Servers…"*. It replaces the earlier in-process *forcing* arc, which concluded that the game's online dispatch scaffold cannot be filled by forcing local state — it fills only when a real Blaze exchange happens. Every claim below is tagged so that on a re-read you can see instantly what is grounded in our own reverse engineering versus what is general public knowledge of Blaze's protocol shape versus what still needs a capture: - **CONFIRMED** — established by our own RE in this project (live memory reads, objdump, hook logs). - **SHAPE** — the general shape of EA's Blaze protocol from public community knowledge. The *ordering* and *purpose* are reliable; exact numeric IDs and field tags for FIFA 23's specific Blaze version are **not** implied by a SHAPE tag. - **UNKNOWN** — must be captured from the live client before it can be implemented. > Clean-room discipline: nothing here derives from leaked EA source. The Blaze > protocol shape is reconstructed from public community reverse-engineering of EA > titles and our own observations of this client. --- ## The three layers that all say "connecting to EA" "Connecting to EA" is not one thing. Three distinct systems wear that label, stacked on top of each other, and it is easy to confuse a stall in one for a stall in another. Understanding which layer the FUT spinner belongs to is the whole point of this document. **Layer 0 — LSX (Local Socket eXchange).** `CONFIRMED working.` This is the Origin/EA-App desktop-client local API: entitlements, the local login handshake, and `GetAuthCode`. It is *not* Blaze — it is a localhost protocol the game uses to talk to whatever is standing in for the EA App. Our native bridge answers it on ports 3216/3217, and answering it is what got the client to the main menu with a working first-party login. Layer 0's output that matters to Blaze is a **Nucleus auth code / access token**: the credential the client will later present to the Blaze authentication component. **Layer 1 — the Blaze core session.** *This is the wall.* Blaze is EA's online backend framework. Reaching it is a multi-step handshake (detailed below) that starts by asking a *redirector* where the real server is, connecting there over TLS, then negotiating configuration, authenticating with the Layer-0 auth code, and finally bringing the session fully online. Until this completes, nothing above it can work. Everything we have observed says the client currently never even *starts* this — see the transport finding below. **Layer 2 — FUT / UTAS.** `SHAPE.` FIFA Ultimate Team specifically is served by the EASFC Blaze component *and* by UTAS, a separate HTTPS REST service (`utas.*.fut.ea.com`). Once a Blaze session exists, the FUT client authenticates to UTAS (`POST /ut/auth`) with the session credential to obtain a FUT session id, and only then does it load squad/club data. The FUT spinner in the screenshot lives at the *top* of this stack, but it cannot clear until Layer 1 is answered — so "minimal Blaze handshake" means **Layer 1**, and Layer 2 is a second, HTTP-shaped project that comes after. --- ## Where the client currently stalls — the transport finding `CONFIRMED.` Across a full menu-plus-FUT-attempt session we observed **zero `getaddrinfo` calls and zero outbound sockets**, and — via the `ELEM_WATCH` probe — the game's per-connection message-handler dispatch container at `element[0]+0x40` stayed a clean, empty, default-constructed vector the entire time, including while the FUT spinner was on screen. `connectState.ctor` fired (the client builds connect-state objects) but registered nothing into that container. The mechanical reading: **that container is populated by an actual Blaze message exchange** — handlers register as component notifications arrive during session bring-up (Layer-1 step 6 below). No Blaze reply → no handler registration → empty container → spinner spins forever. The spinner *is* the Layer-1 wall. This leaves two possible transport situations, and **Milestone 0 exists solely to decide which one we are in** (see the Milestones section): - **(a)** the client expects Blaze on a hardcoded local endpoint and would dial it if a listener were there, or - **(b)** the client only dials the Blaze redirector after an in-process bootstrap (the dial handler `0x144f4d360` `CONFIRMED`) fires — in which case the transport wall and the bootstrap wall are the same wall. --- ## Layer 1 — the minimal Blaze handshake ordering Blaze is a framed binary protocol. Each message is a header — length, component id, command id, message type (request / response / notification / error), error code, and a message/sequence id — followed by a **TDF**-encoded body (EA's tag/type/value binary serialization). `SHAPE.` The exact numeric component/command ids and TDF field tags for FIFA 23's Blaze version are **UNKNOWN** until captured. These are the six steps a responder must satisfy, **in order**, to bring a session online: 1. **`Redirector::getServerInstance`** `SHAPE` — the client asks the redirector for the address of the real Blaze server, naming the FIFA 23 service/SKU. The responder returns a `ServerInstanceInfo` TDF containing a **host:port** (point it at ourselves) plus an SSL flag. Without a valid address the client has nowhere to go. 2. **connect + TLS** `CONFIRMED (bypass)` — the client opens a TLS connection to the returned address. Our ProtoSSL/cert-verify bypass is already in place, so our listener can terminate TLS. 3. **`Util::preAuth`** `SHAPE` — the client sends its config/version/locale; the responder returns server config: the **component list**, ping-site list, telemetry settings (disable), and a misc config bundle. The client uses the component list to know what exists; an empty or malformed response here stalls it. 4. **`Authentication::login`** `CONFIRMED (GetAuthCode feeds here)` + `SHAPE` — the client authenticates, presenting the **Nucleus auth code from Layer 0** (SSO). The responder returns the session: **BlazeId, session key, persona, entitlements**. A failure here is what surfaces as *"EA Servers are down."* 5. **`Util::postAuth`** `SHAPE` — after auth, the client finalizes the session; the responder returns post-auth config (UserManager, association lists, telemetry, ticker/PIN). 6. **`UserSessions::updateNetworkInfo` / `updateHardwareFlags`, then a `UserSessionExtendedDataUpdate` notification** `CONFIRMED (container is Blaze-downstream)` + `SHAPE (which command)` — the client publishes its network/ hardware info and the server pushes extended session data. **The client flips to "online" on receipt of the extended-data notification** — and this is the exact moment the `element[0]+0x40` container we watched would begin filling, as handlers register for the arriving component notifications. At step 6 the top-level "connected to EA" clears, and Layer 2 (UTAS) can begin. ### Why step 6 is the Milestone-1 success signal The container-fill and the "online" flip are the *same event*: handlers register because Blaze notifications arrived. That gives blaze_brain a precise, already-built success detector. The first real win is **not** "full FUT works" — it is a single line in the hook log: ``` ELEM_WATCH: [elem+0x40] CHANGED! … ``` That line means the client accepted our Blaze replies and started registering handlers — i.e. steps 1–6 were convincing enough to bring the session online. The `ELEM_WATCH` probe that emits it is already written and deployed; it is our instrumentation for blaze_brain regardless of anything else. --- ## Milestones - **M0 — transport reachability (this task).** Read-only. Confirm whether the client attempts *any* Blaze-flavored transport (situation a) or none at all (situation b). Gates every decision below. See the M0 section in the hook docs / launch notes. - **M1 — minimal session bring-up.** Stand up a listener at the M0-observed endpoint; answer `getServerInstance` (redirect to ourselves), then `preAuth` / `login` / `postAuth` with minimal valid TDFs and a single hardcoded persona. **Success signal: `ELEM_WATCH: CHANGED` fires and the spinner advances.** - **M2 — session online.** Handle the step-6 session notifications until the top-level "connected to EA" state is reached and stays. - **M3 — FUT / UTAS.** Implement the UTAS REST layer (`POST /ut/auth` and the `/ut/game/...` endpoints already sketched in `endpoint-map.md`) so FUT itself loads. M1 is only reachable if M0 says the client actually dials a listener. If M0 comes back "no traffic" (situation b), M1 in its "answer over a wire" shape is blocked until the dial trigger is solved, and the strategic options are re-opened rather than a next task being obvious. --- ## Honest UNKNOWNs (capture before implementing) - **Numeric component and command ids** for FIFA 23's Blaze version. The *names* and *ordering* above are `SHAPE`-reliable; the wire ids are `UNKNOWN`. - **TDF field tags and body layouts** for each request/response. `UNKNOWN` — every response body must be shaped from a real capture (or careful trial against the client's parser). - **The redirector/Blaze transport itself** — host, port, and whether it is remote, loopback, or a pipe. This is exactly M0's question and is `UNKNOWN` until M0 runs. - **Whether the client dials at all** without the in-process bootstrap firing — the situation (a) vs (b) question. `UNKNOWN` until M0. Until M0 answers the transport question, the ordering above is designable but untestable: there is nothing to answer *to*.