@tacitprotocol/sdk 0.1.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.
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Tacit Protocol — Authenticity Vector Engine
3
+ *
4
+ * Computes and manages the multi-dimensional trust score that
5
+ * replaces self-reported profiles with verifiable behavioral signals.
6
+ */
7
+
8
+ import type {
9
+ AuthenticityVector,
10
+ AuthenticityDimensions,
11
+ TrustLevel,
12
+ VerifiableCredential,
13
+ } from '../types/index.js';
14
+
15
+ // ─── Constants ────────────────────────────────────────────────────
16
+
17
+ const DIMENSION_WEIGHTS = {
18
+ tenure: 0.20,
19
+ consistency: 0.30,
20
+ attestations: 0.25,
21
+ networkTrust: 0.25,
22
+ } as const;
23
+
24
+ const TRUST_LEVEL_THRESHOLDS: [TrustLevel, number][] = [
25
+ ['exemplary', 90],
26
+ ['trusted', 70],
27
+ ['established', 40],
28
+ ['emerging', 20],
29
+ ['new', 0],
30
+ ];
31
+
32
+ const CONSISTENCY_WEIGHTS = {
33
+ intentStability: 0.30,
34
+ profileConsistency: 0.25,
35
+ responseReliability: 0.25,
36
+ interactionQuality: 0.20,
37
+ } as const;
38
+
39
+ const ATTESTATION_WEIGHTS = {
40
+ institutional: 0.40,
41
+ peer: 0.30,
42
+ transaction: 0.30,
43
+ } as const;
44
+
45
+ /** Score decays 0.1% per day after 30 days of inactivity */
46
+ const DECAY_RATE_PER_DAY = 0.001;
47
+ const DECAY_GRACE_PERIOD_DAYS = 30;
48
+
49
+ // ─── Core Engine ──────────────────────────────────────────────────
50
+
51
+ export class AuthenticityEngine {
52
+ /**
53
+ * Compute the tenure dimension score.
54
+ * Linear growth over 365 days, capped at 1.0.
55
+ */
56
+ computeTenure(agentCreatedDate: Date, now: Date = new Date()): number {
57
+ const daysActive = (now.getTime() - agentCreatedDate.getTime()) / (1000 * 60 * 60 * 24);
58
+ return Math.min(1.0, Math.max(0, daysActive / 365));
59
+ }
60
+
61
+ /**
62
+ * Compute the consistency dimension score.
63
+ * Based on behavioral stability signals over time.
64
+ */
65
+ computeConsistency(signals: ConsistencySignals): number {
66
+ return clamp(
67
+ signals.intentStability * CONSISTENCY_WEIGHTS.intentStability +
68
+ signals.profileConsistency * CONSISTENCY_WEIGHTS.profileConsistency +
69
+ signals.responseReliability * CONSISTENCY_WEIGHTS.responseReliability +
70
+ signals.interactionQuality * CONSISTENCY_WEIGHTS.interactionQuality
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Compute the attestation dimension score.
76
+ * Based on third-party verifiable credentials.
77
+ */
78
+ computeAttestations(credentials: VerifiableCredential[]): number {
79
+ if (credentials.length === 0) return 0;
80
+
81
+ let institutional = 0;
82
+ let peer = 0;
83
+ let transaction = 0;
84
+
85
+ for (const cred of credentials) {
86
+ // Categorize credentials
87
+ if (isInstitutionalCredential(cred)) {
88
+ institutional += credentialWeight(cred);
89
+ } else if (isPeerAttestation(cred)) {
90
+ peer += credentialWeight(cred);
91
+ } else {
92
+ transaction += credentialWeight(cred);
93
+ }
94
+ }
95
+
96
+ // Normalize each category to 0-1
97
+ institutional = Math.min(1.0, institutional);
98
+ peer = Math.min(1.0, peer);
99
+ transaction = Math.min(1.0, transaction);
100
+
101
+ return clamp(
102
+ institutional * ATTESTATION_WEIGHTS.institutional +
103
+ peer * ATTESTATION_WEIGHTS.peer +
104
+ transaction * ATTESTATION_WEIGHTS.transaction
105
+ );
106
+ }
107
+
108
+ /**
109
+ * Compute the network trust dimension score.
110
+ * Based on the quality of the agent's network relationships.
111
+ *
112
+ * This is a simplified version — production implementations
113
+ * should use a full PageRank-style algorithm.
114
+ */
115
+ computeNetworkTrust(signals: NetworkTrustSignals): number {
116
+ if (signals.totalInteractions === 0) return 0;
117
+
118
+ const successRate = signals.successfulIntros / Math.max(1, signals.totalIntros);
119
+ const positiveRate = signals.positiveInteractions / signals.totalInteractions;
120
+ const bidirectionalBonus = Math.min(1.0, signals.bidirectionalTrustEdges * 0.1);
121
+
122
+ return clamp(
123
+ successRate * 0.35 +
124
+ positiveRate * 0.35 +
125
+ bidirectionalBonus * 0.30
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Compute the full authenticity vector from all dimensions.
131
+ */
132
+ computeVector(params: {
133
+ agentCreatedDate: Date;
134
+ consistencySignals: ConsistencySignals;
135
+ credentials: VerifiableCredential[];
136
+ networkSignals: NetworkTrustSignals;
137
+ lastActiveDate?: Date;
138
+ }): AuthenticityVector {
139
+ const now = new Date();
140
+
141
+ const dimensions: AuthenticityDimensions = {
142
+ tenure: this.computeTenure(params.agentCreatedDate, now),
143
+ consistency: this.computeConsistency(params.consistencySignals),
144
+ attestations: this.computeAttestations(params.credentials),
145
+ networkTrust: this.computeNetworkTrust(params.networkSignals),
146
+ };
147
+
148
+ // Compute raw score
149
+ let score =
150
+ dimensions.tenure * DIMENSION_WEIGHTS.tenure +
151
+ dimensions.consistency * DIMENSION_WEIGHTS.consistency +
152
+ dimensions.attestations * DIMENSION_WEIGHTS.attestations +
153
+ dimensions.networkTrust * DIMENSION_WEIGHTS.networkTrust;
154
+
155
+ // Apply inactivity decay
156
+ if (params.lastActiveDate) {
157
+ const inactiveDays = (now.getTime() - params.lastActiveDate.getTime()) / (1000 * 60 * 60 * 24);
158
+ if (inactiveDays > DECAY_GRACE_PERIOD_DAYS) {
159
+ const decayDays = inactiveDays - DECAY_GRACE_PERIOD_DAYS;
160
+ const decayFactor = Math.max(0, 1 - decayDays * DECAY_RATE_PER_DAY);
161
+ score *= decayFactor;
162
+ }
163
+ }
164
+
165
+ // Scale to 0-100
166
+ score = Math.round(score * 100);
167
+
168
+ return {
169
+ level: this.scoreToLevel(score),
170
+ score,
171
+ dimensions,
172
+ verifiableCredentials: params.credentials,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Convert a numeric score to a trust level.
178
+ */
179
+ scoreToLevel(score: number): TrustLevel {
180
+ for (const [level, threshold] of TRUST_LEVEL_THRESHOLDS) {
181
+ if (score >= threshold) return level;
182
+ }
183
+ return 'new';
184
+ }
185
+
186
+ /**
187
+ * Check if an agent meets minimum authenticity requirements.
188
+ */
189
+ meetsMinimum(vector: AuthenticityVector, minScore: number): boolean {
190
+ return vector.score >= minScore;
191
+ }
192
+ }
193
+
194
+ // ─── Signal Types ─────────────────────────────────────────────────
195
+
196
+ export interface ConsistencySignals {
197
+ /** 0-1: How stable the agent's intents have been over time */
198
+ intentStability: number;
199
+ /** 0-1: How consistent the profile claims have been */
200
+ profileConsistency: number;
201
+ /** 0-1: How reliably the agent responds to proposals */
202
+ responseReliability: number;
203
+ /** 0-1: Quality ratings from completed interactions */
204
+ interactionQuality: number;
205
+ }
206
+
207
+ export interface NetworkTrustSignals {
208
+ /** Total number of interactions this agent has had */
209
+ totalInteractions: number;
210
+ /** Number of interactions rated positively by the other party */
211
+ positiveInteractions: number;
212
+ /** Total introduction proposals that reached completion */
213
+ totalIntros: number;
214
+ /** Intros that both parties rated as successful */
215
+ successfulIntros: number;
216
+ /** Number of agents that mutually trust this agent */
217
+ bidirectionalTrustEdges: number;
218
+ }
219
+
220
+ // ─── Helpers ──────────────────────────────────────────────────────
221
+
222
+ function clamp(value: number, min = 0, max = 1): number {
223
+ return Math.min(max, Math.max(min, value));
224
+ }
225
+
226
+ const INSTITUTIONAL_TYPES = new Set([
227
+ 'EmploymentCredential',
228
+ 'EducationCredential',
229
+ 'ProfessionalCertification',
230
+ 'GovernmentId',
231
+ 'LicenseCredential',
232
+ ]);
233
+
234
+ function isInstitutionalCredential(cred: VerifiableCredential): boolean {
235
+ return INSTITUTIONAL_TYPES.has(cred.type);
236
+ }
237
+
238
+ const PEER_TYPES = new Set([
239
+ 'PeerEndorsement',
240
+ 'PeerAttestation',
241
+ 'RecommendationCredential',
242
+ ]);
243
+
244
+ function isPeerAttestation(cred: VerifiableCredential): boolean {
245
+ return PEER_TYPES.has(cred.type);
246
+ }
247
+
248
+ function credentialWeight(cred: VerifiableCredential): number {
249
+ // Newer credentials are worth more
250
+ const issued = new Date(cred.issued);
251
+ const ageYears = (Date.now() - issued.getTime()) / (1000 * 60 * 60 * 24 * 365);
252
+
253
+ // Decay: full weight for first year, then 10% per year
254
+ const ageFactor = Math.max(0.1, 1 - (ageYears - 1) * 0.1);
255
+
256
+ // Base weight by type
257
+ const baseWeight = INSTITUTIONAL_TYPES.has(cred.type) ? 0.5 : 0.3;
258
+
259
+ return baseWeight * ageFactor;
260
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Tacit Protocol — DID Identity Management
3
+ *
4
+ * Handles creation, resolution, and management of W3C Decentralized Identifiers
5
+ * for Tacit agents. Uses did:key method for simplicity in v0.1.
6
+ */
7
+
8
+ import type { AgentIdentity, DID } from '../types/index.js';
9
+
10
+ /**
11
+ * Generate a new Ed25519 keypair for agent identity.
12
+ * In production, this should use a secure random number generator
13
+ * and keys should be stored in a secure enclave.
14
+ */
15
+ export async function generateKeypair(): Promise<{
16
+ publicKey: Uint8Array;
17
+ privateKey: Uint8Array;
18
+ }> {
19
+ // Use Web Crypto API for key generation
20
+ const keyPair = await crypto.subtle.generateKey(
21
+ { name: 'Ed25519' },
22
+ true,
23
+ ['sign', 'verify']
24
+ );
25
+
26
+ const pair = keyPair as { publicKey: CryptoKey; privateKey: CryptoKey };
27
+ const publicKeyBuffer = await crypto.subtle.exportKey('raw', pair.publicKey);
28
+ const privateKeyBuffer = await crypto.subtle.exportKey('pkcs8', pair.privateKey);
29
+
30
+ return {
31
+ publicKey: new Uint8Array(publicKeyBuffer),
32
+ privateKey: new Uint8Array(privateKeyBuffer),
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Create a did:key identifier from a public key.
38
+ * did:key encodes the public key directly in the DID string.
39
+ *
40
+ * Format: did:key:z{multibase-encoded-multicodec-public-key}
41
+ * For Ed25519: multicodec prefix is 0xed01
42
+ */
43
+ export function publicKeyToDid(publicKey: Uint8Array): DID {
44
+ // Multicodec prefix for Ed25519 public key
45
+ const ED25519_MULTICODEC = new Uint8Array([0xed, 0x01]);
46
+ const prefixed = new Uint8Array(ED25519_MULTICODEC.length + publicKey.length);
47
+ prefixed.set(ED25519_MULTICODEC);
48
+ prefixed.set(publicKey, ED25519_MULTICODEC.length);
49
+
50
+ // Base58btc encode with 'z' prefix (multibase)
51
+ const encoded = base58btcEncode(prefixed);
52
+ return `did:key:z${encoded}` as DID;
53
+ }
54
+
55
+ /**
56
+ * Create a new agent identity with a fresh keypair and DID.
57
+ */
58
+ export async function createIdentity(): Promise<AgentIdentity> {
59
+ const { publicKey, privateKey } = await generateKeypair();
60
+ const did = publicKeyToDid(publicKey);
61
+
62
+ return {
63
+ did,
64
+ publicKey,
65
+ privateKey,
66
+ created: new Date(),
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Resolve a DID to its public key.
72
+ * For did:key, the public key is embedded in the DID itself.
73
+ */
74
+ export function resolveDid(did: DID): { publicKey: Uint8Array } | null {
75
+ if (!did.startsWith('did:key:z')) {
76
+ // Only did:key supported in v0.1
77
+ return null;
78
+ }
79
+
80
+ const encoded = did.slice('did:key:z'.length);
81
+ const decoded = base58btcDecode(encoded);
82
+
83
+ // Strip multicodec prefix (2 bytes for Ed25519)
84
+ const publicKey = decoded.slice(2);
85
+
86
+ return { publicKey };
87
+ }
88
+
89
+ /**
90
+ * Sign arbitrary data with the agent's private key.
91
+ */
92
+ export async function sign(data: Uint8Array, privateKey: Uint8Array): Promise<Uint8Array> {
93
+ const key = await crypto.subtle.importKey(
94
+ 'pkcs8',
95
+ privateKey.buffer as ArrayBuffer,
96
+ { name: 'Ed25519' },
97
+ false,
98
+ ['sign']
99
+ );
100
+
101
+ const signature = await crypto.subtle.sign('Ed25519', key, data.buffer as ArrayBuffer);
102
+ return new Uint8Array(signature);
103
+ }
104
+
105
+ /**
106
+ * Verify a signature against a public key.
107
+ */
108
+ export async function verify(
109
+ data: Uint8Array,
110
+ signature: Uint8Array,
111
+ publicKey: Uint8Array
112
+ ): Promise<boolean> {
113
+ const key = await crypto.subtle.importKey(
114
+ 'raw',
115
+ publicKey.buffer as ArrayBuffer,
116
+ { name: 'Ed25519' },
117
+ false,
118
+ ['verify']
119
+ );
120
+
121
+ return crypto.subtle.verify('Ed25519', key, signature.buffer as ArrayBuffer, data.buffer as ArrayBuffer);
122
+ }
123
+
124
+ // ─── Base58btc Encoding (simplified) ──────────────────────────────
125
+
126
+ const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
127
+
128
+ function base58btcEncode(bytes: Uint8Array): string {
129
+ if (bytes.length === 0) return '';
130
+
131
+ // Count leading zeros
132
+ let zeros = 0;
133
+ for (const byte of bytes) {
134
+ if (byte !== 0) break;
135
+ zeros++;
136
+ }
137
+
138
+ // Convert to base58
139
+ const digits: number[] = [0];
140
+ for (const byte of bytes) {
141
+ let carry = byte;
142
+ for (let j = 0; j < digits.length; j++) {
143
+ carry += digits[j] << 8;
144
+ digits[j] = carry % 58;
145
+ carry = (carry / 58) | 0;
146
+ }
147
+ while (carry > 0) {
148
+ digits.push(carry % 58);
149
+ carry = (carry / 58) | 0;
150
+ }
151
+ }
152
+
153
+ let result = '';
154
+ for (let i = 0; i < zeros; i++) result += '1';
155
+ for (let i = digits.length - 1; i >= 0; i--) result += BASE58_ALPHABET[digits[i]];
156
+
157
+ return result;
158
+ }
159
+
160
+ function base58btcDecode(str: string): Uint8Array {
161
+ if (str.length === 0) return new Uint8Array(0);
162
+
163
+ const bytes: number[] = [0];
164
+ for (const char of str) {
165
+ const value = BASE58_ALPHABET.indexOf(char);
166
+ if (value === -1) throw new Error(`Invalid base58 character: ${char}`);
167
+
168
+ let carry = value;
169
+ for (let j = 0; j < bytes.length; j++) {
170
+ carry += bytes[j] * 58;
171
+ bytes[j] = carry & 0xff;
172
+ carry >>= 8;
173
+ }
174
+ while (carry > 0) {
175
+ bytes.push(carry & 0xff);
176
+ carry >>= 8;
177
+ }
178
+ }
179
+
180
+ // Count leading '1's (zeros in base58)
181
+ let zeros = 0;
182
+ for (const char of str) {
183
+ if (char !== '1') break;
184
+ zeros++;
185
+ }
186
+
187
+ const result = new Uint8Array(zeros + bytes.length);
188
+ for (let i = 0; i < zeros; i++) result[i] = 0;
189
+ for (let i = 0; i < bytes.length; i++) result[zeros + i] = bytes[bytes.length - 1 - i];
190
+
191
+ return result;
192
+ }
package/src/index.ts ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Tacit Protocol SDK
3
+ *
4
+ * The social layer for the agent era.
5
+ * Enables AI agents to discover, trust, and introduce the humans they represent.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ // Core
11
+ export { TacitAgent } from './core/agent.js';
12
+
13
+ // Identity
14
+ export { createIdentity, publicKeyToDid, resolveDid, sign, verify } from './identity/did.js';
15
+ export { AuthenticityEngine } from './identity/authenticity.js';
16
+ export type { ConsistencySignals, NetworkTrustSignals } from './identity/authenticity.js';
17
+
18
+ // Discovery
19
+ export { IntentBuilder, IntentStore } from './discovery/intent.js';
20
+
21
+ // Matching
22
+ export { MatchScorer } from './matching/scorer.js';
23
+
24
+ // Types
25
+ export type {
26
+ // Identity
27
+ DID,
28
+ AgentIdentity,
29
+ AnonymityLevel,
30
+ SessionPersona,
31
+
32
+ // Agent Card
33
+ AgentCard,
34
+ TransportConfig,
35
+ Domain,
36
+ DomainType,
37
+ AgentPreferences,
38
+
39
+ // Authenticity
40
+ AuthenticityVector,
41
+ AuthenticityDimensions,
42
+ TrustLevel,
43
+ VerifiableCredential,
44
+
45
+ // Intents
46
+ Intent,
47
+ IntentType,
48
+ IntentStatus,
49
+ IntentFilters,
50
+ PrivacyLevel,
51
+
52
+ // Matching
53
+ MatchResult,
54
+ MatchScore,
55
+ MatchAction,
56
+
57
+ // Introductions
58
+ IntroProposal,
59
+ IntroTerms,
60
+ RevealStage,
61
+ ProposalStatus,
62
+
63
+ // Commerce
64
+ ServiceIntent,
65
+ ServiceAttestation,
66
+
67
+ // Events
68
+ TacitEvent,
69
+
70
+ // Config
71
+ TacitConfig,
72
+ RelayConfig,
73
+ } from './types/index.js';