The decoded view used a fixed 10rem right-aligned label column, so short
labels pushed the values ~168px to the right — for transparent files the
block looked like indented record data. efRenderFieldsHtml now uses a
grid with grid-template-columns: max-content 1fr: labels stay right
aligned and values stay aligned, but the column is only as wide as the
widest label of the block. Applies to the file manager decoded view and
the snapshot Decoded block (transparent and per-record fields alike).
- tests: efRenderFieldsHtml markup (grid, no w-40) and decoded view.
- pysimFsInfoHtml puts the FID/type/size line inside the fieldset, so the
selected file is one bordered block with its symbolic name in the border;
files without an FCI keep the plain borderless line.
- The content pane starts hidden and is synced after every mutation
(selection clears it, read success shows it, read failure keeps it hidden,
edit/cancel re-sync): no empty box before a read or edit.
- tests: FCI ordering assertions; new fs_read.test.js (visibility helper,
read success/failure/throw, markup + call sites); fs_edit extraction.
The file manager's decoded FCI rows now sit in a fieldset matching the
phone-simulator containers, with the symbolic file name as its legend
(pysimFsInfoHtml(sel, name)); the metadata line stays above the border.
Without a name the fieldset renders without a legend, and without
fci_hex no block is shown at all.
- tests: fieldset/legend markup, metadata line before the border, no
legend without a name, escaped legend text, no fieldset without FCI.
- help EN/RU mention the named FCI block.
- The short file identifier is merged into the File identifier row
(same row, ' · ' separator) in profilerFciPreviewItems, everywhere the
decoded FCI is rendered.
- New hideFileSize option: pysimFsInfoHtml drops the FCI File size row
when its own header already shows Size (kept otherwise, e.g. DFs).
- tests: merged SFI row, hideFileSize, header-size precedence.
Files without a client-side decoder used to fall back to a raw view when
the Decoded pill was pressed. The decoder is resolved by name/FID
(efFindDecoder) before any read, so the pill can be disabled instead.
- pysimFsHasDecoder() mirrors the decoded view's resolution (tree node
name, FID fallback).
- pysimApplyAvailability() honours a new data-needs-decoder marker:
disabled with the existing 'No decoder for this file' tooltip; the
card hint still wins when no card is equipped.
- pysimFsClickFile() re-runs the availability pass after selecting a
file; pysimFsSetMode('dec') ignores the call without a decoder.
- tests: indicator (enable/disable/tooltip precedence), fs_edit
(decoded guard, name/FID resolution); help EN/RU.
The decoded view is a read-only field table, so pressing Edit there did
nothing (no readonly inputs to strip) and Save found nothing to write.
- Remove the redundant Read button: the Raw/Decoded pills already read
the file, so they become explicit read actions — Read raw / Read
decoded (Читать сырые данные / Декодировать). They are buttons now
so the server/card availability model disables them like the old Read
button did.
- Edit raw (Редактировать сырые данные) always edits the hex view: from
decoded (or with no content loaded yet) it switches back to raw and
reads the file first; the decoded view stays read-only.
- Save/Cancel moved next to Edit raw (right-aligned group); the pills
are inert and dimmed while editing (mode switching would discard
unsaved changes).
- Tests: frontend/tests/fs_edit.test.js (skipRead, edit guard, decoded
auto-switch ordering, read failure abort, cancel/reset); help EN/RU.
The preset ADM key was stored but never used: the header badge showed
whether a key exists and whether the card was verified, yet the only way
to verify was the pySim command line.
- POST /api/verify-adm builds the TS 102 221 VERIFY itself (CHV number
from the card model, short keys padded to 8 bytes with 'f') so the raw
SW is reported: 63Cx -> attempts_left, 6983/9804 -> blocked, 6982 ->
security error. The key is never stored and is redacted from request
logs.
- PWA: the header ADM badge is clickable when the matched preset has a
key; a failed file-manager read/write (6982/9804) shows a Verify ADM
button next to the error. Every retry after a failure asks for
confirmation and shows the remaining attempts (stronger text on the
last attempt); a blocked ADM disables both entry points until the card
session changes. No automatic retries.
- tests: tests/test_adm_verify.py (fake scc, APDU/SW mapping, redaction)
and frontend/tests/adm_verify.test.js (retry prompt, SW classifier,
wiring) + card_state indicator expectations
- docs/api.md, help EN/RU, AGENTS; version trio 2.7.8; sw cache v211
The operator picker depended on a workspace file outside the repo
(<workspace>/samples/mcc-mnc-list.json), so a fresh clone showed
'Operator list not loaded'.
- bundle the list in the package (pysim_simple_server/data/
mcc-mnc-list.json, byte-identical to pbakondy/mcc-mnc-list master
commit 97bc1652, MIT) with license + provenance in
mcc-mnc-list.LICENSE; declared as package data so wheels carry it
- _default_mcc_mnc_list() now points at the bundled file;
--mcc-mnc-list still overrides it
- hide MVNO entries from the simulator picker (search + random): they
do not operate their own network. Detected via the `bands` field
('MVNO', 'Satellite MVNO', ...); the full list stays loaded so the
Network state panel still resolves operator names
- tests/test_mcc_mnc.py: bundled file ships/parses, marker variants,
search/random filters on fixtures and on the real list
- README/AGENTS updated; version trio 2.7.7; sw cache simple-v210
The network-simulation description never switched to Russian: its
data-l10n attribute carries " (decoded to " by the browser), while the
LANG_RU key is raw text inside <script> and keeps the literal ", so
translatePage()'s lookup missed and t() returned the English key.
- LANG_RU: the description key now carries real quotes, matching the
decoded attribute value (RU value unchanged)
- add the missing RU entries for the net-sim labels AUTHENTICATE, MCC,
MNC, LAC, Cell ID, TAC, RAC (acronyms map to themselves; Cell ID ->
«Идентификатор соты»)
- html.test.js: strict cross-check that every data-l10n attribute
(entity-decoded, as the browser does) is a LANG_RU key
- sw.js cache simple-v209; version trio 2.7.6
The second line under the location (e.g. "RAI FFFE") showed only the LAC
part of the current area identity: after every dummy-writing scenario it
was the constant FFFE, and otherwise it was redundant with the
EF.LOCI/PSLOCI/EPSLOCI rows below. netStateRender no longer renders it.
The timestamp line now carries a translated "Last refresh" label
('Last refresh': 'Последнее обновление').
- frontend/index.html: remove #netstate-area (markup + render), label the
read time, LANG_RU entry
- frontend/tests: html test pins the label and the absence of the area
line; netstate test asserts the rendered timestamp
- sw.js cache simple-v208; version trio 2.7.5
- byte 30 b5-b8 were marked RFU; now Proactive UICC: ACTIVATE,
GEOGRAPHICAL LOCATION REQUEST, PROVIDE LOCAL INFORMATION (Broadcast
Network Information) and "Steering of Roaming for I-WLAN" REFRESH
support (TS 102 223 cl. 5.2; TS 31.111 cl. 5.2 names)
- byte 34 b8 "Reserved for future use" -> "Deprecated, bit = 0"
(TS 102 223 cl. 5.2)
- mirrors the UICC_SPECS.md audit fixes (byte 30/34 tables, ESN/MEID
bit positions b5/b6, Data Connection Status Change in extra events)
- SW cache simple-v207; 454 frontend / 298 Python tests green
The location line in the Phone-tab network monitor grew too long once the
status was appended (e.g. "262-01 · Germany · Telekom · Guest (roaming)
⛔ PLMN not allowed"). netStateRender now keeps the identity
(mcc-mnc · country · operator) on the first line and renders the status
elements (roaming tag + forbidden badge) in a block span on a second line;
a location without a status still renders on one line.
Also add the missing Russian translation for the monitor's Refresh button
(t() fell back to English): 'Refresh': 'Обновить'.
- frontend/index.html: two-line location render, LANG_RU Refresh, 2.7.3
- frontend/tests/netstate.test.js: pins the block-span layout and the
one-line no-status case
- sw.js cache simple-v206; version trio 2.7.3
The UICC_NAA.md C3/C3a recipes were re-checked against the corpus: the
EPSLOCI dummy tail is FF*13 + `FF FE` + status - the TAC is always FFFE
(never FF) and the status byte is 01 (not updated), not FF; the old
`<FF*16>` reading was a misparse of that tail. The rejection form
(status 02) is the C3a spec model: the only observed rejection trace never
writes 6FE3 at all.
- build_epsloci_dummy(status=ST_NOT_UPDATED, keep_plmn=None): corrected
tail; keep_plmn selects the NMR style that preserves the last visited
TAI PLMN (the guest style wipes GUTI and TAI PLMN) - both are observed
- write_dummy_locations(): drop the all-FF special case (service loss now
ends `FF FE 01`) and thread keep_plmn through
- tests: exact guest/NMR/rejection vectors, 18-byte length, runner
assertions for service_lost and roaming_denied; netstate fixture updated
- UICC_NAA.md C3a tail `<FF*15> 02` corrected to `<FF*13> FF FE 02`;
the PWA help/param text no longer claims the EPSLOCI dummy keeps the
PLMN; SW cache simple-v205
The startup init (server booted with the card already in the reader) equips
the card and reads the ICCID itself, but never created server.net_state -
only /api/command equip and the auto-equip worker did. With a card present
at boot the monitor stayed empty, net-sim responses carried net_state: null
and the panel only started updating after pressing Refresh (which lazily
created the state). Fix:
- factor _netstate_read(app) / _netstate_install() / _netstate_init() in
server.py; _apply_equipped_card uses _netstate_init()
- __main__ startup: read the monitored EFs in the same CAT-free window as
the ICCID and install the state once the server object exists
- _netstate_ensure() lazily initializes the state in the net-sim/event hooks
so any equip path predating the monitor cannot leave it dead
- netSimRun falls back to netStateFetch() when a response has no net_state
- tests: tests/test_netstate_server.py (candidate order, record vs
transparent, absent files, install, ensure); SW cache simple-v204
server:
- netsim: LOCI/PSLOCI dummy builders take a status; the EPSLOCI dummy is
wiped to 0B F6 + FF per UICC_NAA.md C3; roaming_denied now emulates the
permanent 'PLMN not allowed' rejection (C3a): status 010, EPSNSC dropped,
EF.FPLMN append with TS 31.102 4.2.16 shift semantics and a home-PLMN
guard (HPLMNwAcT/EHPLMN, IMSI fallback), optional 'Rejection: write FPLMN'
toggle; write steps carry the logical key; SCENARIO_SERVICE map;
insert_fplmn/fplmn_entries/parse_imsi helpers
- netstate.py: cached per-session monitor state for the 12 network EFs,
step-based patch, simulated service state, derived location with country/
operator (optional MCC/MNC list) and roaming class
- server: monitor read at equip right after a readable ICCID (skipped
otherwise), cleared on card removal; GET /api/net-state and POST
/api/net-state-refresh; net-sim patches the cache from the written bytes
and re-reads EF.IMSI; Location-status events set the service state and
re-read EF.IMSI (multi-IMSI applets)
frontend:
- Phone tab: 'Network state' panel next to Network simulation with the
simulated service badge (Undefined until simulated; normal/limited/no
service + rejection marker), location/roaming line, compact per-file
summaries with full-decode tooltips and a Refresh button; no card polling
- EF decoders: EF.FPLMN (FFFFFF gaps are not terminators) and EF.EHPLMN
- i18n EN/RU, help updated; SW cache simple-v203
tests: 295 Python / 453 frontend
- ADM/SCP80/SCP81 header indicators become bordered chips (neutral gray
border, rounded, px-1.5 py-0.5); SCP80/SCP81 get an even ml-1 gap, so no
separator glyph is needed
- profiler/snapshot card actions carry the equipped card's ICCID:
'Profile from card <ICCID>', 'Check card <ICCID>' (row button, arrow
dropped), 'New snapshot: <ICCID>'; pysimUpdateCardActionLabels() fills
the spans on every status poll and after a language switch
- these buttons switch to data-needs='card-iccid': disabled without a
readable ICCID ('Card equipped but its ICCID is not readable' hint)
- profiler rows wrap so the longer button cannot break the layout;
help EN/RU updated; SW cache simple-v202
- the ADM badge gains a trailing key glyph (ADM ✓ ⚿ / ADM ✗ ⚿) when the
preset matching the equipped card's ICCID carries an ADM key; the four
tooltip states cover key/no-key x verified/not-verified
- new gray SCP80/SCP81 markers after ADM, shown only when that preset has
all SCP80 fields (KIc/KID, SPI1/SPI2, counter, KIc/KID keys) or the SCP81
PSK pair filled in; hidden without a card session, a matching preset or
the fields, never color-coded
- cardsScp80Complete/cardsScp81Complete/cardsAdmPresent/cardsMatchedPreset
helpers; cardsRender reuses the SCP81 predicate
- tests: ADM four-state badge, marker show/hide, predicate whitespace
handling, header markup order; help EN/RU updated; SW cache simple-v201
- the SCP80 fieldset gains ISD TAR (000000), UICC TAR (B00000) and USIM TAR
(B00001), prefilled with the spec defaults; empty stored values mean the
default for that target (cardsTarValue)
- ADM field between "From card" and "Add", stored in the preset for later
use by the file manager (placeholder ADM (optional), spaces stripped)
- packing a chain selects the preset TAR for its target: RAM/GP -> ISD,
SIM RFM -> UICC, USIM RFM -> USIM (spec default without a preset);
_spTarKey keeps the target when the card preset is picked afterwards,
RAM card selection forces the ISD TAR
- USIM RFM 'from current DF' labelled (ADF.USIM); the list TAR column shows
all three values; help EN/RU updated
- SW cache simple-v199; new cards_form.test.js + html markup guards
(440 frontend / 281 Python tests green)
- new #state-indicator-iccid span between the card image and the ADM badge;
shows the EF.ICCID digits of the equipped card (hidden without a card
session or when the ICCID is unreadable, cleared when the server is lost)
- pysimUpdateIccidIndicator() mirrors the ADM badge pattern (no rewrite when
the value is unchanged); wired into pysimCardStateUpdate() and
pysimSetServerAvailable()
- indicator/card_state tests for show/hide and markup order;
SW cache simple-v197
- Cards and Custom files now expose only Export to file (JSON download) and
Import from file; the Export-as-JSON / Paste & import / Import-JSON-from-
clipboard buttons and their paste textareas are gone
- import feedback moves to a transient status line (new ioStatus helper);
cardsImport/pysimCustomImport no longer read the clipboard
- Network simulation parameters: note under the operator form listing the
scenarios that use the MCC/MNC (EPS/2G attach and SMS received write real
LAI/RAI/TAI; cold boot, service lost, limited service and roaming denied
write dummy files keeping the PLMN; churn/CB/AUTHENTICATE do not use it)
- i18n: removed-button entries dropped, note added EN/RU; help EN/RU updated
- guard tests for the button set and the note; SW cache simple-v196
Live-testing against a UICC without the EPS files (no USIM service 85)
and without the Kc files showed the runner aborting when EPSLOCI/Kc were
absent. write_binary/write_record now take an 'optional' flag: a missing
candidate file is logged as a skip step instead of failing the scenario
(store_epsnsc, real/dummy locations, churn and Kc writes). Also allow
POST /api/write to select by 'path' like /api/select and /api/read (the
netsim live test needed it to restore the captured file values).
Server (pysim_simple_server/netsim.py + POST /api/net-sim):
- pure builders for the observed phone write vocabulary (UICC_NAA.md §13):
EPSNSC (A0 TLV with KSI/KASME/counts/algo, padded to the card's FCP record
size; invalidate-wipe and invalidate-keep-key), LOCI/PSLOCI/EPSLOCI real
and dummy forms (PLMN kept, LAC FFFE, status 01/02), Kc/KcGPRS (9-byte
USIM and 33-byte GSM forms, 07 invalidates), EF.SMSstatus counter bump
(read-modify-write), CBMI/CBMIR, the Location status event (9B 01 <st>,
with the optional 13 07 location info for normal service) and the
AUTHENTICATE APDU (TS 31.102 7.1.2.1) with DB/DC response parsing
- scenario runner executes the recipes under _CARD_LOCK, picks the first
existing candidate path (ADF.USIM vs DF.GSM/DF.TELECOM), reads FCP/current
records where the format is card-specific, skips the event unless the card
subscribed to Location status, honours the scenario toggles and returns a
per-step log; only D6/DC, ENVELOPE and AUTHENTICATE are ever sent
- GET /api/mcc-mnc serves the optional worldwide operator list
(--mcc-mnc-list, default <workspace>/samples/mcc-mnc-list.json): ?q=
search and ?random=1&exclude= for roaming; the list stays out of the repo
PWA:
- Phone pill gains a 'Network simulation' fieldset: one button per scenario
(cold boot, EPS/2G attach, service lost, limited service, roaming denied,
churn, SMS received, CB reconfig, AUTHENTICATE), a collapsed Parameters
block (operator search + random roaming, LAC/Cell ID/TAC/RAC, optional
identities, toggles, churn count/delay) and a step log with SWs
- i18n EN/RU, help/README/api.md/AGENTS.md updated; version 2.5.0,
SW cache simple-v193
Tests: tests/test_netsim.py (19), frontend/tests/netsim.test.js (3) and
html guards - 423 frontend / 280 Python, all passing
Client-side, offline decoder layer (ported from simtrace-analyser, verified
against the pinned specs and pySim test vectors):
- file manager Decoded pill: decoded field table instead of the raw pySim
JSON dump (per-record fields for record files); the server's
read_*_decoded JSON stays behind a 'pySim JSON (server)' disclosure;
switching Raw/Decoded re-reads the open file
- snapshot view: decoded field block for transparent files and a per-record
decoded summary for record files
- profile check / snapshot comparison report: content mismatches of files
with a decoder render as a per-field decoded diff (only the differing
fields, expected | actual); the raw expected/actual pair is kept when the
decoded values match
- coverage: ICCID, IMSI/IST, UST/SST, LI/PL, SPN, PLMNsel/PLMNwAcT/
OPLMNwAcT/HPLMNwAcT, OPL, SPDI, PNN, LOCI/PSLOCI/EPSLOCI/EPSNSC/LOCIGPRS,
Kc/KcGPRS, ACC, PHASE, CBMI/CBMID/CBMIR, ECC, AD, ACL, SMSP, SMS, ARR, DIR,
IMPI/DOMAIN/IMPU, ADN/FDN/SDN/MSISDN/LND/BDN, EXT1-5, SUME (TS 51.011
§10.3.34); unknown files fall back to raw
- new frontend/tests/ef_decode.test.js (14 tests); help EN/RU + READMEs
document the decoded views; version 2.4.0, SW cache simple-v191
- also fix the README/RUS snapshot-comparison text: comparison is always
exact (no content masking)
- version check in pysimConnect() now compares the server major against
SIMPLE_VERSION (2.3.1) instead of the hardcoded 1.x assumption; the header
version is rendered from the same constant
- removed the 'SIM OTA with a Human Face' slogan (span, i18n key, translate
hook) to free header space
- broaden the PWA description in manifest.json/package.json and the help
overview (EN/RU): APDU workbench, OTA lab, CAT/STK simulator, card profiler
- version compatibility tables in README/RUS, help EN/RU and docs/api.md now
describe the major-version rule
- README/RUS: Cards promoted to a top-level section; EN duplicate
Theme/Localisation sections removed
- docs: /api/cardinfo documented; SCP81 'expose framing options' next-work
item dropped (done); local AGENTS.md not-implemented note corrected
- SW cache simple-v190