You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -145,7 +147,7 @@ AgentPin already uses `_agentpin.{domain}` TXT records, but SchemaPin doesn't le
145
147
_schemapin.example.com. IN TXT "v=schemapin1; kid=acme-2026-01; fp=sha256:a1b2c3d4..."
146
148
```
147
149
148
-
### Canonicalization Algorithm Identifier
150
+
### Canonicalization Algorithm Identifier — **Shipped (alpha.3, all 4 langs)**
149
151
150
152
The current spec hardcodes the canonicalization algorithm (sorted keys, no whitespace, UTF-8). If the algorithm ever needs to change (and JSON canonicalization is notoriously tricky across languages), there's no way to signal which algorithm was used.
151
153
@@ -156,7 +158,7 @@ The current spec hardcodes the canonicalization algorithm (sorted keys, no white
156
158
157
159
Trivial to add now, saves a painful migration later.
158
160
159
-
### A2A Context for Schema Verification
161
+
### A2A Context for Schema Verification — **Shipped (alpha.3, all 4 langs)**
160
162
161
163
When agents collaborate via A2A (Agent-to-Agent), tool schemas cross trust boundaries. SchemaPin v1.4.0 ensures that tool integrity verification extends seamlessly into A2A networks — every tool invoked through an A2A bridge is verified against its provider's signed schema.
162
164
@@ -167,16 +169,21 @@ When agents collaborate via A2A (Agent-to-Agent), tool schemas cross trust bound
167
169
| Domain scoping | Accept optional trusted domains as `Vec<String>`, matching the `AllowedDomains` type exported by AgentPin v0.3.0 (extracted from `AgentDeclaration.constraints`). Empty list means no domain restriction. |
168
170
| Intersection check | Scope verification to intersection of caller's allowed domains and tool provider's domain |
169
171
170
-
### Trust Bundle Distribution for A2A Networks
172
+
### Trust Bundle Distribution for A2A Networks — **Shipped (alpha.4, all 4 langs)**
171
173
172
174
| Item | Details |
173
175
|------|---------|
174
-
| Bundle signing | Sign trust bundles with a bundle authority key |
176
+
| Bundle signing | Sign trust bundles with a bundle authority key (ECDSA P-256 over `schemapin-v1` canonical bytes) |
175
177
|`merge_trust_bundles()`| Combine bundles from multiple sources with deduplication (newest wins) |
176
-
| TOFU for bundles | TOFU pinning for bundle authority keys |
177
-
| JSON-RPC method |`schemapin/trustBundle` for A2A bundle exchange |
178
+
| TOFU for bundles | TOFU pinning for bundle authority keys, keyed by `kid`|
179
+
| JSON-RPC method |`schemapin/trustBundle`envelope helpers for A2A bundle exchange |
178
180
179
-
### Scan-Aware Signatures
181
+
Bundle authority key carried as `public_key_pem` (consistent with discovery docs) rather than JWK — see the alpha.4 CHANGELOG for rationale.
182
+
183
+
### Scan-Aware Signatures — **Moved to v1.5.0**
184
+
185
+
> Descoped from v1.4.0 GA (2026-06-21). Additive and independent of the rest of
186
+
> v1.4, so deferring it does not block GA. Spec below is retained as the v1.5 plan.
180
187
181
188
Right now scanning and signing are somewhat independent in the Symbiont/SchemaPin workflow. Making the scan result part of the signature metadata closes this gap — a skill signed with `scan_passed: true` at signing time, with the scanner version and ruleset hash recorded, tells verifiers not just that the content is authentic but that it passed security review at a specific rule version.
182
189
@@ -200,7 +207,10 @@ Right now scanning and signing are somewhat independent in the Symbiont/SchemaPi
200
207
201
208
This is fully optional and backward compatible — v1.3 verifiers ignore the new fields.
202
209
203
-
### Cross-Agent Tool Schema Caching
210
+
### Cross-Agent Tool Schema Caching — **Moved to v1.5.0**
211
+
212
+
> Descoped from v1.4.0 GA (2026-06-21). A local in-memory cache helper with no
213
+
> wire-format impact; deferring it does not block GA. Spec below is the v1.5 plan.
204
214
205
215
| Item | Details |
206
216
|------|---------|
@@ -216,6 +226,11 @@ All four language implementations (Rust, JavaScript, Python, Go) receive matchin
**Carried over from v1.4 (descoped 2026-06-21):** Scan-Aware Signatures and
230
+
Cross-Agent Tool Schema Caching. Both are additive and independent of the v1.4
231
+
trust surface, so they ship in v1.5 rather than blocking the v1.4.0 GA. Their
232
+
full specs live in the v1.4 section above (marked "Moved to v1.5.0").
233
+
219
234
### Multi-Key / Organizational Endorsement
220
235
221
236
The `.well-known/schemapin.json` should support an array of public keys with roles rather than a single `public_key_pem`. This is the enterprise compliance differentiator — organizations can enforce policies like "require both a developer and a reviewer signature."
@@ -324,4 +339,4 @@ We welcome input on roadmap priorities:
324
339
325
340
---
326
341
327
-
*Last updated: 2026-05-01 (v1.4.0-alpha.2 — schema version binding across all four language implementations)*
342
+
*Last updated: 2026-06-21 (v1.4.0-alpha.4 — A2A trust-bundle distribution across all four language implementations; items 7–8 descoped to v1.5.0)*
Copy file name to clipboardExpand all lines: TECHNICAL_SPECIFICATION.md
+47Lines changed: 47 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -588,3 +588,50 @@ Implementations SHOULD copy these helpers from this section rather than taking a
588
588
- v1.3 verifiers do not know about A2A context — they have no `verify_schema_for_a2a` entry point. Callers integrating with v1.3 verifiers fall back to the standard `verify_schema_offline` flow without scope enforcement.
589
589
- The `A2A_SCOPE_VIOLATION` error code is new in v1.4. Older verifiers cannot emit it.
590
590
- All v1.4 alpha.3 additions are additive — they introduce no changes to the signature on the wire (the new error code lives only in verifier output) so v1.4 alpha.1 / alpha.2 signatures verify identically under alpha.3 verifiers.
591
+
592
+
### **21. Trust Bundle Distribution (v1.4)**
593
+
594
+
#### **21.1. Purpose**
595
+
596
+
Allow a **bundle authority** to sign a trust bundle so it can be exchanged between agents over A2A without per-bundle out-of-band trust establishment. The receiving verifier authenticates the bundle and TOFU-pins the authority key by `kid`, the same trust model used for tool signing keys. All fields are optional and additive — an unsigned bundle is unchanged from v1.2/v1.3.
597
+
598
+
#### **21.2. Wire Format**
599
+
600
+
A signed trust bundle adds four OPTIONAL top-level fields:
601
+
602
+
```json
603
+
{
604
+
"schemapin_bundle_version": "1.4",
605
+
"documents": [ "..." ],
606
+
"revocations": [ "..." ],
607
+
"bundle_authority": {
608
+
"kid": "schemapin-bundle-authority-2026",
609
+
"public_key_pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
610
+
},
611
+
"signed_at": "2026-05-15T00:00:00Z",
612
+
"expires_at": "2099-01-01T00:00:00Z",
613
+
"signature": "<base64 DER ECDSA P-256>"
614
+
}
615
+
```
616
+
617
+
The authority public key is carried as `public_key_pem` (consistent with discovery documents), making the bundle self-verifying. Signing sets `schemapin_bundle_version` to `"1.4"`.
618
+
619
+
#### **21.3. Signing Input**
620
+
621
+
The signature covers the `schemapin-v1` canonicalization (§19; recursive sorted keys, compact, UTF-8) of the entire bundle object with the `signature` field set to the empty string `""`. All four SDKs MUST produce the identical byte string so a bundle signed by any SDK verifies in every other.
622
+
623
+
#### **21.4. Operations**
624
+
625
+
-`sign_trust_bundle(bundle, private_key_pem, kid, signed_at, expires_at?)` — derive the authority public key, stamp `bundle_authority` / `signed_at` / `expires_at`, and write the signature.
1. Require `bundle_authority` and `signature` — else `BUNDLE_UNSIGNED`.
628
+
2. Reject when `expires_at` is present and in the past or unparseable — `BUNDLE_EXPIRED`.
629
+
3. TOFU-pin the authority key fingerprint by `kid`; a different key under a pinned `kid` is `KEY_PIN_MISMATCH`.
630
+
4. Verify the signature over the canonical bytes — failure is `SIGNATURE_INVALID`.
631
+
-`merge_trust_bundles(bundles)` — deduplicate `documents` and `revocations` by domain, the newer source (`signed_at`, else `created_at`) winning. Returns an UNSIGNED bundle to be re-signed before redistribution.
632
+
-`schemapin/trustBundle` JSON-RPC envelope helpers — `build_trust_bundle_request` / `build_trust_bundle_response` / `parse_trust_bundle_response`. The libraries define the message envelope; transport and the receiving pin-store update are the host application's responsibility.
633
+
634
+
#### **21.5. Backward Compatibility**
635
+
636
+
- Unsigned bundles omit all four fields and are byte-identical to pre-v1.4 bundles; existing resolvers ignore the new fields.
637
+
- The `BUNDLE_UNSIGNED` and `BUNDLE_EXPIRED` error codes are new in v1.4.
|[Schema Version Binding](schema-version-binding.md)|`schema_version` + `previous_hash` lineage chain to defend against rug-pull substitutions (v1.4-alpha, all 4 languages) |
83
+
|[Trust Bundle Distribution](trust-bundle-distribution.md)| Sign, verify, and merge trust bundles for safe A2A exchange (v1.4-alpha, all 4 languages) |
83
84
|[Deployment](deployment.md)| Serve `.well-known` endpoints in production |
84
85
|[Troubleshooting](troubleshooting.md)| Common issues and solutions |
> **Status:** v1.4.0-alpha.4 — implemented in **Rust, Python, JavaScript, and Go**. A bundle signed by any one SDK verifies in every other (proven by the shared `tests/cross-language/signed_bundle.json` fixture).
4
+
5
+
A [trust bundle](trust-bundles.md) pre-packages discovery and revocation documents for offline verification. Until v1.4 a bundle had no authenticity of its own — you had to trust however it reached you. That is fine for a bundle you build and ship yourself, but not for one an agent hands to another agent over A2A.
6
+
7
+
v1.4 lets a **bundle authority** sign a trust bundle so it can be exchanged between agents without per-bundle out-of-band trust establishment. The receiving agent verifies the signature and TOFU-pins the authority key by `kid`, exactly as it would pin a tool's signing key.
8
+
9
+
All additions are optional fields — an unsigned bundle is byte-for-byte what it was before v1.4, and v1.2/v1.3 consumers ignore the new fields.
10
+
11
+
---
12
+
13
+
## Wire format
14
+
15
+
A signed bundle gains four optional top-level fields:
16
+
17
+
```json
18
+
{
19
+
"schemapin_bundle_version": "1.4",
20
+
"created_at": "2026-05-15T00:00:00Z",
21
+
"documents": [ /* ... */ ],
22
+
"revocations": [ /* ... */ ],
23
+
"bundle_authority": {
24
+
"kid": "schemapin-bundle-authority-2026",
25
+
"public_key_pem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
26
+
},
27
+
"signed_at": "2026-05-15T00:00:00Z",
28
+
"expires_at": "2099-01-01T00:00:00Z",
29
+
"signature": "MEUCIQ..."
30
+
}
31
+
```
32
+
33
+
-**`bundle_authority`** — the authority that signed the bundle. The public key is carried as `public_key_pem` (consistent with discovery documents), so the bundle is self-verifying.
34
+
-**`signed_at`** / **`expires_at`** — RFC 3339 timestamps. `expires_at` is optional even on a signed bundle; when present and past, verification fails with `BUNDLE_EXPIRED`.
35
+
-**`signature`** — base64 DER ECDSA P-256 signature.
36
+
37
+
Signing stamps `schemapin_bundle_version` to `"1.4"`.
38
+
39
+
### Signing input
40
+
41
+
The signature covers the **`schemapin-v1` canonicalization** (recursive sorted keys, compact separators, UTF-8) of the entire bundle object with the `signature` field set to the empty string `""`. This is the same canonicalization used for schema and skill signing, so all four SDKs produce the identical byte string and cross-verify.
42
+
43
+
---
44
+
45
+
## Operations
46
+
47
+
| Operation | Purpose |
48
+
|-----------|---------|
49
+
|`sign_trust_bundle(bundle, private_key_pem, kid, signed_at, expires_at?)`| Stamp authority metadata and write the signature. Derives the authority public key from the private key. |
50
+
|`verify_trust_bundle(bundle, authority_pin_store)`| Verify the signature, reject expired bundles, and TOFU-pin the authority key by `kid`. |
51
+
|`merge_trust_bundles(bundles)`| Combine bundles from multiple sources, deduplicating by domain (newest wins). Returns an **unsigned** bundle to re-sign before redistribution. |
0 commit comments