Skip to main content

SIM Bank — distributed SIM inventory

The SIM Bank shows every SIM in every smart-card reader across every OmniWeb agent. Each agent scans its local PC/SC readers, reads each SIM's identity, and reports the inventory to central exactly like it already reports modems/phones as devices. Central aggregates the inventory per agent and persists a database of SIMs keyed by ICCID, so the "what SIMs are where" view survives reader and agent disconnects.

This is the inventory half of a distributed remote-SIM testbed. The osmo-remsim card-emulation/banking layer that lets you connect any SIM to any cardem over the network is documented separately in Remote SIM (osmo-remsim); this page covers discovery and inventory. Saving, exporting and reusing a card's file system as a portable profile package is covered in SIM Images (SAIP Profile Packages).

The page is reader-centric: one row per physical reader slot, grouped by agent, so empty, unreadable and in-use slots all show — the bank layout is the truth, not just the populated slots.

SIM Bank — one row per physical reader slot, grouped by agent, with card presence and identity


How the agent reads a SIM

The agent scanner mirrors the modem scanner: enumeration is slow, so results are cached and rescanned on an interval.

For each PC/SC reader on the host:

  1. Enumerate the PC/SC readers attached to the host.
  2. Probe each reader with an exclusive connect.
    • No card → present=false.
    • Connect raises a sharing violation → the reader is held by another process (e.g. an osmo-remsim bankd or a card-emulation session) → present=true, in_use=true, and no read is attempted. A busy reader never crashes the scan.
    • Card present and free → read it.
  3. Read the SIM identity with a self-contained set of raw APDUs:
    • UICC-vs-classic SIM detection (select 3F00; 6E00 → classic GSM, so use CLA=A0 and classic selection mode),
    • EF.ICCID (2FE2), EF.IMSI (6F07), EF.SPN (6F46),
    • EF.AD (6FAD) for the MNC length (so MCC/MNC are split correctly),
    • a safe default-PIN (0000) VERIFY on a locked card — it probes the retry counter first (a non-decrementing empty VERIFY) and refuses if only one retry remains, so it never burns the last retry or touches the PUK (a SIM bank must not brick cards),
    • the card ATR.

Each reader becomes a dict:

{
"reader_name": "Identiv SCR35xx USB Smart Card Reader …",
"reader_index": 0,
"present": true,
"in_use": false,
"iccid": "8961…", "imsi": "505…", "mcc": "505", "mnc": "01",
"country": "Australia", "spn": "Telstra", "atr": "3b9f96…"
}

The list is sent to central as a sims message (alongside the device list) on register and whenever it changes; a simbank_rescan command forces an immediate uncached scan.

Card-reading capability

A host reads SIMs through its PC/SC smart-card stack. Where deeper card decoding is needed, the agent uses an optional, separately-licensed smart-card library kept isolated from the rest of the agent (it is not merged into OmniWeb's own code). A host without the smart-card stack still runs the agent fine and simply reports no SIMs — the SIM bank is an optional per-host capability, like modems.

The reference reader is an SCM/Identiv SCR35xx. Multiple readers per host are supported.


Inventory → central → UI flow

agent host                         central (android-role process)         browser
────────── ────────────────────────────── ───────
simbank.list_sims() ──sims──▶ android agent_socket
(PC/SC read) └─ registry.update_sims(agent_id, …) GET /api/simbank/sims
└─ simbank.record_sims(…) ─▶ DB GET /api/simbank/readers
POST /api/simbank/agents/<id>/rescan
◀─simbank_rescan── (forces an uncached scan)
  • The agent dials out to central over the same WebSocket it uses for phones (/api/android/agent). SIM inventory rides that connection — there is no new transport.
  • Central holds the live per-agent reader snapshot in the in-memory android.Registry (update_sims / all_sims), and mirrors each ICCID into the persistent simbank_sims table via simbank.record_sims.
  • Because this depends on the single-process agent Registry, the SIM-bank API is served only by the agent-role backend process (same constraint as Test Devices).

REST API (/api/simbank, JWT, RBAC element type simbank)

MethodRoutePurpose
GET/simsEvery SIM across all agents (incl. offline persisted-only), with live present/in-use overlaid.
GET/readersEvery PC/SC reader on every connected agent (live), incl. empty and in-use readers.
GET/sims/<iccid>One SIM.
PATCH/sims/<iccid>Edit operator-assigned fields (notes today). Creates the row if the ICCID is unseen.
DELETE/sims/<iccid>Forget a SIM (drops the persisted record; reappears on next scan if still inserted).
POST/agents/<id>/rescanForce an immediate uncached PC/SC rescan on one agent and return its fresh inventory.

RBAC mirrors ir38/android-control: admins always pass; otherwise a JWT permission with element_type == "simbank" grants view, and POST in its methods grants control.

Frontend

src/pages/simbank/SimBank.tsx — a searchable, filterable table of all SIMs: status (present / in use / offline), agent/site, reader, ICCID, IMSI, MCC/MNC, country, SPN, editable notes, last seen. Per-agent Rescan button. Routed at /simbank and linked from the sidebar under Roaming Tests › SIM Bank.


Database schema

simbank_sims — one row per ICCID ever seen:

ColumnNotes
iccid (PK)SIM serial.
imsi, mcc, mnc, country, spn, atrLast-known identity (only overwritten by non-empty reads).
notesOperator-assigned, editable.
last_agent_id, last_site, last_reader_name, last_reader_indexWhere it was last (or is currently) seen.
first_seen, last_seenTimestamps (UTC).
remsim_bank_id, remsim_slot_nr, remsim_mapped_cardemPhase-2 reserved (nullable, unused).

Empty and in-use readers carry no ICCID, so they are not persisted as SIMs — they only appear in the live /readers snapshot.


Future: osmo-remsim

Phase 2 maps a physical reader/SIM into an osmo-remsim bank so a SIM in one site can be presented to a modem/phone in another (remote SIM). The hooks are already in place — search the code for # TODO remsim:

  • simbank_sims.remsim_bank_id / remsim_slot_nr / remsim_mapped_cardem (nullable columns, surfaced in to_dict and the TS SimRow type as null),
  • the agent's per-reader in_use flag already distinguishes a reader held by a bankd/emulation session — that's the reader phase 2 will map,
  • PATCH /api/simbank/sims/<iccid> is where editing the remsim mapping will be wired.

Nothing here drives remsim today; the shape is reserved so the mapping slots in without reworking the inventory hot path.