OpenAPI Refinement Sandbox¶
Fecha: 2026-06-02
Objetivo¶
Refinar el contrato OpenAPI generado por PostgREST para que la futura capa
de documentacion interactiva muestre endpoints claros, simples, seguros y
entendibles, sin instalar todavia Scalar, sin usar todavia
developers.alpuntodeventa.com.ar y sin exponer PostgREST fuera de la red
Docker interna.
Safe point inicial¶
git status -sb->## main...origin/maingit rev-parse HEAD->0f0dca7e0fa9de68a3a1f167d899a1b1225dbd84
Estado base heredado de 15.4¶
openclaw-postgres-sandboxvalidado comohealthyen VPSopenclaw-postgrest-sandboxvalidado comorunningen VPSPostgRESTinterno solamentePortBindingsvacioJWTactivo- claims
tenant_id,role,scope,aud,issyexpactivos RLSactivoscope enforcementactivo por recursoAPVve soloalpuntodeventaLa Directave sololadirectaGlobalve ambos tenants/responde200con token valido y entrega contratoswagger: "2.0"- sin secretos en repo ni en esta documentacion
Alcance real de esta etapa¶
- documentar el contrato runtime actual
- definir el contrato objetivo orientado a humanos
- agregar metadata SQL segura para mejorar descripciones
- evaluar si conviene seguir con tablas directas o vistas API
- dejar lista la carpeta de exportes no secretos
- preparar el cierre documental para
15.6
Restricciones mantenidas¶
- no instalar
Scalar - no usar
developers.alpuntodeventa.com.ar - no publicar DNS nuevo
- no instalar
Kong - no conectar datos reales
- no tocar bases productivas
- no debilitar
RLS,JWTniscopes - no imprimir ni commitear tokens
- no exponer
PostgRESTpublicamente
Diagnostico del contrato actual¶
Fuente actual del contrato¶
El runtime actual de PostgREST publica el contrato en:
GET /
Con el stack vigente, el contrato es interno y depende de un token JWT
valido. El compose declara:
PGRST_DB_SCHEMAS=sandbox_mtPGRST_OPENAPI_SERVER_PROXY_URI=http://openclaw-postgrest-sandbox:3000PGRST_DB_PRE_REQUEST=sandbox_private.enforce_postgrest_claims
Version esperada¶
swagger: "2.0"
Recursos visibles por contrato sandbox¶
El contrato y las reglas actuales dejan dentro del sandbox:
/clientes/productos/ventas/tenants/
Recursos fuera de ese contrato son rechazados por db-pre-request con el
mensaje operativo de recurso fuera del alcance sandbox.
Dependencia por token y rol¶
La superficie del contrato depende de credenciales validas, porque:
- sin token la request debe devolver
401 - con token invalido o expirado la request debe devolver
401 - con token valido pero
issinvalido oscopeinsuficiente la request debe devolver403 - con token valido el contrato queda accesible para lectura interna
La separacion real de datos no depende del OpenAPI, sino de dos capas:
db-pre-requestpara validar claims, issuer y scopesRLSpara filtrar filas portenant_id
Hallazgos del contrato actual¶
- el contrato runtime nace de tablas directas del schema
sandbox_mt - los nombres actuales son tecnicos pero aceptables para un sandbox:
clientes,productos,ventas,tenants - faltaba metadata SQL humana en tablas y columnas para enriquecer la salida
- el runtime usa
swagger: "2.0", suficiente para sandbox pero no ideal como experiencia final de portal - el endpoint
/tenantses intencionalmenteglobaly requiereread:all - el contrato no debe prometer paths externos ni DNS publicos porque el runtime sigue interno
Riesgos revisados¶
- no se observa en repo ninguna instruccion que publique puertos al host
- no se debe commitear el contrato exportado si contiene una URL interna que deba sanitizarse
- no conviene agregar recursos nuevos en esta etapa si no aportan claridad inmediata
Contrato objetivo orientado a humanos¶
Principios¶
- pocos recursos
- nombres de negocio simples
- descripciones cortas y claras
- ejemplos con datos ficticios
- seguridad explicita por
scope - diferencia visible entre recursos
tenantyglobal
Recursos objetivo¶
clientes¶
- descripcion: clientes ficticios del sandbox separados por tenant
- uso previsto: validar lectura REST y aislamiento por
tenant_id - alcance:
tenant - scopes:
read:clientesoread:all - request ejemplo:
http
GET /clientes?select=id,tenant_id,customer_name,customer_email&limit=2
Authorization: Bearer <sandbox-jwt>
- response ejemplo:
json
[
{
"id": 1,
"tenant_id": "alpuntodeventa",
"customer_name": "Cliente Demo APV 1",
"customer_email": "cliente1@apv.demo"
}
]
productos¶
- descripcion: productos ficticios del sandbox separados por tenant
- uso previsto: validar catalogo REST aislado por tenant
- alcance:
tenant - scopes:
read:productosoread:all - request ejemplo:
http
GET /productos?select=id,tenant_id,product_name,sku,unit_price&limit=2
Authorization: Bearer <sandbox-jwt>
- response ejemplo:
json
[
{
"id": 1,
"tenant_id": "alpuntodeventa",
"product_name": "Producto Demo APV",
"sku": "APV-DEMO-001",
"unit_price": 1000.00
}
]
ventas¶
- descripcion: ventas ficticias del sandbox para validar aislamiento multi-tenant
- uso previsto: probar joins de negocio ya filtrados por
RLS - alcance:
tenant - scopes:
read:ventasoread:all - request ejemplo:
http
GET /ventas?select=id,tenant_id,sale_reference,quantity,total_amount&limit=2
Authorization: Bearer <sandbox-jwt>
- response ejemplo:
json
[
{
"id": 1,
"tenant_id": "alpuntodeventa",
"sale_reference": "VENTA-APV-001",
"quantity": 1.00,
"total_amount": 1000.00
}
]
tenants¶
- descripcion: tenants demo autorizados para pruebas internas
- uso previsto: validar un recurso
globalcontrolado - alcance:
global - scopes:
read:all - request ejemplo:
http
GET /tenants?select=tenant_id,tenant_name,scope&limit=10
Authorization: Bearer <sandbox-jwt>
- response ejemplo:
json
[
{
"tenant_id": "alpuntodeventa",
"tenant_name": "Al Punto de Venta",
"scope": "tenant"
},
{
"tenant_id": "ladirecta",
"tenant_name": "La Directa",
"scope": "tenant"
}
]
Errores esperados¶
200 OK: token valido yscopesuficiente401 Unauthorized: token ausente, invalido o expirado403 Forbidden:scopeinsuficiente,issinvalido o recurso fuera del contrato sandbox[]: request valida peroRLSno permite filas
Metadata SQL aplicada¶
Se agrega el archivo:
infra/data-foundation/postgres-sandbox/init/005_openapi_metadata.sql
Objetivo:
- describir schema, tablas y columnas con tono humano
- mejorar el
OpenAPIgenerado porPostgREST - no cambiar datos ni permisos
- no tocar el comportamiento de seguridad
Evaluacion de superficie expuesta¶
Opcion A - Mantener tablas directas¶
Ventajas:
- minimo cambio
- aprovecha el contrato ya validado
- no agrega otra capa de objetos SQL
- reduce riesgo de romper
RLSo permisos en una etapa chica
Riesgos:
- nombres y columnas visibles quedan mas cerca del modelo fisico
- el contrato puede ser menos amable que una API view curada
Opcion B - Crear vistas API¶
Ventajas:
- permite nombres y columnas mas curadas
- facilita ocultar campos tecnicos en un futuro
- prepara una separacion entre modelo fisico y modelo expuesto
Riesgos:
- introduce mas objetos y mas superficie a auditar
- obliga a revalidar permisos,
RLSy alcance efectivo - agrega complejidad sin una necesidad inmediata fuerte en este sandbox
Recomendacion¶
Para 15.5 conviene mantener tablas directas y enriquecer metadata.
Motivo:
- el sandbox ya tiene solo cuatro recursos claros
- el principal gap era descriptivo, no estructural
- crear vistas ahora aportaria poco valor inmediato frente al costo de revalidacion
Las vistas api_clientes, api_productos, api_ventas y api_tenants
quedan como opcion futura solo si 15.6 necesita simplificar mas el contrato
visual para Scalar.
Validacion por perfil¶
Evidencia runtime final en VPS¶
Validacion ejecutada por ssh openclaw-vps el 2026-06-02, aplicando
005_openapi_metadata.sql al PostgreSQL sandbox existente y recreando solo
el stack interno de PostgREST.
Estado confirmado:
openclaw-postgres-sandbox->running healthyopenclaw-postgrest-sandbox->runningPortBindingsdeopenclaw-postgrest-sandbox->{}- red efectiva de
openclaw-postgrest-sandbox-> solopg-sandbox-internal
Tokens generados en memoria, sin imprimirlos ni persistirlos:
APV-> presetapv-clientesLa Directa-> presetladirecta-productosGlobal-> presetglobal-all
Resultados sobre GET /:
APV->200La Directa->200Global->200- sin token ->
401 - token invalido ->
401
Contrato runtime observado:
- mantiene
swagger: "2.0" info.title->OpenClaw Sandbox APIinfo.description->Sandbox multi-tenant interno con datos ficticios para validar REST, JWT, RLS y OpenAPI.- recursos visibles exactos ->
/,/clientes,/productos,/tenants,/ventas - no aparecieron endpoints indebidos
rpc/*luego de mover helpers asandbox_private COMMENT ONya se refleja en metadata humana:clientes.description,clientes.customer_name.description,tenants.description- no se detectaron secretos ni tokens en el contrato ni en la evidencia registrada
- el contrato sigue interno y todavia declara
host: openclaw-postgrest-sandbox:3000
Comandos a ejecutar en VPS¶
Generar tokens sin persistirlos:
bash
cd /opt/openclawai/infra/data-foundation/postgrest-sandbox
APV_TOKEN="$(python tools/generate-sandbox-jwt.py apv-clientes --secret-file ./secrets/postgrest_jwt_secret)"
LD_TOKEN="$(python tools/generate-sandbox-jwt.py ladirecta-productos --secret-file ./secrets/postgrest_jwt_secret)"
GLOBAL_TOKEN="$(python tools/generate-sandbox-jwt.py global-all --secret-file ./secrets/postgrest_jwt_secret)"
Validar contrato:
```bash docker run --rm --network pg-sandbox-internal curlimages/curl:8.8.0 -s \ -o /tmp/openapi-apv.json -w '%{http_code}\n' \ -H "Authorization: Bearer ${APV_TOKEN}" \ http://openclaw-postgrest-sandbox:3000/
docker run --rm --network pg-sandbox-internal curlimages/curl:8.8.0 -s \ -o /tmp/openapi-ladirecta.json -w '%{http_code}\n' \ -H "Authorization: Bearer ${LD_TOKEN}" \ http://openclaw-postgrest-sandbox:3000/
docker run --rm --network pg-sandbox-internal curlimages/curl:8.8.0 -s \ -o /tmp/openapi-global.json -w '%{http_code}\n' \ -H "Authorization: Bearer ${GLOBAL_TOKEN}" \ http://openclaw-postgrest-sandbox:3000/
docker run --rm --network pg-sandbox-internal curlimages/curl:8.8.0 -s \ -o /dev/null -w '%{http_code}\n' \ http://openclaw-postgrest-sandbox:3000/
docker run --rm --network pg-sandbox-internal curlimages/curl:8.8.0 -s \ -o /dev/null -w '%{http_code}\n' \ -H 'Authorization: Bearer not-a-real-token' \ http://openclaw-postgrest-sandbox:3000/ ```
Registrar:
APV->200, paths/,/clientes,/productos,/tenants,/ventasLa Directa->200, paths/,/clientes,/productos,/tenants,/ventasGlobal->200, paths/,/clientes,/productos,/tenants,/ventas- sin token ->
401 - token invalido ->
401
Exportes sandbox¶
Carpeta preparada:
infra/data-foundation/postgrest-sandbox/openapi/
Archivos objetivo:
openapi-apv-sandbox.jsonopenapi-ladirecta-sandbox.jsonopenapi-global-sandbox.json
Solo deben commitearse si:
- no incluyen tokens
- no incluyen secretos
- no exponen una URL interna sensible que convenga sanitizar
Resultado final de 15.5:
- no se exportan
openapi-apv-sandbox.json - no se exportan
openapi-ladirecta-sandbox.json - no se exportan
openapi-global-sandbox.json
Motivo:
- el runtime sigue declarando
host: openclaw-postgrest-sandbox:3000 - commitear esos JSON propagaria una URL interna de la red Docker del VPS
- la carpeta queda preparada para una reexportacion futura solo si se decide
sanitizar o publicar un host seguro en
15.6
Relacion con 15.6¶
Esta etapa no instala el portal interactivo.
Deja listo:
- contrato runtime mejor descrito
- criterios de recursos, scopes y errores
- carpeta de exportes segura
- recomendacion de mantener tablas directas por ahora
La etapa 15.6 queda reservada para:
- montar
Scalar - usar
developers.alpuntodeventa.com.ar - publicar la experiencia interactiva de documentacion de APIs
Checklist de cierre¶
SAFE POINTinicial registrado- diagnostico del contrato actual documentado
- contrato objetivo documentado
- metadata SQL mejorada sin tocar seguridad
- superficie expuesta evaluada
- validaciones por perfil documentadas
- carpeta de exportes preparada
- evidencia runtime final en VPS registrada
- sin secretos en repo, docs ni comandos persistidos
PostgRESTsigue internoPortBindingssiguen reservados como vacios en la evidencia vigenteScalarydevelopers.alpuntodeventa.com.arsiguen reservados para15.6