Saltar al contenido principal

¿Cuándo usar funciones públicas?

Usa config: { public: true } cuando el llamador no puede enviar un X-Jelou-Token:
  • Webhooks de servicios externos (Stripe, GitHub, Twilio)
  • Callbacks de pasarelas de pago
  • APIs públicas accesibles desde el navegador

En modo define()

index.ts
import { define, z } from "@jelou/functions";

export default define({
  description: "Webhook de Stripe — recibe eventos de pago",
  input: z.object({
    type: z.string(),
    data: z.object({ id: z.string(), amount: z.number() }),
  }),
  config: {
    public: true,
    methods: ["POST"],
    mcp: false,
  },
  handler: async (input, ctx) => {
    ctx.log("Stripe webhook", { type: input.type, id: input.data.id });
    return { received: true };
  },
});
Con public: true en define(), la autenticación de plataforma se omite completamente — ninguna ruta requiere token.

En modo app() — global

export default app({
  config: { public: true },
  tools: {
    webhookPagos: define({
      description: "Recibe pagos",
      input: z.object({ event: z.string() }),
      config: { mcp: false },
      handler: async (input) => ({ ok: true }),
    }),
    webhookEnvios: define({
      description: "Recibe actualizaciones de envío",
      input: z.object({ trackingId: z.string() }),
      config: { mcp: false },
      handler: async (input) => ({ ok: true }),
    }),
  },
});
Todos los tools son públicos.

En modo app() — per-tool

export default app({
  tools: {
    webhookPagos: define({
      description: "Webhook público de pagos",
      input: z.object({ event: z.string() }),
      config: { public: true, mcp: false },
      handler: async (input) => ({ acknowledged: true }),
    }),
    consultarSaldo: define({
      description: "Consulta saldo (requiere token)",
      input: z.object({ telefono: z.string() }),
      handler: async (input) => ({ saldo: 150.00 }),
    }),
  },
});
Solo /webhook-pagos es público. /consultar-saldo requiere X-Jelou-Token.

Override: app público, tool protegido

export default app({
  config: { public: true },
  tools: {
    apiPublica: define({
      description: "Endpoint público",
      input: z.object({}),
      handler: async () => ({ status: "ok" }),
    }),
    adminProtegido: define({
      description: "Panel de admin (requiere token)",
      input: z.object({ action: z.string() }),
      config: { public: false },
      handler: async (input) => ({ done: true }),
    }),
  },
});
public: false en el tool sobrescribe el public: true global.

Health check

/__health reporta el estado public de cada tool:
{
  "mode": "app",
  "tools": [
    { "key": "webhookPagos", "path": "/webhook-pagos", "public": true },
    { "key": "consultarSaldo", "path": "/consultar-saldo", "public": false }
  ]
}

OpenAPI

La spec generada en /openapi.json refleja la configuración de seguridad:
  • Tools protegidos: "security": [{ "jelouToken": [] }]
  • Tools públicos: "security": []

Seguridad: validar webhooks

Con funciones públicas, la plataforma no valida nada. Tu código es responsable de verificar la autenticidad:
handler: async (input, ctx, request) => {
  const signature = request.headers.get("x-webhook-signature");
  const secret = ctx.env.get("WEBHOOK_SECRET");

  if (!signature || !secret) {
    return { error: "missing_signature" };
  }

  // Verificar HMAC-SHA256
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["verify"],
  );

  const body = await request.clone().text();
  const sigBytes = Uint8Array.from(atob(signature), (c) => c.charCodeAt(0));
  const valid = await crypto.subtle.verify("HMAC", key, sigBytes, encoder.encode(body));

  if (!valid) {
    ctx.log("Firma inválida", { signature });
    return { error: "invalid_signature" };
  }

  // Firma válida — procesar el evento
  ctx.log("Webhook verificado", { event: input.type });
  return { acknowledged: true };
}
jelou secrets set mi-webhook WEBHOOK_SECRET=whsec_...
Nunca confíes en una función pública sin validar la firma. Cualquier persona puede enviar peticiones a la URL.

Rutas siempre públicas

Estas rutas nunca requieren token, independientemente de la configuración public:
RutaDescripción
/__healthHealth check y metadata
/openapi.jsonEspecificación OpenAPI