
Research
/Security News
Malicious Chrome and Firefox Extensions Steal Crypto Traders’ Session and Wallet Data
Malicious Chrome and Firefox extensions target Axiom Trade and Padre users, stealing session tokens and wallet data.
Cron jobs for Next.js. Serverless-native.
Zero runtime dependencies. TypeScript-first. Works with Vercel Cron out of the box.
npm install croncall
// lib/jobs.ts
import { createClockTower } from "croncall";
export const tower = createClockTower({
jobs: {
syncUsers: {
schedule: "0 * * * *", // every hour
handler: async () => {
await db.syncUsersFromExternalAPI();
},
description: "Sync users from external API",
retry: { maxAttempts: 3, backoff: "exponential" },
timeout: 30_000,
},
sendDigest: {
schedule: "0 9 * * 1", // Mondays at 9 AM UTC
handler: async () => {
await email.sendWeeklyDigest();
},
description: "Send weekly digest email",
},
cleanupSessions: {
schedule: "@daily",
handler: async () => {
await db.deleteExpiredSessions();
},
},
},
secret: process.env.CRON_SECRET,
});
// app/api/cron/route.ts
import { createCronHandler } from "croncall/next";
import { tower } from "@/lib/jobs";
export const GET = createCronHandler(tower);
Add cron schedules to vercel.json:
{
"crons": [
{ "path": "/api/cron?job=syncUsers", "schedule": "0 * * * *" },
{ "path": "/api/cron?job=sendDigest", "schedule": "0 9 * * 1" },
{ "path": "/api/cron?job=cleanupSessions", "schedule": "0 0 * * *" }
]
}
Or generate it programmatically:
import { generateVercelCron } from "croncall/next";
import { tower } from "./lib/jobs";
console.log(JSON.stringify(generateVercelCron(tower, "/api/cron"), null, 2));
Each job has:
| Field | Type | Required | Description |
|---|---|---|---|
schedule | string | Yes | Cron expression or shortcut |
handler | () => Promise<void> | Yes | Async function to execute |
description | string | No | Human-readable description |
retry | { maxAttempts, backoff, baseDelay? } | No | Retry on failure |
timeout | number | No | Max execution time in ms |
Standard 5-field cron expressions:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Supported features:
*1-51,3,5*/15, 1-30/2jan, feb, ..., decsun, mon, ..., satShortcuts:
| Shortcut | Equivalent |
|---|---|
@hourly | 0 * * * * |
@daily | 0 0 * * * |
@midnight | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly | 0 0 1 1 * |
@annually | 0 0 1 1 * |
Clocktower is designed to work with Vercel Cron Jobs.
Vercel sends a CRON_SECRET environment variable and includes it in the Authorization: Bearer <secret> header. Clocktower validates this automatically:
options.secret passed to createCronHandlerconfig.secret from createClockTowerprocess.env.CRON_SECRETIf no secret is configured, requests are allowed without authentication.
import { generateVercelCron } from "croncall/next";
import { tower } from "./lib/jobs";
const crons = generateVercelCron(tower, "/api/cron");
// [{ path: "/api/cron?job=syncUsers", schedule: "0 * * * *" }, ...]
Run a specific job on demand:
const result = await tower.run("syncUsers");
console.log(result);
// { success: true, duration: 1234 }
Run all due jobs:
const results = await tower.runDue();
for (const [name, result] of results) {
console.log(`${name}: ${result.success ? "ok" : result.error}`);
}
Via HTTP (useful for testing):
# Run a specific job
curl http://localhost:3000/api/cron?job=syncUsers \
-H "Authorization: Bearer your-secret"
# Run all due jobs
curl http://localhost:3000/api/cron \
-H "Authorization: Bearer your-secret"
const schedule = tower.schedule();
// [
// { jobName: "syncUsers", nextRun: 2026-03-26T15:00:00.000Z, schedule: "0 * * * *" },
// { jobName: "sendDigest", nextRun: 2026-03-30T09:00:00.000Z, schedule: "0 9 * * 1" },
// ]
Configure retries per job:
{
retry: {
maxAttempts: 3, // retry up to 3 times after initial failure
backoff: "exponential", // or "linear"
baseDelay: 1000, // 1s base delay (default)
}
}
The JobResult includes retryCount when retries were attempted:
const result = await tower.run("syncUsers");
if (!result.success) {
console.error(`Failed after ${result.retryCount} retries: ${result.error}`);
}
All types are exported:
import type {
CronExpression,
JobDefinition,
JobRegistry,
ClockTowerConfig,
ClockTower,
JobResult,
JobExecution,
ScheduleEntry,
RetryConfig,
} from "croncall";
Job names are fully typed:
const tower = createClockTower({
jobs: {
syncUsers: { schedule: "@hourly", handler: async () => {} },
},
});
tower.run("syncUsers"); // OK
tower.run("nonexistent"); // Type error
MIT
FAQs
Cron jobs for Next.js. Serverless-native.
The npm package croncall receives a total of 22 weekly downloads. As such, croncall popularity was classified as not popular.
We found that croncall 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.

Research
/Security News
Malicious Chrome and Firefox extensions target Axiom Trade and Padre users, stealing session tokens and wallet data.

Security News
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.