
Security News
PHP Patches Critical RCE Vulnerabilities, Organizations Urged to Take Unpatched Sites Offline
PHP releases emergency patches for two critical RCE vulnerabilities as agencies urge organizations to take unpatched sites offline.
One API key for the boring infrastructure every app needs: scheduled jobs, a webhook relay, PDFs, and email. Built so coding agents (Claude Code, Cursor, and others) can wire it up without anyone reading docs.
npm install oakstack
Get an API key at oakstack.dev/dashboard/api-keys and set it as OAKSTACK_API_KEY.
import { Oakstack } from "oakstack";
const oakstack = new Oakstack(); // reads OAKSTACK_API_KEY
Zero dependencies. Works in Node 18+, Bun, Deno, Vercel Edge, and Cloudflare Workers.
oakstack.clockOakstack calls a URL in your app on a cron schedule, retries failures, and logs every run.
const job = await oakstack.clock.jobs.create({
name: "Nightly cleanup",
schedule: "0 3 * * *", // minute hour day month weekday
timezone: "America/Chicago", // default UTC
url: "https://yourapp.com/api/cron/cleanup",
body: JSON.stringify({ task: "cleanup" }),
});
// Save job.signingSecret as OAKSTACK_JOB_SECRET to verify calls (below). Each job and
// endpoint has its own secret; with several, name them after what they're for.
await oakstack.clock.jobs.run(job.id); // test it now instead of waiting
const runs = await oakstack.clock.jobs.runs(job.id); // an array, newest first: status, response, error
const jobs = await oakstack.clock.jobs.list(); // an array of jobs, newest first (`{ limit }` up to 100)
await oakstack.clock.jobs.pause(job.id);
await oakstack.clock.jobs.resume(job.id);
await oakstack.clock.jobs.update(job.id, { schedule: "*/15 * * * *" });
await oakstack.clock.jobs.delete(job.id);
Each failed call is retried 3 times (after about 30 seconds, 2 minutes, and 10 minutes). A job that fails 10 runs in a row pauses itself with a pausedReason.
oakstack.hookA permanent inbound URL for Stripe, GitHub, or any other sender. Every event is stored, then forwarded to your app with its original headers and body. Delivery is retried for about 11 hours, and any event can be replayed.
const endpoint = await oakstack.hook.endpoints.create({
name: "Stripe",
forwardUrl: "https://yourapp.com/api/webhooks/stripe",
});
console.log(endpoint.url); // https://oakstack.dev/h/... : give this to Stripe
const failed = await oakstack.hook.endpoints.events(endpoint.id, { status: "failed" });
const detail = await oakstack.hook.events.get(failed[0].id); // headers, body, every attempt
await oakstack.hook.events.replay(failed[0].id);
await oakstack.hook.endpoints.update(endpoint.id, { status: "paused" }); // holds events, loses none
Your app keeps verifying the sender's own signature (for example Stripe-Signature) exactly as before, because the original headers and the exact body bytes are forwarded unchanged.
oakstack.printHTML or a hosted template (invoice, proposal, report) in, PDF out. Rendered with real Chrome, stored privately, and returned with a download link.
const { example } = await oakstack.print.templates.get("invoice"); // schema + working example
const pdf = await oakstack.print.pdfs.create({
template: "invoice",
data: { ...example, invoiceNumber: "INV-1042" },
filename: "invoice-1042.pdf",
});
pdf.url; // private link, valid for an hour (get(id) gives a fresh one)
const bytes = await oakstack.print.pdfs.download(pdf.id); // Uint8Array
await oakstack.print.pdfs.create({ html: "<h1>Hello</h1>", options: { format: "A4" } });
Files are kept 1 day (Free), 7 days (Builder), or 30 days (Studio). Save the bytes yourself if you need them longer.
oakstack.postTransactional email from your own domain, with PDFs attached in the same call, plus inbound email delivered to a webhook.
await oakstack.post.domains.create({ domain: "acme.com" }); // returns DNS records; verifies by itself
const email = await oakstack.post.emails.send({
from: "Acme <hello@acme.com>",
to: "riley@example.com",
subject: "Your invoice",
html: "<p>Thanks for your order.</p>",
attachments: [{ filename: "invoice.pdf", print: { template: "invoice", data } }],
});
const { status, events } = await oakstack.post.emails.get(email.id); // delivered, bounced, ...
With an ok_test_ key nothing is delivered: emails are checked and logged, and delivery is simulated. Hard bounces and spam complaints are suppressed automatically. Live email to anyone needs a paid plan; oakstack.post.status() says what applies right now.
Every cron call and forwarded webhook carries an Oakstack-Signature header. Check it with the job's or endpoint's signingSecret:
import { verifySignature } from "oakstack";
export async function POST(req: Request) {
const body = await req.text(); // the raw body, before parsing
const ok = await verifySignature(
process.env.OAKSTACK_JOB_SECRET!,
req.headers.get("oakstack-signature"),
body,
);
if (!ok) return new Response("bad signature", { status: 401 });
// ...
return Response.json({ ok: true });
}
The scheme is the same one Stripe uses: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>." + body>. Signatures older than 5 minutes are rejected.
const { plan, usage } = await oakstack.usage();
// usage: [{ metric: "clock.jobs", label: "scheduled jobs", used: 2, limit: 3, kind: "count" }, ...]
Plans have hard limits that stop instead of overbilling. Hitting one throws OakstackError with errorName: "usage_limit_reached" and a message that says what to do next.
OakstackError, with statusCode, errorName (machine-readable), and a message that says what to fix.maxRetries).Idempotency-Key, so a retry can never create a duplicate. Pass your own to make a call safe across process restarts: oakstack.clock.jobs.create(params, { idempotencyKey: "setup-nightly-v1" }).new Oakstack({
apiKey: "ok_live_...", // default: OAKSTACK_API_KEY
baseUrl: "https://oakstack.dev", // default: OAKSTACK_BASE_URL, then this
maxRetries: 2,
timeoutMs: 30_000,
});
npx oakstack-mcp, or the hosted server at https://oakstack.dev/mcp.MIT licensed.
FAQs
One API key for cron jobs, webhook relay, PDFs, and email. Built for coding agents.
The npm package oakstack receives a total of 32 weekly downloads. As such, oakstack popularity was classified as not popular.
We found that oakstack demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
PHP releases emergency patches for two critical RCE vulnerabilities as agencies urge organizations to take unpatched sites offline.

Threat Research
/Security News
A supply chain attack compromised LiteLLM 1.107.0 on PyPI and Docker, spreading malware that steals cloud credentials, developer secrets, and AI provider keys.

Security News
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.