Skip to content

Commit b3a057a

Browse files
aeoessTymofiii
andauthored
rfc(identity): optional cryptographic identity layer for gitagent manifests (open-gitagent#73)
Closes open-gitagent#70 (proposal). Adds spec/rfcs/identity.md per shreyas-lyzr's review. - Optional identity block in agent.yaml (public_key, key_fingerprint, passport_uri, signatures) - Verification semantics in spec, enforcement in runtime - Two-deep delegation example with parent-signs-child manifest flow - Revocation behavior tight enough to keep two implementations from diverging - Non-goals section bounding scope - Cross-walk to Agent Passport System as one reference impl Backwards compatible: existing agent.yaml files validate unchanged. Co-authored-by: Tymofii Pidlisnyi <tima@aeoess.com>
1 parent 2ed20a2 commit b3a057a

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

spec/rfcs/identity.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# RFC: Optional Cryptographic Identity Layer for gitagent Manifests
2+
3+
| Field | Value |
4+
|---|---|
5+
| RFC | identity |
6+
| Status | Draft |
7+
| Author | Tymofii Pidlisnyi (aeoess) |
8+
| Targets spec | v0.1.x (optional), v0.2 conformance suite (optional MUST) |
9+
| Issue | https://github.com/open-gitagent/gitagent-protocol/issues/70 |
10+
| Reference impl | Agent Passport System (Apache 2.0): https://github.com/aeoess/agent-passport-system |
11+
12+
## 1. Motivation
13+
14+
The gitagent surface (`agent.yaml` + `SOUL.md` + `RULES.md` + `DUTIES.md`) describes what an agent claims to be. It does not give downstream tools a way to verify, at runtime, that the running agent is the one the manifest describes.
15+
16+
For the unregulated case, git-blame plus signed git tags are enough. The repo author signed the commit, the manifest is in the commit, and trust flows transitively through the git host.
17+
18+
For two harder cases, that is not enough:
19+
20+
- **Inter-org delegation.** Org A defines an agent. Org B wants to run a child agent that operates under A's authority. There is no mechanism in the spec for B's agent to prove, to a third party, that A authorized this exact child manifest with this exact scope.
21+
- **Regulated runtime checks.** A FINRA or EU AI Act audit asks for cryptographic proof that the agent that produced an output was the agent in the manifest, that it had authority for the action it took, and that the authority had not been revoked at the time the output was produced. Signed git tags do not bind a running agent's outputs to its manifest.
22+
23+
This RFC adds an **optional** seam to `agent.yaml` that lets implementations bolt on cryptographic identity without changing the canonical surface area. Manifests without the seam continue to work unchanged.
24+
25+
## 2. Two concerns kept separate
26+
27+
The RFC distinguishes two concerns that are often conflated:
28+
29+
1. **Provenance.** The manifest at this commit was authored by the holder of key X. Solvable today with sigstore, signed git tags, or an external attestation store. **No spec change needed.**
30+
2. **Runtime delegation.** The running agent producing this output is acting on behalf of parent agent Y, with scope Z, signed by Y's key, not yet revoked. **This RFC adds the spec hook for this case.**
31+
32+
Sections 3 through 6 cover the runtime-delegation hook only. Provenance is mentioned to draw the line, not because the spec needs to address it.
33+
34+
## 3. The `identity` block in `agent.yaml`
35+
36+
Add an **optional** top-level `identity` block to `agent.yaml`:
37+
38+
```yaml
39+
identity:
40+
public_key: ed25519:BASE64URL-ENCODED-32-BYTE-KEY
41+
key_fingerprint: sha256:HEX-DIGEST
42+
passport_uri: https://example.com/passports/agent-name.json # optional
43+
signatures: # optional
44+
manifest:
45+
signer: ed25519:BASE64URL-ENCODED-PARENT-KEY
46+
signature: ed25519:BASE64URL-ENCODED-SIG
47+
scope: BASE64URL-ENCODED-SCOPE-DOC
48+
not_before: 2026-05-04T00:00:00Z
49+
not_after: 2026-08-04T00:00:00Z
50+
```
51+
52+
### Field semantics
53+
54+
| Field | Required when `identity` is present | Description |
55+
|---|---|---|
56+
| `public_key` | yes | Ed25519 public key the agent signs runtime outputs with. Format: `ed25519:` prefix + base64url-encoded 32-byte key. |
57+
| `key_fingerprint` | yes | SHA-256 hex digest of the canonical key bytes. Lets a tool compare keys without resolving `passport_uri`. |
58+
| `passport_uri` | no | Pointer to a richer identity document for scoped delegation, revocation lists, key rotation, and cascade semantics. The richer document is implementation-defined; this RFC defines only the URI as a pointer. |
59+
| `signatures` | no | One or more signatures over the manifest itself. The `manifest` scope is reserved and signs the canonical-bytes form of `agent.yaml` minus the `signatures` subtree itself. Other scopes are implementation-defined. |
60+
61+
### What the spec MUST say
62+
63+
- `public_key` MUST be Ed25519 in v0.1.x. Other algorithms are reserved for future RFCs and MUST be specified by a separate RFC before being accepted.
64+
- `key_fingerprint` MUST be the SHA-256 of the raw 32-byte key material, not of the prefixed string form.
65+
- `passport_uri` MUST resolve over HTTPS or be retrievable via a runtime-configured resolver. Spec does not constrain the document format at the URI.
66+
- If `signatures.manifest` is present, the signature MUST verify against either the parent's `public_key` (if the parent is also a gitagent manifest with an `identity` block) or against a key resolved through `passport_uri`. Verification MUST happen at the canonical-bytes representation of the manifest with the `signatures` subtree removed and keys sorted lexicographically.
67+
68+
### Verification semantics
69+
70+
Verification semantics live in the spec. Enforcement lives in the runtime. The spec says:
71+
72+
> If `identity.public_key` is set, signed runtime outputs claiming to be from this agent MUST verify against that key. Signatures over the manifest itself, when present at `identity.signatures.<scope>`, MUST verify against either an in-tree parent key or a key resolvable through `identity.passport_uri`.
73+
74+
Each runtime decides how to enforce: refuse to load the agent on signature failure, log and continue, sandbox the agent, or some other policy.
75+
76+
## 4. Two-deep delegation example
77+
78+
Org A operates a research agent. Org A authorizes Org B to run a child trading agent under A's authority, scoped to a specific instrument set and a daily spend cap.
79+
80+
### Parent manifest (`agents/research-agent/agent.yaml`)
81+
82+
```yaml
83+
name: research-agent
84+
version: 1.0.0
85+
description: Equity research agent
86+
87+
identity:
88+
public_key: ed25519:7Pz3Q...redacted
89+
key_fingerprint: sha256:a4b1...redacted
90+
```
91+
92+
### Child manifest (`agents/trading-child/agent.yaml`)
93+
94+
```yaml
95+
name: trading-child
96+
version: 0.2.0
97+
description: Trading child agent operating under research-agent authority
98+
99+
identity:
100+
public_key: ed25519:Cm9k4...redacted
101+
key_fingerprint: sha256:8d7e...redacted
102+
signatures:
103+
manifest:
104+
signer: ed25519:7Pz3Q...redacted # research-agent's key
105+
signature: ed25519:k2pX9...redacted
106+
scope: |
107+
instruments: [equity]
108+
max_daily_spend_usd: 50000
109+
max_per_trade_usd: 5000
110+
valid_until: 2026-12-31T23:59:59Z
111+
not_before: 2026-05-04T00:00:00Z
112+
not_after: 2026-12-31T23:59:59Z
113+
```
114+
115+
### Verification flow
116+
117+
A third-party tool that loads `trading-child`:
118+
119+
1. Reads `identity.public_key` and `identity.key_fingerprint`.
120+
2. Checks for `identity.signatures.manifest`. Found.
121+
3. Resolves the `signer` key: in this case, it matches `research-agent`'s `identity.public_key` in the same repo's `agents/research-agent/agent.yaml`. Tool MAY also resolve the key through `passport_uri` if the parent lives in another repo.
122+
4. Computes the canonical-bytes form of `trading-child/agent.yaml` with the `signatures` subtree removed.
123+
5. Verifies the signature against the canonical bytes, the `signer` key, and the `scope` document.
124+
6. Confirms the current time is within `not_before` / `not_after`.
125+
7. Checks revocation status (Section 5).
126+
127+
If any step fails, the runtime decides what to do.
128+
129+
### Beyond two-deep
130+
131+
The same pattern composes recursively. A grandchild agent's `signatures.manifest.signer` resolves to its parent, whose own `signatures.manifest.signer` resolves to its parent, and so on. Spec does not cap chain depth; a runtime MAY. APS, as one reference, caps at depth 8 by default.
132+
133+
## 5. Revocation
134+
135+
Implementations MUST agree on at least one revocation surface so that two implementations resolving the same `passport_uri` reach the same revocation decision.
136+
137+
### Required behavior
138+
139+
If `identity.passport_uri` is set:
140+
141+
- The document at the URI MUST be retrievable over HTTPS.
142+
- The document MUST be JSON.
143+
- The document MUST contain a top-level `revoked` boolean.
144+
- If `revoked` is `true`, the document MUST contain `revoked_at` (RFC 3339 timestamp) and SHOULD contain `revocation_reason` (free-form string).
145+
- The document MAY contain a `revocation_list` array of revoked subordinate key fingerprints (for cascade revocation of children whose `signatures.manifest.signer` matches a revoked key).
146+
147+
### Freshness
148+
149+
Implementations MUST cap the cache age at 24 hours. The document MAY include `cache_max_age_seconds` to request a shorter cap. Implementations SHOULD honor it; they MUST NOT exceed 24 hours.
150+
151+
### Cascade
152+
153+
When a parent key is revoked, all child manifests whose `signatures.manifest.signer` equals the revoked key MUST be treated as revoked, transitively. Runtime decides whether to refuse to load, sandbox, or warn.
154+
155+
### What revocation does NOT specify
156+
157+
- The wire format for revocation announcements (push channel, WebHook, gossip protocol).
158+
- The trust model for the revocation document host (PKI, key pinning, content addressing).
159+
- The economic or governance mechanism that decides when to revoke.
160+
161+
These are runtime decisions, not spec decisions.
162+
163+
## 6. Non-goals
164+
165+
To keep scope tight:
166+
167+
- **Wire format of signed runtime outputs.** This RFC does not specify how an agent transmits a signed output to a downstream tool. That is the runtime's or target framework's job.
168+
- **Mandatory adoption.** The `identity` block is optional in v0.1.x. A v0.2 conformance test suite MAY require it for specific compliance profiles (for example, a financial-services profile), but mandatory adoption is a separate v0.2 conversation.
169+
- **PKI infrastructure.** This RFC does not specify how implementations distribute, rotate, or rotate-back trust roots. Sigstore, ACME-style enrollment, key pinning, and ledger anchoring are all valid implementation choices.
170+
- **Runtime enforcement details.** The spec says MUST verify. The runtime decides what happens when verification fails (refuse to load, sandbox, log and continue, escalate to human).
171+
- **Quantum-resistant variants.** Reserved for a future RFC. Current Ed25519 commitment is explicit.
172+
- **Sub-agent topology.** The example uses `agents/<name>/agent.yaml`, which is the existing v0.1.0 pattern. This RFC does not propose changes to sub-agent layout.
173+
174+
## 7. Cross-walk to Agent Passport System
175+
176+
APS is one reference implementation that satisfies this RFC's primitives. The mapping:
177+
178+
| `agent.yaml` field | APS primitive |
179+
|---|---|
180+
| `identity.public_key` | `Passport.publicKey` (Ed25519, JWK or raw 32-byte form) |
181+
| `identity.key_fingerprint` | SHA-256 of the canonical key bytes; same definition |
182+
| `identity.passport_uri` | URL where `Passport` JSON is served, plus the APS `RevocationCertificate` endpoint |
183+
| `identity.signatures.manifest.signer` | The parent passport's `publicKey` |
184+
| `identity.signatures.manifest.signature` | An APS `DelegationGrant.signature` over the canonical scope |
185+
| `identity.signatures.manifest.scope` | An APS `DelegationGrant.scope` document, base64url-encoded |
186+
| `identity.signatures.manifest.not_before` / `not_after` | `DelegationGrant.validityWindow` start and end |
187+
188+
APS's revocation cert maps to Section 5's required JSON shape via a thin adapter (~30 lines). The richer APS document at `passport_uri` is a superset of what this RFC requires; gitagent runtimes that only consume the RFC's required fields will work against any APS endpoint without further changes.
189+
190+
This RFC does not prescribe APS. Other implementations are equally valid as long as they satisfy Sections 3 through 5.
191+
192+
## 8. Open questions
193+
194+
These are explicitly left for follow-up RFCs:
195+
196+
1. **Multi-signature authority.** Some compliance profiles require m-of-n approvers for delegation. The current `signatures` map is a multi-signature carrier (`signatures.<scope>` can hold multiple entries), but threshold semantics need their own RFC.
197+
2. **Agent-to-tool delegation.** Sub-agents are covered. Agents delegating to MCP tools, sub-skills, or external services are not. Treating tools as first-class signing parties is plausible but expands scope.
198+
3. **Cross-repo parent resolution.** When the parent manifest lives in a different repo than the child, the resolver protocol matters. This RFC says `passport_uri` resolves; it does not specify a discovery protocol.
199+
4. **Conformance test vectors.** A v0.2 conformance suite would benefit from byte-match-verifiable test vectors covering canonical-bytes representation of `agent.yaml` minus `signatures`, signature verification with reference Ed25519 inputs, and revocation document parsing. APS publishes its own conformance suite (`aeoess/aps-conformance-suite`) and could contribute compatible vectors.
200+
201+
## 9. Backwards compatibility
202+
203+
The `identity` block is additive and optional. Existing `agent.yaml` files validate against the v0.1.x schema unchanged. Tools that do not understand the `identity` block ignore it. This RFC does not change any existing field.
204+
205+
## 10. Acknowledgments
206+
207+
Thanks to **shreyas-lyzr** for the substantive review on issue #70 that shaped Sections 3, 5, and 6, and for the framing distinction in Section 2.
208+
209+
— Tymofii Pidlisnyi (aeoess)

0 commit comments

Comments
 (0)