Diese Seite wurde maschinell übersetzt und wurde noch nicht von einer Person überprüft. Englisch ist die Referenz; Korrekturen auf GitHub sind willkommen. GitHub
Cookwala / SDK und Szenarien / 047
A request whose recipeHash differs from the hub's copy is refused
Every request names the exact recipe revision by its hash. Hash the recipe the hub holds, send a request with a different hash and see the refusal, then send the right hash.
Zuerst lesen: /docs/CORE/ /docs/HUB/
Schritte
Fetch the shakshuka recipe the hub holds.
getRecipeHash it: sha256 over RFC 8785 canonical JSON, excluding the hash and signature fields. This is the value a request must carry.
hashSend a request with a hash of all zeros. The hub refuses with recipe_hash_mismatch and tells you which hash it holds.
startExecutionThe same request with the right hash is accepted.
startExecution
Ergebnis
You can compute the hash a request needs and have seen that a stale or edited recipe is refused. Next: scenario 055 compares a hash with the field inside a recipe file.
Kommandozeile
cookwala hash examples/shakshuka.cookwala.json
cookwala hub --recipes examples --speed 200
jq '. + {"x-hub-human-present": true}' examples/core/execute-request.json | curl -s -X POST http://localhost:7878/v1/executions -H 'Content-Type: application/json' -H 'Idempotency-Key: ex-2026-10-04-001-a' -d @-Aufgezeichnete Ausgabe
Status: ok
[1] getRecipe: {"$schema": "https://cookwala.ai/v1/schemas/recipe.schema.json", "cookwala": "0.1.0", "id": "example-shakshuka", "revision": 2, "updated": "2026-10-03", "hash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "verification": {"level": "V2", "evidence": {"simulatorRuns": 1, "humanReviews": 1, "fieldReports": 0}, "reviewedBy": "fifi.cooking editorial", "notes": "Illustrative example for the spec; values are realistic but not field-verified."}, "dish": {"names": {"en": "Shakshuka", "ar": "شكشوكة", "fr": "Chakchouka"}, "cuisine": ["TN", "MA", "DZ", "LY"], "course": "main…
[2] hash: {"hash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841"}
[3] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-047-hash", "seq": 0, "state": "refused", "request": "ex-047-hash", "updatedAt": "2026-10-07T00:54:31Z", "refusal": {"reason": "recipe_hash_mismatch", "detail": "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841"}}
[4] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-047-ok", "seq": 0, "state": "accepted", "request": "ex-047-ok", "updatedAt": "2026-10-07T00:54:31Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": "human"…Code
Ausführen: python scenarios/out/047-refused-when-the-recipe-hash-does-not-match/python.py
# Scenario 047: A request whose recipeHash differs from the hub's copy is refused
# Every request names the exact recipe revision by its hash. Hash the recipe the hub holds, send a request with a different hash and see the refusal, then send the right hash.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem
def load(path):
with open(path, encoding="utf-8") as f:
return json.load(f)
def pick(obj, path):
for part in path.replace("]", "").replace("[", ".").split("."):
obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
return obj
c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))
# Step 1: Fetch the shakshuka recipe the hub holds.
recipe = c.get_recipe("shakshuka")
assert pick(recipe, "id") == "example-shakshuka", pick(recipe, "id")
print("ok", "id", "=", "example-shakshuka")
# Step 2: Hash it: sha256 over RFC 8785 canonical JSON, excluding the hash and signature fields. This is the value a request must carry.
h = c.hash(recipe)
assert pick(h, "hash") == "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", pick(h, "hash")
print("ok", "hash", "=", "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841")
# Step 3: Send a request with a hash of all zeros. The hub refuses with recipe_hash_mismatch and tells you which hash it holds.
try:
refused = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-047-hash", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-047-hash-a"}, "ex-047-hash-key", True)
except CookwalaProblem as p:
refused = p.body # a refusal is a result, not a crash
print("refused:", p.refusal)
assert pick(refused, "state") == "refused", pick(refused, "state")
print("ok", "state", "=", "refused")
assert pick(refused, "refusal.reason") == "recipe_hash_mismatch", pick(refused, "refusal.reason")
print("ok", "refusal.reason", "=", "recipe_hash_mismatch")
assert pick(refused, "refusal.detail") == "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", pick(refused, "refusal.detail")
print("ok", "refusal.detail", "=", "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841")
# Step 4: The same request with the right hash is accepted.
try:
accepted = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-047-ok", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-047-ok-a"}, "ex-047-ok-key", True)
except CookwalaProblem as p:
accepted = p.body # a refusal is a result, not a crash
print("refused:", p.refusal)
assert pick(accepted, "state") == "accepted", pick(accepted, "state")
print("ok", "state", "=", "accepted")
print("scenario complete")
scenarios/047-refused-when-the-recipe-hash-does-not-match.json