Skip to content

Add BRC-179: Signed Capability Manifest for Wallet and Tool-Server Method Exposure - #245

Open
RexStarBSV wants to merge 3 commits into
bsv-blockchain:masterfrom
RexStarBSV:add-brc-179-capability-manifest
Open

RexStarBSV wants to merge 3 commits into
bsv-blockchain:masterfrom
RexStarBSV:add-brc-179-capability-manifest

Conversation

@RexStarBSV

Copy link
Copy Markdown
Contributor

A capability manifest: a declarative, risk-tiered, operator-gated document that enumerates exactly
which SDK methods and tool-server methods a wallet or wallet-adjacent product exposes, together with
a runtime enforcer that refuses any method not affirmatively declared and enabled.

Each exposed method is a Capability with a globally unique id of the form product.surface.method, a
risk tier drawn from an ordered taxonomy (READ, VERIFY, OPS, CRYPTO, IDENTITY, NETWORK,
SPEND, ADMIN), and an optional operator allow-overlay. Read and verify tiers are exposed by
default. Every tier that mutates state, uses a key, moves value, asserts identity, or reaches the
network is withheld until an operator affirmatively allows it.

The set of (id, tier, effective-enabled) triples reduces to a domain-separated SHA-256 fingerprint
that is stable under documentation edits and changes the instant a capability is added, re-tiered or
toggled. That fingerprint is baked into the build as a pin. At construction the enforcer refuses to
run if the loaded manifest does not fingerprint to the pin, so no config value, environment variable
or swapped file can widen the exposed surface at runtime.

Decisions are fail-closed. An undeclared method is denied, never silently allowed.

The gap this fills

BRC-100 governs per-session application requests. BRC-103 and BRC-104 govern who is calling. Both
operate on the demand side, once a method exists to be requested.

Nothing currently specifies the supply side: which methods a server exposes at all, decided before
any caller is consulted, attestable by a third party, and impossible to widen without changing a
published value. That axis is where an over-exposed method surface becomes an unbounded liability,
and it is the axis this proposal covers.

Relationship to the reserved namespaces

BRC-98 reserves protocol identifiers beginning with p and lets a supported scheme define behaviour
at that dispatch point. BRC-229 is the worked example, defining the ecpm scheme and, in its own
words, using that reserved dispatch point to specialize the meaning of an existing BRC-100 method.

A BRC-98 module therefore changes what an already-exposed method means for a caller who names the
reserved identifier. This specification governs whether that method is exposed at all. The two
compose, and the manifest is evaluated first: if the underlying method is denied, the module's
dispatch point is unreachable and the module's presence does not widen the surface. Implementers are
advised to give a BRC-98 module's underlying method its own capability id, so the exposure decision
is recorded explicitly rather than inherited.

BRC-111 labels are likewise orthogonal. A label describes or permissions an individual action; a
capability id names a method on a server's surface. This specification claims no identifier under
p and a manifest must not be read as reserving one.

What a manifest deliberately does not do

It does not replace user consent. It bounds the menu that consent can be asked about. It does not
authenticate callers. It constrains the server's own surface, caller-independent by design. Section
15 of the specification lists what is deliberately absent from a manifest, so implementers do not
read it as a general policy language.

Numbering and indices

The number is self-assigned as the lowest available under AGENTS.md after BRC-178, checked against
every numbered file on master, all three indices, and the numbers claimed by the seven currently open
pull requests. Availability will be rechecked immediately before merge.

Root README.md, wallet/README.md and SUMMARY.md are all updated in the same commit.

Test vectors

Five vectors are included, covering tier defaults with no overlay, an operator allowing a SPEND
capability, an operator disabling a default-on capability, a demonstration that descriptions are not
trust-bearing, and a build-pin swap refusal.

…thod Exposure

A declarative, risk-tiered, operator-gated document enumerating exactly which SDK
and tool-server methods a wallet or wallet-adjacent product exposes, with a
runtime enforcer that refuses any method not affirmatively declared and enabled,
and a build-pinned fingerprint so no config value or swapped file can widen the
exposed surface at runtime.

Number self-assigned as the lowest available after BRC-178, checked against every
numbered file on master, all three indices, and the numbers claimed by the seven
open pull requests.

Indices updated in this commit: root README.md, wallet/README.md, SUMMARY.md.
…e allow to be a boolean

Section 7.2 left four degrees of freedom that each change the digest, and every
one has a common serializer choosing the other way: solidus escaping (DOMAIN
contains a solidus, so it is in every preimage ever computed), hex case in a
\uXXXX escape, non-BMP characters as a surrogate pair rather than a six-digit
escape, and sort order, where UTF-16 code unit order disagrees with code point
order for astral characters and is what a language comparing UTF-16 units does
silently. Section 4 permits non-ASCII ids, so these are reachable.

The RFC 8785 aside claimed the two canonicalizations agree on this preimage. They
agree only while every id is ASCII. JCS emits raw UTF-8 and does not escape.

Section 9 claimed an environment variable could not widen the exposed surface. The
reference reads its pin from one, and its own source comment says so, scoping the
mechanism to tamper-evidence against a caller rather than a guarantee against host
compromise. The specification now says what the mechanism provides and names what
it does not.

Section 7.1 now requires allow to be a boolean or null and forbids coercion. The
effective-state rule resolves by truthiness, so the JSON string "false" resolved
to enabled and exposed the capability its author meant to withhold, with the
fingerprint recording enabled:true faithfully so no downstream check could see it.
A pin covers the surface whose enforcer holds it and no other. A manifest may
declare capabilities for many surfaces, and declaring them is documentation:
enforcement happens only where a process constructs an enforcer and routes its
calls through it. A reader counting declared capabilities will otherwise overcount
what is enforced, so an implementation should say which surfaces construct one.

Section 3's Required column describes the record, which always carries every field
once constructed. Section 6 governs the document, where description, grounded_in
and allow may be omitted and default. The two were readable as contradicting each
other; they are not, and an implementation whose record type makes them mandatory
arguments is conformant provided its parser supplies the defaults.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant