75 lines
4.2 KiB
Markdown
75 lines
4.2 KiB
Markdown
# openfut-utas-host
|
|
|
|
The FIFA 17 UTAS migration boundary. It accepts the client-visible HTTP surface,
|
|
serves migrated routes from Rust/Core plus host-owned durable stores, and proxies only
|
|
the unclassified tail to the Python behavioral oracle.
|
|
|
|
```
|
|
FIFA 17 ──HTTP──▶ openfut-utas-host
|
|
├── migrated route ──▶ Rust adapter / Core / host stores
|
|
└── unclassified tail ──▶ Python UTAS oracle
|
|
```
|
|
|
|
`src/lib.rs::classify` is the route-level source of truth. The current Rust surface
|
|
includes club/squad/user reads, club rename, auth/session/client data, Store/economy,
|
|
packs, owned-item moves, market/trade-pile, and the observed hub support routes.
|
|
|
|
## Safety model
|
|
|
|
- Classification happens exactly once before execution. There is no "try Rust then
|
|
Python"; a mutation cannot be double-applied.
|
|
- A route classified to Rust never falls back to Python on a Core/store/projection
|
|
failure. Each handler uses its captured fail-closed or honest-empty wire contract.
|
|
- `PUT …/club` and `PUT|POST …/user/club` atomically update the shared account JSON.
|
|
Every input returns the required zero-atom `200 {}` response; rejection and
|
|
persistence failures remain visible in logs.
|
|
- Numeric `GET …/squad/<n>` returns the one Core-backed current squad, matching the
|
|
Python oracle's single-current-squad behavior.
|
|
- Python remains the behavioral oracle and rollback backend for routes not yet
|
|
classified to Rust. New economy behavior belongs in Rust/Core, never Python.
|
|
|
|
## Configuration (env)
|
|
|
|
| Var | Required | Default | Meaning |
|
|
|---|---|---|---|
|
|
| `OPENFUT_UTAS_HOST_ADDR` | yes | — | client-visible host listen address |
|
|
| `OPENFUT_UTAS_PYTHON_URL` | yes | — | Python oracle base for the unclassified tail; must differ from this host |
|
|
| `OPENFUT_FIFA17_CATALOG` | yes | — | FIFA 17 definition identity catalog |
|
|
| `OPENFUT_IDENTITY_STORE` | yes | — | persistent owned-instance ↔ wire-id store |
|
|
| `OPENFUT_PERSONA_ID` | yes | — | non-zero FIFA persona id shared by LSX/Blaze/POW/UTAS |
|
|
| `OPENFUT_MARKET_DB` | yes | — | durable host-owned transfer-market SQLite DB |
|
|
| `OPENFUT_PILE_DB` | yes | — | durable host-owned item-pile SQLite DB |
|
|
| `OPENFUT_CORE_URL` | no | `http://127.0.0.1:8080` | OpenFUT Core base |
|
|
| `OPENFUT_FIFA17_TABLES_DIR` | no | `fifa17-recon/data/tables` | FIFA entity tables |
|
|
| `OPENFUT_CLIENTDATA_DB` | no | identity-store sibling `clientdata.json` | durable opaque client-data JSON |
|
|
| `OPENFUT_ACCOUNT_PATH` | no | `FUT_ACCOUNT_PATH`, then identity-store sibling `active_account.json` | shared FIFA account/club JSON |
|
|
|
|
Startup fails if required identity or durable economy state cannot be opened. No
|
|
placeholder production identity source is substituted.
|
|
|
|
## Identity model (resolved)
|
|
|
|
FIFA renders an owned card by resolving `resourceId & 0xffffff` against the
|
|
client's **own local players table**; an invented id renders a **blank generic
|
|
card** (proven live — `fut_cards.py:11-21`). Two distinct identities, never
|
|
conflated, are resolved by [`Fifa17IdentityResolver`] (the single production path):
|
|
|
|
- **Definition identity** (`resourceId`/`assetId`) — the card's real FIFA asset
|
|
id, from the versioned `OPENFUT_FIFA17_CATALOG`. An unmapped definition is
|
|
**dropped and counted**, never faked.
|
|
- **Instance identity** (`id`) — a stable, persistent, reversible wire integer
|
|
from the generic `openfut-identity` store under the FIFA 17 wire-id policy
|
|
(monotonic from `100_000_001`). The same owned instance keeps its id across
|
|
restart and reverses exactly; two copies of one definition share a
|
|
`resourceId` but get distinct `id`s. The namespace is globally monotonic
|
|
within `(fifa17, owned-item)` — no per-account column is needed because Core
|
|
owned-instance ids are globally-unique UUIDs.
|
|
|
|
Production has a frozen post-P1 baseline and a hot Python rollback. A source change
|
|
passing local tests is **not** deployment approval. Build verification, staging, host
|
|
restart, and live-client promotion remain operator-gated; the current state and
|
|
promotion evidence live in the OpenFUT Obsidian vault.
|
|
|
|
Logs are safe by construction: no auth/session/device/token material — only owner,
|
|
route, filter summary, counts, status.
|