@bounded-authority-protocol/verifier 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -0
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -125,6 +125,105 @@ local-development proof profile (`localLoopbackHttpProofSigningInput` and friend
|
|
|
125
125
|
`127.0.0.1`/`[::1]` targets only, mandatory nonce; standard `dpop+jwt` rejects its bytes).
|
|
126
126
|
The full export list is [`src/index.ts`](src/index.ts).
|
|
127
127
|
|
|
128
|
+
## Pairing with the signer
|
|
129
|
+
|
|
130
|
+
This package verifies; it never signs and never holds a private key. Its sibling
|
|
131
|
+
[`@bounded-authority-protocol/signer`](https://www.npmjs.com/package/@bounded-authority-protocol/signer)
|
|
132
|
+
produces the signed bytes — and it does so by calling THIS package's producer functions
|
|
133
|
+
(`grantSigningInput`, `proofSigningInput`, `boundaryAnchorSigningInput`,
|
|
134
|
+
`keyTransitionSigningInput`) and delegating the cryptography to a caller-owned key handle.
|
|
135
|
+
The dependency direction is one-way: the signer depends on the verifier at runtime
|
|
136
|
+
(`^0.2.0`), never the reverse. Compatibility between the two is governed by the wire
|
|
137
|
+
contract-majors — both packages carry majors 1 and 2 side by side — not by package
|
|
138
|
+
version numbers.
|
|
139
|
+
|
|
140
|
+
A production flow has three roles: the issuer mints a grant, the holder binds one
|
|
141
|
+
invocation of it with a proof, and the resource verifies the pair through this package.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// Issuer — mint a grant. The private key never enters the signer; your handle
|
|
145
|
+
// routes each sign() to your own custody (KMS, HSM, or an in-process test key).
|
|
146
|
+
import { signGrant, signReport, type KeyHandle } from "@bounded-authority-protocol/signer";
|
|
147
|
+
import { checkEnvelope } from "@bounded-authority-protocol/verifier";
|
|
148
|
+
|
|
149
|
+
const issuerHandle: KeyHandle = {
|
|
150
|
+
sign: (message) => issuerCustody.sign(message), // Ed25519, 64 bytes
|
|
151
|
+
publicKey: () => issuerPublicKey32,
|
|
152
|
+
thumbprint: () => issuerThumbprint, // RFC 7638, base64url
|
|
153
|
+
// Grants additionally require an atomic issuer-role identity — the C1 gate:
|
|
154
|
+
// a holder-role handle fails closed before sign() is ever called.
|
|
155
|
+
signingIdentity: () => ({ role: "issuer", keyId: "issuer-key", publicKey: issuerPublicKey32 }),
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// SignerResult mirrors this package's Result<T>: check ok before reading value.
|
|
159
|
+
const grantResult = await signGrant(
|
|
160
|
+
{
|
|
161
|
+
issuer: "https://issuer.example",
|
|
162
|
+
grantId: "g-1",
|
|
163
|
+
audiences: ["https://resource.example"],
|
|
164
|
+
issuedAt: 1_731_728_000,
|
|
165
|
+
notBefore: 1_731_728_000,
|
|
166
|
+
expiresAt: 1_733_728_000,
|
|
167
|
+
holderThumbprint, // RFC 7638 base64url of the holder's public JWK (the thumbprint primitive)
|
|
168
|
+
operations: [
|
|
169
|
+
{ name: "transfer", selectors: [{ kind: "equals", path: ["amount"], value: { t: "int", v: 5000 } }] },
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
issuerHandle,
|
|
173
|
+
);
|
|
174
|
+
if (!grantResult.ok) throw new Error(`signing failed: ${grantResult.error}`);
|
|
175
|
+
const grantCompact = grantResult.value.grant;
|
|
176
|
+
|
|
177
|
+
// Holder — bind one invocation of that grant with a holder proof. The holder's
|
|
178
|
+
// handle is constructed exactly like the issuer's (reports need no
|
|
179
|
+
// signingIdentity — proofs are holder-signed by definition), and the proof
|
|
180
|
+
// timestamp is pinned the same way: this verifier reads no clock.
|
|
181
|
+
const proofResult = await signReport(
|
|
182
|
+
{
|
|
183
|
+
grantCompact: grantCompact,
|
|
184
|
+
operation: "transfer",
|
|
185
|
+
method: "POST",
|
|
186
|
+
targetUri: "https://api.example.test/invoke",
|
|
187
|
+
invocationId: "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d", // a lowercase RFC 4122 UUID — the wire requires it
|
|
188
|
+
castArguments: { t: "object", v: new Map([["amount", { t: "int", v: 5000 }]]) },
|
|
189
|
+
},
|
|
190
|
+
holderHandle,
|
|
191
|
+
{ issuedAt: 1_731_728_030 },
|
|
192
|
+
);
|
|
193
|
+
if (!proofResult.ok) throw new Error(`signing failed: ${proofResult.error}`);
|
|
194
|
+
const envelope = proofResult.value;
|
|
195
|
+
|
|
196
|
+
// Resource — THIS package, and nothing else. Facts, never a decision.
|
|
197
|
+
const result = checkEnvelope(envelope.grant, envelope.proof, {
|
|
198
|
+
trustedIssuer: { keyId: "issuer-key", publicKey: issuerPublicKey32 },
|
|
199
|
+
issuer: "https://issuer.example",
|
|
200
|
+
audience: "https://resource.example",
|
|
201
|
+
method: "POST",
|
|
202
|
+
targetUri: "https://api.example.test/invoke",
|
|
203
|
+
invocationId: "9a1b2c3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
|
|
204
|
+
operation: "transfer",
|
|
205
|
+
castArguments: { t: "object", v: new Map([["amount", { t: "int", v: 5000 }]]) },
|
|
206
|
+
evaluationTime: 1_731_728_060,
|
|
207
|
+
clockSkew: 60,
|
|
208
|
+
proofMaxAge: 300,
|
|
209
|
+
nonce: { kind: "not_required" },
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Two properties make the pairing safe to build against:
|
|
214
|
+
|
|
215
|
+
- **Key custody stays at the caller.** The signer's `KeyHandle` is a callback interface
|
|
216
|
+
(`sign`/`publicKey`/`thumbprint`, plus atomic `keyIdentity`/`signingIdentity` snapshots
|
|
217
|
+
for anchors, transitions, and role-gated grant signing). A handle fault or a
|
|
218
|
+
wrong-key rotation race fails loudly as `signing_failed` — the signer verifies every
|
|
219
|
+
signature against the resolved public key before assembly. The full contract is
|
|
220
|
+
documented in the [signer's README](https://github.com/baselabs/bounded_authority_signer_typescript#readme).
|
|
221
|
+
- **The two packages cross-validate in CI.** The signer's test oracle produces
|
|
222
|
+
compacts through its own surface and verifies every one of them through THIS
|
|
223
|
+
package before release — signing-side drift is caught in the signer's CI against
|
|
224
|
+
the verifier version its lockfile resolves, and verifier-side changes are gated
|
|
225
|
+
by this package's certified conformance corpora.
|
|
226
|
+
|
|
128
227
|
## Certified, not self-tested
|
|
129
228
|
|
|
130
229
|
Every release of this SDK is verified against the protocol's published, cryptographically
|
|
@@ -167,6 +266,18 @@ the host — a facts value is evidence, never a credential.
|
|
|
167
266
|
- [ADR 0014 — cross-language verifier SDKs](https://github.com/baselabs/bounded_authority_protocol/blob/main/docs/adr/0014-cross-language-verifier-sdks.md) ·
|
|
168
267
|
[ADR 0015 — graduation and publish topology](https://github.com/baselabs/bounded_authority_protocol/blob/main/docs/adr/0015-sdk-graduation-and-publish-topology.md)
|
|
169
268
|
|
|
269
|
+
## Related packages
|
|
270
|
+
|
|
271
|
+
- [bounded_authority_protocol (monorepo)](https://github.com/baselabs/bounded_authority_protocol) —
|
|
272
|
+
the protocol source: specifications, ADRs, the certified conformance corpora this
|
|
273
|
+
package vendors, and the reference Elixir implementation.
|
|
274
|
+
- [@bounded-authority-protocol/signer](https://www.npmjs.com/package/@bounded-authority-protocol/signer) —
|
|
275
|
+
the holder/issuer companion: signs proofs, grants, boundary anchors, and key
|
|
276
|
+
transitions through a caller-owned key handle. This verifier holds no private keys;
|
|
277
|
+
the signer produces the bytes it checks (see [Pairing with the signer](#pairing-with-the-signer)).
|
|
278
|
+
- This package on npm:
|
|
279
|
+
[@bounded-authority-protocol/verifier](https://www.npmjs.com/package/@bounded-authority-protocol/verifier).
|
|
280
|
+
|
|
170
281
|
## Development
|
|
171
282
|
|
|
172
283
|
```bash
|
|
@@ -176,6 +287,7 @@ pnpm test # unit + struct + façade corpus-vector tests
|
|
|
176
287
|
pnpm test:permissiveness # the mutation gate
|
|
177
288
|
pnpm conformance # 283/283 + key census (vendored v1 snapshot)
|
|
178
289
|
pnpm conformance:v2 # 268/268 + key census (vendored v2 snapshot)
|
|
290
|
+
pnpm check:currency # dependency-currency gate (latest-first)
|
|
179
291
|
```
|
|
180
292
|
|
|
181
293
|
## License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bounded-authority-protocol/verifier",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Provider-neutral, deterministic verifier SDK for bounded proof-of-possession authority — a typed TypeScript reimplementation of the Bounded Authority Protocol verification profiles (wire contract-majors 1 and 2).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
21
21
|
"lint": "eslint .",
|
|
22
22
|
"license-check": "node tools/license-check.mjs",
|
|
23
|
+
"check:currency": "node tools/check-currency.mjs",
|
|
23
24
|
"test": "node --test --import tsx/esm test/*.test.ts",
|
|
24
25
|
"test:permissiveness": "node --test --import tsx/esm test/permissiveness.ts",
|
|
25
26
|
"conformance": "tsx conformance/run.ts",
|
|
@@ -27,11 +28,12 @@
|
|
|
27
28
|
"census": "tsx conformance/census.ts"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
|
-
"@types/node": "^26.
|
|
31
|
-
"eslint": "^10.
|
|
32
|
-
"
|
|
31
|
+
"@types/node": "^26.6.1",
|
|
32
|
+
"eslint": "^10.10.0",
|
|
33
|
+
"semver": "^7.8.5",
|
|
34
|
+
"tsx": "^4.23.13",
|
|
33
35
|
"typescript": "^6.0.3",
|
|
34
|
-
"typescript-eslint": "^8.
|
|
36
|
+
"typescript-eslint": "^8.70.0"
|
|
35
37
|
},
|
|
36
38
|
"repository": {
|
|
37
39
|
"type": "git",
|