63f02c4fb1
Record the staged retail A/B: FIFA consumed the Rust squad path end-to-end
(userMassInfo overlay, in-game swap -> squad-replace {"id":0}, formation
f442->f433 persisted to Core, cold relaunch returned the persisted squad),
with Python rollback / Rust re-enable proven by host-log presence. Note the
OPENFUT_DEV_CONTENT_GAMES=fifa17 startup requirement (silent empty /collection
if omitted) as a needed deployment/preflight assertion.
167 lines
13 KiB
Markdown
167 lines
13 KiB
Markdown
# OpenFUT — Project State
|
||
|
||
> **Canonical source:** `../OpenFUT-Vault/06 Agent Memory/Project State.md`
|
||
> This file is a mirror. If the two disagree, the vault wins. Update the vault first.
|
||
|
||
Factual snapshot. Prefer this over the stale root `README.md`/`CLAUDE.md` status tables (FIFA 23).
|
||
Last compiled from repository evidence during context initialization.
|
||
|
||
## Working
|
||
|
||
- **FIFA 17 offline FUT stack, end-to-end.** Proven 2026-08-01: auth → Blaze login → device-trust →
|
||
the FUT hub. Brought up by `fifa17-recon/tools/openfut-fut.sh start`. Evidence: `FUT-RUNBOOK.md`,
|
||
`fifa17-recon/README.md`, the five responder scripts, gate-ladder troubleshooting table.
|
||
- **ProtoSSL cert-pin defeat** — two live `/proc/PID/mem` patches (`autopatch.py`), VAs stable
|
||
across launches. gdb-verified which gate was the wall.
|
||
- **LSX / Origin layer** — crypto handshake reversed byte-exact and confirmed against the repack's
|
||
own emu disassembly (`docs/REPACK_INTEL.md`); Origin login gates cleared.
|
||
- **Blaze redirector + Fire2/Heat2** — both hops defeated; preAuth/login/personas answered.
|
||
- **UTAS/RS4 FUT API** — `ut/auth` + boot calls + device-trust reach the hub with hand-authored JSON.
|
||
- **Persistent FIFA 17 account selection** — the launcher synchronizes one configured EA persona to
|
||
the Python backend before starting LSX/FIFA. LSX, Blaze, POW/EASFC, and UTAS then share that
|
||
identity, while FUT coins, inventory, squads, progression, and unopened packs persist in an
|
||
isolated save beneath `fifa17-recon/docker/state/accounts/<persona-id>/`. The POW level/XP/funds
|
||
shown in FIFA's general account bar are account-scoped but remain distinct from FUT club coins.
|
||
A reversible server test on 2026-08-09 verified profile switching, POW values, a 400-coin pack
|
||
debit, five awarded items, and restoration of the original profile.
|
||
- **Account-scoped FUT security compatibility** — launcher account synchronization initializes a
|
||
persisted `securityQuestion` verification record in that persona's FIFA 17 profile. The UTAS
|
||
PHISHING handler returns the complete CardsDLL trusted-console response (`changed`, `exists`,
|
||
`locked`, `trusted`), accepts only well-formed legacy setup/validate requests under `X-UT-SID`,
|
||
and never stores or logs the client-transformed answer. This is server-side emulation; the hook
|
||
and launcher do not contain an answer or add UI automation. Automated contract coverage is in
|
||
`fifa17-recon/docker/ctx/tools/test_security_question.py`; live first/repeat-launch acceptance is
|
||
partially complete: the first launch entered FUT without a security dialog on 2026-08-09; a
|
||
second fresh-process FUT entry is still required to close persistence acceptance.
|
||
- **Safe responder diagnostics** — ordinary LSX logs redact challenge/session/auth-code attributes;
|
||
ordinary Blaze logs redact auth/session keys and no longer emit raw Fire2 hex, decoded TDF, or
|
||
config values. Forensic Blaze capture remains available only with the explicit
|
||
`OPENFUT_BLAZE_DUMP_FRAMES=1` opt-in. LSX and Blaze self-tests cover the new defaults.
|
||
- **OpenFUT Core** — Rust FUT economy backend, feature-complete for its scope and tested: profiles,
|
||
clubs, coins, packs, cards, squads, chemistry styles, SBCs, objectives, matches, market (NPC),
|
||
draft, FUT Champs, seasons, statistics, achievements, events, daily check-in, division
|
||
leaderboard, market trade history. 13 migrations. Integration suite (`tests/integration_test.rs`,
|
||
96 test fns) runs against in-memory SQLite; CI (fmt/clippy/build/test) green on `openfut-core`.
|
||
|
||
## Partially implemented
|
||
|
||
- **FIFA 17 FUT hub depth** — reaching the hub is proven, but how much of FUT is fully navigable
|
||
beyond it (playing matches, pack opening, SBC submission through the *game* UI vs. spinner/error
|
||
states) is not documented as complete. The runbook's gate ladder lists failure modes still
|
||
guarded against. Treat "past the hub" as unverified.
|
||
- **Pack opening through the game UI** — proven live on 2026-08-09 with the recovered CAGE test
|
||
profile: purchase, reveal, item assignment/quick-sell, wallet refresh, and return from the reveal
|
||
all completed. The Python transaction path also passes its 446-check contract suite. FIFA's
|
||
hardcoded post-reveal `mypacks` return is supported by a short-lived active grace record for every
|
||
opened pack; it is excluded from unopened-pack counts and retired at the next hub request.
|
||
- **FIFA 17 FUT match lifecycle** — CardsDLL static analysis and isolated responder tests now cover
|
||
CREATE→READY→PLAY→END. Bare `/match` requests carrying body `matchId` are classified as PLAY
|
||
instead of accidentally allocating another match; READY returns the verified scalar `matchId`
|
||
and `opponentPersonaId` fields; END persists W/D/L, matches played, and coin rewards per account.
|
||
The implementation is deployed and `test_match_lifecycle.py` passes, but no football match has
|
||
started or completed in FIFA yet. The READY opponent `items` contract and client mode-entry gate
|
||
remain unresolved; `FUT_MODES` therefore stays off by default.
|
||
- **Core ↔ emulation integration** — the two halves exist and wiring has **started**. First
|
||
slice (2026-08-11): the My Squad owned-player search. `openfut-core` gained a semantic,
|
||
game-independent owned-inventory query (`services::inventory::{OwnedItemQuery, apply_query}`
|
||
+ a `Quality` tier) that filters (AND) → orders deterministically → paginates, wired into
|
||
`GET /collection`; `openfut-adapter-fifa17::fut::owned_query` parses the FIFA17 `/club` wire
|
||
query and resolves numeric league/nation/team ids → semantic names (unknown id = hard error,
|
||
no raw-id passthrough). Intentional fix, not parity: Python applies only `league`+`team` and
|
||
ignores `level`/`rare`/`position`/`nation`/`start`/`count` (the request-amplification bug);
|
||
Core applies all proven filters and paginates. `rare=SP` semantics UNKNOWN, unimplemented.
|
||
Slice 2 (2026-08-11): `openfut-utas-host` — the first live UTAS host. Serves `GET …/club`
|
||
from Core through the adapter and reverse-proxies every other UTAS route verbatim to the
|
||
Python oracle (`utas_server.py`); plaintext HTTP/1.1 keep-alive, classify-before-execute,
|
||
no python-fallback after a Core error. `CoreAccess` is a host-owned boundary (the adapter
|
||
stays transport-agnostic). 11 host + 22 adapter tests; 10/10 mutations killed; fmt/clippy
|
||
clean. Slice 3 (2026-08-11, `3ef3bc3`): the real `Fifa17IdentityResolver` — catalog
|
||
(card id → real asset id) + persistent `openfut-identity` store (owned instance →
|
||
stable/reversible wire int) + wire-id policy, replacing all placeholders (one
|
||
production path). Wire-id namespace is globally monotonic within `(fifa17, owned-item)`,
|
||
not per-account (Core owned ids are UUIDs → unambiguous reverse). Slice 4 (2026-08-11,
|
||
core `36abd4b`): a curated 32-card real FIFA17 dev content pack
|
||
(`data/games/fifa17/dev/cards.json`, ids `fifa17_<asset>`), loaded only via opt-in
|
||
`Config.dev_content_games`; `seed-dev` grants a `game_id=fifa17` profile+club real
|
||
`OwnedCard`s (no FIFA wire ids — the resolver mints those at request time), idempotent,
|
||
default content untouched. Slice 5 (2026-08-11, `5276dd2`): the host sends
|
||
`X-OpenFUT-Game: fifa17` so `/club` resolves the all-mapped fifa17 profile —
|
||
**composition proven live** over HTTP (real Core+host, no FIFA client): FIFA wire query
|
||
→ real `resourceId`s (catalog) + stable/reversible wire `id`s (store); 33 renderable,
|
||
gold=22, Premier League=18, pagination page1=11/page2=7/overlap=0 (clean paging, no
|
||
drops). **The only remaining gate is the live retail FIFA A/B** (no FIFA client in the
|
||
build env; runbook `openfut-utas-host/README.md`). See the vault UTAS Endpoint Map +
|
||
Known Issues (incl. "identity resolution is NOT authorization" for later mutations).
|
||
**Slice 6 (2026-08-11): `/club` RUNTIME VALIDATED on retail FIFA 17** — operator-assisted
|
||
live A/B (`.105` client → `.120` backend via a source-scoped NAT redirect into a staged
|
||
`36abd4b` Core + `openfut-utas-host`). Every checkpoint passed on the real client:
|
||
transport, per-route Python fallback, Python-negative `/club`, Core `X-OpenFUT-Game`
|
||
scoping, real card + persistent owned-item identity (incl. the two-copy fixture),
|
||
no-filter/Gold/position/nation/league/team/combined-AND filtering, retail pagination
|
||
with no amplification, card selection, identity stability across relaunch, Python
|
||
rollback, and Rust re-enable (identity store byte-identical across the cycle, 0
|
||
reallocation). Live findings: the retail client steps `start += 10` with `count=11`
|
||
(sometimes bulk `count=100`) — Core honours `offset` and terminates; the My Club UI
|
||
nests team under league; the "~1900" club counter is Python `userMassInfo`, not `/club`.
|
||
Remaining is operational only (promote the staged stack to a durable deployment).
|
||
- **Slice 7 (2026-08-12): FUT squad authority (read + write) RUNTIME VALIDATED on retail FIFA 17.**
|
||
Staged operator-assisted A/B (`.105` retail client → `.120`, source-scoped utas switch
|
||
`:8099→:8199` into `openfut-utas-host` over a staged `615c5fd` Core seeded by `seed-dev` with the
|
||
dev 33-card inventory). FIFA itself consumed the Rust squad path end-to-end: FUT boot served
|
||
`RUST_OVERLAY userMassInfo` (squad overlay) and the squad screen rendered the dev XI; a controlled
|
||
in-game squad edit issued `PUT …/squad/0` → host `squad-replace` → Core → `{"id":0}`; an in-game
|
||
formation change f442→f433 persisted to Core (canonical fingerprint changed, `position_index`
|
||
remapped 0–10); a FULL FIFA relaunch cold-fetched and rendered the persisted f433 squad (no client
|
||
cache). Reversibility proven on the exact client path by log presence, not response data (both
|
||
backends coincidentally hold the same dev squad — the Python oracle `fifa17_profile.json` was
|
||
seeded from the same `squad_put_f442.json` capture): disarmed `.105:8099` reached Python (absent
|
||
from host log), re-armed reached Rust (`route=club limit=Some(9)` present); the persistent identity
|
||
store survived the cycle with 0 reallocation. Non-migrated routes (`/ut/auth`, `account/sync`,
|
||
`accountinfo`, `settings`, `hub`, store txn) correctly Python-fallback. NEW startup requirement:
|
||
the Core server loads dev card defs only for games in env `OPENFUT_DEV_CONTENT_GAMES` (comma-sep);
|
||
omitting `fifa17` makes `get_collection` silently drop every owned card (empty `/collection`,
|
||
"no squad") despite a successful seed — MUST become a deployment/preflight assertion. Preceded the
|
||
same day by a staged two-process parity gate (real Core+host over HTTP, no FIFA) confirming byte-
|
||
shape parity of `userMassInfo.squad` vs the captured oracle. Next milestone: real-data Core
|
||
import / profile strategy → production Rust UTAS. Blaze deferred.
|
||
|
||
## Stubbed / planned
|
||
|
||
- **`fifa-blaze`** (Rust) — Milestone 1 capture stub only. Two TLS listeners that log packets; no
|
||
FIFA 23 component/command handlers. Its own README says IDs are unknown. Superseded in practice by
|
||
the Python FIFA 17 responders, kept as the intended FIFA 23 implementation surface.
|
||
- **`openfut-launcher` legacy controls** — core/bridge and FIFA 23 setup controls belong to a
|
||
superseded plan. The launcher now also owns the live FIFA 17 client flow: server/hook config,
|
||
account synchronization, local LSX, privileged autopatch, and game launch.
|
||
- **`tools/`** (file-watch-diff, squad-injector, exporters) — helpers for the FLE-Lua-bridge idea in
|
||
`docs/direction.md`. Not part of the live FIFA 17 path.
|
||
- **`docs/foundational-xi-injection-test.md`** — a planned (not executed) test procedure for the FLE
|
||
bridge route.
|
||
|
||
## Stubbed / blocked (FIFA 23 lineage)
|
||
|
||
- **`openfut-bridge`** — in-process `version.dll` hook on ProtoSSL. Git history: injection works but
|
||
the effort hit an "architectural wall" (async event-driven gate, not a poll). Superseded first by
|
||
the FLE-bridge pivot, then by the FIFA 17 route. Its `CLAUDE.md` task list is historical.
|
||
|
||
## Unknown / requires investigation
|
||
|
||
- Whether the FIFA 17 hub supports actually **playing a FUT match** offline and getting results back.
|
||
- Whether FUT actions beyond the now-verified pack reveal/assignment flow (submit SBC, transfer
|
||
market buy/sell, matches) round-trip correctly through `utas_server.py`.
|
||
- The exact division of FUT state ownership once Core is wired in (who is source of truth).
|
||
- Degree of FIFA 23 wire-format identity — asserted ("identical wire format") but the FIFA 23 client
|
||
has not been re-tested against these responders in this repo's evidence.
|
||
|
||
## Known technical debt / hazards
|
||
|
||
- **Root docs are stale.** `README.md`, `CLAUDE.md`, `openfut-bridge/CLAUDE.md` all describe FIFA 23
|
||
as the target and mark FIFA 23 integration as the open item — they predate the FIFA 17 success.
|
||
- **All host state is volatile** across reboot except the `/etc/hosts` line — re-run
|
||
`openfut-fut.sh start`. Requires `ptrace_scope=0` + root arming (security-relevant).
|
||
- **Whole stack rides on EAAC staying neutralized** and game updates being off; a client update can
|
||
break the memory patches (VAs) and cert bypass.
|
||
- **`fifa17-recon/tools/lsx_responder_v2.py` is currently modified in the working tree** (uncommitted).
|
||
- `33068179` / `CAGE` remains the responder fallback, but the launcher now blocks one-button launch
|
||
until an explicit persona is configured and synchronized across LSX, Blaze, POW, and UTAS.
|