Skip to content
Tenbri pangolinTenbri

Review a policy change

Your refund policy moved from version 14 to version 15. Legal asks which refunds were decided under version 14, and how they ended. Each intent records the policy it read as an input (policy:refund at a version), so a query on that input lists them. You will record five decisions across both versions, list the ones under version 14, and join them to their outcomes.

The tenbri CLI, jq and openssl:

Terminal window
cargo install tenbri-cli --version 0.1.0-beta.1 --locked

In every terminal:

Terminal window
export TENBRI_KEYS=./keys
export TENBRI_DIGEST_KEY='a 32-byte secret, kept apart....'
  1. Record decisions under both versions.

    Each decision is an intent that read policy:refund at a version, then its outcome: ok or refused.

    The decide helper writes an intent with the keyed digest of the policy text it read, then its outcome.

    decide.sh
    tenbri init audit/support > init.out
    PIN=$(sed -n 's/^stream key fingerprint (pin it): //p' init.out)
    decide() { # charge, policy version, status
    digest="hmac-sha256:$(printf 'refund policy, version %s' "$2" \
    | openssl dgst -sha256 -hmac "$TENBRI_DIGEST_KEY" -r | cut -d' ' -f1)"
    id=$(tenbri append audit/support <<EOF | jq -r .id
    {"kind":"decision","phase":"intent","source":"sdk",
    "principal":{"kind":"agent","id":"support-assistant"},
    "action":{"type":"issue_refund","target":"stripe:charge/$1"},
    "inputs":[{"ref":"policy:refund","version":"$2","digest":"$digest"}]}
    EOF
    )
    tenbri append audit/support <<EOF > /dev/null
    {"kind":"decision","phase":"outcome","source":"sdk","intent":"$id","status":"$3",
    "principal":{"kind":"agent","id":"support-assistant"},
    "action":{"type":"issue_refund","target":"stripe:charge/$1"}}
    EOF
    }
    decide ch_1001 14 ok
    decide ch_1002 14 refused
    decide ch_1003 15 ok
    decide ch_1004 14 ok
    decide ch_1005 15 refused
    Terminal window
    source decide.sh
  2. List the decisions under version 14, and join them to their outcomes.

    The input filter is ref@version. The inputs are on the intents; each outcome names its intent by id, so read every issue_refund entry once and join on it.

    query prints one JSON object per line: the entry, its seq, its entry hash, its verification, and as_of (what the answer is current to). With --key and --verify, every row is checked against the pinned fingerprint.

    Terminal window
    tenbri query audit/support --input policy:refund@14 --key $PIN --verify \
    | jq -c '{seq, id: .entry.id, target: .entry.action.target, verification}'
    {"seq":3,"id":"01M5CWMYAHYECM0BSJK1NVGYEF","target":"stripe:charge/ch_1001","verification":"verified"}
    {"seq":5,"id":"01M5CWMYCCWGQ2HAG6Y39JHKAF","target":"stripe:charge/ch_1002","verification":"verified"}
    {"seq":9,"id":"01M5CWMYG4S718BMP25J3ZCPAV","target":"stripe:charge/ch_1004","verification":"verified"}

    Count per version. A bare ref, --input policy:refund, matches any version:

    Terminal window
    tenbri query audit/support --input policy:refund@14 --key $PIN --verify | jq -s length
    tenbri query audit/support --input policy:refund@15 --key $PIN --verify | jq -s length
    tenbri query audit/support --input policy:refund --key $PIN --verify | jq -s length
    3
    2
    5

    Join to the outcomes:

    Terminal window
    tenbri query audit/support --action issue_refund --key $PIN --verify | jq -s -c '
    map(.entry) as $all
    | $all[] | select(.phase == "intent" and any(.inputs[]?; .ref == "policy:refund" and .version == "14")) as $i
    | $all[] | select(.intent == $i.id)
    | {target: .action.target, status}'
    {"target":"stripe:charge/ch_1001","status":"ok"}
    {"target":"stripe:charge/ch_1002","status":"refused"}
    {"target":"stripe:charge/ch_1004","status":"ok"}

    --status refused lists only the refused outcomes, across both versions.

Each intent recorded the exact policy version it read, with a keyed digest of its text, inside a signed entry, so the list cannot be edited after the fact without failing verification. The query matched ref and version exactly, and every row was checked against the pinned fingerprint. The digest also lets you prove later which policy text the agent saw, if you keep the text and the digest key.