@highway1/core 0.1.53 → 0.1.55
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/package.json +5 -18
- package/src/discovery/agent-card-encoder.ts +0 -119
- package/src/discovery/agent-card-schema.ts +0 -87
- package/src/discovery/agent-card-types.ts +0 -99
- package/src/discovery/agent-card.ts +0 -190
- package/src/discovery/bootstrap.ts +0 -63
- package/src/discovery/capability-matcher.ts +0 -167
- package/src/discovery/dht.ts +0 -310
- package/src/discovery/index.ts +0 -3
- package/src/discovery/relay-index.ts +0 -98
- package/src/discovery/search-index.ts +0 -247
- package/src/discovery/semantic-search.ts +0 -218
- package/src/identity/did.ts +0 -48
- package/src/identity/document.ts +0 -77
- package/src/identity/index.ts +0 -4
- package/src/identity/keys.ts +0 -79
- package/src/identity/signer.ts +0 -55
- package/src/index.ts +0 -39
- package/src/messaging/codec.ts +0 -47
- package/src/messaging/defense.ts +0 -236
- package/src/messaging/envelope.ts +0 -107
- package/src/messaging/index.ts +0 -8
- package/src/messaging/queue.ts +0 -181
- package/src/messaging/rate-limiter.ts +0 -85
- package/src/messaging/router.ts +0 -209
- package/src/messaging/storage.ts +0 -281
- package/src/messaging/types.ts +0 -149
- package/src/transport/connection.ts +0 -77
- package/src/transport/index.ts +0 -2
- package/src/transport/node.ts +0 -154
- package/src/transport/relay-client.ts +0 -437
- package/src/transport/relay-types.ts +0 -196
- package/src/trust/endorsement.ts +0 -167
- package/src/trust/index.ts +0 -194
- package/src/trust/interaction-history.ts +0 -155
- package/src/trust/sybil-defense.ts +0 -232
- package/src/trust/trust-score.ts +0 -136
- package/src/utils/errors.ts +0 -38
- package/src/utils/logger.ts +0 -48
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Index for Agent Discovery
|
|
3
|
-
*
|
|
4
|
-
* Maintains a local index of discovered agents for fast semantic search
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import lunr from 'lunr';
|
|
8
|
-
import Fuse from 'fuse.js';
|
|
9
|
-
import type { AgentCard } from './agent-card-types.js';
|
|
10
|
-
import { createLogger } from '../utils/logger.js';
|
|
11
|
-
|
|
12
|
-
const logger = createLogger('search-index');
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Semantic Query Interface
|
|
16
|
-
*/
|
|
17
|
-
export interface SemanticQuery {
|
|
18
|
-
text?: string; // Natural language: "translate Japanese"
|
|
19
|
-
capability?: string; // Structured: "translate"
|
|
20
|
-
filters?: {
|
|
21
|
-
language?: string;
|
|
22
|
-
minTrustScore?: number;
|
|
23
|
-
maxCost?: number;
|
|
24
|
-
tags?: string[];
|
|
25
|
-
};
|
|
26
|
-
limit?: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Search Result with Score
|
|
31
|
-
*/
|
|
32
|
-
export interface SearchResult {
|
|
33
|
-
card: AgentCard;
|
|
34
|
-
score: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Search Index Implementation
|
|
39
|
-
*/
|
|
40
|
-
export class SearchIndex {
|
|
41
|
-
private cards = new Map<string, AgentCard>();
|
|
42
|
-
private lunrIndex?: lunr.Index;
|
|
43
|
-
private fuse?: Fuse<AgentCard>;
|
|
44
|
-
private needsRebuild = false;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Add or update an Agent Card in the index
|
|
48
|
-
*/
|
|
49
|
-
indexAgentCard(card: AgentCard): void {
|
|
50
|
-
this.cards.set(card.did, card);
|
|
51
|
-
this.needsRebuild = true;
|
|
52
|
-
logger.debug('Indexed Agent Card', { did: card.did, capabilities: card.capabilities.length });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Remove an Agent Card from the index
|
|
57
|
-
*/
|
|
58
|
-
removeAgentCard(did: string): void {
|
|
59
|
-
this.cards.delete(did);
|
|
60
|
-
this.needsRebuild = true;
|
|
61
|
-
logger.debug('Removed Agent Card from index', { did });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Search for agents matching a query
|
|
66
|
-
*/
|
|
67
|
-
search(query: SemanticQuery): SearchResult[] {
|
|
68
|
-
if (this.needsRebuild) {
|
|
69
|
-
this.rebuild();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let results: SearchResult[] = [];
|
|
73
|
-
|
|
74
|
-
// Text search using Lunr
|
|
75
|
-
if (query.text && this.lunrIndex) {
|
|
76
|
-
results = this.searchByText(query.text);
|
|
77
|
-
}
|
|
78
|
-
// Capability search using Fuse
|
|
79
|
-
else if (query.capability && this.fuse) {
|
|
80
|
-
results = this.searchByCapability(query.capability);
|
|
81
|
-
}
|
|
82
|
-
// No query - return all
|
|
83
|
-
else {
|
|
84
|
-
results = Array.from(this.cards.values()).map(card => ({
|
|
85
|
-
card,
|
|
86
|
-
score: 1.0,
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Apply filters
|
|
91
|
-
if (query.filters) {
|
|
92
|
-
results = this.applyFilters(results, query.filters);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Sort by score (descending)
|
|
96
|
-
results.sort((a, b) => b.score - a.score);
|
|
97
|
-
|
|
98
|
-
// Apply limit
|
|
99
|
-
if (query.limit) {
|
|
100
|
-
results = results.slice(0, query.limit);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
logger.debug('Search completed', { query, results: results.length });
|
|
104
|
-
return results;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Get all indexed cards
|
|
109
|
-
*/
|
|
110
|
-
getAllCards(): AgentCard[] {
|
|
111
|
-
return Array.from(this.cards.values());
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Clear the index
|
|
116
|
-
*/
|
|
117
|
-
clear(): void {
|
|
118
|
-
this.cards.clear();
|
|
119
|
-
this.lunrIndex = undefined;
|
|
120
|
-
this.fuse = undefined;
|
|
121
|
-
this.needsRebuild = false;
|
|
122
|
-
logger.info('Search index cleared');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Get index size
|
|
127
|
-
*/
|
|
128
|
-
size(): number {
|
|
129
|
-
return this.cards.size;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Rebuild search indexes
|
|
134
|
-
*/
|
|
135
|
-
private rebuild(): void {
|
|
136
|
-
logger.info('Rebuilding search indexes', { cards: this.cards.size });
|
|
137
|
-
|
|
138
|
-
const cards = Array.from(this.cards.values());
|
|
139
|
-
|
|
140
|
-
// Build Lunr index for full-text search
|
|
141
|
-
this.lunrIndex = lunr(function (this: lunr.Builder) {
|
|
142
|
-
this.ref('did');
|
|
143
|
-
this.field('name', { boost: 10 });
|
|
144
|
-
this.field('description', { boost: 5 });
|
|
145
|
-
this.field('capabilities');
|
|
146
|
-
|
|
147
|
-
for (const card of cards) {
|
|
148
|
-
this.add({
|
|
149
|
-
did: card.did,
|
|
150
|
-
name: card.name,
|
|
151
|
-
description: card.description,
|
|
152
|
-
capabilities: card.capabilities
|
|
153
|
-
.map((cap: any) => `${cap.name} ${cap.description}`)
|
|
154
|
-
.join(' '),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Build Fuse index for fuzzy matching
|
|
160
|
-
this.fuse = new Fuse(Array.from(this.cards.values()), {
|
|
161
|
-
keys: [
|
|
162
|
-
{ name: 'name', weight: 0.3 },
|
|
163
|
-
{ name: 'description', weight: 0.2 },
|
|
164
|
-
{ name: 'capabilities.name', weight: 0.3 },
|
|
165
|
-
{ name: 'capabilities.description', weight: 0.2 },
|
|
166
|
-
],
|
|
167
|
-
threshold: 0.4,
|
|
168
|
-
includeScore: true,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
this.needsRebuild = false;
|
|
172
|
-
logger.info('Search indexes rebuilt');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Search by text using Lunr
|
|
177
|
-
*/
|
|
178
|
-
private searchByText(text: string): SearchResult[] {
|
|
179
|
-
if (!this.lunrIndex) return [];
|
|
180
|
-
|
|
181
|
-
const lunrResults = this.lunrIndex.search(text);
|
|
182
|
-
return lunrResults.map(result => ({
|
|
183
|
-
card: this.cards.get(result.ref)!,
|
|
184
|
-
score: result.score,
|
|
185
|
-
}));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Search by capability using Fuse
|
|
190
|
-
*/
|
|
191
|
-
private searchByCapability(capability: string): SearchResult[] {
|
|
192
|
-
if (!this.fuse) return [];
|
|
193
|
-
|
|
194
|
-
const fuseResults = this.fuse.search(capability);
|
|
195
|
-
return fuseResults.map(result => ({
|
|
196
|
-
card: result.item,
|
|
197
|
-
score: 1 - (result.score || 0), // Fuse score is distance, convert to similarity
|
|
198
|
-
}));
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Apply filters to search results
|
|
203
|
-
*/
|
|
204
|
-
private applyFilters(
|
|
205
|
-
results: SearchResult[],
|
|
206
|
-
filters: NonNullable<SemanticQuery['filters']>
|
|
207
|
-
): SearchResult[] {
|
|
208
|
-
return results.filter(result => {
|
|
209
|
-
const { card } = result;
|
|
210
|
-
|
|
211
|
-
// Trust score filter
|
|
212
|
-
if (filters.minTrustScore !== undefined && card.trust) {
|
|
213
|
-
const overallTrust =
|
|
214
|
-
card.trust.interactionScore * 0.4 +
|
|
215
|
-
Math.min(card.trust.endorsements / 10, 1) * 0.2 +
|
|
216
|
-
card.trust.completionRate * 0.2 +
|
|
217
|
-
card.trust.uptime * 0.2;
|
|
218
|
-
|
|
219
|
-
if (overallTrust < filters.minTrustScore) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Language filter (check capability metadata)
|
|
225
|
-
if (filters.language) {
|
|
226
|
-
const hasLanguage = card.capabilities.some(
|
|
227
|
-
cap => {
|
|
228
|
-
const metadata = cap.metadata as Record<string, any> | undefined;
|
|
229
|
-
if (!metadata) return false;
|
|
230
|
-
return metadata.language === filters.language ||
|
|
231
|
-
(Array.isArray(metadata.languages) && metadata.languages.includes(filters.language));
|
|
232
|
-
}
|
|
233
|
-
);
|
|
234
|
-
if (!hasLanguage) return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Tags filter
|
|
238
|
-
if (filters.tags && filters.tags.length > 0) {
|
|
239
|
-
const cardTags = Array.isArray(card.metadata?.tags) ? (card.metadata.tags as string[]) : [];
|
|
240
|
-
const hasAllTags = filters.tags.every(tag => cardTags.includes(tag));
|
|
241
|
-
if (!hasAllTags) return false;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return true;
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Search Engine
|
|
3
|
-
*
|
|
4
|
-
* Provides intelligent agent discovery through semantic search
|
|
5
|
-
* with local-first strategy and network fallback
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { AgentCard } from './agent-card-types.js';
|
|
9
|
-
import { SearchIndex } from './search-index.js';
|
|
10
|
-
import type { SemanticQuery, SearchResult } from './search-index.js';
|
|
11
|
-
import { CapabilityMatcher } from './capability-matcher.js';
|
|
12
|
-
import type { DHTOperations } from './dht.js';
|
|
13
|
-
import { createLogger } from '../utils/logger.js';
|
|
14
|
-
|
|
15
|
-
const logger = createLogger('semantic-search');
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Semantic Search Engine
|
|
19
|
-
*/
|
|
20
|
-
export class SemanticSearchEngine {
|
|
21
|
-
private index: SearchIndex;
|
|
22
|
-
private matcher: CapabilityMatcher;
|
|
23
|
-
|
|
24
|
-
constructor(private dht?: DHTOperations) {
|
|
25
|
-
this.index = new SearchIndex();
|
|
26
|
-
this.matcher = new CapabilityMatcher();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Main search interface
|
|
31
|
-
* Local-first with network fallback
|
|
32
|
-
*/
|
|
33
|
-
async search(query: SemanticQuery): Promise<AgentCard[]> {
|
|
34
|
-
logger.info('Searching for agents', { query });
|
|
35
|
-
|
|
36
|
-
// 1. Search local index
|
|
37
|
-
const localResults = this.index.search(query);
|
|
38
|
-
logger.debug('Local search results', { count: localResults.length });
|
|
39
|
-
|
|
40
|
-
// 2. If insufficient results, query network
|
|
41
|
-
const limit = query.limit || 10;
|
|
42
|
-
if (localResults.length < limit && this.dht) {
|
|
43
|
-
logger.debug('Insufficient local results, querying network');
|
|
44
|
-
const networkResults = await this.searchNetwork(query);
|
|
45
|
-
return this.mergeResults(localResults, networkResults, limit);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return localResults.map(r => r.card);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Index an Agent Card for local search
|
|
53
|
-
*/
|
|
54
|
-
indexAgentCard(card: AgentCard): void {
|
|
55
|
-
this.index.indexAgentCard(card);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Remove an Agent Card from local index
|
|
60
|
-
*/
|
|
61
|
-
removeAgentCard(did: string): void {
|
|
62
|
-
this.index.removeAgentCard(did);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Get all indexed cards
|
|
67
|
-
*/
|
|
68
|
-
getAllIndexedCards(): AgentCard[] {
|
|
69
|
-
return this.index.getAllCards();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Clear local index
|
|
74
|
-
*/
|
|
75
|
-
clearIndex(): void {
|
|
76
|
-
this.index.clear();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Get index size
|
|
81
|
-
*/
|
|
82
|
-
getIndexSize(): number {
|
|
83
|
-
return this.index.size();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Search network via DHT
|
|
88
|
-
*/
|
|
89
|
-
private async searchNetwork(query: SemanticQuery): Promise<SearchResult[]> {
|
|
90
|
-
if (!this.dht) {
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
// For now, use capability-based DHT query
|
|
96
|
-
// In future, implement distributed semantic search overlay
|
|
97
|
-
const capability = query.capability || this.extractPrimaryCapability(query.text);
|
|
98
|
-
|
|
99
|
-
if (!capability) {
|
|
100
|
-
logger.debug('No capability extracted from query, skipping network search');
|
|
101
|
-
return [];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const cards = await this.dht.queryByCapability(capability);
|
|
105
|
-
logger.debug('Network search results', { count: cards.length });
|
|
106
|
-
|
|
107
|
-
// Score network results
|
|
108
|
-
return cards.map(card => {
|
|
109
|
-
const score = this.scoreCard(card, query);
|
|
110
|
-
return { card, score };
|
|
111
|
-
});
|
|
112
|
-
} catch (error) {
|
|
113
|
-
logger.error('Network search failed', { error });
|
|
114
|
-
return [];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Merge local and network results, removing duplicates
|
|
120
|
-
*/
|
|
121
|
-
private mergeResults(
|
|
122
|
-
local: SearchResult[],
|
|
123
|
-
network: SearchResult[],
|
|
124
|
-
limit: number
|
|
125
|
-
): AgentCard[] {
|
|
126
|
-
const seen = new Set<string>();
|
|
127
|
-
const merged: SearchResult[] = [];
|
|
128
|
-
|
|
129
|
-
// Add local results first (they're already scored and sorted)
|
|
130
|
-
for (const result of local) {
|
|
131
|
-
if (!seen.has(result.card.did)) {
|
|
132
|
-
seen.add(result.card.did);
|
|
133
|
-
merged.push(result);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Add network results
|
|
138
|
-
for (const result of network) {
|
|
139
|
-
if (!seen.has(result.card.did)) {
|
|
140
|
-
seen.add(result.card.did);
|
|
141
|
-
merged.push(result);
|
|
142
|
-
|
|
143
|
-
// Also index for future searches
|
|
144
|
-
this.index.indexAgentCard(result.card);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Sort by score and apply limit
|
|
149
|
-
merged.sort((a, b) => b.score - a.score);
|
|
150
|
-
return merged.slice(0, limit).map(r => r.card);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Score a card against a query
|
|
155
|
-
*/
|
|
156
|
-
private scoreCard(card: AgentCard, query: SemanticQuery): number {
|
|
157
|
-
let totalScore = 0;
|
|
158
|
-
let count = 0;
|
|
159
|
-
|
|
160
|
-
for (const capability of card.capabilities) {
|
|
161
|
-
const score = this.matcher.match(query, capability);
|
|
162
|
-
if (score > 0) {
|
|
163
|
-
totalScore += score;
|
|
164
|
-
count++;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Average score across matching capabilities
|
|
169
|
-
const avgScore = count > 0 ? totalScore / count : 0;
|
|
170
|
-
|
|
171
|
-
// Boost by trust score if available
|
|
172
|
-
if (card.trust) {
|
|
173
|
-
const trustBoost = card.trust.interactionScore * 0.2;
|
|
174
|
-
return Math.min(avgScore + trustBoost, 1.0);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return avgScore;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Extract primary capability from natural language text
|
|
182
|
-
*/
|
|
183
|
-
private extractPrimaryCapability(text?: string): string | undefined {
|
|
184
|
-
if (!text) return undefined;
|
|
185
|
-
|
|
186
|
-
const keywords = this.matcher.extractKeywords(text);
|
|
187
|
-
|
|
188
|
-
// Common capability keywords
|
|
189
|
-
const capabilityKeywords = [
|
|
190
|
-
'translate', 'translation',
|
|
191
|
-
'review', 'code',
|
|
192
|
-
'analyze', 'analysis',
|
|
193
|
-
'generate', 'generation',
|
|
194
|
-
'search', 'query',
|
|
195
|
-
'compute', 'calculation',
|
|
196
|
-
'store', 'storage',
|
|
197
|
-
'message', 'messaging',
|
|
198
|
-
'auth', 'authentication',
|
|
199
|
-
];
|
|
200
|
-
|
|
201
|
-
// Find first matching keyword
|
|
202
|
-
for (const keyword of keywords) {
|
|
203
|
-
if (capabilityKeywords.includes(keyword)) {
|
|
204
|
-
return keyword;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Return first keyword as fallback
|
|
209
|
-
return keywords[0];
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Create a semantic search engine
|
|
215
|
-
*/
|
|
216
|
-
export function createSemanticSearch(dht?: DHTOperations): SemanticSearchEngine {
|
|
217
|
-
return new SemanticSearchEngine(dht);
|
|
218
|
-
}
|
package/src/identity/did.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { base58btc } from 'multiformats/bases/base58';
|
|
2
|
-
import { IdentityError } from '../utils/errors.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Derive a did:clawiverse DID from a public key
|
|
6
|
-
* Format: did:clawiverse:<base58btc-encoded-pubkey>
|
|
7
|
-
*/
|
|
8
|
-
export function deriveDID(publicKey: Uint8Array): string {
|
|
9
|
-
try {
|
|
10
|
-
const encoded = base58btc.encode(publicKey);
|
|
11
|
-
return `did:clawiverse:${encoded}`;
|
|
12
|
-
} catch (error) {
|
|
13
|
-
throw new IdentityError('Failed to derive DID', error);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Extract public key from a did:clawiverse DID
|
|
19
|
-
*/
|
|
20
|
-
export function extractPublicKey(did: string): Uint8Array {
|
|
21
|
-
if (!did.startsWith('did:clawiverse:')) {
|
|
22
|
-
throw new IdentityError('Invalid DID format: must start with did:clawiverse:');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const encoded = did.replace('did:clawiverse:', '');
|
|
27
|
-
return base58btc.decode(encoded);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
throw new IdentityError('Failed to extract public key from DID', error);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Validate a did:clawiverse DID format
|
|
35
|
-
*/
|
|
36
|
-
export function validateDID(did: string): boolean {
|
|
37
|
-
if (!did.startsWith('did:clawiverse:')) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const encoded = did.replace('did:clawiverse:', '');
|
|
43
|
-
base58btc.decode(encoded);
|
|
44
|
-
return true;
|
|
45
|
-
} catch {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
package/src/identity/document.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { deriveDID } from './did.js';
|
|
2
|
-
|
|
3
|
-
export interface DIDDocument {
|
|
4
|
-
'@context': string[];
|
|
5
|
-
id: string;
|
|
6
|
-
verificationMethod: VerificationMethod[];
|
|
7
|
-
authentication: string[];
|
|
8
|
-
assertionMethod: string[];
|
|
9
|
-
keyAgreement?: string[];
|
|
10
|
-
service?: ServiceEndpoint[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface VerificationMethod {
|
|
14
|
-
id: string;
|
|
15
|
-
type: string;
|
|
16
|
-
controller: string;
|
|
17
|
-
publicKeyMultibase: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ServiceEndpoint {
|
|
21
|
-
id: string;
|
|
22
|
-
type: string;
|
|
23
|
-
serviceEndpoint: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create a DID Document for a did:clawiverse identity
|
|
28
|
-
*/
|
|
29
|
-
export function createDIDDocument(
|
|
30
|
-
publicKey: Uint8Array,
|
|
31
|
-
services?: ServiceEndpoint[]
|
|
32
|
-
): DIDDocument {
|
|
33
|
-
const did = deriveDID(publicKey);
|
|
34
|
-
const keyId = `${did}#key-1`;
|
|
35
|
-
// Use base58btc encoding for multibase format
|
|
36
|
-
const publicKeyMultibase = `z${Buffer.from(publicKey).toString('hex')}`;
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
'@context': [
|
|
40
|
-
'https://www.w3.org/ns/did/v1',
|
|
41
|
-
'https://w3id.org/security/suites/ed25519-2020/v1',
|
|
42
|
-
],
|
|
43
|
-
id: did,
|
|
44
|
-
verificationMethod: [
|
|
45
|
-
{
|
|
46
|
-
id: keyId,
|
|
47
|
-
type: 'Ed25519VerificationKey2020',
|
|
48
|
-
controller: did,
|
|
49
|
-
publicKeyMultibase,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
authentication: [keyId],
|
|
53
|
-
assertionMethod: [keyId],
|
|
54
|
-
service: services,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Validate a DID Document structure
|
|
60
|
-
*/
|
|
61
|
-
export function validateDIDDocument(doc: unknown): doc is DIDDocument {
|
|
62
|
-
if (typeof doc !== 'object' || doc === null) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const d = doc as Partial<DIDDocument>;
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
Array.isArray(d['@context']) &&
|
|
70
|
-
typeof d.id === 'string' &&
|
|
71
|
-
d.id.startsWith('did:clawiverse:') &&
|
|
72
|
-
Array.isArray(d.verificationMethod) &&
|
|
73
|
-
d.verificationMethod.length > 0 &&
|
|
74
|
-
Array.isArray(d.authentication) &&
|
|
75
|
-
Array.isArray(d.assertionMethod)
|
|
76
|
-
);
|
|
77
|
-
}
|
package/src/identity/index.ts
DELETED
package/src/identity/keys.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import * as ed25519 from '@noble/ed25519';
|
|
2
|
-
import { IdentityError } from '../utils/errors.js';
|
|
3
|
-
|
|
4
|
-
export interface KeyPair {
|
|
5
|
-
publicKey: Uint8Array;
|
|
6
|
-
privateKey: Uint8Array;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Generate a new Ed25519 key pair
|
|
11
|
-
*/
|
|
12
|
-
export async function generateKeyPair(): Promise<KeyPair> {
|
|
13
|
-
try {
|
|
14
|
-
const privateKey = ed25519.utils.randomPrivateKey();
|
|
15
|
-
const publicKey = await ed25519.getPublicKeyAsync(privateKey);
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
publicKey,
|
|
19
|
-
privateKey,
|
|
20
|
-
};
|
|
21
|
-
} catch (error) {
|
|
22
|
-
throw new IdentityError('Failed to generate key pair', error);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sign a message with a private key
|
|
28
|
-
*/
|
|
29
|
-
export async function sign(
|
|
30
|
-
message: Uint8Array,
|
|
31
|
-
privateKey: Uint8Array
|
|
32
|
-
): Promise<Uint8Array> {
|
|
33
|
-
try {
|
|
34
|
-
return await ed25519.signAsync(message, privateKey);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
throw new IdentityError('Failed to sign message', error);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Verify a signature
|
|
42
|
-
*/
|
|
43
|
-
export async function verify(
|
|
44
|
-
signature: Uint8Array,
|
|
45
|
-
message: Uint8Array,
|
|
46
|
-
publicKey: Uint8Array
|
|
47
|
-
): Promise<boolean> {
|
|
48
|
-
try {
|
|
49
|
-
return await ed25519.verifyAsync(signature, message, publicKey);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
throw new IdentityError('Failed to verify signature', error);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Export key pair to JSON format
|
|
57
|
-
*/
|
|
58
|
-
export function exportKeyPair(keyPair: KeyPair): {
|
|
59
|
-
publicKey: string;
|
|
60
|
-
privateKey: string;
|
|
61
|
-
} {
|
|
62
|
-
return {
|
|
63
|
-
publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
|
|
64
|
-
privateKey: Buffer.from(keyPair.privateKey).toString('hex'),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Import key pair from JSON format
|
|
70
|
-
*/
|
|
71
|
-
export function importKeyPair(exported: {
|
|
72
|
-
publicKey: string;
|
|
73
|
-
privateKey: string;
|
|
74
|
-
}): KeyPair {
|
|
75
|
-
return {
|
|
76
|
-
publicKey: new Uint8Array(Buffer.from(exported.publicKey, 'hex')),
|
|
77
|
-
privateKey: new Uint8Array(Buffer.from(exported.privateKey, 'hex')),
|
|
78
|
-
};
|
|
79
|
-
}
|