agent-passport-system 1.29.0 → 1.29.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 CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/agent-passport-system)](https://www.npmjs.com/package/agent-passport-system)
4
4
  [![license](https://img.shields.io/npm/l/agent-passport-system)](https://github.com/aeoess/agent-passport-system/blob/main/LICENSE)
5
- [![tests](https://img.shields.io/badge/tests-1715%20passing-brightgreen)](https://github.com/aeoess/agent-passport-system)
5
+ [![tests](https://img.shields.io/badge/tests-1852%20passing-brightgreen)](https://github.com/aeoess/agent-passport-system)
6
6
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.18749779.svg)](https://doi.org/10.5281/zenodo.18749779)
7
+ [![cited](https://img.shields.io/badge/cited%20by-PDR%20in%20Production%20(UBC)-blue)](https://doi.org/10.5281/zenodo.19323172)
7
8
 
8
- > **For AI agents:** visit [aeoess.com/llms.txt](https://aeoess.com/llms.txt) for machine-readable docs or [llms-full.txt](https://aeoess.com/llms-full.txt) for the complete reference.
9
+ > **For AI agents:** visit [aeoess.com/llms.txt](https://aeoess.com/llms.txt) for machine-readable docs or [llms-full.txt](https://aeoess.com/llms-full.txt) for the complete reference. MCP discovery: [.well-known/mcp.json](https://aeoess.com/.well-known/mcp.json).
9
10
 
10
- **Governance infrastructure for the agent economy.** Identity, delegation, reputation, enforcement, commerce, institutional governance. Not just identity — the full stack.
11
+ **Enforcement infrastructure for the agent economy.** Every action evaluated in under 2ms. 15 constraint dimensions. 403 ops/sec. Sub-millisecond denial. Feeless Nano payments. 95 modules. 1,929 tests. Not just identity — the full enforcement stack.
11
12
 
12
- AI agents represent companies and people. They spend real money, access sensitive data, negotiate contracts, and talk to other agents. APS answers: what is this agent allowed to do? How much can it spend? Is it trustworthy? What happens when it violates a constraint? And can you prove all of this cryptographically?
13
+ AI agents represent companies and people. They spend real money, access sensitive data, negotiate contracts, and talk to other agents. APS is the enforcement layer that answers: what is this agent allowed to do? How much can it spend? Is it trustworthy? What happens when it violates a constraint? And can you prove all of this cryptographically? Independently validated by [PDR in Production (Nanook & Gerundium, UBC)](https://doi.org/10.5281/zenodo.19323172).
13
14
 
14
15
  ```bash
15
16
  npm install agent-passport-system
@@ -27,6 +28,21 @@ npm install agent-passport-system
27
28
 
28
29
  **Revoke authority instantly** — cascade revocation propagates through delegation chains. Revoke a parent, all children are automatically revoked. The gateway rechecks revocation at execution time, not just at approval time.
29
30
 
31
+ ## Benchmarks
32
+
33
+ | Metric | Value | Notes |
34
+ |--------|------:|-------|
35
+ | Policy eval p50 | <2ms | Full 15-dimension constraint check |
36
+ | Policy eval p95 | <5ms | Including reputation lookup |
37
+ | Policy eval p99 | <10ms | Worst case with cold cache |
38
+ | Denial latency | <1ms | Fail-fast on first constraint violation |
39
+ | Throughput | 403 ops/sec | Single-threaded gateway |
40
+ | Cascade revocation | <5ms | Chains up to 100 deep |
41
+ | Receipt generation | <1ms | Ed25519 signed, hash-chained |
42
+ | Nano transaction | <1s | Feeless, delegation-scoped |
43
+
44
+ 15 constraint dimensions: scope, spend, tier, values, revocation, taint, anomaly, circuit, approval, temporal, jurisdiction, purpose, combination, retention, terms. [Full benchmarks →](https://aeoess.com/benchmarks.html)
45
+
30
46
  ## Quick Example: Enforce, Don't Just Identify
31
47
 
32
48
  ```typescript
@@ -124,7 +140,7 @@ const agent = joinSocialContract({ name: 'my-agent', owner: 'alice', floor: floo
124
140
 
125
141
  ## The Stack
126
142
 
127
- 62 core modules + 32 v2 constitutional modules. 1715 tests. Zero heavy dependencies.
143
+ 63 core modules + 32 v2 constitutional modules. 1929 tests. Zero heavy dependencies.
128
144
 
129
145
  | Layer | What it does | Key primitive |
130
146
  |-------|-------------|---------------|
@@ -143,7 +159,7 @@ const agent = joinSocialContract({ name: 'my-agent', owner: 'alice', floor: floo
143
159
 
144
160
  ## MCP Server
145
161
 
146
- 121 tools across all modules. Any MCP client connects agents directly.
162
+ 125 tools across all modules. Any MCP client connects agents directly.
147
163
 
148
164
  ```bash
149
165
  npm install -g agent-passport-system-mcp
@@ -178,7 +194,7 @@ npx agent-passport audit --floor values/floor.yaml
178
194
 
179
195
  ```bash
180
196
  npm test
181
- # 1715 tests across 93 files, 446 suites, 0 failures
197
+ # 1929 tests across 98 files, 486 suites, 0 failures
182
198
  ```
183
199
 
184
200
  50 adversarial tests: Merkle tampering, attribution gaming, compliance violations, floor negotiation attacks, cross-chain confused deputy, taint laundering, authority probing.
@@ -196,7 +212,7 @@ npm test
196
212
  | Signed receipts | 3-sig chain | Proposed | Logs | General | — |
197
213
  | Values enforcement | 8 principles, graduated | — | Rules | — | — |
198
214
  | Coordination | Task lifecycle + MCP | — | — | — | — |
199
- | Tests | 1715 (50 adversarial) | None | Limited | None | None |
215
+ | Tests | 1852 (50 adversarial) | None | Limited | None | None |
200
216
 
201
217
  ## Recognition
202
218
 
@@ -228,6 +244,24 @@ Protocol: [aeoess.com/protocol.html](https://aeoess.com/protocol.html) · Agora:
228
244
  - Quick start: [aeoess.com/llms/quickstart.txt](https://aeoess.com/llms/quickstart.txt)
229
245
  - API reference: [aeoess.com/llms/api.txt](https://aeoess.com/llms/api.txt)
230
246
 
247
+ ## Passport Issuer (CA Model)
248
+
249
+ Passports issued through official AEOESS infrastructure are countersigned with the AEOESS issuer key. Self-signed passports are cryptographically valid but won't pass issuer verification.
250
+
251
+ ```typescript
252
+ import { countersignPassport, verifyIssuerSignature } from 'agent-passport-system'
253
+
254
+ // Issuer countersigns after agent self-signs
255
+ const issued = countersignPassport(signedPassport, issuerPrivateKey, 'aeoess')
256
+
257
+ // Anyone can verify against the published public key
258
+ const AEOESS_KEY = 'e11f46f5831432d17852189d5df10ed21d5774797ae9ee52dbab8c650fec16ae'
259
+ const trusted = verifyIssuerSignature(issued, AEOESS_KEY) // true
260
+ ```
261
+
262
+ Published key: [aeoess.com/.well-known/aeoess-issuer.json](https://aeoess.com/.well-known/aeoess-issuer.json)
263
+ MCP tool: `verify_issuer`
264
+
231
265
  ## License
232
266
 
233
267
  Apache-2.0 — see [LICENSE](LICENSE)
@@ -0,0 +1,64 @@
1
+ /**
2
+ * NVIDIA OpenShell Adapter
3
+ *
4
+ * Maps APS delegation scopes to OpenShell sandbox policy YAML.
5
+ * An agent's delegation chain determines what the sandbox can access.
6
+ *
7
+ * Usage:
8
+ * const policy = delegationToPolicy(delegation, basePolicy)
9
+ * // Write policy to YAML, pass to: openshell sandbox create --policy ./policy.yaml
10
+ */
11
+ import type { Delegation } from '../types/passport.js';
12
+ export interface OpenShellPolicy {
13
+ version: 1;
14
+ identity_policy?: {
15
+ agent_public_key: string;
16
+ issuer_public_key?: string;
17
+ delegation_chain_depth: number;
18
+ };
19
+ filesystem_policy?: {
20
+ read_only: string[];
21
+ read_write: string[];
22
+ };
23
+ network_policies?: Record<string, NetworkPolicyEntry>;
24
+ process?: {
25
+ run_as_user: string;
26
+ run_as_group: string;
27
+ };
28
+ }
29
+ export interface NetworkPolicyEntry {
30
+ name: string;
31
+ endpoints: Array<{
32
+ host: string;
33
+ port: number;
34
+ protocol?: string;
35
+ }>;
36
+ binaries?: Array<{
37
+ path: string;
38
+ }>;
39
+ }
40
+ export interface ScopeMapping {
41
+ scope: string;
42
+ filesystemRead?: string[];
43
+ filesystemWrite?: string[];
44
+ networkAllow?: Array<{
45
+ host: string;
46
+ port: number;
47
+ }>;
48
+ inferenceLocal?: boolean;
49
+ }
50
+ /**
51
+ * Extract effective scopes from a delegation, applying monotonic narrowing.
52
+ */
53
+ export declare function extractEffectiveScopes(delegation: Delegation): string[];
54
+ /**
55
+ * Map APS delegation scopes to OpenShell policy sections.
56
+ * The output policy is the intersection of the delegation scope and the base policy.
57
+ */
58
+ export declare function delegationToPolicy(delegation: Delegation, agentPublicKey: string, issuerPublicKey?: string, customMappings?: Record<string, Partial<ScopeMapping>>): OpenShellPolicy;
59
+ /**
60
+ * Serialize an OpenShell policy to YAML string.
61
+ * Minimal YAML serializer — no external deps.
62
+ */
63
+ export declare function policyToYaml(policy: OpenShellPolicy): string;
64
+ //# sourceMappingURL=openshell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openshell.d.ts","sourceRoot":"","sources":["../../../src/adapters/openshell.ts"],"names":[],"mappings":"AACA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAA;IACV,eAAe,CAAC,EAAE;QAChB,gBAAgB,EAAE,MAAM,CAAA;QACxB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,sBAAsB,EAAE,MAAM,CAAA;KAC/B,CAAA;IACD,iBAAiB,CAAC,EAAE;QAClB,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,UAAU,EAAE,MAAM,EAAE,CAAA;KACrB,CAAA;IACD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;IACrD,OAAO,CAAC,EAAE;QACR,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnE,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACpD,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAuBD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE,CAEvE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,MAAM,EACxB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,GACrD,eAAe,CA4CjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAwC5D"}
@@ -0,0 +1,126 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ /**
3
+ * NVIDIA OpenShell Adapter
4
+ *
5
+ * Maps APS delegation scopes to OpenShell sandbox policy YAML.
6
+ * An agent's delegation chain determines what the sandbox can access.
7
+ *
8
+ * Usage:
9
+ * const policy = delegationToPolicy(delegation, basePolicy)
10
+ * // Write policy to YAML, pass to: openshell sandbox create --policy ./policy.yaml
11
+ */
12
+ const DEFAULT_SCOPE_MAPPINGS = {
13
+ 'filesystem:read': { filesystemRead: ['/sandbox', '/tmp'] },
14
+ 'filesystem:write': { filesystemWrite: ['/sandbox', '/tmp'] },
15
+ 'network:*': { networkAllow: [] },
16
+ 'commerce:*': { networkAllow: [{ host: 'gateway.aeoess.com', port: 443 }] },
17
+ 'inference:local': { inferenceLocal: true },
18
+ };
19
+ /**
20
+ * Check if a delegation scope covers a target scope.
21
+ * Supports wildcards: 'commerce:*' covers 'commerce:send'.
22
+ */
23
+ function scopeCovers(granted, target) {
24
+ if (granted === target)
25
+ return true;
26
+ if (granted.endsWith(':*')) {
27
+ const prefix = granted.slice(0, -1);
28
+ return target.startsWith(prefix);
29
+ }
30
+ return false;
31
+ }
32
+ /**
33
+ * Extract effective scopes from a delegation, applying monotonic narrowing.
34
+ */
35
+ export function extractEffectiveScopes(delegation) {
36
+ return delegation.scope || [];
37
+ }
38
+ /**
39
+ * Map APS delegation scopes to OpenShell policy sections.
40
+ * The output policy is the intersection of the delegation scope and the base policy.
41
+ */
42
+ export function delegationToPolicy(delegation, agentPublicKey, issuerPublicKey, customMappings) {
43
+ const mappings = { ...DEFAULT_SCOPE_MAPPINGS, ...customMappings };
44
+ const scopes = extractEffectiveScopes(delegation);
45
+ const readPaths = new Set(['/usr', '/lib', '/etc']);
46
+ const writePaths = new Set();
47
+ const networkEntries = [];
48
+ for (const scope of scopes) {
49
+ for (const [pattern, mapping] of Object.entries(mappings)) {
50
+ if (scopeCovers(pattern, scope)) {
51
+ if (mapping.filesystemRead)
52
+ mapping.filesystemRead.forEach(p => readPaths.add(p));
53
+ if (mapping.filesystemWrite)
54
+ mapping.filesystemWrite.forEach(p => writePaths.add(p));
55
+ if (mapping.networkAllow)
56
+ networkEntries.push(...mapping.networkAllow);
57
+ }
58
+ }
59
+ }
60
+ const policy = {
61
+ version: 1,
62
+ identity_policy: {
63
+ agent_public_key: agentPublicKey,
64
+ issuer_public_key: issuerPublicKey,
65
+ delegation_chain_depth: delegation.currentDepth || 0,
66
+ },
67
+ filesystem_policy: {
68
+ read_only: [...readPaths],
69
+ read_write: [...writePaths],
70
+ },
71
+ process: { run_as_user: 'sandbox', run_as_group: 'sandbox' },
72
+ };
73
+ if (networkEntries.length > 0) {
74
+ policy.network_policies = {};
75
+ networkEntries.forEach((entry, i) => {
76
+ const key = `aps_${entry.host.replace(/\./g, '_')}`;
77
+ policy.network_policies[key] = {
78
+ name: `APS: ${entry.host}`,
79
+ endpoints: [{ host: entry.host, port: entry.port, protocol: 'rest' }],
80
+ };
81
+ });
82
+ }
83
+ return policy;
84
+ }
85
+ /**
86
+ * Serialize an OpenShell policy to YAML string.
87
+ * Minimal YAML serializer — no external deps.
88
+ */
89
+ export function policyToYaml(policy) {
90
+ const lines = [`version: ${policy.version}`];
91
+ if (policy.identity_policy) {
92
+ lines.push('', 'identity_policy:');
93
+ lines.push(` agent_public_key: "${policy.identity_policy.agent_public_key}"`);
94
+ if (policy.identity_policy.issuer_public_key)
95
+ lines.push(` issuer_public_key: "${policy.identity_policy.issuer_public_key}"`);
96
+ lines.push(` delegation_chain_depth: ${policy.identity_policy.delegation_chain_depth}`);
97
+ }
98
+ if (policy.filesystem_policy) {
99
+ lines.push('', 'filesystem_policy:');
100
+ lines.push(' read_only:');
101
+ policy.filesystem_policy.read_only.forEach(p => lines.push(` - ${p}`));
102
+ lines.push(' read_write:');
103
+ policy.filesystem_policy.read_write.forEach(p => lines.push(` - ${p}`));
104
+ }
105
+ if (policy.process) {
106
+ lines.push('', 'process:');
107
+ lines.push(` run_as_user: ${policy.process.run_as_user}`);
108
+ lines.push(` run_as_group: ${policy.process.run_as_group}`);
109
+ }
110
+ if (policy.network_policies) {
111
+ lines.push('', 'network_policies:');
112
+ for (const [key, entry] of Object.entries(policy.network_policies)) {
113
+ lines.push(` ${key}:`);
114
+ lines.push(` name: "${entry.name}"`);
115
+ lines.push(' endpoints:');
116
+ entry.endpoints.forEach(ep => {
117
+ lines.push(` - host: ${ep.host}`);
118
+ lines.push(` port: ${ep.port}`);
119
+ if (ep.protocol)
120
+ lines.push(` protocol: ${ep.protocol}`);
121
+ });
122
+ }
123
+ }
124
+ return lines.join('\n') + '\n';
125
+ }
126
+ //# sourceMappingURL=openshell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openshell.js","sourceRoot":"","sources":["../../../src/adapters/openshell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { PassportGrade, AttestationFlag, IssuanceChallenge, IssuanceEvidenceRecord, IssuanceContext, PassportAttestationSummary, RuntimeAttestation, ProviderAttestation, ObservedContext, SignalVerificationResult, DerivedSignal, AttestationClass, WorkspaceManifest } from '../types/attestation.js';
2
+ import type { SignedPassport } from '../types/passport.js';
3
+ export declare function createIssuanceChallenge(publicKeyHash: string, options?: {
4
+ requestedClasses?: AttestationClass[];
5
+ expiresInSeconds?: number;
6
+ }): IssuanceChallenge;
7
+ export declare function verifyRuntimeAttestation(attestation: RuntimeAttestation, challenge: IssuanceChallenge, trustedAttesterKeys: Map<string, string>): SignalVerificationResult;
8
+ export declare function computePassportGrade(evidence: IssuanceEvidenceRecord, options?: {
9
+ hasIssuerSignature?: boolean;
10
+ hasVerifiedRuntime?: boolean;
11
+ hasVerifiedProvider?: boolean;
12
+ hasPrincipalEndorsement?: boolean;
13
+ }): PassportGrade;
14
+ export declare function computeAttestationFlags(grade: PassportGrade, evidence: IssuanceEvidenceRecord): AttestationFlag[];
15
+ export declare function computeAttestationBundleHash(evidence: IssuanceEvidenceRecord): string;
16
+ export declare function createIssuanceContext(evidence: IssuanceEvidenceRecord, options?: {
17
+ hasIssuerSignature?: boolean;
18
+ hasVerifiedRuntime?: boolean;
19
+ hasVerifiedProvider?: boolean;
20
+ hasPrincipalEndorsement?: boolean;
21
+ verificationResults?: SignalVerificationResult[];
22
+ derivedSignals?: DerivedSignal[];
23
+ }): IssuanceContext;
24
+ export declare function bindAttestation(signedPassport: SignedPassport, context: IssuanceContext): SignedPassport & {
25
+ attestation: PassportAttestationSummary;
26
+ };
27
+ export declare function createWorkspaceManifest(entries: Array<{
28
+ path: string;
29
+ sizeBytes: number;
30
+ lastModified: Date;
31
+ }>): WorkspaceManifest;
32
+ export declare function createEmptyEvidenceRecord(observed?: Partial<ObservedContext>): IssuanceEvidenceRecord;
33
+ export declare function isChallengeFresh(challenge: IssuanceChallenge): boolean;
34
+ export declare function isGradeAtLeast(grade: PassportGrade, minimum: PassportGrade): boolean;
35
+ export declare function importProviderAttestation(input: {
36
+ /** JWS compact serialization (header.payload.signature) OR raw JSON string OR object */
37
+ attestation: string | Record<string, unknown>;
38
+ /** Provider identifier (e.g. 'red-team-harness', 'cloud-provider', 'oauth-issuer') */
39
+ provider: string;
40
+ /** What kind of subject was attested */
41
+ subjectClass?: string;
42
+ /** Verification method used by the provider */
43
+ verificationMethod?: string;
44
+ }): ProviderAttestation;
45
+ export declare function addIdentityBoundary<T extends Record<string, unknown>>(obj: T, fields?: string[]): T & {
46
+ _identityBoundary: string[];
47
+ _contentHash: string;
48
+ };
49
+ //# sourceMappingURL=attestation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../../../src/core/attestation.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,aAAa,EAAE,eAAe,EAC9B,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EAAE,0BAA0B,EAC3C,kBAAkB,EAAE,mBAAmB,EACvB,eAAe,EAC/B,wBAAwB,EAAE,aAAa,EACvC,gBAAgB,EAChB,iBAAiB,EAElB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAU1D,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IACR,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,iBAAiB,CAYnB;AAKD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,kBAAkB,EAC/B,SAAS,EAAE,iBAAiB,EAC5B,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC,wBAAwB,CA0E1B;AAQD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,sBAAsB,EAChC,OAAO,CAAC,EAAE;IACR,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,GACA,aAAa,CAmBf;AAID,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,sBAAsB,GAC/B,eAAe,EAAE,CAWnB;AAKD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAErF;AAID,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,sBAAsB,EAChC,OAAO,CAAC,EAAE;IACR,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,mBAAmB,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACjD,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC,GACA,eAAe,CAgBjB;AAKD,wBAAgB,eAAe,CAC7B,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,eAAe,GACvB,cAAc,GAAG;IAAE,WAAW,EAAE,0BAA0B,CAAA;CAAE,CAU9D;AAMD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,IAAI,CAAA;CAAE,CAAC,GACtE,iBAAiB,CA0BnB;AAID,wBAAgB,yBAAyB,CACvC,QAAQ,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAClC,sBAAsB,CAaxB;AAID,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAEtE;AAID,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAEpF;AAOD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE;IACL,wFAAwF;IACxF,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7C,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAA;IAChB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,GACA,mBAAmB,CAwCrB;AAMD,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,GAAG,EAAE,CAAC,EACN,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,CAAC,GAAG;IAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAW3D"}
@@ -0,0 +1,294 @@
1
+ // Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
2
+ // Agent Attestation Architecture — Core Functions
3
+ // Phase 1: Foundation types + issuance challenge + grade computation + attestation binding
4
+ import { createHash } from 'crypto';
5
+ import { verify } from '../crypto/keys.js';
6
+ import { canonicalize } from './canonical.js';
7
+ // ── SHA-256 helper ──
8
+ function sha256Hex(input) {
9
+ return createHash('sha256').update(input).digest('hex');
10
+ }
11
+ // ── createIssuanceChallenge ──
12
+ // Phase 1 of the 5-phase issuance flow.
13
+ // Server generates a nonce bound to the agent's public key.
14
+ export function createIssuanceChallenge(publicKeyHash, options) {
15
+ const now = new Date();
16
+ const expiry = new Date(now.getTime() + (options?.expiresInSeconds ?? 300) * 1000);
17
+ return {
18
+ challengeId: `ic_${sha256Hex(Date.now().toString() + Math.random().toString()).slice(0, 24)}`,
19
+ nonce: sha256Hex(Math.random().toString() + Date.now().toString()).slice(0, 32),
20
+ requiredPublicKeyHash: publicKeyHash,
21
+ requestedAttestationClasses: options?.requestedClasses ?? ['runtime'],
22
+ expiresAt: expiry.toISOString(),
23
+ issuedAt: now.toISOString(),
24
+ };
25
+ }
26
+ // ── verifyRuntimeAttestation ──
27
+ // Phase 3: verify an infrastructure attestation.
28
+ // Checks: attester signature, nonce binding to challenge, key binding, freshness.
29
+ export function verifyRuntimeAttestation(attestation, challenge, trustedAttesterKeys // attester URI -> public key
30
+ ) {
31
+ const now = new Date();
32
+ // Check freshness
33
+ if (new Date(attestation.expiresAt) < now) {
34
+ return {
35
+ signalKey: 'runtime_attestation',
36
+ status: 'failed',
37
+ detail: 'Runtime attestation has expired',
38
+ verifiedAt: now.toISOString(),
39
+ };
40
+ }
41
+ // Check nonce binding
42
+ if (attestation.nonce !== challenge.nonce) {
43
+ return {
44
+ signalKey: 'runtime_attestation',
45
+ status: 'failed',
46
+ detail: 'Nonce does not match issuance challenge',
47
+ verifiedAt: now.toISOString(),
48
+ };
49
+ }
50
+ // Check key binding
51
+ if (attestation.publicKeyHash !== challenge.requiredPublicKeyHash) {
52
+ return {
53
+ signalKey: 'runtime_attestation',
54
+ status: 'failed',
55
+ detail: 'Public key hash does not match challenge requirement',
56
+ verifiedAt: now.toISOString(),
57
+ };
58
+ }
59
+ // Check attester is trusted
60
+ const attesterKey = trustedAttesterKeys.get(attestation.attester);
61
+ if (!attesterKey) {
62
+ return {
63
+ signalKey: 'runtime_attestation',
64
+ status: 'observed',
65
+ detail: `Attester ${attestation.attester} is not in trusted registry`,
66
+ verifiedAt: now.toISOString(),
67
+ };
68
+ }
69
+ // Verify attester signature
70
+ const payload = canonicalize({
71
+ attester: attestation.attester,
72
+ nonce: attestation.nonce,
73
+ publicKeyHash: attestation.publicKeyHash,
74
+ runtimeClass: attestation.runtimeClass,
75
+ bootEpoch: attestation.bootEpoch,
76
+ runtimeInstanceIdHash: attestation.runtimeInstanceIdHash,
77
+ storageIdentityHash: attestation.storageIdentityHash,
78
+ processIdentityHash: attestation.processIdentityHash,
79
+ issuedAt: attestation.issuedAt,
80
+ expiresAt: attestation.expiresAt,
81
+ });
82
+ const sigValid = verify(payload, attestation.signature, attesterKey);
83
+ if (!sigValid) {
84
+ return {
85
+ signalKey: 'runtime_attestation',
86
+ status: 'failed',
87
+ detail: 'Attester signature verification failed',
88
+ verifiedAt: now.toISOString(),
89
+ };
90
+ }
91
+ return {
92
+ signalKey: 'runtime_attestation',
93
+ status: 'verified',
94
+ detail: `Verified by trusted attester ${attestation.attester}`,
95
+ verifiedAt: now.toISOString(),
96
+ };
97
+ }
98
+ // ── computePassportGrade ──
99
+ // Determines grade from available verified attestations.
100
+ // Grade 0: self-signed (bare keypair)
101
+ // Grade 1: issuer countersigned
102
+ // Grade 2: runtime-bound (issuer + challenge-response + trusted attestation)
103
+ // Grade 3: runtime + principal bound
104
+ export function computePassportGrade(evidence, options) {
105
+ const has = options ?? {};
106
+ // Check from highest to lowest
107
+ if (has.hasVerifiedRuntime && has.hasPrincipalEndorsement && has.hasIssuerSignature) {
108
+ return 3;
109
+ }
110
+ if (has.hasVerifiedRuntime && has.hasIssuerSignature) {
111
+ return 2;
112
+ }
113
+ // Provider attestation without runtime can still be Grade 2
114
+ // if the provider is strong enough (e.g., verified cloud tenant)
115
+ if (has.hasVerifiedProvider && has.hasIssuerSignature) {
116
+ return 2;
117
+ }
118
+ if (has.hasIssuerSignature) {
119
+ return 1;
120
+ }
121
+ return 0;
122
+ }
123
+ // ── computeAttestationFlags ──
124
+ // Derive public flags from evidence and grade.
125
+ export function computeAttestationFlags(grade, evidence) {
126
+ const flags = [];
127
+ if (grade >= 1)
128
+ flags.push('issuer_bound');
129
+ if (grade >= 2 && evidence.runtimeAttestations.length > 0)
130
+ flags.push('runtime_bound');
131
+ if (grade >= 2 && evidence.providerAttestations.length > 0)
132
+ flags.push('provider_bound');
133
+ if (grade >= 3)
134
+ flags.push('principal_bound');
135
+ if (evidence.priorPassportRef)
136
+ flags.push('recovery_linked');
137
+ if (evidence.priorContinuityProof)
138
+ flags.push('continuity_proven');
139
+ return flags;
140
+ }
141
+ // ── computeAttestationBundleHash ──
142
+ // SHA-256 of the canonical evidence record. Allows verifiers to confirm
143
+ // the passport's attestation summary matches real evidence without seeing the evidence.
144
+ export function computeAttestationBundleHash(evidence) {
145
+ return sha256Hex(canonicalize(evidence));
146
+ }
147
+ // ── createIssuanceContext ──
148
+ // Combine evidence + assessment into a complete issuance record.
149
+ export function createIssuanceContext(evidence, options) {
150
+ const grade = computePassportGrade(evidence, options);
151
+ const flags = computeAttestationFlags(grade, evidence);
152
+ const bundleHash = computeAttestationBundleHash(evidence);
153
+ return {
154
+ evidence,
155
+ assessment: {
156
+ passportGrade: grade,
157
+ attestationBundleHash: bundleHash,
158
+ flags,
159
+ verificationResults: options?.verificationResults ?? [],
160
+ derivedSignals: options?.derivedSignals,
161
+ assessedAt: new Date().toISOString(),
162
+ },
163
+ };
164
+ }
165
+ // ── bindAttestation ──
166
+ // Attach PassportAttestationSummary to a SignedPassport.
167
+ // This is backward compatible: the attestation field is optional.
168
+ export function bindAttestation(signedPassport, context) {
169
+ const summary = {
170
+ passportGrade: context.assessment.passportGrade,
171
+ attestationBundleHash: context.assessment.attestationBundleHash,
172
+ flags: context.assessment.flags,
173
+ };
174
+ return {
175
+ ...signedPassport,
176
+ attestation: summary,
177
+ };
178
+ }
179
+ // ── createWorkspaceManifest ──
180
+ // Compute a workspace manifest from file entries.
181
+ // Hash structure, not content. Privacy-preserving: paths are hashed,
182
+ // timestamps floored to hour.
183
+ export function createWorkspaceManifest(entries) {
184
+ const now = new Date();
185
+ // Sort entries deterministically by path hash
186
+ const manifestEntries = entries
187
+ .map(e => {
188
+ const hourFloor = new Date(e.lastModified);
189
+ hourFloor.setMinutes(0, 0, 0);
190
+ return {
191
+ pathHash: sha256Hex(e.path),
192
+ sizeBytes: e.sizeBytes,
193
+ lastModifiedBucket: hourFloor.toISOString(),
194
+ };
195
+ })
196
+ .sort((a, b) => a.pathHash.localeCompare(b.pathHash));
197
+ const totalSize = manifestEntries.reduce((sum, e) => sum + e.sizeBytes, 0);
198
+ const manifestHash = sha256Hex(canonicalize(manifestEntries));
199
+ return {
200
+ entries: manifestEntries,
201
+ totalFiles: manifestEntries.length,
202
+ totalSizeBytes: totalSize,
203
+ computedAt: now.toISOString(),
204
+ manifestHash,
205
+ };
206
+ }
207
+ // ── createEmptyEvidenceRecord ──
208
+ // Initialize a minimal evidence record. The server fills Tier 0 observed signals.
209
+ export function createEmptyEvidenceRecord(observed) {
210
+ const now = new Date();
211
+ return {
212
+ requestId: `req_${sha256Hex(Date.now().toString() + Math.random().toString()).slice(0, 24)}`,
213
+ requestedAt: now.toISOString(),
214
+ observed: {
215
+ observedAt: now.toISOString(),
216
+ ...observed,
217
+ },
218
+ runtimeAttestations: [],
219
+ providerAttestations: [],
220
+ selfDeclaredSignals: [],
221
+ };
222
+ }
223
+ // ── isChallengeFresh ──
224
+ // Check if an issuance challenge is still valid.
225
+ export function isChallengeFresh(challenge) {
226
+ return new Date(challenge.expiresAt) > new Date();
227
+ }
228
+ // ── isGradeAtLeast ──
229
+ // Check if a passport grade meets a minimum requirement.
230
+ export function isGradeAtLeast(grade, minimum) {
231
+ return grade >= minimum;
232
+ }
233
+ // ── importProviderAttestation ──
234
+ // Accept external attestation reports (JWS or raw JSON) and convert to ProviderAttestation.
235
+ // Enables composable trust: security test results, cloud attestations, or any third-party
236
+ // verification report feeds into the IssuanceEvidenceRecord as Tier 2 evidence.
237
+ // Connected to msaleme's machine-readable attestation schema (A2A #1696).
238
+ export function importProviderAttestation(input) {
239
+ let payload;
240
+ let signature;
241
+ if (typeof input.attestation === 'string') {
242
+ const parts = input.attestation.split('.');
243
+ if (parts.length === 3) {
244
+ // JWS compact: header.payload.signature
245
+ try {
246
+ const decoded = Buffer.from(parts[1], 'base64url').toString('utf-8');
247
+ payload = JSON.parse(decoded);
248
+ signature = parts[2];
249
+ }
250
+ catch {
251
+ // Not valid JWS, treat as raw JSON
252
+ payload = JSON.parse(input.attestation);
253
+ }
254
+ }
255
+ else {
256
+ payload = JSON.parse(input.attestation);
257
+ }
258
+ }
259
+ else {
260
+ payload = input.attestation;
261
+ }
262
+ // Extract subject identifier and hash it
263
+ const subjectId = String(payload.sub || payload.subject_id || payload.agentId || payload.agent_id || '');
264
+ const subjectIdHash = sha256Hex(subjectId);
265
+ return {
266
+ provider: input.provider,
267
+ subjectClass: input.subjectClass || String(payload.subject_class || 'agent'),
268
+ subjectIdHash,
269
+ nonce: payload.nonce ? String(payload.nonce) : undefined,
270
+ publicKeyHash: payload.public_key ? sha256Hex(String(payload.public_key)) : undefined,
271
+ verificationMethod: input.verificationMethod || String(payload.verification_method || 'jwt'),
272
+ issuedAt: String(payload.iat ? new Date(Number(payload.iat) * 1000).toISOString() : payload.issued_at || new Date().toISOString()),
273
+ expiresAt: payload.exp ? new Date(Number(payload.exp) * 1000).toISOString() : (payload.expires_at ? String(payload.expires_at) : undefined),
274
+ signature,
275
+ };
276
+ }
277
+ // ── addIdentityBoundary ──
278
+ // Add a self-describing identity boundary to any object. The boundary declares which
279
+ // fields are included in the content hash, making cross-system attribution possible
280
+ // without agreeing on a single hashing standard. Inspired by xsa520's decision artifact spec.
281
+ export function addIdentityBoundary(obj, fields) {
282
+ const boundary = (fields || Object.keys(obj)).filter(k => !k.startsWith('_')).sort();
283
+ const hashInput = { _identityBoundary: boundary };
284
+ for (const k of boundary) {
285
+ if (k in obj)
286
+ hashInput[k] = obj[k];
287
+ }
288
+ return {
289
+ ...obj,
290
+ _identityBoundary: boundary,
291
+ _contentHash: sha256Hex(JSON.stringify(hashInput)),
292
+ };
293
+ }
294
+ //# sourceMappingURL=attestation.js.map