Esta página foi traduzida por máquina e ainda não foi revisada por uma pessoa. O inglês é a referência; correções são bem-vindas no GitHub. GitHub

Cookwala / SDK e cenários / 082

A device maker sees firmware only while a consent grant stands

Device self-state facets travel by consent. Derive the device maker's view without a grant, with the household's grant, after the grant is withdrawn, and when another role tries to use the same grant.

Público: appliance maker · Objetivo: Robôs para pessoas · Nível: intermediate

Leia primeiro: /docs/HOUSEHOLD-CONTEXT/ /trust/

Passos

  1. Without consents the maker receives one derived constraint (robot runtime, from the battery facet); the firmware facet is withheld for lack of consent. deriveConstraints

  2. The withheld entry names the facet and the reason, so the maker's software can ask the owner for a grant instead of guessing.

  3. With the household's grant cg-maker-1 (scope: the self family, recipient role device_maker, purpose warranty and recalls) the firmware facet is disclosed. deriveConstraints

  4. The same grant with a withdrawnAt date no longer counts: the facet goes back to withheld. deriveConstraints

  5. A grant names its recipient role. Presented for the insurer role, the maker's grant unlocks nothing. deriveConstraints

Resultado

You can design a warranty or recall flow that asks for a ConsentGrant, respects its withdrawal, and never receives the household's other facts. Next: 083 shows the narrowest commercial role, the insurer.

Linha de comando

cookwala constraints examples/household/context.json --role device_maker
jq '.consents' examples/household/context.json

Saída gravada

Status: ok

[1] deriveConstraints: {"constraints": [{"type": "robot_runtime_minutes", "derivedFrom": ["cw.facet.self.battery"]}], "disclosed": [], "withheld": [{"facet": "cw.facet.household.health.allergies", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.diets", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.medications", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.people.schedule", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.space.environment.foot_traffic", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.pets.animals", "reason":…
[3] deriveConstraints: {"constraints": [{"type": "robot_runtime_minutes", "derivedFrom": ["cw.facet.self.battery"]}], "disclosed": ["cw.facet.self.firmware"], "withheld": [{"facet": "cw.facet.household.health.allergies", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.diets", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.medications", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.people.schedule", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.space.environment.foot_traffic", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.…
[4] deriveConstraints: {"constraints": [{"type": "robot_runtime_minutes", "derivedFrom": ["cw.facet.self.battery"]}], "disclosed": [], "withheld": [{"facet": "cw.facet.household.health.allergies", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.diets", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.medications", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.people.schedule", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.space.environment.foot_traffic", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.pets.animals", "reason":…
[5] deriveConstraints: {"constraints": [], "disclosed": [], "withheld": [{"facet": "cw.facet.household.health.allergies", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.diets", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.health.medications", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.people.schedule", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.space.environment.foot_traffic", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.household.pets.animals", "reason": "not_allowed_for_role"}, {"facet": "cw.facet.commerce.receiving_rules", "r…

Código

Executar: python scenarios/out/082-device-maker-consent-grant-and-withdrawal/python.py

# Scenario 082: A device maker sees firmware only while a consent grant stands
# Device self-state facets travel by consent. Derive the device maker's view without a grant, with the household's grant, after the grant is withdrawn, and when another role tries to use the same grant.
# 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: Without consents the maker receives one derived constraint (robot runtime, from the battery facet); the firmware facet is withheld for lack of consent.
none = c.derive_constraints(pick(load("examples/household/context.json"), "facets"), "device_maker")
assert pick(none, "constraints.length") == 1, pick(none, "constraints.length")
print("ok", "constraints.length", "=", 1)
assert pick(none, "constraints[0].type") == "robot_runtime_minutes", pick(none, "constraints[0].type")
print("ok", "constraints[0].type", "=", "robot_runtime_minutes")
assert pick(none, "disclosed.length") == 0, pick(none, "disclosed.length")
print("ok", "disclosed.length", "=", 0)
assert pick(none, "withheld.length") == 11, pick(none, "withheld.length")
print("ok", "withheld.length", "=", 11)

# Step 2: The withheld entry names the facet and the reason, so the maker's software can ask the owner for a grant instead of guessing.
assert pick(none, "withheld[9].facet") == "cw.facet.self.firmware", pick(none, "withheld[9].facet")
print("ok", "withheld[9].facet", "=", "cw.facet.self.firmware")
assert pick(none, "withheld[9].reason") == "no_consent", pick(none, "withheld[9].reason")
print("ok", "withheld[9].reason", "=", "no_consent")

# Step 3: With the household's grant cg-maker-1 (scope: the self family, recipient role device_maker, purpose warranty and recalls) the firmware facet is disclosed.
granted = c.derive_constraints(pick(load("examples/household/context.json"), "facets"), "device_maker", pick(load("examples/household/context.json"), "consents"))
assert pick(granted, "disclosed") == ["cw.facet.self.firmware"], pick(granted, "disclosed")
print("ok", "disclosed", "=", ["cw.facet.self.firmware"])
assert pick(granted, "withheld.length") == 10, pick(granted, "withheld.length")
print("ok", "withheld.length", "=", 10)
assert pick(granted, "constraints.length") == 1, pick(granted, "constraints.length")
print("ok", "constraints.length", "=", 1)

# Step 4: The same grant with a withdrawnAt date no longer counts: the facet goes back to withheld.
withdrawn = c.derive_constraints(pick(load("examples/household/context.json"), "facets"), "device_maker", [{"id": "cg-maker-1", "grantedBy": "owner", "scope": {"families": ["self"]}, "recipientRole": "device_maker", "recipient": "did:web:robots.example", "purpose": "Warranty support and recall handling for robot neo-1", "grantedAt": "2026-10-01T18:05:00+03:00", "withdrawnAt": "2026-10-03T09:00:00+03:00", "withdrawable": True}])
assert pick(withdrawn, "disclosed.length") == 0, pick(withdrawn, "disclosed.length")
print("ok", "disclosed.length", "=", 0)
assert pick(withdrawn, "withheld.length") == 11, pick(withdrawn, "withheld.length")
print("ok", "withheld.length", "=", 11)
assert pick(withdrawn, "withheld[9].reason") == "no_consent", pick(withdrawn, "withheld[9].reason")
print("ok", "withheld[9].reason", "=", "no_consent")

# Step 5: A grant names its recipient role. Presented for the insurer role, the maker's grant unlocks nothing.
insurer = c.derive_constraints(pick(load("examples/household/context.json"), "facets"), "insurer", pick(load("examples/household/context.json"), "consents"))
assert pick(insurer, "disclosed.length") == 0, pick(insurer, "disclosed.length")
print("ok", "disclosed.length", "=", 0)
assert pick(insurer, "constraints.length") == 0, pick(insurer, "constraints.length")
print("ok", "constraints.length", "=", 0)
assert pick(insurer, "withheld.length") == 12, pick(insurer, "withheld.length")
print("ok", "withheld.length", "=", 12)

print("scenario complete")

scenarios/082-device-maker-consent-grant-and-withdrawal.json

Anterior: 081 Próximo: 083