Skip to content

Portable agents

Milestone 9 begins with the strict bnw.agent-manifest/1 descriptor and the agent.portable capability kind. A manifest references immutable instructions, exact stable capabilities under unique roles, immutable policy objects, and an optional memory capability. It uses the fixed agent/run operation and the existing payload-contract vocabulary, but publishing a manifest does not execute it or grant access to any dependency.

Terminal window
bnw capability descriptor create-agent portable-agent.json \
--name "Portable research agent" \
--instructions <INSTRUCTIONS_BLOB_ID> \
--require model=<INFERENCE_CAPABILITY_ID> \
--require search=<SEARCH_CAPABILITY_ID> \
--policy <POLICY_BLOB_ID> \
--memory <MEMORY_CAPABILITY_ID>
bnw capability descriptor validate portable-agent.json
bnw capability publish agent.portable <AGENT_DESCRIPTOR_ID>

In the web client, Provide a capability → Agent publishes a manifest. An agent’s capability page shows how each requirement resolves, and Run this agent here sets up this node’s agent runtime from it.

Version 1 intentionally uses exact capability IDs instead of fuzzy model/tool requirements. Deterministic local resolution, provider selection, trust policy, pricing, and substitutions are separate layers. See PORTABLE_AGENTS.md for the complete contract and workflow.

Providers can attach strict bnw.offer-terms/1 metadata to their existing signed offers. All prices use integer microcredits (1 credit = 1,000,000 microcredits) for transparency, while an issuer BNW ID prevents unrelated service-credit systems from being mistaken for one global currency. bnw agent resolve validates exact dependencies and active offers locally, with optional issuer and per-invocation ceiling constraints. See ECONOMICS.md.

An invocation binds one canonical manifest, one active provider offer, and one input CID. It expires after one hour by default, cannot outlive its offer, and is capped at 24 hours. Successful completion and every progress update require an active delegation issued by that provider for the exact capability:invoke:<CAPABILITY_ID> permission. Progress forms an invocation-specific signed chain with a machine-safe stage, optional percentage, optional update CID, and optional details. A provider may reject or report failure without granting access. A provider publishes exactly one CLI-level terminal response: completed with an output CID, or rejected/failed without one. If a faulty provider signs conflicting responses or progress forks, every peer makes the same content-ID-based selection. All lifecycle records replicate through both participants’ inbox scopes; unrelated peers do not retain them.

The optional bnw.capability-profile/1 descriptor defines one operation’s accepted media types, schema references, byte limits, and privacy requirements without changing the signed capability wire object. Discovery alone remains compact; descriptor bytes are retrieved either by an explicit catalog inspect or after the user follows the capability. prepare-input and prepare-output validate that profile, encrypt the file exactly for requester and provider, and return the encrypted-blob manifest CID used by the existing request or response command. Both participants must first publish principal records containing their encryption keys.

The requester can ask the provider to stop work:

Terminal window
# Requester:
bnw invoke cancel <INVOCATION_ID> \
--idempotency-key cancel-demo-1 \
--reason "no longer needed"
# Provider, after receiving the cancellation:
bnw invoke respond <INVOCATION_ID> rejected --details "canceled by requester"

Cancellation is an immutable signed request, not deletion. The running node validates and signs it, and the required idempotency key makes an identical retry return the original cancellation instead of signing another record. Reusing that key for a different cancellation is rejected. A provider CLI that has received the cancellation will refuse further progress and successful completion, but may acknowledge it with a rejected or failed terminal result. If cancellation and a terminal result cross in flight, the signed terminal result wins the displayed lifecycle deterministically and both records remain available as audit evidence.

The provider can require approval as well:

Terminal window
# Provider:
bnw policy set capability:invoke:<CAPABILITY_ID> require-approval
# Requester, after creating the invocation:
bnw approval request <PROVIDER_BNW_ID> \
capability:invoke:<CAPABILITY_ID> <INVOCATION_ID>
# Provider:
bnw approval inbox
bnw approval decide <APPROVAL_REQUEST_ID> approve
bnw invoke authorize <INVOCATION_ID>
bnw invoke respond <INVOCATION_ID> completed --output <OUTPUT_CONTENT_ID>

The approval must name the same requester, provider, exact permission, and invocation content ID. A completed result records the supporting delegation and approval decision CIDs as signed parent evidence. Later revocation therefore does not erase which records supported the decision at completion time.

BNW core only coordinates signed references and does not conclude that a claimed result is correct. The optional local MCP and inference adapters interpret strict profiles and execute provider-configured services above that core boundary; other capability kinds and execution environments remain application responsibilities. Use encrypted blobs addressed to the provider for sensitive inputs. Invocation metadata itself is signed but not end-to-end encrypted.

Private message bodies are encrypted end to end, but envelope metadata—including author, channel, recipient identifiers, timing, and payload size—remains visible to replicating peers. This initial design uses one sealed ciphertext per recipient; it is not MLS and does not yet provide forward secrecy or membership-hiding.