CEMI

El nodo de planta y las máquinas

Simulación del Ecosistema Financiero Virtual - Laboratorio de Innovación Financiera de CEMI
Una iniciativa del Financial Innovation Lab de CEMI
English Español Français

El nodo de planta y las máquinas

Léase esto primero

Qué es esto. El Laboratorio tiene cinco máquinas NCR Atleos en su planta. Este documento
dice cómo se integran a la Simulación del Ecosistema Financiero Virtual, qué sigue sin
conocerse sobre ellas y en qué orden va el trabajo.

Qué no es esto. No es una descripción de cómo funciona una NCR Atleos. **El manual de
referencia de NCR para estas máquinas no ha sido leído**, no se consultó ningún documento de
NCR, y cada valor de protocolo que carga el andamiaje es la lectura que hace el Laboratorio de
descripciones de NDC que circulan públicamente - registrada como los supuestos A-46,
A-47 y A-48 en [../spec/assumptions.md](../spec/assumptions.md) y sobrescribible,
máquina por máquina, desde nodes/floor/<machine>.yaml. Nada de lo aquí escrito debe tomarse
como una descripción de lo que las máquinas de la planta realmente hacen hasta que el
inventario de más abajo esté hecho.


Por qué existe un nodo de planta

Un terminal de autoservicio NCR real no decide nada. Sus tablas de estado dicen *«en este
estado, envía una solicitud de transacción y espera»*, y lo que ocurre después - cuál
pantalla, cuál acción de dispositivo, cuánto sale de los casetes - lo decide un host que
es dueño de esas pantallas y responde por NDC (NCR Direct Connect), sobre un enlace TCP.

El ecosistema simulado vive en Cloud Run, y Cloud Run no puede aceptar TCP crudo. A un
servicio de Cloud Run se llega por HTTP(S) a través del front end de Google, en el único
puerto que el contenedor declara; no hay un segundo escucha al que una máquina pueda abrirle
un socket, y nada en la configuración del servicio puede publicar uno. El mismo párrafo ya
está en [../deploy/README.md](../deploy/README.md) a propósito del escucha ISO 8583, por la
misma razón.

Así que el Laboratorio corre la misma imagen dos veces:

DóndeQué es
La pasarela en la nubeCloud Run, ecosystem-gatewayHTTP, la consola, todos los carriles, el mundo de cada equipo. Sin escucha TCP, y no se configura ninguno.
El nodo de plantauna PC pequeña en la LAN del Laboratorio, en el segmento propio de las máquinasFLOOR_NODE=1. Sostiene los puertos TCP de NDC e ISO 8583 que las máquinas marcan, y reenvía lo que piden al carril de máquinas de la nube con su propia llave de equipo.

El nodo de planta es un participante del mundo en la nube, no un segundo mundo. No lleva
libro, no liquida nada, y no guarda ningún estado que un reinicio perdería salvo el diario del
día que todavía no ha entregado. Todo lo que una máquina pide lo responde el switch simulado
en la nube, sobre /v1/atm/**, exactamente igual que se le respondería a una integración que
llamara esos endpoints desde una laptop. El nodo de planta agrega un protocolo, no una
capacidad.


La lista de investigación

Nada de lo de abajo se sabe todavía. Cada línea es una pregunta con un responsable (Carlos), y
cada una bloquea algo concreto - por eso la lista es una tabla y no un deseo.

#Por averiguarQué bloquea
1Modelo exacto de cada una de las cinco máquinas, y la versión de software que tiene. Conocido (2026-09-06): la insignia es una NCR Atleos SelfServ 62 (recicladora de efectivo de bolsillo para lobby interior sobre el Scalable Recycler, pantalla táctil de 15 pulgadas, panel superior de 18.5 pulgadas) corriendo NCR Atleos NDC Enterprise sobre Windows 11; NCR presenta NDC Enterprise como el sucesor multimarca de Advance NDC. Sigue pendiente: los otros cuatro modelos, y la versión de NDC Enterprise en cada unotodo; no se puede escribir un perfil para una máquina que nadie ha identificado
2Versión de NDC que habla el software, y la versión de XFS por debajotodo el dialecto: entramado, clases de mensaje, orden de campos (A-46, A-47)
3El manual de referencia de ese modelo y esa versiónreemplaza cada valor supuesto en packages/ndc-host/src/dialect.ts por uno leído
4Acceso de supervisor - el modo, la credencial y quién la tieneleer la configuración, el diario y los contadores de la máquina, del todo
5Estado de llaves del EPP - llaves cargadas, puestas en cero, o nunca cargadas; y si el Laboratorio puede cargar las suyaschip y PIN, paso cuatro. Nada anterior a eso necesita una llave, que es la razón por la que nada anterior espera
6Tarjetas: qué existe, y si el Laboratorio puede grabar o codificar las suyas en el rango de BIN del Laboratorio que empieza en 9los flujos con tarjeta presente. El prefijo de PAN del Laboratorio está deliberadamente fuera de los rangos asignados
7Llaves de CA para chip - cuáles llaves públicas de autoridad de certificación tienen las máquinas, y si se pueden cargar llaves de prueba del LaboratorioEMV, paso cuatro
8Modo de prueba de efectivo - si existe en estas máquinas y cómo se entradispensar sin mover billetes reales, paso tres
9El diario electrónico - su formato, dónde se guarda, y cómo se lee desde la máquinareemplaza la línea de diario del Laboratorio (A-48) por la propia de la máquina
11Quién es dueño de las pantallas y del flujo. La página de NCR dice que NDC Enterprise le permite a un operador «gestionar el flujo de transacción y la interfaz de usuario a través del servidor empresarial». Si ese servidor está instalado en las máquinas del Laboratorio, y si el host (el nodo de planta) sigue descargando tablas de estado y pantallas a la manera clásica de NDC o lo hace el servidor empresarial, decide cuánto del papel de datos de personalización del host nos toca jugarlos mensajes de datos de personalización del host (A-46); posiblemente todo el reparto entre el host y el servidor empresarial
10La VLAN - en cuál segmento están las máquinas, qué más lo alcanza, y si el nodo de planta puede ser la única ruta de salidael límite de seguridad de todo el arreglo. Ver abajo

La VLAN es el límite de seguridad, y eso es una decisión

No hay credencial en el enlace NDC, y eso se declara en vez de omitirse. Un terminal NDC
no tiene campo donde llevar una llave de equipo ni noción de presentar una, así que un escucha
que la exigiera sería un escucha con el que ninguna máquina puede hablar.

Lo que protege el enlace es la red en la que está. Las máquinas y el nodo de planta están en
una VLAN propia; el nodo de planta es lo único en esa VLAN que puede alcanzar internet; y
la llave de equipo vive en el nodo de planta, la presenta el nodo de planta al carril en
la nube, y nunca viaja por el enlace de las máquinas en ninguna dirección. Cualquier cosa que
alcance el puerto NDC puede manejar las pantallas de una máquina, así que la respuesta a
«quién puede alcanzar el puerto» es «las máquinas, y nada más».

En cualquier enlace que salga de la planta, configure NDC_TLS_CERT y NDC_TLS_KEY (y el par
de ISO 8583 junto a ellos). El TCP plano es el valor por defecto porque el hardware de planta
a menudo no puede hacer más y un escucha que rechaza la máquina no es un escucha.


Activos que el Laboratorio ya tiene

Existen renders y animaciones 3D de la SelfServ 62, un modelo de ella en Blender, y
fotografías realistas, y pueden proveerse a solicitud (Carlos, 2026-09-06). No están en este
repositorio. Sirven a las películas, a la página pública de la simulación y a un ensayo de la
disposición física de la planta; no hacen falta para el host, que solo llega a ver los
mensajes de la máquina.

Los cuatro pasos

El cable y NDC van en el mismo nodo de planta. Una caja, una llave de equipo, dos puertos:
NDC_TCP_PORT para las máquinas que hablan NDC e ISO8583_TCP_PORT para una máquina o un POS
en la misma planta que hable ISO 8583 en su lugar. Eso no es una comodidad, es el arreglo -
ambos escuchas necesitan TCP crudo, Cloud Run no lleva ninguno, y una segunda caja significaría
una segunda llave de equipo, un segundo diario por conciliar y dos lugares donde equivocarse
con la VLAN. La mitad de ISO 8583 tiene su propio perfil
([../spec/iso8583-wire.md](../spec/iso8583-wire.md)), su propio sign-on y su propio paquete
de conformidad (npm run wire:conformance); todo lo de abajo sobre la VLAN, el archivo de la
llave y las recetas de servicio le aplica palabra por palabra. Ese mismo emparejamiento es lo
que levanta la receta de Compute Engine en
[../deploy/README.md](../deploy/README.md) cuando el enlace tiene que salir de la planta.

1. Inventario. Trabaje la lista de arriba, máquina por máquina, y escriba lo encontrado en
nodes/floor/<machine>.yaml - el bloque de dialecto para lo que dice el manual, la lista
pending para lo que sigue abierto. En este paso ninguna máquina se pone en la red.

2. Nodo de planta y host, manejados por el ATM virtual y el switch virtual. *(Esto es lo
que existe hoy.)* El nodo de planta corre, ambos escuchas están arriba, y a cada uno lo
ejercita un doble de prueba con forma de cliente que marca un socket real y envía tramas
reales: NDC por packages/ndc-host/test/virtual-atm.ts - que ejecuta el function id que se le
da e informa de vuelta - e ISO 8583 por packages/iso8583-wire/test/virtual-switch.ts, que
hace sign-on, autoriza, reversa, retira con tarjeta y sin ella, y le responde el mismo mundo
del que responden los carriles REST. Ambos quedan probados antes de que exista una máquina,
que es la única manera de llegar al paso tres con algo que valga la pena conectar.

3. La primera transacción sin efectivo, en UNA máquina. Una máquina, en la VLAN, marcando
al nodo de planta. Solo consulta de saldo: lee una cuenta, imprime una cifra, no mueve
nada y no dispensa nada. Es la transacción más segura que existe y lo correcto para correr de
primero en una máquina cuyo acceso de supervisor y cuyo diario apenas se acaban de entender.
Lo que aquí se prueba es el enlace y el dialecto, no el ecosistema - el ecosistema lleva
meses respondiendo estas operaciones sobre REST.

4a. Retiro sin tarjeta con billetes de prueba. El código de un solo uso que se le dio al
teléfono del titular se teclea en la máquina, el switch gasta la orden exactamente una vez, y
los billetes salen - en modo de prueba de efectivo si las máquinas lo tienen, y con
billetes de prueba si no lo tienen. Sin tarjeta, sin PIN, sin EMV, que es lo que lo hace
ejecutable antes de que se cargue una sola llave. El diario electrónico se lee de la máquina
al final de la corrida y se concilia por POST /v1/atm/journal.

4b. Chip y PIN, después de la carga de llaves. Solo después de que los puntos 5, 6 y 7 de
la lista queden resueltos. Necesita un EPP con llaves, tarjetas en el rango de BIN del
Laboratorio, llaves de CA que las máquinas acepten, y un dialecto que haya sido leído en
vez de supuesto. Nada en el andamiaje calcula hoy un bloque de PIN, guarda una llave o
verifica un certificado, y nada debería hacerlo hasta este paso.


Cómo se corre un nodo de planta

El nodo de planta es la imagen ordinaria con entorno de nodo de planta. No necesita compilación
propia ni una rama de código.

docker run -d --name finlab-floor \
  --restart unless-stopped \
  -p 8583:8583 -p 9100:9100 -p 8787:8787 \
  -e FLOOR_NODE=1 \
  -e NDC_TCP_PORT=9100 \
  -e ISO8583_TCP_PORT=8583 \
  -e CLOUD_GATEWAY_URL=https://ecosystem.financial \
  -e FLOOR_TEAM_KEY_FILE=/run/secrets/floor-team-key \
  -v /etc/finlab/floor-team-key:/run/secrets/floor-team-key:ro \
  -v /etc/finlab/nodes/floor:/app/nodes/floor:ro \
  ecosystem-gateway:latest

El entorno

VariablePor defectoQué es
FLOOR_NODEvacío1 enciende el modo de nodo de planta. Vacío significa que este proceso no es un nodo de planta, que es lo que es cada instancia en la nube
NDC_TCP_PORT(requerido en modo planta)el puerto que las máquinas marcan para NDC
NDC_TCP_HOST0.0.0.0la interfaz a la que se enlaza el escucha
CLOUD_GATEWAY_URL(requerido en modo planta)la pasarela en la nube a la que se reenvían las operaciones de máquina, p. ej. https://ecosystem.financial
FLOOR_TEAM_KEY_FILEvacíoun archivo que guarda la llave de equipo de la planta. Preferido: una variable de entorno la puede leer cualquier cosa que pueda listar el proceso, y un nodo de planta está donde alguien tiene acceso físico
FLOOR_TEAM_KEYvacíola misma llave en el entorno, cuando un archivo es impráctico. Una de las dos es requerida
FLOOR_NETWORK_IDatm-networkel id del administrador de la red de ATM en el mundo en la nube, para la única operación a la que el carril de máquinas no le nombra una ruta
FLOOR_PROFILESnodes/floorde dónde se leen los perfiles de máquina
FLOOR_UNKNOWN_MACHINErefuselo que recibe una máquina cuyo LUNO no está en ningún perfil. refuse cierra la conexión - el valor por defecto correcto en una planta. default habla el dialecto por defecto del Laboratorio, que es lo que quiere un banco de pruebas
FLOOR_TIMEOUT_MS15000antes de que a una máquina se le diga que el host no pudo alcanzar su mundo
NDC_MAX_CONNECTIONS16máquinas sostenidas a la vez; una de más se rechaza en el socket en vez de encolarse
NDC_IDLE_TIMEOUT_SECONDS300silencio antes de que el escucha cierre una conexión
NDC_TLS_CERT / NDC_TLS_KEYvacíorutas PEM. Ambas o ninguna: una sin la otra impide que el nodo de planta levante, en vez de arrancar calladamente un escucha en texto plano que alguien creyó cifrado
ISO8583_TCP_PORTvacíoel escucha ISO 8583, para una máquina o un POS en la misma planta que lo hable en su lugar. Lo lee la pasarela misma; ver [../deploy/README.md](../deploy/README.md)

**Un nodo de planta arrancado sin llave, o sin una pasarela a la cual reenviar, se niega a
levantar.** Esa es la severidad correcta: una caja que acepta el socket de una máquina y luego
falla cada transacción es peor para la persona frente a la máquina que una caja que nunca
respondió.

Como servicio de systemd (PC de planta con Linux)

/etc/systemd/system/finlab-floor.service:

[Unit]
Description=FinLab floor node - NDC and ISO 8583 listeners for the Lab machines
After=network-online.target docker.service
Requires=docker.service

[Service]
Type=simple
Restart=always
RestartSec=5
# the key is a file the service user can read and nothing else can: 0400, owned by root
ExecStartPre=-/usr/bin/docker rm -f finlab-floor
ExecStart=/usr/bin/docker run --rm --name finlab-floor \
  -p 9100:9100 -p 8583:8583 -p 8787:8787 \
  -e FLOOR_NODE=1 -e NDC_TCP_PORT=9100 -e ISO8583_TCP_PORT=8583 \
  -e CLOUD_GATEWAY_URL=https://ecosystem.financial \
  -e FLOOR_TEAM_KEY_FILE=/run/secrets/floor-team-key \
  -v /etc/finlab/floor-team-key:/run/secrets/floor-team-key:ro \
  -v /etc/finlab/nodes/floor:/app/nodes/floor:ro \
  ecosystem-gateway:latest
ExecStop=/usr/bin/docker stop finlab-floor

[Install]
WantedBy=multi-user.target
sudo install -m 0400 -o root -g root floor-team-key /etc/finlab/floor-team-key
sudo systemctl daemon-reload && sudo systemctl enable --now finlab-floor
journalctl -u finlab-floor -f

Como servicio de Windows (las máquinas son basadas en Windows, y la PC de planta puede serlo también)

Windows no trae un envoltorio nativo de «corre esto como servicio», así que use uno. Con
[NSSM](https://nssm.cc/) y Node 22 instalados, corriendo el repositorio directamente en vez del
contenedor:

# the key, readable by the service account and nobody else
icacls C:\finlab\floor-team-key /inheritance:r /grant:r "NT SERVICE\finlab-floor:(R)"

nssm install finlab-floor "C:\Program Files\nodejs\npm.cmd" "run gateway"
nssm set finlab-floor AppDirectory C:\finlab\finlab-ecosystem
nssm set finlab-floor AppEnvironmentExtra ^
  FLOOR_NODE=1 ^
  NDC_TCP_PORT=9100 ^
  ISO8583_TCP_PORT=8583 ^
  CLOUD_GATEWAY_URL=https://ecosystem.financial ^
  FLOOR_TEAM_KEY_FILE=C:\finlab\floor-team-key
nssm set finlab-floor AppStdout C:\finlab\logs\floor.log
nssm set finlab-floor AppStderr C:\finlab\logs\floor.log
nssm set finlab-floor Start SERVICE_AUTO_START
nssm start finlab-floor

Abra los dos puertos hacia la VLAN de las máquinas y hacia nada más:

New-NetFirewallRule -DisplayName "FinLab NDC" -Direction Inbound -Protocol TCP `
  -LocalPort 9100 -RemoteAddress 10.20.30.0/24 -Action Allow
New-NetFirewallRule -DisplayName "FinLab ISO 8583" -Direction Inbound -Protocol TCP `
  -LocalPort 8583 -RemoteAddress 10.20.30.0/24 -Action Allow

Los dos flujos que el host responde hoy

Ambos están mapeados sobre operaciones que el switch simulado ya publica
(packages/atm-network/src/network.ts) y endpoints que el carril de máquinas ya lleva
(/v1/atm/**). El host no agrega ninguna capacidad propia.

Consulta de saldo

machine  ->  Transaction Request   opcode buffer = the balance-inquiry code (A-47)
                                   general-purpose buffer B = the account number
host     ->  /v1/atm/balance-inquiries  { terminalId, issuerId, accountNumber }
host     ->  Transaction Reply     function id = print only, screen = the balance screen,
                                   the figure in the screen update and on the receipt
machine  ->  Solicited Status      the reply was carried out

No se mueve nada y no sale nada. El function id es print only en cada camino a través de
este flujo.

Retiro sin tarjeta por código

(phone)  ->  /v1/atm/cardless/orders   the holder's OWN institution mints the one-time code.
                                       A machine never raises an order.
machine  ->  Transaction Request   opcode buffer = the cardless-withdrawal code (A-47)
                                   general-purpose buffer B = the one-time code
host     ->  /v1/atm/cardless/redemptions  { terminalId, code }
host     ->  Transaction Reply     approved: function id = dispense and print, and the
                                     amount is THE ORDER'S, never the machine's
                                   declined: function id = print only, the decline screen,
                                     and the ISO 8583 DE039 on the receipt
machine  ->  Solicited Status      the notes went out, or they did not
host     ->  a journal line, completed by that status

Se sostienen cuatro propiedades, y cada una está afirmada en
packages/ndc-host/test/flows.test.ts:

  1. Toda solicitud recibe una respuesta. Una máquina que recibe silencio no imprime nada, se le vence el tiempo, y le enseña a un cliente que las máquinas del Laboratorio se cuelgan.
  2. Un rechazo no dispensa nada. El function id se fija a partir del resultado, y no hay camino por el que una no-aprobación llegue a dispense and print.
  3. El monto es el del switch. Lo que el cliente tecleó en la máquina se registra y luego se ignora: la orden es la autoridad, y una máquina que pudiera nombrar su propio monto sería una máquina que podría pagarse a sí misma.
  4. Nada se dispensa dos veces. El switch gasta la orden antes de autorizar, y el host envía la misma respuesta ante un número de coordinación repetido en vez de iniciar una segunda transacción - que es lo que realmente necesita una máquina que perdió una respuesta y reenvió.

Conciliación del diario electrónico

Cada máquina lleva su propio registro del día. El switch lleva el suyo. Conciliar es encontrar
dónde discrepan, y la dirección interesante es la de la máquina: una línea de diario de la
que el switch no tiene registro es un pago que nadie va a liquidar, y tiene la forma de una
dispensación que ocurrió después de que se cayó el enlace. La otra dirección se encuentra de
todos modos, cuando se cuentan los casetes.

curl -sS https://ecosystem.financial/v1/atm/journal \
  -H "x-lab-api-key: $FLOOR_TEAM_KEY" \
  -H 'content-type: application/json' \
  -d '{"journal":"2026-09-06T13:00:00.000Z|TERM-LAB-0001|cardless-withdrawal|ATM-atm-network-00001|200000|DOP|00|A"}'

El reporte dice qué cuadró (por la referencia, o - para una línea sin referencia - por el monto
y el terminal), en qué discrepa un par cuadrado, qué está solo en el diario, qué está solo en el
switch, y los dos totales con su diferencia.

El formato de línea es propio del Laboratorio, y es un marcador de posición (supuesto
A-48). El formato de los diarios de estas máquinas es el punto 9 de la lista. Cuando llegue, un
analizador para él produce los mismos objetos JournalLine y nada aguas abajo cambia - ni el
comparador, ni el reporte, ni el endpoint. Eso es lo que hace de esto un andamiaje y no una
adivinanza: la forma de la respuesta está resuelta y la lectura de los bytes no.


Dónde está el código

packages/ndc-host/
├── src/dialect.ts    the dialect table - READ THIS FIRST. A-46, A-47, all profile-driven
├── src/messages.ts   the six NDC message families as objects
├── src/codec.ts      bytes to messages and back, and the TCP stream framer
├── src/profile.ts    nodes/floor/<machine>.yaml, and the dialect overrides in it
├── src/lane.ts       the three switch operations, over HTTP or in-process
├── src/host.ts       the state machine: the two flows above
├── src/server.ts     the TCP listener, one host per connected machine
├── src/floor.ts      FLOOR_NODE and the environment
├── src/journal.ts    the journal line and the matcher
└── test/virtual-atm.ts   the machine that does not exist, on a socket that does

Cómo se conecta un switch socio, y el paquete de conformidad

Cómo se conecta un switch socio

La dirección es wire.ecosystem.financial:8583, y es un MARCADOR DE POSICIÓN. Se escribe
aquí para que una integración tenga un nombre que configurar y un solo lugar que cambiar cuando
de verdad se levante un enlace. **Hoy no hay nada escuchando en ella, y no hay ningún escucha
desplegado en ninguna parte.** Decir lo contrario sería la afirmación falsa más fácil de hacer
en este repositorio y la más cara de haber hecho.

Por qué todavía no hay a qué marcar, en simple:

El escucha necesita un host capaz de TCP, y la pasarela desplegada no lo es. La pasarela del ecosistema corre en Cloud Run, al que se llega por HTTP(S) a través del front end de Google, en el único puerto que el contenedor declara; no hay un segundo escucha al que un switch pueda abrirle un socket, ni configuración que publique uno. Así que el escucha corre donde el TCP crudo es posible, lo que hoy significa uno de exactamente dos lugares: el nodo de planta del Laboratorio - esta misma imagen en una PC pequeña en el segmento de red propio de las máquinas - o una VM pequeña de Compute Engine que el desplegador arranca, corriendo la misma imagen con ISO8583_TCP_PORT configurado. Ambas son recetas que una persona sigue; ninguna está corriendo. docs/deploy/README.md lleva la receta de la VM como comandos gcloud escritos por completo, y no han sido ejecutados.

Lo que hace un socio, en orden:

  1. Pedir una llave de equipo. El sign-on del cable toma una llave de equipo - el mismo valor que toman los carriles REST en x-lab-api-key, de FINLAB_API_KEYS. Una identidad de servicio es una credencial HTTP y aquí no se acepta: su alcance se expresa en métodos y carriles HTTP, que un socket no tiene.
  2. Decir desde cuáles direcciones marca el switch. Van en ISO8583_ALLOWED_CIDRS en el escucha. Un par fuera de esa lista se destruye en el socket sin respuesta - sin negativa que leer, sin gastar un cupo de conexión, sin nada de qué aprender. En una VLAN de planta la lista puede dejarse vacía y la red es el límite; en cualquier enlace que salga de la planta no es opcional.
  3. Tomar TLS. ISO8583_TLS_CERT e ISO8583_TLS_KEY, ambas o ninguna. Recuerde lo que va dentro del mensaje: DE052 lleva un PIN del Laboratorio en claro - la simulación no guarda llaves y no calcula ningún bloque de PIN, y §3 lo dice en la tabla - así que el transporte es lo único que lo protege. Sea simulado el PIN o no, un enlace que filtra uno enseña un mal hábito. El certificado de planta del Laboratorio es autofirmado; un socio debería fijarlo (pinning) en vez de apagar la verificación.
  4. Correr el paquete de conformidad (§9) contra el escucha, desde la red propia del switch, y guardar el reporte.
  5. Hacer sign-on, y permanecer con sign-on. Una conexión, un mundo, una credencial presentada una vez. Se espera que el enlace sostenga el socket y haga eco (0800 DE070 301) en vez de reconectar por transacción.

El entorno que lee el escucha

Cada una de estas la lee el proceso de pasarela que sostiene el puerto. Vacío significa que el
escucha no existe; eso es lo que usa cada instancia de Cloud Run.

VariablePor defectoQué hace
ISO8583_TCP_PORTvacíoel puerto. Vacío significa que no hay escucha en absoluto
ISO8583_TCP_HOST0.0.0.0la interfaz a la que se enlaza
ISO8583_ALLOWED_CIDRSvacíodirecciones y bloques CIDR separados por coma o por salto de línea que pueden conectarse: 203.0.113.0/24, 10.20.0.5, 2001:db8::/32. Vacío significa que cualquier par puede conectarse. Un cliente IPv4 sobre un socket de doble pila (::ffff:203.0.113.7) se compara contra las reglas IPv4 como la dirección IPv4 que es. Una entrada malformada impide que el proceso levante, en vez de ser omitida
ISO8583_TLS_CERT / ISO8583_TLS_KEYvacíorutas PEM. Ambas o ninguna: una sin la otra impide que la pasarela levante, en vez de arrancar calladamente un escucha en texto plano que alguien creyó cifrado
ISO8583_MAX_CONNECTIONS32conexiones sostenidas a la vez; una de más se rechaza en el socket en vez de encolarse
ISO8583_MAX_MESSAGES_PER_MINUTE600por conexión, sobre un cubo de fichas que se rellena continuamente. Pasado el límite, 96 con el reintento en DE126 — la conexión no se cierra
ISO8583_IDLE_TIMEOUT_SECONDS180silencio antes de que el escucha cierre una conexión
ISO8583_MAX_SIGN_ON_ATTEMPTS3llaves erróneas antes de que se cierre la conexión, para que el socket no sea un oráculo de llaves

Cada mensaje está en la traza del mundo

Una conexión con sign-on escribe en la traza propia del equipo, bajo el actor iso8583-wire:
un event para el sign-on, luego un message y un reply por intercambio, cada uno cargando
el nombre de la conexión (iso8583-0007), el par, el equipo, y una **huella de ocho
hexadecimales de la llave** — suficiente para distinguir la credencial de un nodo de planta de
una llave de banco de pruebas seis semanas después, inútil como credencial en sí misma. La
representación es la misma que usa el log, así que **DE052 y DE127 se descartan antes de que se
escriba un carácter**. Eso es lo que hace distinguibles, después del hecho, un retiro por cable
y un retiro por REST, que es la pregunta que hace una conciliación.


El paquete de conformidad

La lista de lo que un switch tiene que hacer bien, y un ejecutor para ella:

npm run wire:conformance -- --host=127.0.0.1 --port=8583 --key=<team key> \
                            --gateway=http://127.0.0.1:8787
npm run wire:conformance -- --host=wire.example --port=8583 --key=… --tls --json

Marca al escucha con packages/iso8583-wire/test/virtual-switch.ts — el mismo switch de
tarjetas virtual que maneja la suite de pruebas, que es justo el punto: **el paquete no es una
descripción de las pruebas, es las pruebas, apuntadas a otra parte.** Sin --gateway, los
casos que necesitan material del Laboratorio (una tarjeta, un aceptante, un terminal, una orden
sin tarjeta) reportan SKIP con la razón y nunca se cuentan como aprobados; la disciplina del
enlace corre contra cualquier cosa. El código de salida es 0 cuando nada falló, 1 cuando
algo falló, y --json imprime el reporte completo.

**Un aprobado dice que el escucha habla este perfil. No dice nada sobre la certificación de
ninguna marca de tarjetas, y una corrida nunca sustituye a una.**

CasoMensajesQué debe hacer un switch
sign-on<br>hacer sign-on con la llave de equipo en DE1270800 0810un 0800 con DE070 001 y la llave en DE127 se responde 0810 DE039 00, y la respuesta no lleva DE127: una credencial que regresa es una credencial en cada captura del enlace.
sign-on-refused<br>una llave que el escucha no tiene se rechaza, y el socket no es un oráculo0800 0810una llave equivocada se responde 57 y la conexión se cierra tras el número configurado de intentos, de modo que el enlace no puede usarse para adivinar una.
unsigned-refused<br>un mensaje financiero antes del sign-on se rechaza0200 0210cualquier mensaje que no sea el sign-on, en una conexión que no ha hecho sign-on, se responde 57 con DE126 indicando hacer sign-on primero. Nunca se ejecuta.
credential-once<br>DE127 en cualquier mensaje que no sea el sign-on se rechaza de plano0800 0100 0200una credencial en un mensaje financiero se rechaza con 57 en vez de ignorarse: un terminal que presenta su llave en cada mensaje es un terminal cuya llave está en cada captura de paquetes.
echo<br>prueba de eco0800 0810un 0800 con DE070 301 se responde 00 mientras el enlace esté arriba. Es con lo que un switch sondea y la manera más barata de distinguir un enlace muerto de uno callado.
authorization<br>una compra en un aceptante0100 0110un 0100 que lleva DE002, DE042 y DE004 se responde 0110 con DE039, y en una aprobación con DE037 y DE038. El PAN regresa ENMASCARADO: la respuesta es la proyección, y la proyección lo enmascara.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
authorization-declined<br>un rechazo es una respuesta0100 0110una autorización que el emisor no va a aprobar llega como un 0110 bien formado con su propio DE039 - nunca como una conexión caída. Una máquina que recibe silencio no imprime nada y reintenta.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
reversal<br>la reversa de una autorización, por su propia referencia0400 0410un 0400 que lleva el DE037 con el que regresó el 0110 se responde 0410 00, y DE090 apunta al mensaje que se está reversando. Un 0400 sin DE037 se responde 30.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
withdrawal-cardless<br>un retiro sin tarjeta en una máquina0200 0210un 0200 con DE003 010000, SIN DE002 y el código de un solo uso en DE102 se responde 0210. La orden se gasta exactamente una vez: un segundo canje del mismo código se rechaza.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
withdrawal-card<br>un retiro con tarjeta y su PIN del Laboratorio0200 0210un 0200 con DE003 010000, DE002, DE041, DE100 y DE052 se responde 0210, y DE052 nunca se devuelve en eco y nunca se registra en log.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
deposit<br>un depósito en una máquina0200 0210un 0200 con DE003 210000, el monto declarado en DE004 y DE002 o DE102 se responde 0210 con el resultado al que llegó el switch, reconocimiento incluido.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
balance-inquiry<br>una consulta de saldo, que no mueve nada0200 0210un 0200 con DE003 310000, DE100 y DE102 se responde 0210 00 con la cifra en DE126. No contabiliza nada: DE054 no está en este perfil y no se registra ninguna transacción.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
clearing<br>una autorización levantada por el cable compensa y liquida en neto0100 0110EL PERFIL NO LLEVA NINGÚN MENSAJE DE COMPENSACIÓN - ni 0320, ni 0500, ni 0600. Lo que debe sostenerse es que una transacción autorizada por el cable sea la misma transacción que el ciclo de compensación del adquirente recoge y liquida en neto, lo cual el Laboratorio prueba a través de POST /v1/cards/cycles y no a través de un MTI.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
wire-rest-parity<br>el cable y el carril REST coinciden, en el resultado y en los asientos0100 0110la misma operación enviada por el cable y por el carril REST de tarjetas, en el mismo mundo, llega al mismo DE039 y mueve el mismo dinero. La respuesta ES la proyección REST, así que una diferencia aquí es una diferencia en el ecosistema y no en el enlace.<br><em>necesita material del Laboratorio (una tarjeta, un aceptante, un terminal): pase --gateway.</em>
unknown-mti<br>una familia de mensajes que el perfil no lleva se rechaza, no se descarta0420 0500un MTI fuera de 0100/0200/0400/0800 - un aviso de reversa 0420, una reconciliación 0500 - se responde 12 con DE126 nombrando las cuatro familias. La conexión sobrevive.
format-error<br>una trama que no es un mensaje se responde, y el enlace sobrevive0810bytes que no decodifican se responden 30 - en el MTI de respuesta propio de la solicitud cuando el MTI era legible, si no en un 0810 - y la conexión se mantiene arriba.
oversized-frame<br>una trama que pasa el máximo del perfil termina la conexión0810un encabezado de longitud que declara más de 4096 bytes, o cero, se responde 30 una vez y la conexión se cierra: no hay delimitador en este protocolo sobre el cual resincronizar un flujo.
rate-limit<br>una avalancha se frena, no se desconecta0800 0810pasado el presupuesto por conexión un mensaje se responde 96 con el reintento en DE126, y la conexión NO se cierra: un terminal que solo va rápido debe frenar, no caerse de la red.<br><em>un caso de límite: pase --include-slow.</em>
sign-off<br>hacer sign-off, y el escucha cierra0800 0810un 0800 con DE070 002 se responde 00 y la conexión la cierra el escucha, de modo que un switch que terminó no retiene un cupo.

_19 casos, 10 de los cuales corren contra cualquier escucha con nada más que un host, un puerto y una llave. Generado desde packages/iso8583-wire/src/conformance.ts por npm run spec:wire._

Deliberadamente **no hay mensaje de compensación en el paquete, porque no hay ninguno en el
perfil** (§2: ni 0320, ni 0500, ni 0600). La compensación y la liquidación aquí son un
ciclo que corre el adquirente, así que el caso clearing prueba que una transacción
autorizada por el cable es la que ese ciclo recoge y liquida en neto — a través de
POST /v1/cards/cycles, no a través de un MTI inventado. Por la misma razón un aviso de
reversa 0420 está en el paquete solo como algo que un switch debe rechazar con 12: la
reversa del perfil es 0400/0410.