resonate-http-service-design-typescript — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resonate-http-service-design-typescript (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
SDK version: This skill reflects @resonatehq/sdk v0.11.4 (current on npm).Use this skill to design HTTP services where route handlers start or await durable workflows. The HTTP server is the entrypoint; durable functions do the work and coordinate via Resonate. Downstream services (like a database service) expose their own durable functions and are invoked via RPC.
client -> HTTP routes -> Resonate Client (beginRpc/run)
-> worker group (durable workflow)
-> ctx.rpc -> db worker group (durable db functions)function* with yield*.POST /jobs starts a workflow and returns jobId.GET /jobs/:id returns status or result.POST /jobs starts a workflow and returns jobId.POST /webhooks/service resolves a durable promise tied to a workflow.import express from "express";
import { Resonate } from "@resonatehq/sdk";
import crypto from "node:crypto";
const app = express();
app.use(express.json());
const resonate = new Resonate({
url: "http://localhost:8001",
group: "api",
});
// Start workflow
app.post("/jobs", async (req, res) => {
const jobId = `job/${crypto.randomUUID()}`;
await resonate.beginRpc(
jobId,
"process-job",
req.body,
resonate.options({ target: "poll://any@workers" })
);
res.status(202).json({ id: jobId });
});
// Poll status
app.get("/jobs/:id", async (req, res) => {
const handle = await resonate.get(req.params.id);
const result = await handle.result();
res.json({ id: req.params.id, result });
});
// Look up a pending approval promise for a job.
// The worker records the generated promise id against the job (see below);
// this route reads it back through the db service. There is no promise-search
// method on the SDK client, so the mapping has to live in your own storage.
app.get("/jobs/:id/approval", async (req, res) => {
const promiseId = await resonate.rpc(
`approval-lookup/${req.params.id}`,
"db.getApprovalPromiseId",
req.params.id,
resonate.options({ target: "poll://any@db" })
);
if (!promiseId) {
res.status(404).json({ error: "no pending approval" });
return;
}
res.json({ promiseId });
});
// Webhook: external system resolves promise
app.post("/webhooks/approval", async (req, res) => {
const { promiseId, approved } = req.body;
const data = Buffer.from(JSON.stringify({ approved })).toString("base64");
await resonate.promises.resolve(promiseId, { data });
res.status(204).end();
});import { Resonate, type Context } from "@resonatehq/sdk";
const resonate = new Resonate({ url: "http://localhost:8001", group: "workers" });
function* processJob(ctx: Context, payload: { accountId: string }) {
const account = yield* ctx.rpc(
"db.getAccount",
payload.accountId,
ctx.options({ target: "poll://any@db" })
);
const result = yield* ctx.run(processAccount, account);
// The id is generated by the SDK, so create the promise first, then record
// the id where the HTTP layer can find it.
const approval = yield* ctx.promise<boolean>({ tags: { job: ctx.id } });
yield* ctx.rpc(
"db.saveApprovalPromiseId",
{ jobId: ctx.id, promiseId: approval.id },
ctx.options({ target: "poll://any@db" })
);
const ok = yield* approval;
if (!ok) {
throw new Error("rejected");
}
yield* ctx.rpc(
"db.saveResult",
{ id: ctx.id, result },
ctx.options({ target: "poll://any@db" })
);
return { status: "done", result };
}
resonate.register("process-job", processJob);import { Resonate, type Context } from "@resonatehq/sdk";
const resonate = new Resonate({ url: "http://localhost:8001", group: "db" });
function* getAccount(_: Context, id: string) {
return await db.loadAccount(id);
}
function* saveResult(_: Context, record: { id: string; result: unknown }) {
await db.save(record);
return { ok: true };
}
function* saveApprovalPromiseId(
_: Context,
record: { jobId: string; promiseId: string }
) {
await db.saveApprovalPromiseId(record.jobId, record.promiseId);
return { ok: true };
}
function* getApprovalPromiseId(_: Context, jobId: string) {
return await db.loadApprovalPromiseId(jobId);
}
resonate.register("db.getAccount", getAccount);
resonate.register("db.saveResult", saveResult);
resonate.register("db.saveApprovalPromiseId", saveApprovalPromiseId);
resonate.register("db.getApprovalPromiseId", getApprovalPromiseId);ctx.date.now() and ctx.math.random() inside durable functions.ctx.run, ctx.rpc).function* aggregate(ctx: Context, ids: string[]) {
const futures = ids.map((id) =>
ctx.beginRpc("db.getAccount", id, ctx.options({ target: "poll://any@db" }))
);
const results = [];
for (const f of futures) {
results.push(yield* f);
}
return results;
}ctx.options({ timeout: ... }) to bound retries.job/<uuid>, db/op/<jobId>.ctx.promise() does not accept a caller-supplied id — read promise.id off the returned RFI and propagate it (e.g. as a tag) if something outside the workflow needs to resolve it.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.