v3.29.0: the batch kinds (delete / reinstall) + the delete-package pre-step

The batch plan is a list of named steps ({name, apdu, delete}) now and
_ram_install_run generalized into _ram_plan_run; POST /api/ram-batch/start
dispatches on kind:
- install-cap (default; delete_package prepends a cascade DELETE on the
  package AID - a leftover package blocks INSTALL [for load]),
- reinstall-app (_ram_reinstall_plan: DELETE the applet instance + INSTALL
  [for install] from the loaded package - no .cap, no LOAD),
- delete (_ram_delete_plan: one GP DELETE, aid + target app/package ->
  P2=00/P2=80).

A delete step tolerates 'not found' (6A88/6A82 - the object is already
gone, the goal is met); any other delete failure stops the chain with the
delete's reason.  The policy serves all three kinds and the single
/api/ram-install-app, which now deletes the instance first (make
selectable stays one step); /api/ram-install takes delete_package too.

The status/summary carry the run's kind (the panel labels follow:
installed/deleted/reinstalled) and each attempt carries the last three
steps (steps + steps_total) for the panel's per-step detail.  The PWA's
three batch entries are grouped at the end of the operation selector (an
optgroup); ramIsBatchOp() is the one source for "per-card batch".

Docs: help EN/RU, api.md, the repo AGENTS.  Tests: the plan builders per
kind, the validations, the delete-package prepend, the not-found
tolerance, the kind dispatch/status/notice, the steps tail, the endpoint
steps, the PWA wiring.  925 frontend + 943 Python; sw cache simple-v447.
This commit is contained in:
2026-10-15 00:27:52 +03:00
parent 5ae8b1fdd1
commit cf7ee77a9e
11 changed files with 766 additions and 228 deletions
+5 -3
View File
@@ -321,10 +321,12 @@
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 (Extended Card Resources: the number of installed applications and the free non-volatile/volatile memory). The compact listing carries the AID, life cycle and privileges (the legacy format has no version); if a card rejects it (<code class="font-mono text-sm">P2.b2=0</code>, deprecated by GP) the queries are retried in the expanded <code class="font-mono text-sm">E3</code> form, and the <strong>Fetch expanded registry details (load-file / SD associations)</strong> checkbox also requests it as an extra pass and merges the expanded-only fields: the Executable Load File version, Load File AID, Security Domain AID, module lists, the applet STK menu entries (SCP Registry Data, TS 102 226 8.2.1.6) and any other registry data objects the card returns (shown raw, never dropped) &mdash; it costs a few extra secured packets per Explore and is <strong>on by default</strong> (remembered per browser; untick to skip). Cards differ in what they accept, so the expanded query is tried in three forms &mdash; the reference tag list (with the TS 102 226 <code class="font-mono text-sm">EA</code>), the same list without <code class="font-mono text-sm">EA</code>, and no tag list at all (the GlobalPlatformPro form) &mdash; and the explorer reports which form worked and where the run fell back to the compact listing (package versions are then unavailable). When the ELF rows still carry no version, the tag-list forms are retried for both P1=10 and P1=20, skipping the combinations already refused, and the walk stops as soon as a version appears. Results appear in an explorer view (the success line stays minimal; the counts are in the section headings) with per-item <strong>Delete</strong> buttons and the detected RAM command format (hover for what compact vs expanded means).</td></tr>
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1&ndash;240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) &mdash; grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. The <strong>Platform</strong> line names the minimum Java Card level the card must provide (derived from the <code class="font-mono text-sm">javacard.framework</code> import; the header <code class="font-mono text-sm">int</code> flag additionally requires int support, Java Card 2.2+) and the <strong>ETSI release</strong> the UICC API imports come from (<code class="font-mono text-sm">uicc.toolkit</code> version &rarr; REL-N, per the workspace <code class="font-mono text-sm">sim-tools</code> corpus; shown with its basis in brackets): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the <strong>Import</strong> component is complete (the step line carries the card's remote SW and names the component the block completed, e.g. <em>completes Import</em>): the failure hint and the popup then point at the <strong>CAP compatibility test</strong> (one click &mdash; the same <code class="font-mono text-sm">.cap</code> is still selected) to find the missing package and version; a smaller LOAD block only helps when the block exceeds the card's buffer. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 &mdash; card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested <strong>C6</strong> follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data).</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Batch install (.cap file, per card)</td><td class="py-1 px-2">The bulk workflow: one <code class="font-mono text-sm">.cap</code> and one set of install parameters, applied to card after card. <strong>Start batch</strong> needs no card &mdash; the server owns the loop: it waits for a card, installs the moment the equip finishes, then waits for the next one. Every card's <strong>preset is matched by ICCID</strong> and supplies the keyset (the ISD role TAR's default, v3.26.0), the keys and the counter; a card with no matching preset or no usable ISD keyset is reported and skipped. Each attempt lands in the list with its time, ICCID, preset, verdict and duration; a failure shows the failing step and the remote status word &mdash; the batch never retries and never stops on a failure (failures are yours to manage afterwards). A card is attempted <strong>once per run</strong>: re-equipping it reports &ldquo;already processed&rdquo; &mdash; restart the batch to retry it. <strong>Stop</strong> finishes the current card first (an interrupted LOAD chain can leave a half-loaded package) and then ends the run; <strong>Clear</strong> drops the finished report. The loop runs in the server, so closing the tab, switching views or reloading the page does not interrupt it; a server restart ends it. A batch and a test run exclude each other.</td></tr>
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1&ndash;240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) &mdash; grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. The <strong>Platform</strong> line names the minimum Java Card level the card must provide (derived from the <code class="font-mono text-sm">javacard.framework</code> import; the header <code class="font-mono text-sm">int</code> flag additionally requires int support, Java Card 2.2+) and the <strong>ETSI release</strong> the UICC API imports come from (<code class="font-mono text-sm">uicc.toolkit</code> version &rarr; REL-N, per the workspace <code class="font-mono text-sm">sim-tools</code> corpus; shown with its basis in brackets): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the <strong>Import</strong> component is complete (the step line carries the card's remote SW and names the component the block completed, e.g. <em>completes Import</em>): the failure hint and the popup then point at the <strong>CAP compatibility test</strong> (one click &mdash; the same <code class="font-mono text-sm">.cap</code> is still selected) to find the missing package and version; a smaller LOAD block only helps when the block exceeds the card's buffer. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 &mdash; card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested <strong>C6</strong> follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data). The <strong>Delete the package before installing (with its applications)</strong> checkbox prepends a cascade GP <code class="font-mono text-sm">DELETE</code> on the package AID (the load file with its modules and applications, P2=<code class="font-mono text-sm">0x80</code>) &mdash; a leftover package blocks INSTALL[for load]; 'not found' (a fresh card) proceeds, any other delete failure stops the chain with its reason.</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Batch install (.cap file, per card)</td><td class="py-1 px-2">The bulk workflow: one <code class="font-mono text-sm">.cap</code> and one set of install parameters, applied to card after card. <strong>Start batch</strong> needs no card &mdash; the server owns the loop: it waits for a card, installs the moment the equip finishes, then waits for the next one. Every card's <strong>preset is matched by ICCID</strong> and supplies the keyset (the ISD role TAR's default, v3.26.0), the keys and the counter; a card with no matching preset or no usable ISD keyset is reported and skipped. Each attempt lands in the list with its time, ICCID, preset, verdict and duration; a failure shows the failing step and the remote status word &mdash; the batch never retries and never stops on a failure (failures are yours to manage afterwards). A card is attempted <strong>once per run</strong>: re-equipping it reports &ldquo;already processed&rdquo; &mdash; restart the batch to retry it. <strong>Stop</strong> finishes the current card first (an interrupted LOAD chain can leave a half-loaded package) and then ends the run; <strong>Clear</strong> drops the finished report. The loop runs in the server, so closing the tab, switching views or reloading the page does not interrupt it; a server restart ends it. A batch and a test run exclude each other. The batch shares the install form, so the <strong>Delete the package before installing</strong> option applies to every card.</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Batch reinstall app (per card)</td><td class="py-1 px-2">The repair workflow: for every equipped card, delete the applet instance (tolerating 'not found') and run INSTALL [for install] again from the already loaded package &mdash; no <code class="font-mono text-sm">.cap</code> and no LOAD. The fields are the single INSTALL [for install] parameters (Load File / Module / Instance AIDs, privileges, the toolkit/STK parameters, <em>make selectable</em>); the preset, keyset and counter come from each card's own preset like the other batch operations, and the attempts list shows both verdicts (delete and install).</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Batch delete (per card)</td><td class="py-1 px-2">Deletes one object on every equipped card: an application (instance only, P2=<code class="font-mono text-sm">0x00</code>) or a package with its applications (cascade, P2=<code class="font-mono text-sm">0x80</code>). Fields: the AID and the target. A 'not found' result (the object is already gone) is reported as such and counted OK; any other failure is reported with the card's status word.</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">CAP compatibility test</td><td class="py-1 px-2">Answers one question without installing anything: <strong>can this card satisfy the CAP's import list?</strong> The server runs the format check, INSTALL[for load] and the LOAD blocks only up to the block that completes the <strong>Import</strong> component &mdash; the point where the JCRE verifies the import list &mdash; then stops: no last-block flag and no INSTALL[for install], so nothing is committed and nothing has to be deleted afterwards. The <strong>Import versions to test</strong> list is pre-filled from the CAP analysis: <code class="font-mono text-sm">AID=version</code> rewrites an import's requested version, <code class="font-mono text-sm">+AID=version</code> appends a synthetic import (a card-capability query: the LOAD passes only if the card exports that package at &ge; that version), and <strong>Set all 0.0</strong> drops every requirement. Two-step use: press Set all 0.0 &mdash; if the test passes, the import list was the blocker; then restore one line at a time to find the package and version the card lacks. The verdict states it covers the <strong>LOAD/import gate only</strong>: the link gate (whether the applet's actual API calls exist) is what a real install checks at INSTALL[for install]. The run and its result appear in a popup (like the install): the step lines, the verdict and a per-AID detail block &mdash; on a rejection it names the CAP section the failing block covers (load-file byte range and components, and whether that block completes <strong>Import</strong>), the remote status word with its meaning, the import list exactly as tested (CAP versions, probe rewrites, appended synthetic entries, unmatched lines) and what the probe state implies; on success it lists the import list that passed.</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">INSTALL [for install] (loaded package)</td><td class="py-1 px-2">One INSTALL [for install] APDU for a package that is already loaded on the card (its Load File AID and Module AID, privileges, install/STK parameters and an optional <em>make selectable</em>) &mdash; iterate the install parameters without deleting and re-loading the <code class="font-mono text-sm">.cap</code> every time.</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">INSTALL [for install] (loaded package)</td><td class="py-1 px-2">For a package that is already loaded on the card (its Load File AID and Module AID, privileges, install/STK parameters and an optional <em>make selectable</em>). The applet instance is deleted first (tolerating 'not found' &mdash; a fresh card just installs; any other delete failure stops the chain with its reason), so the install parameters can be iterated without re-loading the <code class="font-mono text-sm">.cap</code>.</td></tr>
<tr><td class="py-1 px-2">INSTALL [for make selectable]</td><td class="py-1 px-2">One INSTALL [for make selectable] APDU (the instance AID) for an INSTALLED, non-selectable applet instance.</td></tr>
</tbody>
</table>