Explore still missed the F0414C46416101 package on a card whose responses
wrap the R-APDU in the TS 102 226 5.2.2 Response Scripting template
(`AB <len> 80 <count> 23 <len> <R-APDU>`): `_decode_por` parsed that as a
compact response, so the frontend got `last_status_word` 81d0/7680 and data
starting `80 01 01 23 ...` instead of the listing.
- server: `_parse_response_scripting()` (AB definite / AF 80 ... 00 00
indefinite) extracts the executed-command count and the last R-APDU's SW
and data; `_decode_por` exposes it as `response_type: scripting` in the
same `decoded` shape as compact, so the RAM explore paging and the RAM
install `por_sw` see the real 9000/6310.
- frontend: the compact listing walk is deterministic on the AID length
(`len AID life ver`; P1=10 adds `module_count (len module_AID)*`).
`_parseRawAppEntry` no longer guesses `rawLen-1` for >8-byte AIDs (16-byte
A113 applet AIDs were truncated), and `_parseRawElfEntry` no longer scans
for `0x10` (a length/AID byte equal to 0x10 derailed the walk: a live page
parsed to 1 entry with no F0414C46416101).
- tests: exact trace fixtures - the ELF page lists F0414C46416101 (11
entries), the P1=10 page attaches its F0414C4641610101 module, the app page
keeps the 16-byte A113 AIDs; Python covers the scripting template
(definite/indefinite) against the live `AB 12` ISD and listing vectors.
610 frontend / 495 Python green; version 3.6.5; sw simple-v278.
Explore returned partial data (no ELF/module entries, the installed package
missing) and then failed with cntr_low. Two causes, both in the
actual-response SMS-SUBMIT path the card uses for big listings:
- `_find_sms_tpdu` read TLV lengths as a single byte; the FETCH carries
`8B 81 97 ...` (BER long form) for the big pages, so it returned a
corrupted, truncated TPDU.
- `_calc_ud_offset` treated the SMS-SUBMIT relative validity period (VPF=10)
as 7 bytes instead of 1, shifting the UD offset: `_parse_sms_concat` then
read a bogus UDH and reported no concatenation, so the segments never
assembled and the PoR/data was dropped ("RAM RESPONSE-PACKET: empty").
The step was marked failed, the counter did not advance, the same counter
was retried and the card answered `cntr_low`, which also blocked P1=10
(modules) where the installed package appears.
- the captured segments now always store the assembled UD (`submit_ud_hex`);
`_sms_submit_por()` rebuilds the DELIVER-style packet (`02 71 00` + UD) for
`_decode_por`. Verified against the live capture: por_ok, remote SW 6310,
438 hex chars of listing data (the exact bytes of the P1=20 page).
- `spPorAccepted` counts `actual_response_sms_submit` (0x0B) as accepted, so
the counter advances when the data follows via SMS-SUBMIT.
Explore queries follow GP Card Spec v2.3.1 11.4.2.2: compact listings
(P2.b2=0) with the chained GET RESPONSE (`ramGetStatusApdu`, paging P2=00 ->
P2=01), the malformed P2=02 attempt is gone, and the ISD-only query (P1=80)
never pages with next-occurrence (the card shall reject it).
Tests: Python SmsSubmitCaptureTest with the live FETCH bytes (TPDU length,
UD offset, concat reassembly, 027100+UD decode); ram.test.js checks the APDU
builder and the missing P2=02 attempt; cards_counter covers 0x0B.
608 frontend / 492 Python green; version 3.6.4; sw simple-v277.
The SCP80/RAM forms hold a copy of the preset (counter, keys, TAR, SPI).
Editing the preset on the Cards tab saved correctly, but the form kept the
old copy: the operation sent the stale counter (the card answers cntr_low)
and the post-send sync wrote the stale value back over the preset - the
saved counter silently reverted. Reproduced in a real DOM:
after select in SCP80: preset=0000000001 sp=0000000001
after Cards edit+save: preset=00000000AA sp=0000000001
after a sync: preset=0000000001 (edit lost)
- `cardsApply()` split into `cardsApplyFields()` (field copy, no packet) and
`cardsApply()` = fields + genSp; new `spRefreshFromPreset(selId)` re-reads
the selected preset from `sp-card-sel` / `ram-card-sel` and re-applies it.
- `pysimSendOta()` and `ramExecute()` call it before starting, so every
SCP80/RAM operation uses the preset as it is now.
- A rejected send no longer advances the counter: new `spPorAccepted(por)`
gates the advance+write-back in `pysimSendOta`, the Explore pagination and
its GET DATA step, and the server's RAM install (`_ram_next_cntr`: advance
only for `por_ok`/`no_por` steps). A failed install still returns
`final_cntr` (the accepted prefix) and the PWA persists it, so a retry
never replays a counter the card already consumed.
- tests: cards_counter.test.js (the stale-form regression, RAM selector,
fields-without-genSp, spPorAccepted) and `_ram_next_cntr` cases; the
cards_form/ram harnesses updated for the split.
- docs/api.md counter semantics; AGENTS preset-source-of-truth rule.
607 frontend / 488 Python green; version 3.6.3; sw simple-v276.
The RAM installer read `por['decoded']['response_status']`, but the compact
response decoder's `decoded` only carries {number_of_commands,
last_status_word, last_response_data} - so the suffix was always empty and
every step showed the useless `por_error_`, even when the PoR was por_ok.
The PoR verdict is the top-level `response_status`.
With that fixed, the decoded details also show that the remote command's own
status word was the real verdict all along: a captured live install returned
por_ok with `last_status_word` 6700/6F00 (the card rejected every RAM APDU)
while the installer reported success.
- new `_ram_step_result()`/`_por_remote_sw()`/`_ram_remote_sw_ok()`: a step
fails on a non-por_ok PoR, on a remote SW outside the success set (9000,
61xx more data, 62xx/63xx warnings, CAFE GP "more data"), or on an
undecodable PoR (a 9000 transport SW with no PoR at all is `no_por`, not a
failure). A decoded 61xx is explicitly success, per GP/ISO.
- step records gain por_sw/por_type/por_cntr/por_data/por_raw and
`por_error`; the response carries `failed_step` and a detailed error such
as `LOAD (1/9): remote SW 6700`; the log line now prints
`status=por_ok remote_sw=6700`.
- PWA: new pure `ramStepLine()` renders e.g.
`❌ Шаг 2: LOAD (1/9) — PoR ok · remote SW 6700 (Wrong length in Lc) · 274 B / 3 SMS`
(SW meaning via the existing lookupSw decoder) and the transport SW only
when it is not 9000.
- tests: tests/test_ota_helpers.py RamPorStepTest (success set incl. 61xx,
the captured 6700 failure, no-PoR/undecodable, expanded responses);
ram.test.js ramStepLine cases.
- docs/api.md RAM install step fields + failure semantics; AGENTS updated.
603 frontend / 487 Python green; version 3.6.2; sw simple-v275.
The Import component (JC VM spec 6.6) is now exposed by /api/cap-info and
rendered in the CAP analysis box:
- imports: the libraries the CAP is linked against with the export-file
versions and the number of distinct constant-pool references (6.7),
displayed as "name >= version" (a card resolves an import only with the
same major and a minor >= the recorded one, 4.5.2). Standard names come
from the AID table; each gets a family label (Oracle JavaCard / ETSI SIM
2G / ETSI UICC / 3GPP USIM-ISIM / GlobalPlatform) and, for
javacard.framework, a Java Card SDK release hint derived from the local
Oracle SDK kit corpus (jc211..jc305u4 exports; unknown versions stay
unhinted). Vendor/applet AIDs stay bare.
- Header package flags (Table 6-4: int / exports / applet package) and the
optional JC 2.2 package_name (absent in all CAP 2.1 files).
- components: every archive entry in load-file order with its size and
share of the load file (including the Directory/Export entries capmem
does not parse); the sizes sum to load_file_bytes.
- The PWA box leads with "Requires: ..." when imports exist, keeps the
compiled-against line (Java Card hint + CAP format), the package/applet
identity, the import details (family, refs, AID) and the component
breakdown in Details; a memory-only response still renders.
Tests: Python +2 (imports/flags/name/components; header flags + package
name) with the synthetic CAP builder extended; frontend +2 renderer cases
(unknown AIDs, no-import responses) plus jcAidNorm/jcAidFamily tests; the
jcAidNorm extraction added to the ram/scp81 harnesses.
Help EN/RU, docs/api.md, AGENTS.
591 frontend / 473 Python green; version 3.5.16; sw simple-v269.
The RAM Explore view, the SCP81 GET STATUS script results and the R-APDU
parser tree showed package AIDs as bare hex. A shared resolver now
annotates the known standard JavaCard / ETSI / 3GPP / GlobalPlatform
package AIDs with their library name (workspace export-file study
`docs/JAVACARD.md`, 2026-09-26, plus the GP default ISD AID from GP Card
Spec v2.3.1 H.1.3); a RID table gives a weak owner hint for otherwise
unknown AIDs, and vendor/applet AIDs stay bare.
- JC_AID_NAMES / JC_AID_RIDS + jcAidName / jcAidSuffix / jcAidHtml.
- Wired into ramRenderExploreHtml (ISD, applications, ELFs, module and SD
AIDs), scp81ResultLines GET STATUS listings and decodeTlvValue
(4F/84/C4/CC - the R-APDU parser tree).
- aid_names.test.js (families, normalisation, RID hints, malformed input,
table sanity) plus render assertions in ram.test.js and scp81.test.js.
- Help EN/RU note the annotation; table is hand-maintained from the note.
561 frontend / 421 Python green; version 3.5.7; sw cache simple-v260.
- ramOpChanged() now clears the previous execution status (result line,
steps, explorer, progress) only when the Operation value actually
changes, so switching Explore -> Install Package no longer shows the
old 'Partial - ELF Modules: no data' until Execute; re-entering the
RAM subtab still preserves the last result
- ramClearResults() also hides the progress caption
- the Card preset dropdown no longer resets to the placeholder after
every executed operation (ramSaveCntr -> ramRender rebuilt it) or on
subtab re-entry: ramRender keeps the current/remembered index,
ramApplyCard and ramExecute remember the selection for the session,
and cardsRemove keeps _ramCardIdx aligned via ramCardIdxAfterRemove
- tests: op-change clearing, selection preservation across rebuilds,
pick/execute remembering, index bookkeeping; SW cache v126 -> v127.
The RAM explorer HTML is generated after load, so its data-l10n sections
and Delete/Delete All buttons stayed English until a language switch, and
the memory/AID/lifecycle labels were hardcoded with no key at all.
- ramRenderExploreHtml() now builds every label and button with t()
(reusing the existing Delete/Delete All/heading/AID keys and wiring the
three previously-unused Application/Instance, Load File and Module AID
keys); new RU keys cover Applications:, Free NV:, Free Volatile:, AID:,
Lifecycle:, Privileges:, SD AID:, Implicit sel:, Version: and (none)
- the explorer dataset is cached in _ramExplorerData and re-rendered by
refreshDynamicI18n() on language switch (cleared on op change/results
reset)
- RAM flow strings localized too: Partial/OK summaries, error labels
(Memory/ISD/Apps/ELFs/ELF Modules/no data), GET DATA progress, delete
confirm/labels, install alerts/progress/steps and Error:
- tests: ram.test.js asserts every explorer label/button goes through t()
and the (none) placeholder; SW cache v125 -> v126.
Key fix: re-add chained C0000000 (GET RESPONSE Le=00) in paginate() so the
full GP APDU inside SCP80 is 80F2<p1>p2024F0000C0000000, matching the
working tool. The card's SCP80 layer executes both commands internally
(GET STATUS → 61XX → GET RESPONSE) and puts the final 9000 + data in
the PoR.
Also: server logging improvements (RAM RESPONSE-PACKET label, no truncation
of FETCH/PoR hex), docs for /api/ram-install endpoint, minor test fix.