@grc-claw/agent-identity 0.8.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 +133 -0
- package/dist/index.js +344 -0
- package/package.json +29 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export interface IdentityDatabase {
|
|
2
|
+
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
|
|
3
|
+
rows: T[];
|
|
4
|
+
rowCount: number;
|
|
5
|
+
}>;
|
|
6
|
+
execute(sql: string, params?: unknown[]): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface VerificationMethod {
|
|
9
|
+
id: string;
|
|
10
|
+
type: 'Ed25519VerificationKey2020' | 'X25519KeyAgreementKey2020';
|
|
11
|
+
controller: string;
|
|
12
|
+
publicKeyHex: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ServiceEndpoint {
|
|
15
|
+
id: string;
|
|
16
|
+
type: 'GRCGateway' | 'EvidenceVault' | 'SIEMIngest' | 'SOAREngine';
|
|
17
|
+
serviceEndpoint: string;
|
|
18
|
+
}
|
|
19
|
+
export interface VerifiableCredential {
|
|
20
|
+
'@context': string[];
|
|
21
|
+
type: string[];
|
|
22
|
+
issuer: string;
|
|
23
|
+
issuanceDate: string;
|
|
24
|
+
expirationDate: string;
|
|
25
|
+
credentialSubject: AgentCredentialSubject;
|
|
26
|
+
proof: CredentialProof;
|
|
27
|
+
}
|
|
28
|
+
export interface AgentCredentialSubject {
|
|
29
|
+
id: string;
|
|
30
|
+
framework: 'iso27001' | 'soc2' | 'cmmc' | 'iso42001' | 'nist_csf' | 'gdpr' | 'hipaa' | 'pci_dss';
|
|
31
|
+
certifiedControls: string[];
|
|
32
|
+
toolTierAccess: ('read' | 'write' | 'destructive')[];
|
|
33
|
+
tenantScope: string[];
|
|
34
|
+
sovereignBoundary: 'us-only' | 'eu-only' | 'global' | 'airgapped';
|
|
35
|
+
}
|
|
36
|
+
export interface CredentialProof {
|
|
37
|
+
type: 'Ed25519Signature2020';
|
|
38
|
+
created: string;
|
|
39
|
+
verificationMethod: string;
|
|
40
|
+
proofPurpose: 'assertionMethod';
|
|
41
|
+
proofValue: string;
|
|
42
|
+
}
|
|
43
|
+
export interface AgentDID {
|
|
44
|
+
'@context': string[];
|
|
45
|
+
id: string;
|
|
46
|
+
controller: string;
|
|
47
|
+
created: string;
|
|
48
|
+
updated: string;
|
|
49
|
+
verificationMethod: VerificationMethod[];
|
|
50
|
+
authentication: string[];
|
|
51
|
+
service: ServiceEndpoint[];
|
|
52
|
+
credentials: VerifiableCredential[];
|
|
53
|
+
status: 'active' | 'revoked' | 'suspended';
|
|
54
|
+
riskScore: number;
|
|
55
|
+
metadata: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
export interface AgentIdentityRegistry {
|
|
58
|
+
agents: Map<string, AgentDID>;
|
|
59
|
+
revokedDids: Set<string>;
|
|
60
|
+
}
|
|
61
|
+
export declare class AgentIdentityManager {
|
|
62
|
+
private registry;
|
|
63
|
+
private db?;
|
|
64
|
+
constructor(database?: IdentityDatabase);
|
|
65
|
+
setDatabase(database?: IdentityDatabase): void;
|
|
66
|
+
initializeDatabase(): Promise<void>;
|
|
67
|
+
loadFromDatabase(): Promise<void>;
|
|
68
|
+
/** Generate a new DID for an agent */
|
|
69
|
+
createAgentDID(opts: {
|
|
70
|
+
controller: string;
|
|
71
|
+
tenantScope: string[];
|
|
72
|
+
sovereignBoundary?: 'us-only' | 'eu-only' | 'global' | 'airgapped';
|
|
73
|
+
services?: ServiceEndpoint[];
|
|
74
|
+
}): Promise<AgentDID>;
|
|
75
|
+
/** Issue a Verifiable Credential to an agent */
|
|
76
|
+
issueCredential(agentDid: string, credential: {
|
|
77
|
+
framework: AgentCredentialSubject['framework'];
|
|
78
|
+
certifiedControls: string[];
|
|
79
|
+
toolTierAccess: ('read' | 'write' | 'destructive')[];
|
|
80
|
+
tenantScope: string[];
|
|
81
|
+
sovereignBoundary: AgentCredentialSubject['sovereignBoundary'];
|
|
82
|
+
validDays?: number;
|
|
83
|
+
}): Promise<VerifiableCredential>;
|
|
84
|
+
/** Verify an agent has a valid credential for a framework */
|
|
85
|
+
verifyCredential(agentDid: string, framework: AgentCredentialSubject['framework']): {
|
|
86
|
+
valid: boolean;
|
|
87
|
+
reason: string;
|
|
88
|
+
credential?: VerifiableCredential;
|
|
89
|
+
};
|
|
90
|
+
/** Check if agent is authorized for a tool tier */
|
|
91
|
+
authorizeToolAccess(agentDid: string, tier: 'read' | 'write' | 'destructive'): {
|
|
92
|
+
authorized: boolean;
|
|
93
|
+
reason: string;
|
|
94
|
+
};
|
|
95
|
+
/** Revoke an agent DID (propagates immediately) */
|
|
96
|
+
revokeDID(agentDid: string): Promise<{
|
|
97
|
+
ok: boolean;
|
|
98
|
+
reason: string;
|
|
99
|
+
}>;
|
|
100
|
+
/** Suspend an agent DID (temporary) */
|
|
101
|
+
suspendDID(agentDid: string): {
|
|
102
|
+
ok: boolean;
|
|
103
|
+
reason: string;
|
|
104
|
+
};
|
|
105
|
+
/** Reinstate a suspended agent DID */
|
|
106
|
+
reinstateDID(agentDid: string): {
|
|
107
|
+
ok: boolean;
|
|
108
|
+
reason: string;
|
|
109
|
+
};
|
|
110
|
+
/** Update agent risk score based on behavioral signals */
|
|
111
|
+
updateRiskScore(agentDid: string, score: number, signals: string[]): void;
|
|
112
|
+
/** Get agent by DID */
|
|
113
|
+
getAgent(agentDid: string): AgentDID | undefined;
|
|
114
|
+
/** List all active agents */
|
|
115
|
+
listActiveAgents(): AgentDID[];
|
|
116
|
+
/** List all agents (including revoked/suspended) */
|
|
117
|
+
listAllAgents(): AgentDID[];
|
|
118
|
+
/** Get registry statistics */
|
|
119
|
+
getStats(): {
|
|
120
|
+
total: number;
|
|
121
|
+
active: number;
|
|
122
|
+
suspended: number;
|
|
123
|
+
revoked: number;
|
|
124
|
+
avgRiskScore: number;
|
|
125
|
+
};
|
|
126
|
+
/** Sign an attestation with an agent's DID */
|
|
127
|
+
signAttestation(agentDid: string, payload: Record<string, unknown>): {
|
|
128
|
+
attestation: string;
|
|
129
|
+
agentDid: string;
|
|
130
|
+
timestamp: string;
|
|
131
|
+
signatureHash: string;
|
|
132
|
+
};
|
|
133
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
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
|
+
db;
|
|
18
|
+
constructor(database) {
|
|
19
|
+
this.db = database;
|
|
20
|
+
}
|
|
21
|
+
setDatabase(database) {
|
|
22
|
+
this.db = database;
|
|
23
|
+
}
|
|
24
|
+
async initializeDatabase() {
|
|
25
|
+
if (!this.db)
|
|
26
|
+
return;
|
|
27
|
+
await this.db.execute(`
|
|
28
|
+
CREATE TABLE IF NOT EXISTS agent_did_registry (
|
|
29
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
30
|
+
did VARCHAR(500) NOT NULL UNIQUE,
|
|
31
|
+
public_key TEXT NOT NULL,
|
|
32
|
+
private_key TEXT NOT NULL,
|
|
33
|
+
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
|
34
|
+
risk_score DECIMAL(5,2) NOT NULL DEFAULT 0,
|
|
35
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
36
|
+
)
|
|
37
|
+
`);
|
|
38
|
+
await this.db.execute(`
|
|
39
|
+
CREATE TABLE IF NOT EXISTS agent_credentials (
|
|
40
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
41
|
+
holder_did VARCHAR(500) NOT NULL,
|
|
42
|
+
issuer_did VARCHAR(500) NOT NULL,
|
|
43
|
+
type VARCHAR(100) NOT NULL,
|
|
44
|
+
claims JSONB NOT NULL DEFAULT '{}',
|
|
45
|
+
proof JSONB NOT NULL DEFAULT '{}',
|
|
46
|
+
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
47
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
48
|
+
revoked BOOLEAN NOT NULL DEFAULT false
|
|
49
|
+
)
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
async loadFromDatabase() {
|
|
53
|
+
if (!this.db)
|
|
54
|
+
return;
|
|
55
|
+
try {
|
|
56
|
+
const { rows: agentRows } = await this.db.query(`SELECT * FROM agent_did_registry`);
|
|
57
|
+
for (const row of agentRows) {
|
|
58
|
+
const keyPair = crypto.generateKeyPairSync('ed25519');
|
|
59
|
+
const verificationMethod = {
|
|
60
|
+
id: `${row.did}#key-1`,
|
|
61
|
+
type: 'Ed25519VerificationKey2020',
|
|
62
|
+
controller: row.did,
|
|
63
|
+
publicKeyHex: row.public_key,
|
|
64
|
+
};
|
|
65
|
+
const agentDid = {
|
|
66
|
+
'@context': [
|
|
67
|
+
'https://www.w3.org/ns/did/v1',
|
|
68
|
+
'https://w3id.org/security/suites/ed25519-2020/v1',
|
|
69
|
+
'https://grc-claw.a2zsoc.com/ns/agent-identity/v1',
|
|
70
|
+
],
|
|
71
|
+
id: row.did,
|
|
72
|
+
controller: '',
|
|
73
|
+
created: row.created_at,
|
|
74
|
+
updated: row.created_at,
|
|
75
|
+
verificationMethod: [verificationMethod],
|
|
76
|
+
authentication: [verificationMethod.id],
|
|
77
|
+
service: [],
|
|
78
|
+
credentials: [],
|
|
79
|
+
status: row.status,
|
|
80
|
+
riskScore: row.risk_score,
|
|
81
|
+
metadata: {},
|
|
82
|
+
};
|
|
83
|
+
if (row.status === 'revoked') {
|
|
84
|
+
this.registry.revokedDids.add(row.did);
|
|
85
|
+
}
|
|
86
|
+
this.registry.agents.set(row.did, agentDid);
|
|
87
|
+
}
|
|
88
|
+
const { rows: credRows } = await this.db.query(`SELECT * FROM agent_credentials`);
|
|
89
|
+
for (const row of credRows) {
|
|
90
|
+
const agent = this.registry.agents.get(row.holder_did);
|
|
91
|
+
if (agent) {
|
|
92
|
+
const vc = {
|
|
93
|
+
'@context': [
|
|
94
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
95
|
+
'https://grc-claw.a2zsoc.com/ns/compliance-credential/v1',
|
|
96
|
+
],
|
|
97
|
+
type: ['VerifiableCredential', 'ComplianceCertification'],
|
|
98
|
+
issuer: row.issuer_did,
|
|
99
|
+
issuanceDate: row.issued_at,
|
|
100
|
+
expirationDate: row.expires_at,
|
|
101
|
+
credentialSubject: row.claims,
|
|
102
|
+
proof: row.proof,
|
|
103
|
+
};
|
|
104
|
+
agent.credentials.push(vc);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
console.log(`[AGENT-IDENTITY] Loaded ${agentRows.length} agents, ${credRows.length} credentials from database`);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error('[AGENT-IDENTITY] Failed to load from database:', err instanceof Error ? err.message : err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Generate a new DID for an agent */
|
|
114
|
+
async createAgentDID(opts) {
|
|
115
|
+
const uuid = crypto.randomUUID();
|
|
116
|
+
const did = `did:grc:${uuid}`;
|
|
117
|
+
const keyPair = crypto.generateKeyPairSync('ed25519');
|
|
118
|
+
const pubKeyHex = keyPair.publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
|
|
119
|
+
const privKeyHex = keyPair.privateKey.export({ type: 'pkcs8', format: 'der' }).toString('hex');
|
|
120
|
+
const verificationMethod = {
|
|
121
|
+
id: `${did}#key-1`,
|
|
122
|
+
type: 'Ed25519VerificationKey2020',
|
|
123
|
+
controller: did,
|
|
124
|
+
publicKeyHex: pubKeyHex,
|
|
125
|
+
};
|
|
126
|
+
const agentDid = {
|
|
127
|
+
'@context': [
|
|
128
|
+
'https://www.w3.org/ns/did/v1',
|
|
129
|
+
'https://w3id.org/security/suites/ed25519-2020/v1',
|
|
130
|
+
'https://grc-claw.a2zsoc.com/ns/agent-identity/v1',
|
|
131
|
+
],
|
|
132
|
+
id: did,
|
|
133
|
+
controller: opts.controller,
|
|
134
|
+
created: new Date().toISOString(),
|
|
135
|
+
updated: new Date().toISOString(),
|
|
136
|
+
verificationMethod: [verificationMethod],
|
|
137
|
+
authentication: [verificationMethod.id],
|
|
138
|
+
service: opts.services ?? [],
|
|
139
|
+
credentials: [],
|
|
140
|
+
status: 'active',
|
|
141
|
+
riskScore: 0,
|
|
142
|
+
metadata: {
|
|
143
|
+
tenantScope: opts.tenantScope,
|
|
144
|
+
sovereignBoundary: opts.sovereignBoundary ?? 'global',
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
this.registry.agents.set(did, agentDid);
|
|
148
|
+
if (this.db) {
|
|
149
|
+
try {
|
|
150
|
+
await this.db.execute(`INSERT INTO agent_did_registry (did, public_key, private_key, status, risk_score, created_at)
|
|
151
|
+
VALUES ($1, $2, $3, $4, $5, NOW())
|
|
152
|
+
ON CONFLICT (did) DO NOTHING`, [did, pubKeyHex, privKeyHex, 'active', 0]);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
console.error('[AGENT-IDENTITY] Failed to persist DID to database:', err instanceof Error ? err.message : err);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return agentDid;
|
|
159
|
+
}
|
|
160
|
+
/** Issue a Verifiable Credential to an agent */
|
|
161
|
+
async issueCredential(agentDid, credential) {
|
|
162
|
+
const agent = this.registry.agents.get(agentDid);
|
|
163
|
+
if (!agent)
|
|
164
|
+
throw new Error(`Agent DID not found: ${agentDid}`);
|
|
165
|
+
if (agent.status === 'revoked')
|
|
166
|
+
throw new Error(`Agent DID revoked: ${agentDid}`);
|
|
167
|
+
const now = new Date();
|
|
168
|
+
const expiry = new Date(now.getTime() + (credential.validDays ?? 365) * 86400000);
|
|
169
|
+
const credentialSubject = {
|
|
170
|
+
id: agentDid,
|
|
171
|
+
framework: credential.framework,
|
|
172
|
+
certifiedControls: credential.certifiedControls,
|
|
173
|
+
toolTierAccess: credential.toolTierAccess,
|
|
174
|
+
tenantScope: credential.tenantScope,
|
|
175
|
+
sovereignBoundary: credential.sovereignBoundary,
|
|
176
|
+
};
|
|
177
|
+
const proofPayload = JSON.stringify(credentialSubject);
|
|
178
|
+
const proofHash = crypto.createHash('sha256').update(proofPayload).digest('hex');
|
|
179
|
+
const vc = {
|
|
180
|
+
'@context': [
|
|
181
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
182
|
+
'https://grc-claw.a2zsoc.com/ns/compliance-credential/v1',
|
|
183
|
+
],
|
|
184
|
+
type: ['VerifiableCredential', 'ComplianceCertification'],
|
|
185
|
+
issuer: agent.controller,
|
|
186
|
+
issuanceDate: now.toISOString(),
|
|
187
|
+
expirationDate: expiry.toISOString(),
|
|
188
|
+
credentialSubject,
|
|
189
|
+
proof: {
|
|
190
|
+
type: 'Ed25519Signature2020',
|
|
191
|
+
created: now.toISOString(),
|
|
192
|
+
verificationMethod: agent.verificationMethod[0]?.id ?? `${agentDid}#key-1`,
|
|
193
|
+
proofPurpose: 'assertionMethod',
|
|
194
|
+
proofValue: `grc_vc_proof_${proofHash.substring(0, 32)}`,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
agent.credentials.push(vc);
|
|
198
|
+
agent.updated = now.toISOString();
|
|
199
|
+
if (this.db) {
|
|
200
|
+
try {
|
|
201
|
+
await this.db.execute(`INSERT INTO agent_credentials (holder_did, issuer_did, type, claims, proof, issued_at, expires_at, revoked)
|
|
202
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, false)`, [
|
|
203
|
+
agentDid,
|
|
204
|
+
agent.controller,
|
|
205
|
+
'ComplianceCertification',
|
|
206
|
+
JSON.stringify(credentialSubject),
|
|
207
|
+
JSON.stringify(vc.proof),
|
|
208
|
+
now.toISOString(),
|
|
209
|
+
expiry.toISOString(),
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
console.error('[AGENT-IDENTITY] Failed to persist credential to database:', err instanceof Error ? err.message : err);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return vc;
|
|
217
|
+
}
|
|
218
|
+
/** Verify an agent has a valid credential for a framework */
|
|
219
|
+
verifyCredential(agentDid, framework) {
|
|
220
|
+
const agent = this.registry.agents.get(agentDid);
|
|
221
|
+
if (!agent)
|
|
222
|
+
return { valid: false, reason: 'agent_not_found' };
|
|
223
|
+
if (agent.status === 'revoked')
|
|
224
|
+
return { valid: false, reason: 'agent_revoked' };
|
|
225
|
+
if (agent.status === 'suspended')
|
|
226
|
+
return { valid: false, reason: 'agent_suspended' };
|
|
227
|
+
const now = new Date();
|
|
228
|
+
const vc = agent.credentials.find((c) => c.credentialSubject.framework === framework &&
|
|
229
|
+
new Date(c.expirationDate) > now);
|
|
230
|
+
if (!vc)
|
|
231
|
+
return { valid: false, reason: `no_valid_credential_for_${framework}` };
|
|
232
|
+
return { valid: true, reason: 'credential_valid', credential: vc };
|
|
233
|
+
}
|
|
234
|
+
/** Check if agent is authorized for a tool tier */
|
|
235
|
+
authorizeToolAccess(agentDid, tier) {
|
|
236
|
+
const agent = this.registry.agents.get(agentDid);
|
|
237
|
+
if (!agent)
|
|
238
|
+
return { authorized: false, reason: 'agent_not_found' };
|
|
239
|
+
if (agent.status !== 'active')
|
|
240
|
+
return { authorized: false, reason: `agent_${agent.status}` };
|
|
241
|
+
const now = new Date();
|
|
242
|
+
const validCreds = agent.credentials.filter((c) => new Date(c.expirationDate) > now);
|
|
243
|
+
const hasAccess = validCreds.some((c) => c.credentialSubject.toolTierAccess.includes(tier));
|
|
244
|
+
return hasAccess
|
|
245
|
+
? { authorized: true, reason: 'tool_access_granted' }
|
|
246
|
+
: { authorized: false, reason: `no_credential_grants_${tier}_access` };
|
|
247
|
+
}
|
|
248
|
+
/** Revoke an agent DID (propagates immediately) */
|
|
249
|
+
async revokeDID(agentDid) {
|
|
250
|
+
const agent = this.registry.agents.get(agentDid);
|
|
251
|
+
if (!agent)
|
|
252
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
253
|
+
agent.status = 'revoked';
|
|
254
|
+
agent.updated = new Date().toISOString();
|
|
255
|
+
this.registry.revokedDids.add(agentDid);
|
|
256
|
+
if (this.db) {
|
|
257
|
+
try {
|
|
258
|
+
await this.db.execute(`UPDATE agent_did_registry SET status = 'revoked' WHERE did = $1`, [agentDid]);
|
|
259
|
+
await this.db.execute(`UPDATE agent_credentials SET revoked = true WHERE holder_did = $1`, [agentDid]);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
console.error('[AGENT-IDENTITY] Failed to persist revocation to database:', err instanceof Error ? err.message : err);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return { ok: true, reason: 'did_revoked' };
|
|
266
|
+
}
|
|
267
|
+
/** Suspend an agent DID (temporary) */
|
|
268
|
+
suspendDID(agentDid) {
|
|
269
|
+
const agent = this.registry.agents.get(agentDid);
|
|
270
|
+
if (!agent)
|
|
271
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
272
|
+
agent.status = 'suspended';
|
|
273
|
+
agent.updated = new Date().toISOString();
|
|
274
|
+
return { ok: true, reason: 'did_suspended' };
|
|
275
|
+
}
|
|
276
|
+
/** Reinstate a suspended agent DID */
|
|
277
|
+
reinstateDID(agentDid) {
|
|
278
|
+
const agent = this.registry.agents.get(agentDid);
|
|
279
|
+
if (!agent)
|
|
280
|
+
return { ok: false, reason: 'agent_not_found' };
|
|
281
|
+
if (agent.status === 'revoked')
|
|
282
|
+
return { ok: false, reason: 'cannot_reinstate_revoked_did' };
|
|
283
|
+
agent.status = 'active';
|
|
284
|
+
agent.updated = new Date().toISOString();
|
|
285
|
+
return { ok: true, reason: 'did_reinstated' };
|
|
286
|
+
}
|
|
287
|
+
/** Update agent risk score based on behavioral signals */
|
|
288
|
+
updateRiskScore(agentDid, score, signals) {
|
|
289
|
+
const agent = this.registry.agents.get(agentDid);
|
|
290
|
+
if (!agent)
|
|
291
|
+
return;
|
|
292
|
+
agent.riskScore = Math.max(0, Math.min(100, score));
|
|
293
|
+
agent.metadata.lastRiskSignals = signals;
|
|
294
|
+
agent.metadata.lastRiskUpdate = new Date().toISOString();
|
|
295
|
+
agent.updated = new Date().toISOString();
|
|
296
|
+
// Auto-suspend agents with critical risk score
|
|
297
|
+
if (score >= 90) {
|
|
298
|
+
agent.status = 'suspended';
|
|
299
|
+
agent.metadata.autoSuspendReason = `risk_score_${score}_exceeded_threshold`;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Get agent by DID */
|
|
303
|
+
getAgent(agentDid) {
|
|
304
|
+
return this.registry.agents.get(agentDid);
|
|
305
|
+
}
|
|
306
|
+
/** List all active agents */
|
|
307
|
+
listActiveAgents() {
|
|
308
|
+
return Array.from(this.registry.agents.values()).filter((a) => a.status === 'active');
|
|
309
|
+
}
|
|
310
|
+
/** List all agents (including revoked/suspended) */
|
|
311
|
+
listAllAgents() {
|
|
312
|
+
return Array.from(this.registry.agents.values());
|
|
313
|
+
}
|
|
314
|
+
/** Get registry statistics */
|
|
315
|
+
getStats() {
|
|
316
|
+
const all = Array.from(this.registry.agents.values());
|
|
317
|
+
const active = all.filter((a) => a.status === 'active');
|
|
318
|
+
const avgRisk = all.length > 0
|
|
319
|
+
? all.reduce((sum, a) => sum + a.riskScore, 0) / all.length
|
|
320
|
+
: 0;
|
|
321
|
+
return {
|
|
322
|
+
total: all.length,
|
|
323
|
+
active: active.length,
|
|
324
|
+
suspended: all.filter((a) => a.status === 'suspended').length,
|
|
325
|
+
revoked: all.filter((a) => a.status === 'revoked').length,
|
|
326
|
+
avgRiskScore: Math.round(avgRisk * 100) / 100,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/** Sign an attestation with an agent's DID */
|
|
330
|
+
signAttestation(agentDid, payload) {
|
|
331
|
+
const agent = this.registry.agents.get(agentDid);
|
|
332
|
+
if (!agent || agent.status !== 'active') {
|
|
333
|
+
throw new Error(`Cannot sign: agent ${agentDid} is ${agent?.status ?? 'not found'}`);
|
|
334
|
+
}
|
|
335
|
+
const attestationPayload = JSON.stringify({ agentDid, payload, timestamp: new Date().toISOString() });
|
|
336
|
+
const hash = crypto.createHash('sha256').update(attestationPayload).digest('hex');
|
|
337
|
+
return {
|
|
338
|
+
attestation: attestationPayload,
|
|
339
|
+
agentDid,
|
|
340
|
+
timestamp: new Date().toISOString(),
|
|
341
|
+
signatureHash: `did_attestation_sig_${hash.substring(0, 32)}`,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grc-claw/agent-identity",
|
|
3
|
+
"version": "0.8.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
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/AAH20/GRC_Claw"
|
|
28
|
+
}
|
|
29
|
+
}
|