@grc-claw/agent-identity 2.0.0
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/dist/index.d.ts +121 -0
- package/dist/index.js +211 -0
- package/package.json +25 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export interface VerificationMethod {
|
|
2
|
+
id: string;
|
|
3
|
+
type: 'Ed25519VerificationKey2020' | 'X25519KeyAgreementKey2020';
|
|
4
|
+
controller: string;
|
|
5
|
+
publicKeyHex: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ServiceEndpoint {
|
|
8
|
+
id: string;
|
|
9
|
+
type: 'GRCGateway' | 'EvidenceVault' | 'SIEMIngest' | 'SOAREngine';
|
|
10
|
+
serviceEndpoint: string;
|
|
11
|
+
}
|
|
12
|
+
export interface VerifiableCredential {
|
|
13
|
+
'@context': string[];
|
|
14
|
+
type: string[];
|
|
15
|
+
issuer: string;
|
|
16
|
+
issuanceDate: string;
|
|
17
|
+
expirationDate: string;
|
|
18
|
+
credentialSubject: AgentCredentialSubject;
|
|
19
|
+
proof: CredentialProof;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentCredentialSubject {
|
|
22
|
+
id: string;
|
|
23
|
+
framework: 'iso27001' | 'soc2' | 'cmmc' | 'iso42001' | 'nist_csf' | 'gdpr' | 'hipaa' | 'pci_dss';
|
|
24
|
+
certifiedControls: string[];
|
|
25
|
+
toolTierAccess: ('read' | 'write' | 'destructive')[];
|
|
26
|
+
tenantScope: string[];
|
|
27
|
+
sovereignBoundary: 'us-only' | 'eu-only' | 'global' | 'airgapped';
|
|
28
|
+
}
|
|
29
|
+
export interface CredentialProof {
|
|
30
|
+
type: 'Ed25519Signature2020';
|
|
31
|
+
created: string;
|
|
32
|
+
verificationMethod: string;
|
|
33
|
+
proofPurpose: 'assertionMethod';
|
|
34
|
+
proofValue: string;
|
|
35
|
+
}
|
|
36
|
+
export interface AgentDID {
|
|
37
|
+
'@context': string[];
|
|
38
|
+
id: string;
|
|
39
|
+
controller: string;
|
|
40
|
+
created: string;
|
|
41
|
+
updated: string;
|
|
42
|
+
verificationMethod: VerificationMethod[];
|
|
43
|
+
authentication: string[];
|
|
44
|
+
service: ServiceEndpoint[];
|
|
45
|
+
credentials: VerifiableCredential[];
|
|
46
|
+
status: 'active' | 'revoked' | 'suspended';
|
|
47
|
+
riskScore: number;
|
|
48
|
+
metadata: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
export interface AgentIdentityRegistry {
|
|
51
|
+
agents: Map<string, AgentDID>;
|
|
52
|
+
revokedDids: Set<string>;
|
|
53
|
+
}
|
|
54
|
+
export declare class AgentIdentityManager {
|
|
55
|
+
private registry;
|
|
56
|
+
/** Generate a new DID for an agent */
|
|
57
|
+
createAgentDID(opts: {
|
|
58
|
+
controller: string;
|
|
59
|
+
tenantScope: string[];
|
|
60
|
+
sovereignBoundary?: 'us-only' | 'eu-only' | 'global' | 'airgapped';
|
|
61
|
+
services?: ServiceEndpoint[];
|
|
62
|
+
}): AgentDID;
|
|
63
|
+
/** Issue a Verifiable Credential to an agent */
|
|
64
|
+
issueCredential(agentDid: string, credential: {
|
|
65
|
+
framework: AgentCredentialSubject['framework'];
|
|
66
|
+
certifiedControls: string[];
|
|
67
|
+
toolTierAccess: ('read' | 'write' | 'destructive')[];
|
|
68
|
+
tenantScope: string[];
|
|
69
|
+
sovereignBoundary: AgentCredentialSubject['sovereignBoundary'];
|
|
70
|
+
validDays?: number;
|
|
71
|
+
}): VerifiableCredential;
|
|
72
|
+
/** Verify an agent has a valid credential for a framework */
|
|
73
|
+
verifyCredential(agentDid: string, framework: AgentCredentialSubject['framework']): {
|
|
74
|
+
valid: boolean;
|
|
75
|
+
reason: string;
|
|
76
|
+
credential?: VerifiableCredential;
|
|
77
|
+
};
|
|
78
|
+
/** Check if agent is authorized for a tool tier */
|
|
79
|
+
authorizeToolAccess(agentDid: string, tier: 'read' | 'write' | 'destructive'): {
|
|
80
|
+
authorized: boolean;
|
|
81
|
+
reason: string;
|
|
82
|
+
};
|
|
83
|
+
/** Revoke an agent DID (propagates immediately) */
|
|
84
|
+
revokeDID(agentDid: string): {
|
|
85
|
+
ok: boolean;
|
|
86
|
+
reason: string;
|
|
87
|
+
};
|
|
88
|
+
/** Suspend an agent DID (temporary) */
|
|
89
|
+
suspendDID(agentDid: string): {
|
|
90
|
+
ok: boolean;
|
|
91
|
+
reason: string;
|
|
92
|
+
};
|
|
93
|
+
/** Reinstate a suspended agent DID */
|
|
94
|
+
reinstateDID(agentDid: string): {
|
|
95
|
+
ok: boolean;
|
|
96
|
+
reason: string;
|
|
97
|
+
};
|
|
98
|
+
/** Update agent risk score based on behavioral signals */
|
|
99
|
+
updateRiskScore(agentDid: string, score: number, signals: string[]): void;
|
|
100
|
+
/** Get agent by DID */
|
|
101
|
+
getAgent(agentDid: string): AgentDID | undefined;
|
|
102
|
+
/** List all active agents */
|
|
103
|
+
listActiveAgents(): AgentDID[];
|
|
104
|
+
/** List all agents (including revoked/suspended) */
|
|
105
|
+
listAllAgents(): AgentDID[];
|
|
106
|
+
/** Get registry statistics */
|
|
107
|
+
getStats(): {
|
|
108
|
+
total: number;
|
|
109
|
+
active: number;
|
|
110
|
+
suspended: number;
|
|
111
|
+
revoked: number;
|
|
112
|
+
avgRiskScore: number;
|
|
113
|
+
};
|
|
114
|
+
/** Sign an attestation with an agent's DID */
|
|
115
|
+
signAttestation(agentDid: string, payload: Record<string, unknown>): {
|
|
116
|
+
attestation: string;
|
|
117
|
+
agentDid: string;
|
|
118
|
+
timestamp: string;
|
|
119
|
+
signatureHash: string;
|
|
120
|
+
};
|
|
121
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @grc-claw/agent-identity
|
|
3
|
+
* DID-based Agent Identity Fabric with Verifiable Credentials
|
|
4
|
+
*
|
|
5
|
+
* Provides cryptographic identity for every AI agent in the GRC_Claw ecosystem.
|
|
6
|
+
* Every agent gets a did:grc:<uuid> Decentralized Identifier bound to
|
|
7
|
+
* Verifiable Credentials encoding framework certifications, tool tier access,
|
|
8
|
+
* and tenant scope.
|
|
9
|
+
*/
|
|
10
|
+
import * as crypto from 'crypto';
|
|
11
|
+
// ─── Identity Manager ────────────────────────────────────────────────
|
|
12
|
+
export class AgentIdentityManager {
|
|
13
|
+
registry = {
|
|
14
|
+
agents: new Map(),
|
|
15
|
+
revokedDids: new Set(),
|
|
16
|
+
};
|
|
17
|
+
/** Generate a new DID for an agent */
|
|
18
|
+
createAgentDID(opts) {
|
|
19
|
+
const uuid = crypto.randomUUID();
|
|
20
|
+
const did = `did:grc:${uuid}`;
|
|
21
|
+
const keyPair = crypto.generateKeyPairSync('ed25519');
|
|
22
|
+
const pubKeyHex = keyPair.publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
|
|
23
|
+
const verificationMethod = {
|
|
24
|
+
id: `${did}#key-1`,
|
|
25
|
+
type: 'Ed25519VerificationKey2020',
|
|
26
|
+
controller: did,
|
|
27
|
+
publicKeyHex: pubKeyHex,
|
|
28
|
+
};
|
|
29
|
+
const agentDid = {
|
|
30
|
+
'@context': [
|
|
31
|
+
'https://www.w3.org/ns/did/v1',
|
|
32
|
+
'https://w3id.org/security/suites/ed25519-2020/v1',
|
|
33
|
+
'https://grc-claw.a2zsoc.com/ns/agent-identity/v1',
|
|
34
|
+
],
|
|
35
|
+
id: did,
|
|
36
|
+
controller: opts.controller,
|
|
37
|
+
created: new Date().toISOString(),
|
|
38
|
+
updated: new Date().toISOString(),
|
|
39
|
+
verificationMethod: [verificationMethod],
|
|
40
|
+
authentication: [verificationMethod.id],
|
|
41
|
+
service: opts.services ?? [],
|
|
42
|
+
credentials: [],
|
|
43
|
+
status: 'active',
|
|
44
|
+
riskScore: 0,
|
|
45
|
+
metadata: {
|
|
46
|
+
tenantScope: opts.tenantScope,
|
|
47
|
+
sovereignBoundary: opts.sovereignBoundary ?? 'global',
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
this.registry.agents.set(did, agentDid);
|
|
51
|
+
return agentDid;
|
|
52
|
+
}
|
|
53
|
+
/** Issue a Verifiable Credential to an agent */
|
|
54
|
+
issueCredential(agentDid, credential) {
|
|
55
|
+
const agent = this.registry.agents.get(agentDid);
|
|
56
|
+
if (!agent)
|
|
57
|
+
throw new Error(`Agent DID not found: ${agentDid}`);
|
|
58
|
+
if (agent.status === 'revoked')
|
|
59
|
+
throw new Error(`Agent DID revoked: ${agentDid}`);
|
|
60
|
+
const now = new Date();
|
|
61
|
+
const expiry = new Date(now.getTime() + (credential.validDays ?? 365) * 86400000);
|
|
62
|
+
const credentialSubject = {
|
|
63
|
+
id: agentDid,
|
|
64
|
+
framework: credential.framework,
|
|
65
|
+
certifiedControls: credential.certifiedControls,
|
|
66
|
+
toolTierAccess: credential.toolTierAccess,
|
|
67
|
+
tenantScope: credential.tenantScope,
|
|
68
|
+
sovereignBoundary: credential.sovereignBoundary,
|
|
69
|
+
};
|
|
70
|
+
const proofPayload = JSON.stringify(credentialSubject);
|
|
71
|
+
const proofHash = crypto.createHash('sha256').update(proofPayload).digest('hex');
|
|
72
|
+
const vc = {
|
|
73
|
+
'@context': [
|
|
74
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
75
|
+
'https://grc-claw.a2zsoc.com/ns/compliance-credential/v1',
|
|
76
|
+
],
|
|
77
|
+
type: ['VerifiableCredential', 'ComplianceCertification'],
|
|
78
|
+
issuer: agent.controller,
|
|
79
|
+
issuanceDate: now.toISOString(),
|
|
80
|
+
expirationDate: expiry.toISOString(),
|
|
81
|
+
credentialSubject,
|
|
82
|
+
proof: {
|
|
83
|
+
type: 'Ed25519Signature2020',
|
|
84
|
+
created: now.toISOString(),
|
|
85
|
+
verificationMethod: agent.verificationMethod[0]?.id ?? `${agentDid}#key-1`,
|
|
86
|
+
proofPurpose: 'assertionMethod',
|
|
87
|
+
proofValue: `grc_vc_proof_${proofHash.substring(0, 32)}`,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
agent.credentials.push(vc);
|
|
91
|
+
agent.updated = now.toISOString();
|
|
92
|
+
return vc;
|
|
93
|
+
}
|
|
94
|
+
/** Verify an agent has a valid credential for a framework */
|
|
95
|
+
verifyCredential(agentDid, framework) {
|
|
96
|
+
const agent = this.registry.agents.get(agentDid);
|
|
97
|
+
if (!agent)
|
|
98
|
+
return { valid: false, reason: 'agent_not_found' };
|
|
99
|
+
if (agent.status === 'revoked')
|
|
100
|
+
return { valid: false, reason: 'agent_revoked' };
|
|
101
|
+
if (agent.status === 'suspended')
|
|
102
|
+
return { valid: false, reason: 'agent_suspended' };
|
|
103
|
+
const now = new Date();
|
|
104
|
+
const vc = agent.credentials.find((c) => c.credentialSubject.framework === framework &&
|
|
105
|
+
new Date(c.expirationDate) > now);
|
|
106
|
+
if (!vc)
|
|
107
|
+
return { valid: false, reason: `no_valid_credential_for_${framework}` };
|
|
108
|
+
return { valid: true, reason: 'credential_valid', credential: vc };
|
|
109
|
+
}
|
|
110
|
+
/** Check if agent is authorized for a tool tier */
|
|
111
|
+
authorizeToolAccess(agentDid, tier) {
|
|
112
|
+
const agent = this.registry.agents.get(agentDid);
|
|
113
|
+
if (!agent)
|
|
114
|
+
return { authorized: false, reason: 'agent_not_found' };
|
|
115
|
+
if (agent.status !== 'active')
|
|
116
|
+
return { authorized: false, reason: `agent_${agent.status}` };
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const validCreds = agent.credentials.filter((c) => new Date(c.expirationDate) > now);
|
|
119
|
+
const hasAccess = validCreds.some((c) => c.credentialSubject.toolTierAccess.includes(tier));
|
|
120
|
+
return hasAccess
|
|
121
|
+
? { authorized: true, reason: 'tool_access_granted' }
|
|
122
|
+
: { authorized: false, reason: `no_credential_grants_${tier}_access` };
|
|
123
|
+
}
|
|
124
|
+
/** Revoke an agent DID (propagates immediately) */
|
|
125
|
+
revokeDID(agentDid) {
|
|
126
|
+
const agent = this.registry.agents.get(agentDid);
|
|
127
|
+
if (!agent)
|
|
128
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
129
|
+
agent.status = 'revoked';
|
|
130
|
+
agent.updated = new Date().toISOString();
|
|
131
|
+
this.registry.revokedDids.add(agentDid);
|
|
132
|
+
return { ok: true, reason: 'did_revoked' };
|
|
133
|
+
}
|
|
134
|
+
/** Suspend an agent DID (temporary) */
|
|
135
|
+
suspendDID(agentDid) {
|
|
136
|
+
const agent = this.registry.agents.get(agentDid);
|
|
137
|
+
if (!agent)
|
|
138
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
139
|
+
agent.status = 'suspended';
|
|
140
|
+
agent.updated = new Date().toISOString();
|
|
141
|
+
return { ok: true, reason: 'did_suspended' };
|
|
142
|
+
}
|
|
143
|
+
/** Reinstate a suspended agent DID */
|
|
144
|
+
reinstateDID(agentDid) {
|
|
145
|
+
const agent = this.registry.agents.get(agentDid);
|
|
146
|
+
if (!agent)
|
|
147
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
148
|
+
if (agent.status === 'revoked')
|
|
149
|
+
return { ok: false, reason: 'cannot_reinstate_revoked_did' };
|
|
150
|
+
agent.status = 'active';
|
|
151
|
+
agent.updated = new Date().toISOString();
|
|
152
|
+
return { ok: true, reason: 'did_reinstated' };
|
|
153
|
+
}
|
|
154
|
+
/** Update agent risk score based on behavioral signals */
|
|
155
|
+
updateRiskScore(agentDid, score, signals) {
|
|
156
|
+
const agent = this.registry.agents.get(agentDid);
|
|
157
|
+
if (!agent)
|
|
158
|
+
return;
|
|
159
|
+
agent.riskScore = Math.max(0, Math.min(100, score));
|
|
160
|
+
agent.metadata.lastRiskSignals = signals;
|
|
161
|
+
agent.metadata.lastRiskUpdate = new Date().toISOString();
|
|
162
|
+
agent.updated = new Date().toISOString();
|
|
163
|
+
// Auto-suspend agents with critical risk score
|
|
164
|
+
if (score >= 90) {
|
|
165
|
+
agent.status = 'suspended';
|
|
166
|
+
agent.metadata.autoSuspendReason = `risk_score_${score}_exceeded_threshold`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** Get agent by DID */
|
|
170
|
+
getAgent(agentDid) {
|
|
171
|
+
return this.registry.agents.get(agentDid);
|
|
172
|
+
}
|
|
173
|
+
/** List all active agents */
|
|
174
|
+
listActiveAgents() {
|
|
175
|
+
return Array.from(this.registry.agents.values()).filter((a) => a.status === 'active');
|
|
176
|
+
}
|
|
177
|
+
/** List all agents (including revoked/suspended) */
|
|
178
|
+
listAllAgents() {
|
|
179
|
+
return Array.from(this.registry.agents.values());
|
|
180
|
+
}
|
|
181
|
+
/** Get registry statistics */
|
|
182
|
+
getStats() {
|
|
183
|
+
const all = Array.from(this.registry.agents.values());
|
|
184
|
+
const active = all.filter((a) => a.status === 'active');
|
|
185
|
+
const avgRisk = all.length > 0
|
|
186
|
+
? all.reduce((sum, a) => sum + a.riskScore, 0) / all.length
|
|
187
|
+
: 0;
|
|
188
|
+
return {
|
|
189
|
+
total: all.length,
|
|
190
|
+
active: active.length,
|
|
191
|
+
suspended: all.filter((a) => a.status === 'suspended').length,
|
|
192
|
+
revoked: all.filter((a) => a.status === 'revoked').length,
|
|
193
|
+
avgRiskScore: Math.round(avgRisk * 100) / 100,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Sign an attestation with an agent's DID */
|
|
197
|
+
signAttestation(agentDid, payload) {
|
|
198
|
+
const agent = this.registry.agents.get(agentDid);
|
|
199
|
+
if (!agent || agent.status !== 'active') {
|
|
200
|
+
throw new Error(`Cannot sign: agent ${agentDid} is ${agent?.status ?? 'not found'}`);
|
|
201
|
+
}
|
|
202
|
+
const attestationPayload = JSON.stringify({ agentDid, payload, timestamp: new Date().toISOString() });
|
|
203
|
+
const hash = crypto.createHash('sha256').update(attestationPayload).digest('hex');
|
|
204
|
+
return {
|
|
205
|
+
attestation: attestationPayload,
|
|
206
|
+
agentDid,
|
|
207
|
+
timestamp: new Date().toISOString(),
|
|
208
|
+
signatureHash: `did_attestation_sig_${hash.substring(0, 32)}`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grc-claw/agent-identity",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "DID-based agent identity fabric with Verifiable Credentials for GRC_Claw",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"test": "node --test dist/**/*.test.js 2>/dev/null || true"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|