resonate-http-service-design-python — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resonate-http-service-design-python (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.
Route handlers in a Python web framework (FastAPI, Flask, Django) are the entrypoint for durable workflows; they start or await invocations via the Resonate client. Actual business logic runs in worker processes that register durable functions. Downstream services (a DB worker, a search worker) expose their own durable functions and are invoked via RPC.
This skill covers the shape of that split: what runs where, how routes interact with workers, and how webhooks resolve durable promises.
client → HTTP routes (FastAPI/Flask)
→ Resonate client (r.run / r.rpc / r.promises.resolve)
→ worker group A (business logic)
→ ctx.rpc → worker group B (specialized, e.g. DB)r.register(...)-ed functions; connects to the Resonate serverr.run, r.rpc, r.promises.*). Never use Context APIs in a route handler.async def with await ctx.run/rpc + r.register(fn).f"order:{order_id}") — not uuid.uuid4() at route-handler time unless you persist the ID in the response so the client can poll.Client submits a job, gets an ID, polls for status:
from __future__ import annotations
import os, time
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from resonate.resonate import Resonate
app = FastAPI()
r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"))
class JobSubmission(BaseModel):
order_id: str
items: list[str]
@app.post("/jobs")
async def submit_job(body: JobSubmission) -> dict:
job_id = f"order:{body.order_id}"
# Fire and forget — workflow runs durably in the background
r.options(target="order-workers").rpc(job_id, "process_order", body.order_id, body.items)
return {"job_id": job_id, "status": "started"}
@app.get("/jobs/{job_id}")
async def get_job(job_id: str) -> dict:
try:
record = await r.promises.get(job_id)
state = record.state if hasattr(record, "state") else "pending"
if state == "resolved":
return {"job_id": job_id, "status": "done", "result": record.value}
elif state == "rejected":
return {"job_id": job_id, "status": "failed", "reason": record.value}
else:
return {"job_id": job_id, "status": "running"}
except Exception:
raise HTTPException(status_code=404, detail="job not found")Client submits; workflow blocks on ctx.promise(); external system resolves via webhook:
from resonate.types import Value
# Worker
async def approve_then_ship(ctx, order_id: str) -> dict:
approval = ctx.promise()
approval_id = await approval.id()
await ctx.run(notify_approver, order_id, approval_id)
decision = await approval
if not decision.get("approved"):
return {"status": "rejected"}
await ctx.run(ship_order, order_id)
return {"status": "shipped"}
# Route — start the workflow
@app.post("/orders/{order_id}/approve-and-ship")
async def start(order_id: str) -> dict:
r.options(target="order-workers").rpc(
f"order:{order_id}", "approve_then_ship", order_id
)
return {"status": "pending_approval"}
# Route — webhook resolves the durable promise
@app.post("/webhooks/approval/{order_id}")
async def approval_webhook(order_id: str, payload: dict) -> dict:
await r.promises.resolve(
f"approval:{order_id}",
Value(data={"approved": payload.get("approved", False)}),
)
return {"ok": True}If the workflow completes quickly, block on it and return the result directly:
@app.post("/price-quote")
async def price_quote(body: QuoteRequest) -> dict:
quote_id = f"quote:{body.sku}:{body.qty}"
result = await r.options(target="pricing-workers").rpc(
quote_id,
"compute_quote",
body.sku,
body.qty,
).result()
return {"quote_id": quote_id, "price": result["price"]}r.rpc(...) returns a handle; calling .result() and awaiting it blocks the route handler. Keep timeouts tight — a slow worker holds the HTTP request open.
A worker process:
Resonate(url=..., group=...)while True sleep)# workers/order_worker.py
from __future__ import annotations
import asyncio, os
from typing import TYPE_CHECKING
from resonate.resonate import Resonate
if TYPE_CHECKING:
from resonate.context import Context
r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"), group="order-workers")
async def process_order(ctx: Context, order_id: str, items: list[str]) -> dict:
await ctx.run(validate_order, order_id, items)
await ctx.run(charge_payment, order_id)
await ctx.run(create_shipment, order_id)
return {"order_id": order_id, "status": "done"}
r.register(process_order)
if __name__ == "__main__":
async def main() -> None:
try:
# Block until Ctrl-C; SDK long-polls for work
await asyncio.Event().wait()
finally:
await r.stop()
asyncio.run(main())For larger services, split specialization across worker groups:
# db worker group
r_db = Resonate(url=url, group="db")
async def query_orders(ctx: Context, user_id: str) -> list:
db = ctx.get_dependency(psycopg.Connection)
return db.execute("SELECT * FROM orders WHERE user_id = %s", (user_id,)).fetchall()
r_db.register(query_orders)
# business worker group
r_biz = Resonate(url=url, group="business")
async def place_order(ctx: Context, user_id: str, cart: list[str]) -> dict:
# Reach into the DB group via RPC
history = await ctx.options(target="db").rpc("query_orders", user_id)
# ... business logic ...
return {"user_id": user_id, "order_id": "..."}
r_biz.register(place_order)Each group runs in its own process (or cluster of processes). RPC routes through Resonate's promise store with the server handling fair dispatch.
Small services often put the HTTP server and the worker in the same process:
from __future__ import annotations
import asyncio, os
from fastapi import FastAPI
from resonate.resonate import Resonate
app = FastAPI()
r = Resonate(url=os.environ.get("RESONATE_URL", "http://localhost:8001"))
async def process_order(ctx, order_id: str) -> dict:
await ctx.run(charge_payment, order_id)
return {"order_id": order_id, "status": "done"}
r.register(process_order)
@app.post("/orders/{order_id}")
async def start(order_id: str) -> dict:
r.run(f"order:{order_id}", process_order, order_id)
return {"status": "started"}
@app.on_event("shutdown")
async def shutdown() -> None:
await r.stop()Because r.register registers in the same process, both RPC-routing and in-process r.run(...) work. For higher throughput, move workers to dedicated processes.
async def route handlers.BaseModel) for request bodies — idiomatic + validates at parse time.handle.result(). You can fire-and-forget by not awaiting the handle. are all async** — always await` them in async route handlers.r.promises.resolve/reject.ctx only exists inside a r.register()-ed function body. A route handler gets a Resonate client, not a Context.@app.on_event("shutdown")) to drain in-flight work gracefully.r.rpc(...), specify .options(target="group-name") so the invocation routes to a worker group.resonate-basic-ephemeral-world-usage-python — Resonate, r.run, r.rpc, r.options(target=), promise managementresonate-basic-durable-world-usage-python — r.register, ctx.run, ctx.rpc, ctx.promiseresonate-human-in-the-loop-pattern-python — webhook-driven promise resolution (pattern 2 in this skill, expanded)resonate-saga-pattern-python — multi-step workflows suitable for pattern 3 (sync HTTP)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.