Skip to content

Inference end to end

PROVIDER_BNW_ID=“” PROVIDER_PEER_ID=“”

REQUESTER_BNW_ID=“”

If mDNS does not connect them, restart the requester node using one of the provider’s advertised addresses:
```bash
RUST_LOG=info bnw node start \
--bootstrap /ip4/<PROVIDER_LAN_IP>/tcp/<PORT>/p2p/<PROVIDER_PEER_ID>

On the provider:

Terminal window
bnw principal publish service --name "Inference Provider"

On the requester:

Terminal window
bnw principal publish human --name "Inference Requester"

Wait for replication and verify each side can see the other.

On the requester:

Terminal window
bnw principal inspect "$PROVIDER_BNW_ID"

On the provider:

Terminal window
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:

Terminal window
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 2048

Record the printed value:

descriptor: <DESCRIPTOR_ID>

Set it locally:

Terminal window
DESCRIPTOR_ID="<DESCRIPTOR_ID>"

Validate and publish it:

Terminal window
bnw capability descriptor validate p2p-inference-v1.json
bnw capability publish inference.model "$DESCRIPTOR_ID"

Record the printed capability ID:

Terminal window
CAPABILITY_ID="<CAPABILITY_ID>"

Publish an offer valid for 24 hours:

Terminal window
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:

Terminal window
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 120

Verify it:

Terminal window
bnw inference inspect "$CAPABILITY_ID"
bnw inference check "$CAPABILITY_ID"

You should see:

claimed model: example/model-8b
runtime model: gemma:2b
check: ready

The public model claim and local runtime model are intentionally allowed to differ.

Run on the requester:

Terminal window
bnw capability discover inference.model

After a few seconds:

Terminal window
bnw capability list --kind inference.model
bnw capability inspect "$CAPABILITY_ID"
bnw capability follow "$CAPABILITY_ID"

Wait for the offer to replicate:

Terminal window
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:

Terminal window
bnw capability descriptor inspect "$DESCRIPTOR_ID"

Run on the provider:

Terminal window
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.

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
}
}

On the requester, the simplest route stages the input and sends the invocation in one step, replacing steps 8 and 9:

Terminal window
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:

Terminal window
bnw invoke prepare-input \
"$CAPABILITY_ID" \
"$PROVIDER_BNW_ID" \
inference-request.json

The command prints several IDs. Use the value labeled input: as the input manifest:

Terminal window
INPUT_MANIFEST_ID="<value-labeled-input>"

Do not use the plaintext or ciphertext blob ID in the invocation request.

On the requester:

Terminal window
REQUEST_KEY="inference-test-$(date +%s)"
bnw invoke request \
"$CAPABILITY_ID" \
"$PROVIDER_BNW_ID" \
"$INPUT_MANIFEST_ID" \
--idempotency-key "$REQUEST_KEY"

Record:

Terminal window
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:

Terminal window
--offer <OFFER_ID>

On the provider, wait for replication:

Terminal window
bnw invoke inbox

Once the invocation appears:

Terminal window
bnw invoke authorize "$INVOCATION_ID"

Expected result:

permission: capability:invoke:<CAPABILITY_ID>
authorization: allowed
delegation: <DELEGATION_ID>

Optionally confirm that the encrypted request arrived and can be decrypted:

Terminal window
bnw invoke inspect-payload "$INPUT_MANIFEST_ID"
bnw invoke open-payload \
"$INPUT_MANIFEST_ID" \
received-inference-request.json

Run once on the provider:

Terminal window
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:

Terminal window
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.

On the requester, wait for the result, retrieve its encrypted output, and decrypt it in one step:

Terminal window
bnw invoke result "$INVOCATION_ID" inference-result.json --wait

Or follow the steps manually. First wait for the result to replicate:

Terminal window
bnw invoke status "$INVOCATION_ID"

The final status should include:

status: Completed
output: <OUTPUT_MANIFEST_ID>
authorization: <DELEGATION_ID>

Decrypt the response:

Terminal window
bnw invoke open-payload \
"$OUTPUT_MANIFEST_ID" \
inference-result.json

View it:

Terminal window
cat inference-result.json

The 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"
}
}

After the evidence record replicates, either participant can inspect every claim associated with the invocation:

Terminal window
bnw inference evidence "$INVOCATION_ID"

For the normal path, expect one claim with:

claim-count: 1
method: provider-self-report
claimed-model: example/model-8b
configured-runtime-model: gemma:2b
runtime-reported-model: gemma:2b
adapter: openai-chat
links: consistent
authority: provider-self-reported; not proof of model execution

The 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.

Approval is an additional requirement; it does not replace the exact delegation.

Before execution, enable the policy on the provider:

Terminal window
bnw policy set \
"capability:invoke:$CAPABILITY_ID" \
require-approval

After creating the invocation, the requester sends:

Terminal window
bnw approval request \
"$PROVIDER_BNW_ID" \
"capability:invoke:$CAPABILITY_ID" \
"$INVOCATION_ID"

The provider then runs:

Terminal window
bnw approval inbox
bnw approval decide <APPROVAL_REQUEST_ID> approve
bnw 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.