Inference end to end
PROVIDER_BNW_ID=“
On the requester Mac:
Section titled “On the requester Mac:”REQUESTER_BNW_ID=“
If mDNS does not connect them, restart the requester node using one of the provider’s advertised addresses:
```bashRUST_LOG=info bnw node start \ --bootstrap /ip4/<PROVIDER_LAN_IP>/tcp/<PORT>/p2p/<PROVIDER_PEER_ID>2. Publish encryption keys
Section titled “2. Publish encryption keys”On the provider:
bnw principal publish service --name "Inference Provider"On the requester:
bnw principal publish human --name "Inference Requester"Wait for replication and verify each side can see the other.
On the requester:
bnw principal inspect "$PROVIDER_BNW_ID"On the provider:
bnw principal inspect "$REQUESTER_BNW_ID"Both checks must work before encrypted invocation payloads can be exchanged.
3. Create and publish the inference capability
Section titled “3. Create and publish the inference capability”Run on the provider:
bnw capability descriptor create-inference p2p-inference-v1.json \ --name "Local Gemma inference" \ --model-id example/model-8b \ --revision v1 \ --input-modality text \ --output-modality text \ --context-tokens 8192 \ --max-output-tokens 2048Record the printed value:
descriptor: <DESCRIPTOR_ID>Set it locally:
DESCRIPTOR_ID="<DESCRIPTOR_ID>"Validate and publish it:
bnw capability descriptor validate p2p-inference-v1.jsonbnw capability publish inference.model "$DESCRIPTOR_ID"Record the printed capability ID:
CAPABILITY_ID="<CAPABILITY_ID>"Publish an offer valid for 24 hours:
OFFER_EXPIRES_AT=$(($(date +%s) + 86400))
bnw capability offer "$CAPABILITY_ID" \ --endpoint "bnw.peer=$PROVIDER_PEER_ID" \ --expires-at "$OFFER_EXPIRES_AT"The bnw.peer endpoint requires the 12D3Koo... peer ID—not the provider’s hexadecimal BNW ID.
4. Register the provider’s Ollama runtime
Section titled “4. Register the provider’s Ollama runtime”Still on the provider:
bnw inference register "$CAPABILITY_ID" \ --adapter openai-chat \ --url http://127.0.0.1:11434/v1/chat/completions \ --runtime-model gemma:2b \ --payload-egress local \ --timeout-seconds 120Verify it:
bnw inference inspect "$CAPABILITY_ID"bnw inference check "$CAPABILITY_ID"You should see:
claimed model: example/model-8bruntime model: gemma:2bcheck: readyThe public model claim and local runtime model are intentionally allowed to differ.
5. Discover and follow the capability
Section titled “5. Discover and follow the capability”Run on the requester:
bnw capability discover inference.modelAfter a few seconds:
bnw capability list --kind inference.modelbnw capability inspect "$CAPABILITY_ID"bnw capability follow "$CAPABILITY_ID"Wait for the offer to replicate:
bnw capability offers "$CAPABILITY_ID"Do not continue until the provider’s active offer is listed.
You can also verify the descriptor locally using the descriptor ID shown by capability inspect:
bnw capability descriptor inspect "$DESCRIPTOR_ID"6. Grant the exact invocation delegation
Section titled “6. Grant the exact invocation delegation”Run on the provider:
DELEGATION_EXPIRES_AT=$(($(date +%s) + 86400))
bnw delegate grant "$REQUESTER_BNW_ID" \ --capability "capability:invoke:$CAPABILITY_ID" \ --expires-at "$DELEGATION_EXPIRES_AT"Record the delegation content ID if desired for auditing.
The important details are:
- The provider issues the delegation.
- The subject is the requester’s hexadecimal BNW ID.
- The permission contains the exact capability ID.
7. Create the inference request
Section titled “7. Create the inference request”On the requester, create inference-request.json:
{ "profile": "bnw.inference-request/1", "system": "Answer concisely.", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Reply with one sentence confirming that this request traveled through BNW." } ] } ], "generation": { "max_output_tokens": 100, "temperature": 0.2 }}8. Encrypt and prepare the request
Section titled “8. Encrypt and prepare the request”On the requester, the simplest route stages the input and sends the invocation in one step, replacing steps 8 and 9:
REQUEST_KEY="inference-test-$(date +%s)"bnw invoke request "$CAPABILITY_ID" "$PROVIDER_BNW_ID" \ --input-file inference-request.json \ --idempotency-key "$REQUEST_KEY"To prepare the input separately instead:
bnw invoke prepare-input \ "$CAPABILITY_ID" \ "$PROVIDER_BNW_ID" \ inference-request.jsonThe command prints several IDs. Use the value labeled input: as the input manifest:
INPUT_MANIFEST_ID="<value-labeled-input>"Do not use the plaintext or ciphertext blob ID in the invocation request.
9. Send the invocation
Section titled “9. Send the invocation”On the requester:
REQUEST_KEY="inference-test-$(date +%s)"
bnw invoke request \ "$CAPABILITY_ID" \ "$PROVIDER_BNW_ID" \ "$INPUT_MANIFEST_ID" \ --idempotency-key "$REQUEST_KEY"Record:
INVOCATION_ID="<value-labeled-invocation>"The daemon automatically selects the newest active offer from that provider. Repeating the exact command with the same key returns the original invocation instead of creating another. To select a particular offer explicitly, add:
--offer <OFFER_ID>10. Receive and authorize it
Section titled “10. Receive and authorize it”On the provider, wait for replication:
bnw invoke inboxOnce the invocation appears:
bnw invoke authorize "$INVOCATION_ID"Expected result:
permission: capability:invoke:<CAPABILITY_ID>authorization: alloweddelegation: <DELEGATION_ID>Optionally confirm that the encrypted request arrived and can be decrypted:
bnw invoke inspect-payload "$INPUT_MANIFEST_ID"
bnw invoke open-payload \ "$INPUT_MANIFEST_ID" \ received-inference-request.json11. Execute the inference request
Section titled “11. Execute the inference request”Run once on the provider:
bnw invoke execute-inference "$INVOCATION_ID"It should print three progress IDs followed by:
output: <OUTPUT_MANIFEST_ID>result: <RESULT_ID>evidence: <EXECUTION_CLAIM_ID>Record the output value:
OUTPUT_MANIFEST_ID="<value-labeled-output>"execute-inference performs the provider call, encrypts the normalized response for both participants, and publishes the terminal result. Do not run a separate invoke respond command afterward.
Also avoid retrying execution blindly after an ambiguous crash: BNW places an at-most-once local claim before contacting the model.
12. Retrieve the result
Section titled “12. Retrieve the result”On the requester, wait for the result, retrieve its encrypted output, and decrypt it in one step:
bnw invoke result "$INVOCATION_ID" inference-result.json --waitOr follow the steps manually. First wait for the result to replicate:
bnw invoke status "$INVOCATION_ID"The final status should include:
status: Completedoutput: <OUTPUT_MANIFEST_ID>authorization: <DELEGATION_ID>Decrypt the response:
bnw invoke open-payload \ "$OUTPUT_MANIFEST_ID" \ inference-result.jsonView it:
cat inference-result.jsonThe result should resemble:
{ "profile": "bnw.inference-response/1", "content": [ { "type": "text", "text": "..." } ], "stop_reason": "end_turn", "usage": { "input_tokens": 30, "output_tokens": 12 }, "provider": { "adapter": "openai-chat", "runtime_model": "gemma:2b" }}13. Inspect the signed execution evidence
Section titled “13. Inspect the signed execution evidence”After the evidence record replicates, either participant can inspect every claim associated with the invocation:
bnw inference evidence "$INVOCATION_ID"For the normal path, expect one claim with:
claim-count: 1method: provider-self-reportclaimed-model: example/model-8bconfigured-runtime-model: gemma:2bruntime-reported-model: gemma:2badapter: openai-chatlinks: consistentauthority: provider-self-reported; not proof of model executionThe link check confirms that the signed record names the locally available invocation, capability descriptor, completed result, and encrypted output. It does not prove that the provider actually loaded the advertised weights or generated a correct response. If the provider signs multiple claims, BNW displays all of them instead of selecting one as canonical.
Optional: require approval too
Section titled “Optional: require approval too”Approval is an additional requirement; it does not replace the exact delegation.
Before execution, enable the policy on the provider:
bnw policy set \ "capability:invoke:$CAPABILITY_ID" \ require-approvalAfter creating the invocation, the requester sends:
bnw approval request \ "$PROVIDER_BNW_ID" \ "capability:invoke:$CAPABILITY_ID" \ "$INVOCATION_ID"The provider then runs:
bnw approval inboxbnw approval decide <APPROVAL_REQUEST_ID> approvebnw invoke authorize "$INVOCATION_ID"bnw invoke execute-inference "$INVOCATION_ID"The authorization result should now say approved and list both the approval and delegation evidence.
