← Todas las utilidades

NSNatAPI

Versión 26.1 (build 185)
NSNatAPI
Índice

Qué es NSNatAPI

nsnatapi (NSNatAPI) es la pasarela de Nacarsoft que expone subprogramas Natural como endpoints REST (GET/POST/PUT/DELETE) para clientes máquina (Java, scripts, cron, microservicios), sin EntireX ni broker RPC. Cada endpoint se define en la consola, se protege con tokens Bearer y se ejecuta en uno de dos modos:

  • batch (por defecto): un proceso Natural por llamada (~30–90 ms de arranque).
  • session (opt-in): un pool de sesiones Natural persistentes reutilizadas (~1–5 ms por llamada) para tráfico de alta frecuencia.

Los nombres de programas, hosts, tokens y cuentas de los ejemplos son ficticios.

Arquitectura de NSNatAPI: servicios consumidores → nsnatapi → Natural → Adabas

Presentación comercial: PDF (ES) · PPTX (ES) · PDF (EN) · PPTX (EN)

Requisitos

  • Linux (RHEL 8/9).
  • Software AG Natural + cliente Adabas (sagenv) con buffer pool arrancado.
  • FUSER con los subprogramas de negocio (lectura y escritura para desplegar).
  • Proxy inverso con HTTPS en producción.

Puesta en marcha

nsnatapi init ./data                 # config.yaml (0600), db, usuario admin
nsnatapi start ./data                # daemon, puerto 11032 por defecto
nsnatapi status ./data

Consola web en http://host:11032/ (login admin; cambia la contraseña tras el primer acceso). El comando nsnatapi passwd permite recuperar el acceso.

Orden recomendado: 1) escribe el subprograma Natural, 2) crea el endpoint, 3) haz Deploy, 4) crea el token con --endpoint <nombre> (ese nombre ya tiene que existir).

El módulo Natural, paso a paso

El único código que escribes tú es el subprograma .NSN, con el convenio de la casa: exactamente tres parámetros.

El contrato mínimo

0010* MIECHO - eco mínimo (plantilla funcional)
0020DEFINE DATA PARAMETER
00301 #PARM-IN  (A) DYNAMIC   /* entrada: una línea de params (<=240 chars) */
00401 #PARM-OUT (A) DYNAMIC   /* salida: respuesta (ver modos de respuesta) */
00501 #RESP     (I4)          /* 0 = ok, >0 = error de negocio */
0060END-DEFINE
0070RESET #PARM-OUT #RESP
0080COMPRESS 'ECHO:' #PARM-IN INTO #PARM-OUT
0090END

Qué recibe y qué devuelve cada parámetro:

ParámetroDirecciónContenido
#PARM-IN (A) DYNAMICEntradaUna sola línea con los parámetros, unidos con el separador del endpoint (máx. 240 caracteres)
#PARM-OUT (A) DYNAMICSalidaLa respuesta del negocio (texto, campos separados o JSON, según response_mode)
#RESP (I4)Salida0 = ok; >0 = error de negocio (el cliente lo recibe en rc)

Reglas obligatorias

  • Nombre de programa A-Z0-9-_, máximo 8 caracteres, en mayúsculas.
  • Terminar siempre con END (nunca STOP ni LOGON: matarían la sesión batch).
  • Stateless por llamada: nada de globales/AIVs entre llamadas.
  • Cierra siempre la unidad de trabajo: END TRANSACTION si fue bien, BACKOUT si falla una actualización.
  • Despliega el .NSN por tu proceso habitual (nsdeploy/SYSOBJH); nsnatapi no recompila tus programas, solo genera y compila sus stubs/drivers.

Ejemplo 1: consulta con RC de negocio

Un GET /v1/saldo/:cuenta donde la cuenta viaja en la línea de entrada y el programa decide el resultado con #RESP:

0010* CPOLIZA - consulta de póliza (plantilla)
0020DEFINE DATA PARAMETER
00301 #PARM-IN  (A) DYNAMIC
00401 #PARM-OUT (A) DYNAMIC
00501 #RESP     (I4)
0060LOCAL
00701 #CUENTA (A8)
0080END-DEFINE
0090RESET #PARM-OUT #RESP #CUENTA
0100MOVE #PARM-IN TO #CUENTA
0110* Aquí iría tu READ a Adabas con tu vista/DDM; si no encuentra:
0120IF #CUENTA = '999999'
0130  MOVE 'CUENTA NO ENCONTRADA' TO #PARM-OUT
0140  MOVE 4 TO #RESP
0150ELSE
0160  COMPRESS 'SALDO 1234.56' INTO #PARM-OUT
0170  MOVE 0 TO #RESP
0180END-IF
0190END TRANSACTION
0200END

Llamada y respuesta (response_mode=text):

curl -H "Authorization: Bearer nstm_..." \
     http://host:11032/api/ext/v1/saldo/001234
{ "ok": true, "rc": 0, "output": "SALDO 1234.56", "duration_ms": 22 }

Con una cuenta inexistente, HTTP sigue siendo 200 porque el programa se ejecutó; el error de negocio viaja en rc:

{ "ok": true, "rc": 4, "output": "CUENTA NO ENCONTRADA", "duration_ms": 19 }

Ejemplo 2: POST con dos campos y separador

Un POST /v1/pagos con JSON body. Los valores se unen en una línea (importe|concepto) que el programa separa:

0010* CPAGOS - alta de pago (2 campos separados por |)
0020DEFINE DATA PARAMETER
00301 #PARM-IN  (A) DYNAMIC
00401 #PARM-OUT (A) DYNAMIC
00501 #RESP     (I4)
0060LOCAL
00701 #IMPORTE  (A15)
00801 #CONCEPTO (A60)
0090END-DEFINE
0100RESET #PARM-OUT #RESP #IMPORTE #CONCEPTO
0110SEPARATE #PARM-IN INTO #IMPORTE #CONCEPTO WITH DELIMITER '|'
0120IF #IMPORTE = ' ' OR #IMPORTE = ''
0130  MOVE 'FALTA IMPORTE' TO #PARM-OUT
0140  MOVE 1 TO #RESP
0150ELSE
0160  COMPRESS 'PAGO REGISTRADO|' #IMPORTE '|' #CONCEPTO INTO #PARM-OUT
0170  MOVE 0 TO #RESP
0180END-IF
0190END TRANSACTION
0200END
curl -H "Authorization: Bearer nstm_..." -H 'Content-Type: application/json' \
     -d '{"importe":"1500","concepto":"prima"}' \
     http://host:11032/api/ext/v1/pagos
{ "ok": true, "rc": 0, "output": "PAGO REGISTRADO|1500|prima", "duration_ms": 31 }

Ejemplo 3: respuesta JSON real

Si el endpoint usa response_mode=json, el programa debe emitir JSON válido. Ojo: en este entorno Natural un literal " del fuente se compila como '; construye las comillas con H'22' (o usa GENERATE JSON):

0010* JECHO - devuelve un objeto JSON (response_mode=json)
0020DEFINE DATA
0030PARAMETER
00401 #PARM-IN (A) DYNAMIC
00501 #PARM-OUT (A) DYNAMIC
00601 #RESP (I4)
0070LOCAL
00801 #Q (A1)
0090END-DEFINE
0100RESET #PARM-OUT #RESP
0110MOVE H'22' TO #Q
0120COMPRESS '{' #Q 'msg' #Q ':' #Q #PARM-IN #Q '}' INTO #PARM-OUT LEAVING NO SPACE
0130END

Respuesta: output trae siempre el texto crudo y data el objeto ya parseado:

{
  "ok": true,
  "rc": 0,
  "output": "{\"msg\":\"hola\"}",
  "duration_ms": 24,
  "data": { "msg": "hola" }
}

Si el programa emite comillas simples en lugar de dobles, la llamada falla con 502 {"ok":false,"error":"invalid json from program"}.

Crear el endpoint, paso a paso

1. Campos del endpoint

CampoObligatorioDescripción
nameSíNombre legible (máx. 64, sin caracteres de control)
methodSíGET, POST, PUT o DELETE
path_templateSíEmpieza por /; capturas con :nombre (únicas, letra inicial, máx. 32). Ej.: /v1/saldo/:cuenta
librarySí*Librería Natural (mayúsculas). Si se omite, se usa la librería por defecto del entorno
programSíSubprograma existente (A-Z0-9-_, máx. 8)
environmentNoEntorno Natural (DEV/TST/PRD); vacío = entorno por defecto
fields_jsonNoCampos extra por orden: query en GET/DELETE, JSON body en POST/PUT. Formato: [{"name":"importe","required":true}]
separatorNoUn único carácter ASCII imprimible (defecto |)
exec_modeNobatch (defecto) o session
response_modeNotext (defecto), fields o json
response_fields_jsonSolo fieldsNombres y tipos de cada posición: [{"name":"saldo","type":"number"}]; tipos: text, number, int, bool, json
timeout_secNoTimeout de la llamada (0 = defecto del servidor; tope configurable)

Regla de oro del mapeo: capturas del path (en orden) + campos (en el orden de fields_json), unidos con el separador en una línea. Por eso, si el parámetro ya viaja en el path (:cuenta), fields_json debe ser []: no lo repitas ahí, o te pedirá además el mismo valor por query.

2. Crear por consola

Endpoints → Crear: método, plantilla, librería, entorno, programa, campos, separador, timeout y modo de ejecución. Pulsa Deploy y luego Try it para probarlo.

Ejemplo rápido: GET, plantilla /v1/saldo/:cuenta, librería NSNAT, entorno DEV, programa CPOLIZA, fields_json [], modo batch.

3. Crear por API (admin)

# Login de consola (guarda la cookie 'session')
curl -s -c cookies.txt -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"********"}' \
  http://host:11032/api/auth/login

# Endpoint GET con captura de path (fields_json vacío: el valor ya viene en :cuenta)
curl -s -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"name":"saldo","method":"GET","path_template":"/v1/saldo/:cuenta",
       "library":"NSNAT","program":"CPOLIZA","environment":"DEV",
       "exec_mode":"batch","response_mode":"text","fields_json":"[]",
       "separator":"|","timeout_sec":30}' \
  http://host:11032/api/endpoints

# Endpoint POST con dos campos de body
curl -s -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"name":"pagos","method":"POST","path_template":"/v1/pagos",
       "library":"NSNAT","program":"CPAGOS","environment":"DEV",
       "exec_mode":"batch","response_mode":"text",
       "fields_json":"[{\"name\":\"importe\",\"required\":true},{\"name\":\"concepto\",\"required\":false}]",
       "separator":"|","timeout_sec":30}' \
  http://host:11032/api/endpoints

# Desplegar (el id sale en la respuesta de creación)
curl -s -b cookies.txt -X POST http://host:11032/api/endpoints/1/deploy

4. Deploy: qué compila nsnatapi

  • batch: Deploy genera y compila un stub NX000042 (reporting-mode) en la librería del entorno. El stub hace INPUT, CALLNAT '<tu-programa>' y escribe la respuesta con los centinelas NSNAT:OUT:<len> / NSNAT:C:<len>:<chunk> / NSNAT:END RC=<n>.
  • session: Deploy compila el driver compartido NSRPDRV (structured mode) una vez por librería; el pool lo reutiliza y despacha a tus subprogramas con CALLNAT dinámico.

Tus .NSN no cambian entre modos. No edites los NXnnnnnn a mano: se regeneran en cada Deploy.

5. Token con alcance

nsnatapi token ./data --name java-prd --endpoint saldo --days 365 \
    --cidr 10.0.0.0/8,192.168.1.10
# → nstm_xxxxxxxx…  (guárdalo; no se vuelve a mostrar)
nsnatapi token ./data --list

Sin --endpoint, el token vale para todos los endpoints. Puedes limitar por IP/CIDR, caducidad y revocación, además del rate limiting por token.

El secreto solo se muestra al crearlo (con botón Copiar en la consola); después es irrecuperable —en BD solo hay su hash— así que si se pierde se rota desde la consola (botón Rotar) o con POST /api/tokens/:id/rotate: mismo id y metadatos, secreto nuevo, el anterior deja de funcionar.

Entregar un token a una aplicación: 1) endpoint desplegado y probado; 2) un token por app, con scope mínimo, caducidad y CIDRs si aplica; 3) informa el email de contacto y la app le envía un aviso con un botón Get token: enlace de un solo uso (48 h) donde ve el secreto una vez (reenvíable con ✉️); 4) copia el secreto una vez, directo al almacén de secretos de la app (nunca notepad ni correo); 5) verifica con una llamada de prueba en Llamadas; 6) ante pérdida o filtración, rota; si la app se da de baja, revoca.

Cómo se construye la línea de parámetros

CasoDe dónde sale cada valorEjemplo
GET /v1/saldo/001234, fields_json: []Captura :cuenta001234
GET /v1/saldo/001234?formato=corto, fields: [formato]Captura + query001234|corto
POST /v1/pagos, body {"importe":"1500","concepto":"prima"}, fields: [importe, concepto]Body en orden1500|prima
PUT /v1/saldo/001234, body {"importe":"1500"}Captura + body001234|1500

El separador por defecto es | y debe ser un único carácter ASCII imprimible. La línea total no puede superar 240 caracteres ni contener saltos de línea.

Respuestas JSON

Todas las llamadas máquina usan /api/ext/... con Authorization: Bearer nstm_....

Modo text (defecto)

Solo output con el texto crudo del programa:

{ "ok": true, "rc": 0, "output": "SALDO 1234.56", "duration_ms": 22 }

Modo fields: objeto tipado además del crudo

El programa devuelve posiciones separadas (123456|Juan Pérez|1520.75|ACTIVA) y el endpoint declara response_mode: fields con:

[{"name":"cuenta","type":"text"},{"name":"titular","type":"text"},
 {"name":"saldo","type":"number"},{"name":"estado","type":"text"}]

Respuesta:

{
  "ok": true,
  "rc": 0,
  "output": "123456|Juan Pérez|1520.75|ACTIVA",
  "duration_ms": 22,
  "data": {
    "cuenta": "123456",
    "titular": "Juan Pérez",
    "saldo": 1520.75,
    "estado": "ACTIVA"
  }
}

Modo json: objeto validado

El programa emite JSON (ver ejemplo JECHO más arriba) y la respuesta incluye el objeto en data. Si no es JSON válido: 502 {"ok":false,"error":"invalid json from program"}.

Errores: forma común

Todos los errores macchina usan la misma forma:

{ "ok": false, "error": "<mensaje>" }
HTTPEjemplo de errorSignificado
200n/a (rc: 4, output: "CUENTA NO ENCONTRADA")Se ejecutó; el negocio dice que no
400missing param cuentaFalta un campo requerido (query o body)
400json body required / invalid json bodyPOST/PUT sin body JSON válido
400params too long (max 240 chars)Línea de parámetros demasiado larga
401invalid tokenToken ausente, revocado o caducado
404no such endpointNo hay endpoint activo con ese método + path
429rate limit exceededLímite por token/IP alcanzado
502natural execution failedFalló Natural (detalle en el log de llamadas)
502invalid json from programresponse_mode=json pero la salida no es JSON
503endpoint not deployedFalta el Deploy del stub/driver
503server too busy, try again laterConcurrencia o pool de sesiones llenos
504natural call timed outEl programa superó timeout_sec

Llamadas desde clientes

# GET con captura de path
curl -H "Authorization: Bearer nstm_…" \
     http://host:11032/api/ext/v1/saldo/001234

# POST con JSON body (campos definidos en el endpoint)
curl -H "Authorization: Bearer nstm_…" -H 'Content-Type: application/json' \
     -d '{"importe":"1500","concepto":"prima"}' \
     http://host:11032/api/ext/v1/pagos

Cliente Java mínimo (Java 11+, sin dependencias; ejemplo completo en examples/java/NatApiClient.java del proyecto):

var req = HttpRequest.newBuilder(URI.create(base + "/api/ext/v1/saldo/001234"))
    .header("Authorization", "Bearer " + token).GET().build();
var body = client.send(req, BodyHandlers.ofString()).body();
// {"ok":true,"rc":0,"output":"SALDO 1234.56","duration_ms":22}
// HTTP 200 = se ejecutó (aunque rc≠0); comprueba siempre rc.

Referencia de la API

Fuentes de verdad: api/openapi.yaml (inglés) y api/openapi.es.yaml (español), OpenAPI 3.1, en el proyecto. La app las sirve en /api/docs (Swagger UI interactivo, con botón Try it out), /api/openapi.json y /api/openapi.yaml, con ?lang=es|en o negociación por Accept-Language. Cubre la API máquina (/api/ext/*, Bearer) y la API consola (/api/*, cookie de sesión), con el permiso RBAC (x-permission) de cada operación y ejemplos curl.

Además, cada endpoint activo aparece como operación propia (su plantilla, método y parámetros reales): el desarrollador ve qué hay disponible, cómo llamarlo, qué devuelve, y lo prueba con su token (rellénalo arriba o pasa ?token=nstm_…).

Visibilidad (en caliente, config.yaml o Configuration → API docs):

apiDocs:
  access: private   # public (sin login) | private (sesión de consola) | off (404)

Guía completa de uso (auth, convenciones, paginación, errores, versionado): nsnatapi/doc/api.md.

Consola: panel, correo y sesiones

El Dashboard muestra telemetría viva del servidor (CPU, memoria, carga, disco, RSS/heap/goroutines del proceso, slots Natural en uso y uptime), muestreada de /proc sin dependencias externas y refrescada cada 5 s desde GET /api/system/stats (solo admin).

La sección correo (SMTP o sendmail, con botón Probar email) habilita la recuperación de contraseña: ¿Olvidaste tu contraseña? envía un enlace de un solo uso (60 min); las direcciones desconocidas reciben siempre la misma respuesta genérica (sin enumeración de usuarios). La clave SMTP puede ir por variable (NSNATAPI_SMTP_PASSWORD).

Las sesiones de consola rotan el refresh token en cada uso con detección de reuso (un token repetido revoca todas las sesiones); users.token_version invalida todo al cambiar/resetear la contraseña o forzar el logout. Se suma auth.tokenPepper (HMAC a los tokens API), cookies con Secure automático, server.trustedProxies (loopback por defecto) para la integridad de la IP en auditoría y anti-fuerza-bruta, y errores genéricos al cliente.

Endpoints y modos

  • batch: al pulsar Deploy se genera y compila un stub NX000042 (reporting-mode) en la librería del entorno.
  • session: Deploy compila el driver compartido NSRPDRV (structured mode) una vez por librería; el pool lo reutiliza.

Mapeo de parámetros: capturas del path (en orden) + campos (query en GET/DELETE, JSON body en POST/PUT), unidos con el separador en una línea.

Sesiones persistentes

natural:
  numSessions: 4            # tamaño del pool por (entorno, librería)
  maxSessions: 16           # tope (el límite real es NATBP MAXUSERS)
  sessionIdleSec: 600       # cierra sesiones ociosas > 10 min (0 = off)
environments:
  DEV: { bp: NATBP, numSessions: 6 }
  TST: { bp: NATTST, numSessions: 2, sessionIdleSec: 300 }

Cada sesión es un proceso Natural (consume un usuario del buffer pool; deja margen en MAXUSERS). Un error fatal recicla la sesión automáticamente.

Evitar NAT3009: una sesión persistente que abrió Adabas puede caducar por no-actividad (TNAA/TNAE/TNAX) y la siguiente llamada falla con NAT3009 (subcode 3). Con sessionIdleSec se cierra la sesión antes de que caduque (debe ser menor que el TNAE efectivo, con margen): la siguiente petición crea una sesión nueva con Adabas fresco y nunca aparece el 3009. El cierre se registra en el log del servidor (recycled by idle timeout) y en la auditoría (SESSION_IDLE_TIMEOUT), y se cuenta por pool (recycled_idle).

Base vs entornos: environments es la fuente de verdad y cada entorno es autónomo. El bloque natural de nivel superior es solo la base por defecto (pre-rellena entornos nuevos y sirve de respaldo si no hay ninguno); no es un entorno. defaultEnvironment elige el entorno usado cuando un endpoint no especifica ninguno.

Se configura en la consola, Configuration → Natural environments: el campo Session pool size (numSessions) fija el valor por defecto y el modal de cada entorno el suyo (el valor del entorno manda sobre el global; es el número que arranca Precalentar). El cambio se aplica en caliente: el pool se redimensiona (al bajar, se reciclan las sesiones inactivas sobrantes). Los pools están separados por entorno (p. ej. 3 sesiones en DEV y 8 en TST, con valores propios); un endpoint sin entorno explícito usa el entorno por defecto. La página Sessions etiqueta cada pool con su entorno y el registro de llamadas guarda el entorno (columna y filtro Environment en Calls). El panel Natural RPC session pools (Configuration) lista todos los entornos y permite Nuevo pool (por librería), Editar límites, Precalentar, Reiniciar y Borrar. Por API: GET /api/natural/sessions, POST /api/natural/sessions/warm|restart|delete; las métricas también salen en GET /api/system/stats → natural.sessions.

La página Sessions (admin, auto-refresh cada 5 s) muestra en vivo cada sesión Natural: PID, estado (idle/busy/dead), uptime, última llamada, llamadas servidas (proxy de transacciones), errores, último programa, latencias (última/media), CPU (%) instantánea y media, y memoria (RSS), con acciones Precalentar / Reiniciar / Matar sesión.

Ejemplos de uso

1. Alta rápida de un endpoint (consola)

Endpoints → Crear: método GET, plantilla /v1/saldo/:cuenta, librería NSNAT, programa CPOLIZA, entorno DEV, modo batch, fields_json []. Pulsa Deploy para compilar el stub y Try it para probarlo.

2. La misma alta por API (admin)

Ver la sección «Crear por API» más arriba: el fields_json queda vacío porque el valor ya viaja en la captura :cuenta.

3. Endpoint en modo sesión (alta frecuencia)

curl -s -b cookies.txt -H 'Content-Type: application/json' \
  -d '{"name":"saldo-session","method":"GET","path_template":"/v1/saldo-session/:cuenta",
       "library":"NSNAT","program":"CPOLIZA","exec_mode":"session",
       "fields_json":"[]"}' \
  http://host:11032/api/endpoints

# Deploy compila el driver NSRPDRV (una vez por librería)
curl -s -b cookies.txt -X POST http://host:11032/api/endpoints/2/deploy

4. Token de API con alcance y allowlist

Ver la sección «Token con alcance» más arriba (crea primero el endpoint cuyo nombre pasas en --endpoint).

5. Cliente que distingue rc de HTTP

var body = client.send(req, BodyHandlers.ofString()).body();
// HTTP 200 + {"ok":true,"rc":4,"output":"CUENTA NO ENCONTRADA",...}
// → no es un fallo técnico: el negocio dice que la cuenta no existe.

6. Observabilidad

# Métricas por endpoint (admin, cookie de consola)
curl -s -b cookies.txt http://host:11032/api/stats | jq '.[] | {endpoint_id,calls,errors,avg_ms}'
# Ocupación del pool de sesiones
curl -s -b cookies.txt http://host:11032/api/system/stats | jq '.natural.sessions'
# Llamadas filtradas por librería (la página Calls muestra Environment, Library y PID)
curl -s -b cookies.txt 'http://host:11032/api/calls?library=NSNAT' | jq '.items[] | {environment,library,exec_mode,session_pid,http_status}'

Configuración (config.yaml)

BloqueClaveDescripción
serverhost / port / trustedProxiesEscucha (def. 0.0.0.0:11032) y proxies de confianza
databasepathSQLite (RBAC, endpoints, tokens, auditoría)
authjwtSecret / jwtExpiryMin / cookieName / tokenPepper / secureCookiesSesión de consola y hash de tokens
naturalnaturalBin / sagenv / bp / natparm / fuserDbid / fuserFnr / fuserPath / numSessions / maxSessions / sessionIdleSecEntorno por defecto
defaultEnvironment<DEV/TST/PRD>Entorno usado cuando un endpoint no especifica ninguno
environments<DEV/TST/PRD>Overrides por entorno (heredan del bloque natural)
execdefaultTimeoutSec / maxTimeoutSec / maxConcurrent / queueTimeoutSec / ratePerMin / callRetentionDays / maxBodyBytes / maxOutputBytesLímites de ejecución
correomailer, host, port, …Envío de correo (reset de contraseña, avisos de token)
loggingdir / maxSizeMB / maxAgeDays / maxBackups / compressRotación de logs
licensetrialDaysDías de trial si no hay license.key
apiDocsaccessVisibilidad del spec: public / private / off
updatesurlReservado: se guarda y se edita, pero hoy no lo consume ningún componente

Todo se edita también desde la consola (Configuration, solo admin). Se aplican al instante: timeouts, concurrencia, cola, entornos y apiDocs.access. Requieren reinicio: server.host, server.port, database.path, auth.cookieName, logging.dir y auth.jwtSecret (la propia página lo indica). El rate limit (exec.ratePerMin) se fija al arrancar: cambiarlo no surte efecto hasta el siguiente reinicio.

Seguridad

  • Tokens Bearer por endpoint con alcance, caducidad, revocación y allowlist de IP (--cidr), más rate limiting por IP y por token.
  • Consola con JWT, bcrypt, TOTP 2FA y RBAC; rotación de refresh tokens con detección de reuso.
  • Roles y grupos: los permisos se conceden a roles, los grupos asignan un rol y los usuarios pertenecen a grupos (permisos = unión). Catálogo: dashboard, endpoints, tokens, calls, sessions (cada uno view/manage), users, config, logs. Página Roles & Groups; el grupo de sistema Administrators tiene todos los permisos y init deja ahí al usuario admin.
  • Recuperación de contraseña: en Users, Reset link genera un enlace de un solo uso (60 min) y lo envía por correo; si el correo no está configurado o falla, la consola muestra el motivo y copia el enlace para entrega manual.
  • Validación estricta de todo valor que llega a Natural o al FUSER; sin shell con datos de usuario; sin path traversal.
  • Auditoría de cada llamada (solo metadatos, nunca bodies); retención configurable.

Problemas típicos

SíntomaCausa probable
502 protocol: missing OUT sentinelStub no desplegado o el programa no devuelve control (STOP, bucle)
502 natural execution failedError de Natural; revisa el log de llamadas
Sesión: session boot timeoutFalta el driver NSRPDRV o la librería/fuserPath/bp no resuelven
Sesión: session error (RC=935)La firma DEFINE DATA PARAMETER no sigue el convenio (3 parámetros)
503 server too busy / no free Natural sessionConcurrencia o pool llenos: sube maxConcurrent/numSessions
504 timeoutEl programa tarda más que timeout_sec
502 invalid json from programEl programa emite comillas simples: usa H'22' (o GENERATE JSON) para las comillas dobles
400 missing param XCampo requerido sin valor (query en GET/DELETE, body en POST/PUT); revisa que las capturas del path no estén duplicadas en fields_json
400 params too long (max 240 chars)Demasiados campos o valores largos para una línea

Límites (framing v2)

Entrada: una línea de params (≤240 caracteres por INPUT). Salida: las respuestas largas (>240) se transmiten fragmentadas (framing v2: NSNAT:OUT:<len> + NSNAT:C:<len>:<chunk>×N + NSNAT:END RC=<n>), así que el tope de 240 del WRITE de una línea no aplica al payload total; el tope real de salida es exec.maxOutputBytes (1 MB). Timeout por endpoint (def. 60 s, tope 300 s), concurrencia configurable y rate limit por token.

Referencia rápida

ComandoDescripción
nsnatapi init [dir]Crea configuración, base de datos y usuario admin
nsnatapi start|stop|status [dir]Ciclo de vida del servicio
nsnatapi token [dir] --name X --endpoint Y --days N [--cidr ...]Crea un token de API (el endpoint Y debe existir)
nsnatapi token [dir] --list | --revoke <prefix>Lista o revoca tokens
nsnatapi passwd [dir] <usuario> [clave]Restablece la contraseña de consola (se genera si se omite)
nsnatapi license [dir]Estado de la licencia (código de salida 1 si no es válida)
nsnatapi --helpAyuda y flags globales

También disponible en: en