Commit Graph

664 Commits

Author SHA1 Message Date
catarrh 105efe62da release: 3.39.0 — decoded-editing batch
release / release (push) Waiting to run
Bump VERSION/pyproject/PWA header to 3.39.0 and the SW cache to
simple-v484 (index.html + help changed).  CHANGELOG [3.39.0] folds the
unreleased 3.38.2 start-script fix and documents the decoded-editing
batch: DF-context resolution, EF.ECC/EF.AD split, path-based selection,
EF.SST tri-state editor, the Services editor + path-exact FS check, and
the UST 147-150 completion.
v3.39.0
2026-10-26 00:11:26 +03:00
catarrh 4835bc8585 Services editor: throbber while the filesystem check runs
Show the established animate-spin throbber next to the Check filesystem
button while the scan (tree load + probe-all + report) is in flight, and
hide it when it settles.  Frontend 1126 tests green.
2026-10-26 00:07:54 +03:00
catarrh b173e73437 Services editor: path-exact filesystem check
Rework the FS check from FID-level to path-exact:

- SERVICES_FS_MAP now maps a feature to symbolic roles (registry names),
  not FIDs; the (name, root) -> path index resolves each role to its
  unambiguous canonical path (same-FID files in other DFs - EF.SAI vs
  EF.PBR, EF.AST - resolve separately).
- Per-tree declaration: SST -> MF, UST/EST -> ADF.USIM, IST -> ADF.ISIM,
  with a DF.TELECOM fallback for USIM/ISIM roles whose DFs live under
  DF.TELECOM (MMS storage, MCPTT, V2X, A2X), except the GSM-application
  mapped copies (ADN/EXT1/ECCP).
- The report carries the path (and declaring tree) for missing and
  present-but-undeclared, plus an honest "unchecked" bucket for roles
  with no path yet (DF.GRAPHICS/IMG, SIM DF.SoLSA/MExE copies,
  EF.3GPPPSDATAOFFservicelist).
- efDfFromPath maps A000000087100B -> ADF.USIM-NON-IMSI.

Frontend 1126 + Python 1091 tests green.  (Version/sw bump still
deferred for the decoded-editing batch.)
2026-10-26 00:01:23 +03:00
catarrh a1892094f4 uicc_files: supplement optional files missing from pySim's model
Add a supplement to the cardless registry generator for the 13 files the
Services editor's FS check references but pySim does not emit (optional,
service-gated): EF.LRPLMNSI/IAL/IPD/3GPPPSDATAOFF/TVCONFIG, DF.MExE +
EF.MExE-ST/ORPK/ARPK/TPRPK, DF.SoLSA + EF.SAI/EF.SLL, DF.BCAST,
DF.ACDC + EF.ACDC_LIST, DF.TV, and DF.A2X + EF.AST/EF.A2X_CONFIG (under
DF.TELECOM).  Emitted with distinct (name, path) so same-FID files in
other DFs (EF.SAI vs EF.PBR, EF.AST) stay separate; ADF.USIM rows are
mirrored to ADF.USIM-NON-IMSI.  490 -> 527 entries.

Regenerated the asset; test_uicc_files.py pins the 20 new paths, the
FID-collision cases and (name, root) uniqueness.  1091 Python tests.
2026-10-26 00:01:23 +03:00
catarrh 12ff552df8 Services editor: filesystem check
Add a "Check filesystem" action to the Services pill:

- SERVICES_FS_MAP: the feature -> FID list transcribed from
  docs/services-editor.md §A (127 file-backed + 34 capability features,
  157 distinct FIDs; a completeness/data test pins it).
- servicesFsReport: pure two-direction check - declared features whose
  mapped files are absent, and present files whose features are
  undeclared.  FID-level (not DF-exact) for this first pass; documented.
- The Check runs the tree probe (once per session) and renders the
  missing/extra lists or the "matches" line, with an FID-level note.

Also fix a doc typo (eCall Data's EF.FDN was written as 6F3A = EF.ADN).
Frontend 1120 + Python 1090 tests green.  (Version/sw bump still
deferred for the decoded-editing batch.)
2026-10-25 22:22:33 +03:00
catarrh e10228c37c Services editor (core): cross-file service tables
Add a Services pill to the Card reader tab that shows the card's service
tables side by side:

- SERVICES_CROSS_REF: the 161-row feature cross-reference (SST/UST/EST/IST
  numbers + class) transcribed from docs/services-editor.md §A; a
  completeness test pins every non-RFU service number to exactly one row.
- Reads the four files (DF.GSM/6F38, ADF.USIM/6F38, ADF.USIM/6F56,
  ADF.ISIM/6F07) via /api/read, cached per card session; a column renders
  only for a present file (no ISIM -> no IST column).
- SST cell is on/allocated-off only (never de-allocates); UST/EST/IST are
  checkboxes; per-file Save buttons write via /api/write, preserving
  unmodelled bits and reporting each result.

Frontend 1105 + Python 1090 tests green.  (Version/sw bump deferred;
FS-check is the next step.)
2026-10-25 22:06:31 +03:00
catarrh fc27feba8e EF.SST: tri-state editor (deleted / allocated-off / on)
Give EF.SST its own per-file editor for the 2-bit SIM service table
(TS 51.011 10.3.7: allocated + activated per service):

- efEncSst encodes the pair layout, preserving unmodelled pairs and the
  file length; validates service number, fit and state.
- A new `bits2` field kind renders a three-state select per service and
  collects {n, name, allocated, activated}; registered as EF_SST_FIELDS
  with the encoder (df '7F20'), and SST leaves EF_READONLY_NAMES.
- i18n: Deleted/Off/On + the state-error and hint strings (EN/RU).

Docs/help updated: SST is no longer read-only.  Frontend 1093 + Python
1090 tests green.  (Version/sw bump still deferred for the unreleased
decoded-editing batch.)
2026-10-25 21:43:07 +03:00
catarrh 5ebcd0773d file manager: path-based selection + ADF AID normalization
Make the DF context from the resolver actually reach the live views:

- pysimFsSelected now stores the canonical path (pysimFsNodePath), not
  the node name; pysimFsRenderNode emits the path in the click/toggle
  handlers; every consumer resolves via pysimFsFindNodeByPath.  The
  name-based pysimFsFindNode is removed - two same-named files under
  different DFs (EF.ECC/EF.AD, DF.PHONEBOOK copies) are now distinct.
- efDfFromPath maps ADF-rooted parent segments by RID (A0000000871002
  -> ADF.USIM, A0000000871004 -> ADF.ISIM), so snapshot/profile paths
  rooted by AID resolve to the same DF key as the named form.
- efDecEcc shares efEccCategory with efDecEccUsim (TS 24.008
  Table 10.5.135d), so the SIM and USIM category displays can't drift.

Frontend 1087 + Python 1090 tests green.  (Version/sw bump deferred -
  this is part of the unreleased decoded-editing batch.)
2026-10-25 21:01:11 +03:00
catarrh 8e0a97f025 EF resolver: resolve by (DF, FID); split EF.ECC and EF.AD per DF
The decoder/encoder lookup now takes an optional DF context and resolves
DF-first, then name, then FID (the spec keys files by FID within their
DF; the name is a display label).  efDfFromPath derives the parent DF
segment of a canonical path.

EF.ECC is split into its two DF-specific decoders: DF.GSM (7F20)
transparent 3n (existing) and ADF.USIM linear-fixed X+4 - the new
efDecEccUsim reads the 3-byte Emergency Call Code, the Annex A alpha and
the Emergency Service Category (decoded per TS 24.008 Table 10.5.135d).
EF.AD is registered as DF-keyed entries (7F20 / ADF.USIM / ADF.ISIM),
reusing the existing encoder.

The df is threaded through the file-manager decoded-view/edit/save and
the snapshot/compare decode.  No ECC editor yet (read-only).

Frontend 1081 + Python 1090 tests green.
2026-10-25 12:40:51 +03:00
catarrh 6d762b9d9c ust: complete the USIM service table (147-150) + Services editor data fixes
The PWA's EF_UST_SERVICES stopped at 146; TS 31.102 §4.2.8 pins 147-150
(5MBS UE pre-configuration, Operator Controlled Signal Threshold per
Access Technology, A2X, IMS Data Channel Indication).  Add them and fix
the two test fixtures that assumed the 146-service table.

Resolve the three Services-editor FS mappings against the spec:
- eCall Data (89) -> EF.SDN 6F49 (+ EF.FDN 6F3A)
- eCall Data over IMS (112) -> EF.SDNURI 6FEF (+ EF.FDNURI 6FED)
- EARFCN list for MTC/NB-IOT (121) -> EF.EARFCNList 6FFD; "Extended
  EARFCN support" (100) is a capability, not a file.

Record the FID-first rule: the resolver and FS check key by (DF, FID),
the symbolic name is only a label; and clarify that the FS check is
card-only (independent of the cardless uicc_files.json list).
2026-10-25 12:10:14 +03:00
catarrh 1d5a049ea1 docs: Services editor data tables (draft)
Feature cross-reference (SST/UST/IST/EST) and feature->filesystem
mapping for the planned cross-file Services editor.  Reconciliation
exact: 160 feature rows, every service number exactly once.

Flags for review: UST list is 4 short of the spec (147-150 missing
from EF_UST_SERVICES), GSM Access gates DF.GSM-ACCESS 5F3B (not DF.GSM),
CCP/CCP2/ECCP equivalence, and three unresolved file mappings.
2026-10-25 11:52:28 +03:00
catarrh 504bf1d66e docs: SIM/USIM format divergence + cross-file Services editor design
- correct finding 12 (EF.LP is 1-byte; EF.PL is 2-byte like EF.LI)
- name-collision class now names EF.ECC and EF.AD
- split EF.LP/EF.PL and EF.AD (SIM/USIM) survey rows
- add §2.4 SIM/USIM divergence table (groups A/B/C)
- add §6 Services editor + §6.1 cross-application candidates
2026-10-25 11:36:12 +03:00
catarrh 62c5b4e1a9 start: name the requested --http-host/--http-port in the opening line
The first line was hardcoded to http://127.0.0.1:8080 while the flags
were passed through, so ./start.sh --http-host 10.0.28.161 printed
127.0.0.1 although the server bound 10.0.28.161 (its own listening banner
named it).  Both start scripts now parse --http-host/--http-port (space
and = forms, the last value wins like argparse) and echo the effective
address; start.bat parses with shift (%* is unaffected, so the server
still receives every argument).

Bump 3.38.2 (server, pyproject, PWA header, sw simple-v483) and add the
CHANGELOG entry.

Tests: tests/test_pcsc_startup.py pins the parsing/echo (2 new).
1090 Python + 1075 frontend.
2026-10-25 03:25:22 +03:00
catarrh 393b1098ce release: date the 3.38.1 entry
release / release (push) Has been cancelled
v3.38.1
2026-10-25 00:52:48 +03:00
catarrh 295f5553fc pcsc: guide by PolicyKit actions, not a pcscd group
A headless/SSH box can show a world-writable /run/pcscd/pcscd.comm
(srw-rw-rw- root root) and still be denied: pcscd enforces its PolicyKit
policy, whose defaults allow only active local sessions.  The startup
report, the start.sh pre-flight and the README troubleshooting now name
the exact rule (99-pcscd-allow.rules, org.debian.pcsc-lite.access_pcsc
and access_card) and explain why the socket mode does not matter.  The
pcscd-group instruction is gone: there may be no such group, and it is
not the gate.  The group route stays only as a note for a pcscd built
without PolicyKit.

Tests: tests/test_pcsc_startup.py asserts the polkit action IDs, the
rule filename and the 0666-socket explanation.
1088 Python + 1075 frontend.
2026-10-25 00:50:24 +03:00
catarrh 2ecf7bb45c setup: recreate an incomplete .venv instead of activating it
A failed venv creation (e.g. the Debian/Ubuntu python3-venv package
missing) left the .venv directory behind; the next run saw it, skipped
creation and failed to source/activate a venv without activate/pip.
setup.sh now validates bin/activate, bin/python and bin/pip, removes an
incomplete directory and creates it again; a creation failure removes the
partial directory and names the venv package.  setup.bat does the same
with Scripts\activate.bat/python.exe/pip.exe and !errorlevel!.

READMEs (prerequisites + scripts table), CHANGELOG (folded into the
unreleased 3.38.1) and AGENTS updated.

Tests: tests/test_setup_scripts.py (5: the shell/bat source pins and
bash -n).  1088 Python + 1075 frontend.
2026-10-25 00:42:36 +03:00
catarrh a269f8bd2e startup: report PC/SC access denial instead of a silent serial fallback
When no reader was named and readers() failed (e.g. SCARD_E_NO_ACCESS -
the user may not use pcscd), the auto-detect swallowed the error and pySim
fell back to the Serial transport, failing on /dev/ttyUSB0 while the
server still printed "listening".  The startup now probes PC/SC,
classifies the failure by hresult (access/service/reader), reports the
cause with the pcscd group/polkit and pcsc_scan guidance and exits 2; the
python -m guard uses sys.exit(main()).  An explicit -d/--device keeps the
serial path; a reader without a card stays a normal cardless start; a
readerless start exits with its own message.

start.sh runs the same access pre-flight before launching (readers() with
a 3x retry for socket activation; -d is detected and skips it).  READMEs,
CHANGELOG (folded into the unreleased 3.38.1) and AGENTS updated.

Tests: tests/test_pcsc_startup.py (16: the error classifier, the probe,
the report guidance, the serial-arg detection and the source pins).
1083 Python + 1075 frontend.
2026-10-25 00:29:26 +03:00
catarrh 0231df905c equip/init: read the selected EF directly, bound the startup drain
pySim's scc.read_binary/read_record call select_path(ef) first, so the
ICCID, network-state, EF.ARR and EF.PBR readers - which had already made a
parent-scoped selection and hold its FCP - sent a second SELECT per file
and per record.  _read_selected_ef() issues READ BINARY (chunked at
max_cmd_len from the FCP file size) / READ RECORD (b2<rec>04<len> at the
FCP record length) directly; the pySim helpers stay as the fallback when
the FCP lacks the value.

The startup TERMINAL PROFILE drain now breaks on the chain's idle SW
instead of polling STATUS once more.

Measured on the last startup: ~3.4 s traced init (netstate/ARR/PBR 271
APDUs / 1.26 s incl. 169 SELECTs; the card's own TP processing 1.58 s);
the change removes ~0.7-0.8 s of the read phase without touching any data
or the TP.

v3.38.1; tests: tests/test_read_selected.py (direct reads, chunking, the
fallback and the drain source pin) and the ARR/PBR/ICCID/netstate fakes.
1067 Python + 1075 frontend.  sw cache simple-v482.
2026-10-24 18:54:31 +03:00
catarrh 80cbd8e935 load tests: show the live timing in the run banner
release / release (push) Has been cancelled
The test-run banner (visible on every Test-script view) now carries the
same live stats as the script head and the suite summary: elapsed so far,
the average per completed iteration and the estimated time left.  The
text is built by a pure helper (testRunBannerText) so it is testable;
testRender only calls it while a run is active, and non-load runs keep
their position text (step or suite-member progress).

The existing banner-source assertion moved to the helper.  Tests: the
banner text for a load suite and a plain run.  1061 Python + 1075
frontend; sw cache simple-v481.  CHANGELOG/help/docs/AGENTS updated.
v3.38.0
2026-10-24 12:03:23 +03:00
catarrh 8ebcfd8af4 load tests: show elapsed, average and ETA in the live stats
The script-run head and the suite summary now display, while a load run
repeats: the elapsed time so far, the average per completed iteration and
the estimated time left (testLoadTiming / testDurationText /
testIterAvgText), recomputed on every 2 s status poll.  A finished run
keeps the server's final elapsed_ms/avg_ms (no estimate), and before the
first completed iteration only the elapsed time is shown - no bogus
average or ETA.

The timing is client-side over the existing load block (started, done,
iterations), so it works with the running server as well; no server
change is needed.

Tests: the timing math and the two formatters, the script head render and
the suite summary render (3 new).  1061 Python + 1074 frontend.
sw cache simple-v480; CHANGELOG [3.38.0], help EN/RU,
docs/test-scripts.md and the AGENTS notes.
2026-10-24 11:58:51 +03:00
catarrh c6a6bf0bc6 load tests: raise the iteration cap to 1,000,000,000
Both layers: the server's _TEST_LOAD_MAX and the PWA's
TEST_ITERATIONS_MAX plus the two input max attributes.  The value stays
inside 2^53-1 (the JavaScript safe-integer range), so the counters and the
progress display survive the JSON/JS round trip exactly; the practical
ceiling is the run duration (50 ms/iteration ~1.6 years at the cap).

The server's range error names the new cap automatically.  The frontend
tests read the cap from index.html instead of stubbing a literal, and
clamp at it (above the cap, exactly at it, and the input max).

Docs: api.md, test-scripts.md, help EN/RU, the CHANGELOG (folded into the
unreleased 3.38.0) and the AGENTS notes.  sw cache simple-v479.
1061 Python + 1071 frontend.
2026-10-24 10:59:58 +03:00
catarrh cd0db3f9d3 test scripts: known-file picker for the file actions
The UPDATE file / READ file step editor's path field gains the selector
the APDU builder's SELECT rows use: the loaded file-manager tree, the
custom files and the standard uicc_files.json list, merged by
rfmFileEntries, grouped by root with a filter.  Picking a file fills
test-f-path; the text field stays authoritative (a name path or an
unlisted file can still be typed) and the current path is preselected
when known.  Only EFs are offered - the action reads/writes a file, so a
DF/ADF is not a target.

v3.38.0: server.py VERSION, pyproject.toml and the PWA header;
sw cache simple-v478 (help/index changed); a CHANGELOG [3.38.0] section,
the help EN/RU, docs/test-scripts.md and the AGENTS notes.

Tests: frontend/tests/testscripts.test.js - the option list/grouping, the
EF-only filter, the filter input, the pick->path fill and the form's
picker render for write and read.  1061 Python + 1071 frontend.
2026-10-24 10:38:08 +03:00
catarrh 513f16d9d2 PLI: return the 7-byte location object, bound every proactive chain
release / release (push) Has been cancelled
The PLI answer used the 5-byte form 13 05 <MCC/MNC> <LAC> with LAC 0000
and no Cell ID.  That is the *Network Rejection event download* form
(TS 31.111 8.19.1); a PLI location for GERAN is 7 bytes - MCC/MNC, LAC
and Cell ID.  A card applet that re-reads its location (the Alfa
MultiIMSI applet, after a *LOCI status change) treated it as empty and
re-requested PLI on every STATUS tick.

The default is now 13 07 00F110 0001 0001: PLMN 001/01 (the test
network), LAC 0001, Cell ID 0001.  _build_tr injects it after the
Result; the TR data is pinned by a test.

While verifying a live PLI, the manual /api/status-poll chain turned
out to be unbounded: it looped on the PLI storm while holding
_CARD_LOCK, so the request never returned and every card operation
waited.  _handle_proactive_chain's max_rounds now defaults to
_POLL_CHAIN_MAX (the background poll always passed it; the manual poll,
ENVELOPE, menu and TP chains did not), and the manual poll passes it
explicitly.  A card that re-queues a command can no longer wedge the
card lock from any call site.

Tests: the PLI default + the built TR, the default chain bound and the
manual-poll call pin.  1061 Python + 1069 frontend; sw cache simple-v477.
v3.37.0
2026-10-24 01:51:35 +03:00
catarrh 643c6609f3 test runner: add the stuck-card watchdog
A card that stops answering (no SW, no driver error) leaves the runner
inside a blocking SCardTransmit while it holds the card lock - Stop
cannot take effect, the presence monitor waits for the lock and a load
test hangs in that iteration without explanation.

The runner now beats a progress heartbeat (each step start/finish and
each attempt of a bounded STATUS/drain loop); after _TEST_STEP_TIMEOUT
(30 s) the watchdog flags the run `stuck`, records the step and the idle
time, sets the stop flag and tries to cancel the blocked transmit by
disconnecting the PC/SC link on a daemon thread.  The disconnect must
run in the background: pcsc-lite serializes it behind the pending
transmit (both take the per-context lock), so a synchronous call would
block the watchdog instead of cancelling anything.  Where a platform
does cancel, the step fails through the ordinary on_fail path; either
way the finalisers map `stuck` to `error` - a stuck card is not a user
stop - and the pending drain is skipped on the dead link.  The
transport is marked stale once the disconnect returns, so the next equip
rebuilds it.

The run status gains `stuck` and `heartbeat`.  Help EN/RU,
docs/test-scripts.md, docs/api.md, the CHANGELOG (folded into the
unreleased 3.37.0) and both AGENTS describe the behaviour and the
background-disconnect caveat; the sw cache is simple-v476 (help is
precached).

Tests: tests/test_test_watchdog.py (17: the check rule, the abort, the
final status of the single/load/suite runs and a blocked-runner
integration case).  1058 Python + 1069 frontend.
2026-10-24 00:43:39 +03:00
catarrh 2181ef6bbf v3.37.0: load testing for test scripts and suites
POST /api/test/run takes an optional iterations count (the PWA's
Iterations field): the run repeats, successful iterations are discarded
(steps/log cleared, only load.done and the timing grow), the first error
stops the loop and keeps that iteration's report, a stop ends it after
the current iteration, and a card session change between iterations is
an error.  The SCP80 counter state is shared across iterations, so
packets continue from what the previous run consumed.

The status gains a load block (iteration/done/failed_iteration/
elapsed_ms/avg_ms; disabled for a normal run).  The suite runner was
split into a per-iteration body (_test_suite_execute_once) + the
single-run finaliser, and two load workers own the loops.

Review fixes: a raised runner error finalises as error (no stuck
running state), a suite-internal session change is an error, a stopped
member ends the suite loop, the final status keeps a warning from any
iteration, avg_ms covers the attempted iterations, and each suite
iteration's counters_before is read fresh.

1041 Python + 1069 frontend; simple-v475.
2026-10-23 23:44:42 +03:00
catarrh 851ff65af6 changelog: note the README Telegram channel link
release / release (push) Has been cancelled
v3.36.0
2026-10-23 21:46:10 +03:00
catarrh 68a22e854b readme: link the project news Telegram channel
Add @youeyeseesee (releases and updates for my GitHub projects) near the
top of both READMEs.
2026-10-23 21:44:45 +03:00
catarrh 39d384636e batch waiting cue: move the contact pad to the bottom edge
The card's contact pad sat mid-card; it now sits near the bottom edge,
away from the top-right cut corner (the cut side is the card's top).
No other geometry or animation changes.

1024 Python + 1063 frontend; simple-v473.
2026-10-23 21:41:30 +03:00
catarrh 14d15b1fe8 batch waiting cue: use the app's SIM silhouette for the card
Redraw the inline card-into-reader SVG after sim.svg: a rounded card
with the top-right cut corner and the 3x2 contact pad, sliding behind
the reader bar.  The viewBox grew to 72x48 and the resting position is
0, so the card is no longer clipped at the top; the insert slide is
20px.  The currentColor/Tailwind theming and the reduced-motion
fallback are unchanged.

1024 Python + 1063 frontend; simple-v472.
2026-10-23 21:37:15 +03:00
catarrh 110dc1f1ad batch delete: report an already-absent object as such
A delete run whose DELETE answered 6A88/6A82 is still a success (the
object is gone, the goal is met) but it is no longer reported as a plain
"OK": the attempt result is 'already_absent', the counts gain that
bucket ("Deleted: N - Already absent: M"), the verdict reads "Already
absent" and the notice says the card was already absent.  A reinstall's
tolerated pre-delete keeps the install outcome (the install is the
point).

1024 Python + 1063 frontend; simple-v471.
2026-10-23 21:23:55 +03:00
catarrh fb65644328 v3.36.0: a deliberate "insert the next card" cue for batch runs
The waiting phase is the only moment the operator must act, so it now:
- shows a card-into-reader SVG animation in the status modal (inline
  SVG, CSS keyframes in src/style.css; reduced-motion users get the
  static card-above-the-slot picture);
- plays a short Web Audio chime once per card, edge-triggered in the
  2 s poll so it never repeats (the AudioContext is unlocked by the
  Start click);
- sets the tab title to "Insert next card" while the window is hidden;
- offers a speaker toggle next to the status (persisted as
  simple_ram_batch_sound, on by default).

The prebuilt style.css was rebuilt: it had gone stale and lacked
utilities the UI already used (bg-blue-50 on the batch notice,
inline-flex, mx-2, max-h-48, w-44, min-w-24).

1022 Python + 1062 frontend green; simple-v470.
2026-10-23 21:14:32 +03:00
catarrh 194b88e7d1 changelog: fold the unreleased 3.34.0 entry into 3.35.0
release / release (push) Has been cancelled
v3.34.0 was committed but never tagged; its changes ship in the v3.35.0
release, so the changelog names only released versions (the same rule
applied to 3.32.3 -> 3.32.4 and 3.32.6/3.32.7 -> 3.33.0).
v3.35.0
2026-10-23 20:11:59 +03:00
catarrh d5e67360fe file manager: an enabled inactive read pill is outlined, not grey
The Read decoded pill used the same grey fill as a disabled control, so
an enabled pill looked unavailable (reported live).  The inactive-but-
enabled state is now an outlined button (blue on white/slate) and the
grey fill plus dimming are reserved for the actually disabled state.
The active pill keeps a border of the same width so the row does not
shift.

Pinned in fs_edit.test.js.  1022 Python + 1058 frontend; simple-v469.
2026-10-23 17:06:50 +03:00
catarrh 87bf4acbbc v3.35.0: EF.SMSP decoded editor (the full SMS-parameter form)
TS 31.102 4.2.27 scope: alpha; TP-DA and TS-SCA with their own length
conventions (DA = digits, SCA = octets, 0 = default SMSC) and a TON
select; TP-PID; TP-DCS with TS 23.038 hints; the relative TP-VP with the
TS 23.040 ladder; a per-parameter "absent (the ME default applies)"
state.  Unchanged slots are copied verbatim (the live fixture's
DCS-absent 00 and present VP FF survive), alphanumeric TON-5 addresses
stay read-only, and every checked record is validated before any write.

Decoder fixes: the parameter-indicator bits were shifted (SCA on 5,
PID on 2, DCS on 3, VP on 4; the spec is DA/SCA/PID/DCS/VP on bits 1-5
with 6-8 reserved, 1 = absent), and the TP-DA length was read as octets
instead of digits (TS 23.040 9.1.2.5).

Live fixture: ffffffffffffffffffffffffffe9ffffffffffffffffffffffff07919733824009f0ffffffff0000ff
(SCA +79332804900, PID 00, DCS absent/00, VP FF) round-trips
byte-identically.

1022 Python + 1058 frontend green; simple-v468.
2026-10-23 16:54:12 +03:00
catarrh 180dd50c06 v3.34.0: Edit decoded wave 2 — service tables, scalars, PLMN lists, BCD, CB lists
New client-side editors: the service tables EF.UST/IST/EST (a filterable
bit grid; unmodelled bits and the file length preserved), EF.ACC,
EF.PHASE, EF.HPPLMN, EF.AD (op-mode enum, additional info, MNC length
incl. 0 and the 3-byte SIM form), the PLMN-only lists
EF.PLMNsel/FPLMN/EHPLMN, EF.ICCID/EF.IMSI behind a confirmation, and the
cell-broadcast lists EF.CBMI/CBMID/CBMIR.

Framework: a bits form kind, a per-entry model(dec) hook, a per-entry
confirm prompt and hex fields.

Decoder fixes: EF.ACC class numbering (byte1 b8..b1 = 15..8, byte2 =
7..0), EF.AD operation mode (whole byte) and its MNC length rules,
EF.SST decoded with the SIM 2-bit allocated/activated layout (editing
stays disabled until that UI exists), and the EF.IMSI parity nibble
(9 odd / 1 even).  A no-edit save of the new list files no longer
writes all-FF (the collected rows and the encoders disagreed on the
value shape).

Decoded editing for EF.DIR/EF.ARR/EF.SMS/EF.PBR is postponed by
decision (read-only viewing is enough); the survey records it.

1022 Python + 1033 frontend green; simple-v467.
2026-10-23 14:21:00 +03:00
catarrh 101ef21e9f changelog: fold the unreleased 3.32.6/3.32.7 entries into 3.33.0
release / release (push) Has been cancelled
Both versions were committed but never tagged; their changes ship in the
v3.33.0 release, so the changelog names only released versions (the same
rule applied to 3.32.3 -> 3.32.4).
v3.33.0
2026-10-23 12:18:35 +03:00
catarrh d036ef79a1 v3.33.0: Edit decoded — SPN, PLMNwAcT family, ADN family, PNN
The file manager gains a client-side decoded editor.  EF_ENCODERS pairs
each editable EF with a pure encoder and a form schema (fields / list /
items); the file is read raw, the decoder fills the form, the browser
re-encodes it and the existing update path writes it.

Shapes: SPN (display-condition bits + Annex A text), PLMNwAcT/
OPLMNwAcT/HPLMNwAcT (rows with PLMN + ACT, add/remove),
ADN/FDN/SDN/MSISDN/LND/BDN (one form per record with a checkbox that
auto-checks on change; BCD, length and EXT handling; BDN's own X+15
layout) and PNN (per-record TLV items 43/45/80, add/remove, unknown
TLVs preserved).

Unchanged fields are copied verbatim (SSC/extended-BCD numbers, PLMN
rows including unmodelled satellite ACT bits, PNN codings/AddCI bits,
all-FF files, the BDN comparison-method byte), every checked record is
validated and encoded before anything is written, and edits that would
break a reference are refused.

Also: docs/decoded-editing.md (the spec survey behind the design and
the release waves), the efPlmnAct EC-GSM-IoT-only 0x88 fix, the PNN
spare-bit decode fix, and the /api/write|read cmd2-exception
false-success fix.

1022 Python + 1013 frontend green; simple-v466.
2026-10-23 11:55:31 +03:00
catarrh 9411fd775d v3.32.7: drop the pySim JSON disclosure from the file manager
The decoded view is the client-side EF decoder's table over the raw
read's bytes.  The collapsed "pySim JSON (server)" block was an
unrelated second decode - opening it ran another /api/read with mode
'decoded' (pySim's read_binary_decoded/read_records_decoded on the
card) - and it existed only when our decoder covered the file, so it
could not serve as a cross-check where one would matter.

/api/read keeps mode 'decoded' for API callers.  Help, READMEs and the
project notes now state the table is built in the browser from the same
raw bytes; ef_decode.test.js pins the view's output and that no
mode 'decoded' read remains in the PWA.

1016 Python + 955 frontend green; simple-v465.
2026-10-23 09:50:09 +03:00
catarrh ded63c94e4 v3.32.6: the empty preset banner no longer flashes on the RAM view
ramOpChanged() (v3.28.0) un-hid #ram-preset-warning by the operation
kind, but the banner's text and visibility belong to
spPresetWarningRender() (which already suppresses it for the batch
ops).  With no warning text the empty amber box stayed visible until
the next status poll re-hid it - a flash on every RAM view / operation
switch.  The visibility is content-driven again: the setHidden line is
gone and the renderer is called.

Pinned in frontend/tests/ram.test.js with the real renderer (the
pre-fix code fails the test).  1016 Python + 954 frontend green;
simple-v464.
2026-10-23 09:31:06 +03:00
catarrh 1174a340db changelog: fold the unreleased 3.32.3 entry into 3.32.4
The v3.32.3 changes (the traceback tail and the link-release wording)
were committed together with v3.32.4 and never tagged separately, so
the changelog named a version no release exists for.  The entry is
merged into [3.32.4] and the published v3.32.4 release body matches it.
2026-10-21 15:05:55 +03:00
catarrh f92eb8f131 v3.32.5: a card removed during the startup init leaves it cardless
release / release (push) Has been cancelled
Live log: a card was pulled while the server started and the TERMINAL
PROFILE send raised CardConnectionException 0x80100016
(SCARD_E_NOT_TRANSACTED).  The init's except caught it (no crash), but
the code then continued with the card object it had already built:
card_present and _CARD_CONNECTED became True - a phantom equipped card -
and the auto-equip's _CARD_CONNECTED early return then skipped every
later insertion, leaving only a manual Equip or a restart.

A PC/SC error in the startup init now prints one line (a traceback is
noise for a normal removal), clears the locals that feed those flags and
calls _clear_app_card_state(app); the presence monitor equips the next
card.  Non-PC/SC errors keep their traceback.

Pinned in tests/test_startup_cardless.py (the branch and the flag
sources).  1016 Python + 953 frontend green; simple-v463.
v3.32.5
2026-10-21 14:52:12 +03:00
catarrh 7e8c483eb1 v3.32.4: rebuild the link on card insertion (the first attempt)
release / release (push) Has been cancelled
v3.32.2 fixed the endless re-arm, but on Windows the first attempt
still failed on the old link (seen in the confirmation log: the
"self._con.connect()" traceback) and only the retry recovered - a failed
init, a 2-4 s delay and a traceback in the log.

The monitor's insert path now marks the transport stale under
_CARD_LOCK, so the first equip attempt rebuilds the PC/SC link.  It also
covers a link created at a cardless startup (never connected).  The
rebuild still happens in the equip worker, never in the pyscard monitor
thread, and _ensure_transport releases the old link first.

Docs: the READMEs' troubleshooting bullet.
Tests: an insertion marks the transport for the rebuild.
1014 Python + 953 frontend green; simple-v462.
v3.32.4
2026-10-21 14:44:34 +03:00
catarrh 8a08f642d4 v3.32.2: the Windows card-swap auto-equip loop
release / release (push) Has been cancelled
After a card swap (or the first insertion after startup) on Windows the
auto-equip looped forever: "card inserted, initializing" / "card gone
during initialization", with no "retry 2/3" lines.

An equip with the card still present produced no card (app.card None),
and that branch reported "card gone" and returned True - a no-op success
- so the three retries never ran and the watchdog re-armed every ~5 s.

- The card-gone no-op is now only for a real removal (card_present
  False); a card-present init that produced no card raises a failure.
- Any failed attempt marks the transport stale, so the next attempt
  rebuilds the PC/SC link (a Windows swap can leave the old link unable
  to connect; _ensure_transport releases the old link before building
  the new one, so the 2026-09-24 stale-handle bug cannot recur).
- The error pySim swallowed (cmd2) is printed as "AUTO-EQUIP: init
  output:" with its traceback - the failure is diagnosable from the log.
- Docs: the READMEs' troubleshooting bullet.
- Version strings + the PWA cache bumped together (simple-v460).

Tests: a card-present init without a card is a failure, a mid-init
removal stays a silent no-op, any failure marks the transport, the
stale-handle traceback reaches the log, and an end-to-end retry: a
failed init, the old link released, a fresh one built, the retry
succeeds.  1012 Python + 953 frontend green.
v3.32.2
2026-10-21 14:35:44 +03:00
catarrh cdd3820064 v3.32.1: "Start counter value" in the outfile import
release / release (push) Has been cancelled
The import created presets with the store's default counter (1).  Cards
whose counters are already past it had to be edited preset by preset
afterwards.

- The outfile preview form gains a "Start counter value" field (default
  1, the store's value).  The value is written into every created keyset
  of every imported card - parse_outfile's `start_counter`, 1-10 hex
  digits, whitespace stripped and uppercased; a bad value raises and the
  endpoint answers 400.  Without it the store's default still applies
  (the field is omitted), so the API keeps its old behaviour.
- The preview shows the counter; an import stores the protocol's
  10-digit form (counter_hex).
- Docs: api.md (the field), help EN/RU (the form).
- Version strings + the PWA cache bumped together (simple-v459).

Tests: the parser (every keyset, normalisation, unset, the bad values),
the endpoint (the preview's keysets, the 400, the stored padded form),
the PWA (the field's default, the controls' normalisation, both bodies).
1008 Python + 953 frontend green.
v3.32.1
2026-10-21 13:20:28 +03:00
catarrh aa8c9c6f12 v3.32.0: the packaged-install updater + a repo CHANGELOG
release / release (push) Has been cancelled
Reinstalling the package was slow because setup rebuilds pysim's
prerequisites (pyscard compiles from source on Linux), while a clone
updates with a plain `git pull`.

- tools/update.py (+ update.sh / update.bat in the package, and the
  release workflow shipping them) updates a packaged install in place:
  it downloads the latest release package, extracts it (zip-slip guard,
  permission bits restored - zipfile drops them), replaces the app files
  over the app directory and re-installs the app into the existing
  .venv.  The .venv and simple_lastrun.log are not in the package, so
  pysim and its prerequisites survive: seconds, not a rebuild.
  --check / --version / --zip (offline) / --no-pip / --force; the same
  version is a no-op.
- CHANGELOG.md (Keep a Changelog, from the first packaged release;
  earlier history is the git log).  The release workflow now uses the
  version's CHANGELOG section as the release body
  (tools/changelog_section.py, falling back to generated notes).
- The READMEs document the update path and the `git clone --depth 1`
  option for the clone path; setup prints the update hint.
- Version strings + the PWA cache bumped together (simple-v458).

Verified: 1007 Python + 953 frontend; the release-package test covers
the new files; the updater was exercised end to end (a v3.31.2 install
with a real venv updated to the 3.32.0 package: files replaced, the
venv marker kept, pip metadata 3.32.0, exec bits kept, a second run a
no-op, --check correct).
v3.32.0
2026-10-21 00:35:47 +03:00
catarrh 2237a35db5 v3.31.2: the SCP81 TLS-PSK Python floor, documented and guarded
release / release (push) Has been cancelled
ssl's TLS-PSK callbacks (PskTlsServer) exist only since Python 3.13 - the
first CI run on 3.12 errored in all 20 PSK tests, and the docs confirm
"Added in version 3.13".

- PskTlsServer._make_context raises a clear RuntimeError naming the
  requirement instead of AttributeError; a source pin keeps the guard.
- tests/test_scp81.py skips PskTlsServerTest where the callbacks are
  missing (it runs on 3.13+), so contributors on 3.9-3.12 no longer see
  20 failures.
- The READMEs state the requirement, and the Windows notes now strongly
  recommend Python 3.13: pyscard has wheels up to 3.13, and on 3.14
  pyscard and some pysim dependency wheels are unavailable and build
  from source (MSVC).  setup.bat and setup.sh print the same guidance.
- Version strings + the PWA cache bumped together (simple-v457).

998 Python + 953 frontend green.
v3.31.2
2026-10-20 23:47:10 +03:00
catarrh bccca45486 ci: install the frontend npm deps before the suite
The v3.31.1 run's frontend suite failed on sp.test.js: require('des.js')
(aes-js likewise) needs the npm dependencies, which exist in a working
tree but not on a fresh runner.  The workflow now runs npm ci in
frontend/ (the lockfile is checked in) before node --test.
2026-10-20 23:39:15 +03:00
catarrh 648c676024 ci: run the release suite on Python 3.13 (TLS-PSK needs it)
The v3.31.1 run's Python suite failed: ssl.SSLContext.set_psk_server_callback
(SCP81's PskTlsServer) was added in Python 3.13 - the job used 3.12 and all
20 SCP81 PSK tests errored.  The workflow now pins 3.13; the package itself
is the tagged source and is unaffected by this CI-only change.
2026-10-20 23:36:50 +03:00
catarrh 8f2d9732d6 v3.31.1: the Linux PC/SC headers (pyscard builds from source)
release / release (push) Has been cancelled
The first release run failed installing the test dependencies:
pyscard 2.3.1 publishes macOS and Windows wheels only (no manylinux),
so on Linux it always builds from source and needs the PC/SC headers -
the job stopped at "winscard.h: No such file or directory".

- The release workflow installs libpcsclite-dev before the dependencies.
- The READMEs and setup.sh name the header package next to pcscd/ccid:
  libpcsclite-dev (Debian/Ubuntu), pcsc-lite-devel (Fedora), included
  in Arch's pcsclite.  This affects every Linux user running setup, not
  just CI.
- Version strings + the PWA cache bumped together (simple-v456).

No runtime change: 997 Python + 953 frontend green; the release package
test builds with the new version.
v3.31.1
2026-10-20 23:34:15 +03:00
catarrh 9ee9c85b09 release: the curated release package + a tag-triggered workflow
release / release (push) Has been cancelled
The READMEs now point users at a release package instead of a clone; this
adds the machinery behind it.

- tools/build_release.py builds dist/simple-<version>.zip: the runtime
  tree (the server package incl. its bundled MCC/MNC list, the PWA
  runtime files, docs/, tools/, the setup/start scripts, pyproject,
  requirements-pysim.txt, the READMEs, LICENSE and THIRD-PARTY-NOTICES)
  under a single top-level directory, with no tests, no frontend
  sources/build config, no bytecode and no git metadata.  The version
  comes from server.py; --expect-version guards a mislabeled tag.
- .github/workflows/release.yml runs both test suites, builds the
  package and publishes it on a v* tag (workflow_dispatch for an
  existing tag); a re-run uploads with --clobber.  pysim is checked out
  next to the repo for the Python suite and is NOT bundled.
- tests/test_release_package.py pins the package's shape: the runtime
  files, the absence of the development files, and that sw.js's
  precache list is fully covered - so a new asset cannot silently go
  missing.
- dist/ is gitignored; the READMEs name the artifact.

Verified: 997 Python + 953 frontend green; the built zip (53 files,
~0.95 MB) was unpacked in a clean directory and its setup.sh installed it
into a fresh venv - the console script runs and the installed server
resolves web_dir to the unpacked frontend/ (index.html, sw.js,
uicc_files.json present).  The workflow YAML parses; GitHub-side
execution is unverified until the first tag is pushed.
2026-10-20 23:29:44 +03:00