@happyvertical/smrt-profiles 0.30.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.
Files changed (42) hide show
  1. package/AGENTS.md +53 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +176 -0
  5. package/dist/chunks/ApiKey-B2LKEaP8.js +143 -0
  6. package/dist/chunks/ApiKey-B2LKEaP8.js.map +1 -0
  7. package/dist/chunks/ApiKeyCollection-B6Op817e.js +91 -0
  8. package/dist/chunks/ApiKeyCollection-B6Op817e.js.map +1 -0
  9. package/dist/chunks/AuditLogCollection-BYqCj0uE.js +195 -0
  10. package/dist/chunks/AuditLogCollection-BYqCj0uE.js.map +1 -0
  11. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js +3065 -0
  12. package/dist/chunks/NostrIdentityCollection-DadQBHWy.js.map +1 -0
  13. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js +122 -0
  14. package/dist/chunks/ProfileAssetCollection-D_tk1kKG.js.map +1 -0
  15. package/dist/chunks/ProfileCollection-DU6wUJTO.js +782 -0
  16. package/dist/chunks/ProfileCollection-DU6wUJTO.js.map +1 -0
  17. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js +120 -0
  18. package/dist/chunks/ProfileMetadataCollection-DEhmljMY.js.map +1 -0
  19. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js +184 -0
  20. package/dist/chunks/ProfileMetafieldCollection-DMKhSHXX.js.map +1 -0
  21. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js +177 -0
  22. package/dist/chunks/ProfileRelationshipCollection-C0IM8UQR.js.map +1 -0
  23. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js +117 -0
  24. package/dist/chunks/ProfileRelationshipTermCollection-CXem_qT-.js.map +1 -0
  25. package/dist/chunks/ProfileRelationshipType-BXBLldea.js +103 -0
  26. package/dist/chunks/ProfileRelationshipType-BXBLldea.js.map +1 -0
  27. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js +48 -0
  28. package/dist/chunks/ProfileRelationshipTypeCollection-CF8YvLTV.js.map +1 -0
  29. package/dist/chunks/index-jFtOWsAV.js +1014 -0
  30. package/dist/chunks/index-jFtOWsAV.js.map +1 -0
  31. package/dist/index.d.ts +1848 -0
  32. package/dist/index.js +70 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/manifest.json +11829 -0
  35. package/dist/smrt-knowledge.json +3846 -0
  36. package/dist/types.d.ts +41 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils.d.ts +61 -0
  40. package/dist/utils.js +49 -0
  41. package/dist/utils.js.map +1 -0
  42. package/package.json +75 -0
@@ -0,0 +1,1848 @@
1
+ import { Asset } from '@happyvertical/smrt-assets';
2
+ import { PromptDefinition } from '@happyvertical/smrt-prompts';
3
+ import { ResolvedPromptAI } from '@happyvertical/smrt-prompts';
4
+ import { SmrtCollection } from '@happyvertical/smrt-core';
5
+ import { SmrtJunction } from '@happyvertical/smrt-core';
6
+ import { SmrtObject } from '@happyvertical/smrt-core';
7
+ import { SmrtObjectOptions } from '@happyvertical/smrt-core';
8
+
9
+ export declare class ApiKey extends SmrtObject {
10
+ /**
11
+ * Link to the Profile (Person, Organization, Bot)
12
+ */
13
+ profileId?: string;
14
+ /**
15
+ * SHA-256 hash of the API key
16
+ */
17
+ keyHash: string;
18
+ /**
19
+ * First 8 characters of the key for identification (e.g., "sk_live_a1b2...")
20
+ */
21
+ keyPrefix: string;
22
+ /**
23
+ * Human-readable name for the key (e.g., "CI Bot", "Local CLI")
24
+ */
25
+ name: string;
26
+ /**
27
+ * Scopes/permissions for this key
28
+ */
29
+ scopes: string[];
30
+ /**
31
+ * Last time this key was used
32
+ */
33
+ lastUsedAt: Date | null;
34
+ /**
35
+ * When this key expires (null = never)
36
+ */
37
+ expiresAt: Date | null;
38
+ /**
39
+ * When this key was revoked (null = active)
40
+ */
41
+ revokedAt: Date | null;
42
+ constructor(options?: ApiKeyOptions);
43
+ /**
44
+ * Get the linked Profile
45
+ */
46
+ getProfile(): Promise<Profile | null>;
47
+ /**
48
+ * Check if this key is valid (not expired, not revoked)
49
+ */
50
+ isValid(): boolean;
51
+ /**
52
+ * Check if this key has a specific scope
53
+ */
54
+ hasScope(scope: string): boolean;
55
+ /**
56
+ * Revoke this key
57
+ */
58
+ revoke(): Promise<void>;
59
+ /**
60
+ * Record usage of this key
61
+ */
62
+ recordUsage(): Promise<void>;
63
+ /**
64
+ * Hash an API key
65
+ */
66
+ static hashKey(key: string): string;
67
+ /**
68
+ * Generate a new API key for a profile
69
+ */
70
+ static generate(profile: Profile, options: {
71
+ name: string;
72
+ scopes?: string[];
73
+ expiresAt?: Date | null;
74
+ db?: SmrtObjectOptions['db'];
75
+ }): Promise<GenerateKeyResult>;
76
+ /**
77
+ * Verify an API key and return the ApiKey record if valid
78
+ */
79
+ static verify(key: string, options?: SmrtObjectOptions): Promise<ApiKey | null>;
80
+ }
81
+
82
+ export declare class ApiKeyCollection extends SmrtCollection<ApiKey> {
83
+ static readonly _itemClass: typeof ApiKey;
84
+ /**
85
+ * Find all keys for a profile
86
+ */
87
+ findByProfile(profileId: string): Promise<ApiKey[]>;
88
+ /**
89
+ * Find active (non-revoked, non-expired) keys for a profile
90
+ */
91
+ findActiveByProfile(profileId: string): Promise<ApiKey[]>;
92
+ /**
93
+ * Find key by hash
94
+ */
95
+ findByHash(keyHash: string): Promise<ApiKey | null>;
96
+ /**
97
+ * Verify an API key string and return the record if valid
98
+ */
99
+ verify(key: string): Promise<ApiKey | null>;
100
+ /**
101
+ * Generate a new key for a profile
102
+ */
103
+ generateForProfile(profile: Profile, options: {
104
+ name: string;
105
+ scopes?: string[];
106
+ expiresAt?: Date | null;
107
+ }): Promise<GenerateKeyResult>;
108
+ /**
109
+ * Revoke all keys for a profile
110
+ */
111
+ revokeAllForProfile(profileId: string): Promise<number>;
112
+ /**
113
+ * Revoke a specific key by prefix
114
+ */
115
+ revokeByPrefix(keyPrefix: string): Promise<boolean>;
116
+ /**
117
+ * Clean up expired keys (soft delete by setting revokedAt)
118
+ */
119
+ cleanupExpired(): Promise<number>;
120
+ }
121
+
122
+ export declare interface ApiKeyOptions extends SmrtObjectOptions {
123
+ profileId?: string;
124
+ name?: string;
125
+ scopes?: string[];
126
+ expiresAt?: Date | null;
127
+ }
128
+
129
+ export declare class AuditLog extends SmrtObject {
130
+ tenantId: string | null;
131
+ /**
132
+ * The profile who performed the action
133
+ */
134
+ profileId?: string;
135
+ /**
136
+ * Action performed (e.g., 'issue.update', 'feedback.incorporate')
137
+ */
138
+ action: string;
139
+ /**
140
+ * Type of resource affected (e.g., 'Issue', 'Repository')
141
+ */
142
+ resourceType: string;
143
+ /**
144
+ * ID of the affected resource
145
+ */
146
+ resourceId: string;
147
+ /**
148
+ * Source of the action
149
+ */
150
+ source: AuditSource;
151
+ /**
152
+ * Additional context (CI run ID, request ID, etc.)
153
+ */
154
+ metadata: Record<string, any>;
155
+ /**
156
+ * For pass-through identity in CI - the actual person who triggered
157
+ */
158
+ onBehalfOfId?: string | null;
159
+ constructor(options?: AuditLogOptions);
160
+ /**
161
+ * Get the profile who performed the action
162
+ */
163
+ getProfile(): Promise<Profile | null>;
164
+ /**
165
+ * Get the profile on whose behalf the action was performed (if any)
166
+ */
167
+ getOnBehalfOf(): Promise<Profile | null>;
168
+ /**
169
+ * Get the effective actor (onBehalfOf if set, otherwise profile)
170
+ */
171
+ getEffectiveActor(): Promise<Profile | null>;
172
+ /**
173
+ * Create an audit log entry
174
+ */
175
+ static record(options: AuditLogOptions & {
176
+ profile: Profile;
177
+ onBehalfOf?: Profile | null;
178
+ }): Promise<AuditLog>;
179
+ }
180
+
181
+ export declare class AuditLogCollection extends SmrtCollection<AuditLog> {
182
+ static readonly _itemClass: typeof AuditLog;
183
+ /**
184
+ * Find logs for a profile (actions they performed)
185
+ */
186
+ findByProfile(profileId: string): Promise<AuditLog[]>;
187
+ /**
188
+ * Find logs for a resource
189
+ */
190
+ findByResource(resourceType: string, resourceId: string): Promise<AuditLog[]>;
191
+ /**
192
+ * Find logs by action type
193
+ */
194
+ findByAction(action: string): Promise<AuditLog[]>;
195
+ /**
196
+ * Find logs by source
197
+ */
198
+ findBySource(source: AuditSource): Promise<AuditLog[]>;
199
+ /**
200
+ * Find logs where actions were performed on behalf of someone
201
+ */
202
+ findOnBehalfOf(profileId: string): Promise<AuditLog[]>;
203
+ /**
204
+ * Record a new audit log entry
205
+ */
206
+ record(options: {
207
+ profile: Profile;
208
+ action: string;
209
+ resourceType: string;
210
+ resourceId: string;
211
+ source?: AuditSource;
212
+ metadata?: Record<string, any>;
213
+ onBehalfOf?: Profile | null;
214
+ }): Promise<AuditLog>;
215
+ /**
216
+ * Get recent activity for a profile
217
+ */
218
+ getRecentActivity(profileId: string, limit?: number): Promise<AuditLog[]>;
219
+ /**
220
+ * Get activity timeline for a resource
221
+ */
222
+ getResourceTimeline(resourceType: string, resourceId: string): Promise<AuditLog[]>;
223
+ }
224
+
225
+ export declare interface AuditLogOptions extends SmrtObjectOptions {
226
+ profileId?: string;
227
+ action?: string;
228
+ resourceType?: string;
229
+ resourceId?: string;
230
+ source?: AuditSource;
231
+ metadata?: Record<string, any>;
232
+ onBehalfOfId?: string | null;
233
+ tenantId?: string | null;
234
+ }
235
+
236
+ export declare type AuditSource = 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';
237
+
238
+ /**
239
+ * Context provided to resolveIdentity
240
+ */
241
+ export declare interface AuthContext {
242
+ /**
243
+ * API key from X-API-Key header or similar
244
+ */
245
+ apiKey?: string | null;
246
+ /**
247
+ * OIDC session data (from @auth/sveltekit or similar)
248
+ */
249
+ oidcSession?: {
250
+ sub?: string;
251
+ iss?: string;
252
+ email?: string;
253
+ name?: string;
254
+ } | null;
255
+ /**
256
+ * Actor identifier for CI pass-through identity
257
+ * Usually the GitHub actor (username) who triggered the workflow
258
+ */
259
+ actor?: string | null;
260
+ /**
261
+ * Nostr authentication data (NIP-42 style)
262
+ */
263
+ nostrAuth?: {
264
+ /** Signed Nostr event for authentication */
265
+ event: NostrEvent;
266
+ /** Expected challenge (to prevent replay attacks) */
267
+ challenge: string;
268
+ } | null;
269
+ /**
270
+ * Database/persistence options
271
+ */
272
+ db?: SmrtObjectOptions['db'];
273
+ }
274
+
275
+ /**
276
+ * Bot profile type
277
+ *
278
+ * Represents automated agents, bots, and AI entities.
279
+ */
280
+ export declare class Bot extends Profile {
281
+ constructor(options?: ProfileOptions);
282
+ }
283
+
284
+ /**
285
+ * Compute the event ID (SHA-256 hash of serialized event)
286
+ */
287
+ export declare function computeEventId(event: NostrEvent): string;
288
+
289
+ /**
290
+ * Create a Nostr event for authentication (NIP-42 style)
291
+ * @param privkey - Private key to sign with
292
+ * @param challenge - Server challenge string
293
+ * @param relay - Relay URL (optional)
294
+ */
295
+ export declare function createAuthEvent(privkey: string, challenge: string, relay?: string): NostrEvent;
296
+
297
+ /**
298
+ * Create a magic link service instance
299
+ */
300
+ export declare function createMagicLinkService(config: MagicLinkConfig): MagicLinkService;
301
+
302
+ /**
303
+ * Create a NIP-05 handler
304
+ *
305
+ * @example
306
+ * // SvelteKit
307
+ * import { createNip05Handler } from '@happyvertical/smrt-profiles';
308
+ *
309
+ * const handler = createNip05Handler({ db: { type: 'postgres', url: DATABASE_URL } });
310
+ *
311
+ * export async function GET({ url }) {
312
+ * const result = await handler({ name: url.searchParams.get('name') });
313
+ * return new Response(JSON.stringify(result.body), {
314
+ * status: result.status,
315
+ * headers: result.headers,
316
+ * });
317
+ * }
318
+ *
319
+ * @example
320
+ * // Express
321
+ * import { createNip05Handler } from '@happyvertical/smrt-profiles';
322
+ *
323
+ * const handler = createNip05Handler({ db: { type: 'postgres', url: DATABASE_URL } });
324
+ *
325
+ * app.get('/.well-known/nostr.json', async (req, res) => {
326
+ * const result = await handler({ name: req.query.name });
327
+ * res.status(result.status).set(result.headers).json(result.body);
328
+ * });
329
+ */
330
+ export declare function createNip05Handler(config: Nip05HandlerConfig): (request: Nip05Request) => Promise<Nip05HandlerResult>;
331
+
332
+ /**
333
+ * Create a profile from Nostr identity (used by magic link service)
334
+ *
335
+ * This is typically called internally by the magic link service,
336
+ * but can be used directly if needed.
337
+ *
338
+ * @param email - Email address for the profile
339
+ * @param nostrData - Encrypted Nostr keypair data
340
+ * @param options - Database options
341
+ * @returns The created profile with linked Nostr identity
342
+ */
343
+ export declare function createProfileFromNostr(email: string, nostrData: {
344
+ pubkey: string;
345
+ encryptedPrivkey: string;
346
+ encryptionIv: string;
347
+ encryptionTag: string;
348
+ nip05Username?: string;
349
+ }, options: SmrtObjectOptions): Promise<{
350
+ profile: Profile;
351
+ nostrIdentity: NostrIdentity;
352
+ created: boolean;
353
+ }>;
354
+
355
+ /**
356
+ * Create a profile from OIDC claims if it doesn't exist
357
+ *
358
+ * Supports email-based account linking: if a user signs in with Google
359
+ * and later with GitHub using the same email, they get the same profile.
360
+ *
361
+ * Resolution order:
362
+ * 1. If OIDC identity (iss + sub) already exists → return linked profile
363
+ * 2. If verified email provided, check if profile with same email exists → link new identity
364
+ * 3. Otherwise, create new profile + identity
365
+ *
366
+ * Security considerations:
367
+ * - Email-based linking only occurs when `email_verified` is true. This prevents
368
+ * attackers from claiming unverified emails to hijack accounts.
369
+ * - Linking is automatic and irreversible through this API. Multiple OIDC
370
+ * identities from different providers sharing the same verified email will
371
+ * be associated to the same Profile.
372
+ * - If an OIDC provider does not supply an email, or the email changes later,
373
+ * existing links are not automatically updated. New sign-ins without an email
374
+ * or with a different email may result in a new Profile being created.
375
+ * - This function trusts the OIDC provider to assert correct email_verified status.
376
+ * Only use with trusted providers.
377
+ *
378
+ * @param claims - OIDC token claims
379
+ * @param provider - Provider name (e.g., 'keycloak', 'google', 'github')
380
+ * @param options - Database options
381
+ * @returns The created or existing profile with linked OIDC identity
382
+ */
383
+ export declare function createProfileFromOidc(claims: {
384
+ sub: string;
385
+ iss: string;
386
+ email?: string;
387
+ email_verified?: boolean;
388
+ name?: string;
389
+ preferred_username?: string;
390
+ }, provider: string, options: SmrtObjectOptions): Promise<{
391
+ profile: Profile;
392
+ oidcIdentity: OidcIdentity;
393
+ created: boolean;
394
+ }>;
395
+
396
+ /**
397
+ * Decrypt a private key using AES-256-GCM
398
+ * @param encrypted - Encrypted key data
399
+ * @param masterSecret - Server master secret (from env)
400
+ */
401
+ export declare function decryptPrivkey(encrypted: EncryptedKey, masterSecret: string): string;
402
+
403
+ /**
404
+ * Derive an encryption key from the master secret using HKDF
405
+ * @param masterSecret - Server master secret
406
+ */
407
+ export declare function deriveEncryptionKey(masterSecret: string): Buffer;
408
+
409
+ export declare interface EncryptedKey {
410
+ /** Base64-encoded ciphertext */
411
+ ciphertext: string;
412
+ /** Base64-encoded initialization vector */
413
+ iv: string;
414
+ /** Base64-encoded authentication tag */
415
+ tag: string;
416
+ }
417
+
418
+ /**
419
+ * Encrypt a private key using AES-256-GCM
420
+ * @param privkey - Hex-encoded private key
421
+ * @param masterSecret - Server master secret (from env)
422
+ */
423
+ export declare function encryptPrivkey(privkey: string, masterSecret: string): EncryptedKey;
424
+
425
+ export declare interface GenerateKeyResult {
426
+ key: string;
427
+ apiKey: ApiKey;
428
+ }
429
+
430
+ /**
431
+ * Generate a new Nostr keypair (secp256k1)
432
+ */
433
+ export declare function generateNostrKeypair(): NostrKeypair;
434
+
435
+ export declare interface GenerateTokenResult {
436
+ /** The plaintext token (only available at creation time) */
437
+ token: string;
438
+ /** The MagicLinkToken record */
439
+ magicLinkToken: MagicLinkToken;
440
+ }
441
+
442
+ /**
443
+ * Get public key from private key
444
+ */
445
+ export declare function getPublicKey(privkey: string): string;
446
+
447
+ export declare interface InitiateResult {
448
+ success: boolean;
449
+ /** The Nostr identity (existing or new) */
450
+ nostrIdentity?: NostrIdentity;
451
+ /** The profile (existing or new) */
452
+ profile?: Profile;
453
+ /** True if a new profile was created */
454
+ created: boolean;
455
+ /** Error message if success is false */
456
+ error?: string;
457
+ /** Error code for programmatic handling */
458
+ errorCode?: 'RATE_LIMITED_EMAIL' | 'RATE_LIMITED_IP' | 'EMAIL_SEND_FAILED';
459
+ }
460
+
461
+ /**
462
+ * Validate a NIP-05 identifier format
463
+ * @param identifier - The identifier to validate (e.g., "alice@example.com")
464
+ */
465
+ export declare function isValidNip05Identifier(identifier: string): boolean;
466
+
467
+ /**
468
+ * Validate a hex-encoded private key
469
+ */
470
+ export declare function isValidPrivkey(privkey: string): boolean;
471
+
472
+ /**
473
+ * Validate a hex-encoded public key
474
+ */
475
+ export declare function isValidPubkey(pubkey: string): boolean;
476
+
477
+ export declare interface MagicLinkConfig {
478
+ /** Base URL for magic links (e.g., "https://example.com") */
479
+ baseUrl: string;
480
+ /** Path for verification endpoint (default: "/auth/verify") */
481
+ verifyPath?: string;
482
+ /** Token expiration in minutes (default: 15) */
483
+ expiresInMinutes?: number;
484
+ /** Maximum tokens per email per hour (default: 5) */
485
+ maxTokensPerEmailPerHour?: number;
486
+ /** Maximum tokens per IP per hour (default: 10) */
487
+ maxTokensPerIpPerHour?: number;
488
+ /** Server master secret for encryption */
489
+ masterSecret: string;
490
+ /** Callback to send the email */
491
+ sendEmail: (to: string, magicLink: string) => Promise<void>;
492
+ /** Database options */
493
+ db: SmrtObjectOptions['db'];
494
+ }
495
+
496
+ export declare interface MagicLinkService {
497
+ /**
498
+ * Initiate magic link flow - generates token and sends email
499
+ */
500
+ initiate(email: string, options?: {
501
+ requestedFromIp?: string;
502
+ nip05Username?: string;
503
+ }): Promise<InitiateResult>;
504
+ /**
505
+ * Verify a magic link token and return decrypted keypair
506
+ */
507
+ verify(token: string): Promise<VerifyResult>;
508
+ }
509
+
510
+ export declare class MagicLinkToken extends SmrtObject {
511
+ /**
512
+ * Link to the NostrIdentity this token is for
513
+ */
514
+ nostrIdentityId?: string;
515
+ /**
516
+ * SHA-256 hash of the token (never store plaintext)
517
+ */
518
+ tokenHash: string;
519
+ /**
520
+ * Email this token was sent to
521
+ */
522
+ email: string;
523
+ /**
524
+ * When this token expires
525
+ */
526
+ expiresAt: Date;
527
+ /**
528
+ * When this token was used (null = unused)
529
+ */
530
+ usedAt: Date | null;
531
+ /**
532
+ * IP address that requested the token (for rate limiting)
533
+ */
534
+ requestedFromIp: string;
535
+ /**
536
+ * When this token was created (for rate limiting)
537
+ */
538
+ createdAt: Date;
539
+ constructor(options?: MagicLinkTokenOptions);
540
+ /**
541
+ * Hash a token using SHA-256
542
+ */
543
+ static hashToken(token: string): string;
544
+ /**
545
+ * Generate a new magic link token
546
+ * @param nostrIdentity - The identity this token is for
547
+ * @param email - The email address to send to
548
+ * @param options - Additional options
549
+ */
550
+ static generate(nostrIdentity: NostrIdentity, email: string, options?: {
551
+ expiresInMinutes?: number;
552
+ requestedFromIp?: string;
553
+ db?: SmrtObjectOptions['db'];
554
+ }): Promise<GenerateTokenResult>;
555
+ /**
556
+ * Verify a token and return the MagicLinkToken if valid
557
+ * @param token - The plaintext token to verify
558
+ * @param options - Database options
559
+ * @returns The MagicLinkToken if valid, null otherwise
560
+ */
561
+ static verify(token: string, options?: SmrtObjectOptions): Promise<MagicLinkToken | null>;
562
+ /**
563
+ * Check if this token is valid (not expired and not used)
564
+ */
565
+ isValid(): boolean;
566
+ /**
567
+ * Check if this token has expired
568
+ */
569
+ isExpired(): boolean;
570
+ /**
571
+ * Check if this token has been used
572
+ */
573
+ isUsed(): boolean;
574
+ /**
575
+ * Mark this token as used
576
+ */
577
+ markUsed(): Promise<void>;
578
+ /**
579
+ * Get the NostrIdentity this token is for
580
+ */
581
+ getNostrIdentity(): Promise<NostrIdentity | null>;
582
+ /**
583
+ * Get time remaining until expiration in seconds
584
+ */
585
+ getTimeRemainingSeconds(): number;
586
+ }
587
+
588
+ export declare class MagicLinkTokenCollection extends SmrtCollection<MagicLinkToken> {
589
+ static readonly _itemClass: typeof MagicLinkToken;
590
+ /**
591
+ * Find token by hash
592
+ */
593
+ findByTokenHash(tokenHash: string): Promise<MagicLinkToken | null>;
594
+ /**
595
+ * Find active (non-expired, non-used) tokens for an email
596
+ */
597
+ findActiveForEmail(email: string): Promise<MagicLinkToken[]>;
598
+ /**
599
+ * Find tokens for a NostrIdentity
600
+ */
601
+ findByIdentity(nostrIdentityId: string): Promise<MagicLinkToken[]>;
602
+ /**
603
+ * Count tokens created for an email within a time window (for rate limiting)
604
+ * @param email - Email address
605
+ * @param withinMinutes - Time window in minutes
606
+ */
607
+ countRecentByEmail(email: string, withinMinutes: number): Promise<number>;
608
+ /**
609
+ * Count tokens created from an IP within a time window (for rate limiting)
610
+ * @param ip - IP address
611
+ * @param withinMinutes - Time window in minutes
612
+ */
613
+ countRecentByIp(ip: string, withinMinutes: number): Promise<number>;
614
+ /**
615
+ * Verify a token and return it if valid
616
+ */
617
+ verify(token: string): Promise<MagicLinkToken | null>;
618
+ /**
619
+ * Clean up expired tokens by deleting them
620
+ * @returns Number of tokens deleted
621
+ */
622
+ cleanupExpired(): Promise<number>;
623
+ /**
624
+ * Revoke all tokens for a NostrIdentity (e.g., when identity is deleted)
625
+ */
626
+ revokeForIdentity(nostrIdentityId: string): Promise<number>;
627
+ /**
628
+ * Check if rate limit is exceeded for email
629
+ * @param email - Email address
630
+ * @param maxPerHour - Maximum tokens allowed per hour (default: 5)
631
+ */
632
+ isRateLimitedByEmail(email: string, maxPerHour?: number): Promise<boolean>;
633
+ /**
634
+ * Check if rate limit is exceeded for IP
635
+ * @param ip - IP address
636
+ * @param maxPerHour - Maximum tokens allowed per hour (default: 10)
637
+ */
638
+ isRateLimitedByIp(ip: string, maxPerHour?: number): Promise<boolean>;
639
+ }
640
+
641
+ export declare interface MagicLinkTokenOptions extends SmrtObjectOptions {
642
+ nostrIdentityId?: string;
643
+ tokenHash?: string;
644
+ email?: string;
645
+ expiresAt?: Date;
646
+ usedAt?: Date | null;
647
+ requestedFromIp?: string;
648
+ createdAt?: Date;
649
+ }
650
+
651
+ export declare interface Nip05HandlerConfig {
652
+ /** Database options */
653
+ db: SmrtObjectOptions['db'];
654
+ /** Optional: Additional relays to include for all users */
655
+ defaultRelays?: string[];
656
+ /** Optional: Cache TTL in seconds (default: 300 = 5 minutes) */
657
+ cacheTtlSeconds?: number;
658
+ }
659
+
660
+ export declare interface Nip05HandlerResult {
661
+ /** The NIP-05 response body */
662
+ body: Nip05Response;
663
+ /** Suggested headers for the response */
664
+ headers: Record<string, string>;
665
+ /** HTTP status code */
666
+ status: number;
667
+ }
668
+
669
+ export declare interface Nip05Request {
670
+ /** The 'name' query parameter from the request */
671
+ name?: string | null;
672
+ }
673
+
674
+ export declare interface Nip05Response {
675
+ names: Record<string, string>;
676
+ relays?: Record<string, string[]>;
677
+ }
678
+
679
+ export declare interface NostrEvent {
680
+ id?: string;
681
+ pubkey: string;
682
+ created_at: number;
683
+ kind: number;
684
+ tags: string[][];
685
+ content: string;
686
+ sig?: string;
687
+ }
688
+
689
+ export declare class NostrIdentity extends SmrtObject {
690
+ /**
691
+ * Link to the Profile (Person, Organization, Bot)
692
+ */
693
+ profileId?: string;
694
+ /**
695
+ * Hex-encoded secp256k1 public key (64 characters)
696
+ */
697
+ pubkey: string;
698
+ /**
699
+ * AES-256-GCM encrypted private key (base64-encoded ciphertext)
700
+ */
701
+ encryptedPrivkey: string;
702
+ /**
703
+ * Initialization vector for AES-GCM decryption (base64)
704
+ */
705
+ encryptionIv: string;
706
+ /**
707
+ * Authentication tag from AES-GCM (base64)
708
+ */
709
+ encryptionTag: string;
710
+ /**
711
+ * Email address associated with this identity
712
+ */
713
+ email: string;
714
+ /**
715
+ * Username for NIP-05 verification (e.g., "alice" for alice@domain.com)
716
+ */
717
+ nip05Username: string;
718
+ /**
719
+ * Last time this identity was used for authentication
720
+ */
721
+ lastUsedAt: Date | null;
722
+ /**
723
+ * When this identity was verified via magic link
724
+ */
725
+ verifiedAt: Date | null;
726
+ constructor(options?: NostrIdentityOptions);
727
+ /**
728
+ * Get the linked Profile
729
+ */
730
+ getProfile(): Promise<Profile | null>;
731
+ /**
732
+ * Find identity by public key
733
+ */
734
+ static findByPubkey(pubkey: string, options?: SmrtObjectOptions): Promise<NostrIdentity | null>;
735
+ /**
736
+ * Find identity by email
737
+ */
738
+ static findByEmail(email: string, options?: SmrtObjectOptions): Promise<NostrIdentity | null>;
739
+ /**
740
+ * Find identity by NIP-05 username
741
+ */
742
+ static findByNip05(username: string, options?: SmrtObjectOptions): Promise<NostrIdentity | null>;
743
+ /**
744
+ * Decrypt the private key using the server master secret
745
+ * @param masterSecret - SERVER_MASTER_SECRET from environment
746
+ * @returns Hex-encoded private key
747
+ */
748
+ decryptPrivkey(masterSecret: string): string;
749
+ /**
750
+ * Get the full keypair (requires master secret)
751
+ * @param masterSecret - SERVER_MASTER_SECRET from environment
752
+ */
753
+ getKeypair(masterSecret: string): {
754
+ pubkey: string;
755
+ privkey: string;
756
+ npub: string;
757
+ nsec: string;
758
+ };
759
+ /**
760
+ * Record usage of this identity
761
+ */
762
+ recordUsage(): Promise<void>;
763
+ /**
764
+ * Mark this identity as verified
765
+ */
766
+ markVerified(): Promise<void>;
767
+ /**
768
+ * Check if this identity is verified
769
+ */
770
+ isVerified(): boolean;
771
+ /**
772
+ * Get the NIP-05 identifier (username@domain)
773
+ * Note: Domain must be provided by the application
774
+ */
775
+ getNip05Identifier(domain: string): string;
776
+ }
777
+
778
+ export declare class NostrIdentityCollection extends SmrtCollection<NostrIdentity> {
779
+ static readonly _itemClass: typeof NostrIdentity;
780
+ /**
781
+ * Find identities for a profile
782
+ */
783
+ findByProfile(profileId: string): Promise<NostrIdentity[]>;
784
+ /**
785
+ * Find identity by public key
786
+ */
787
+ findByPubkey(pubkey: string): Promise<NostrIdentity | null>;
788
+ /**
789
+ * Find identity by email
790
+ */
791
+ findByEmail(email: string): Promise<NostrIdentity | null>;
792
+ /**
793
+ * Find identity by NIP-05 username
794
+ */
795
+ findByNip05(username: string): Promise<NostrIdentity | null>;
796
+ /**
797
+ * Link a new Nostr identity to a profile with a new keypair
798
+ * @param profile - The profile to link to
799
+ * @param email - Email address for this identity
800
+ * @param masterSecret - Server master secret for encryption
801
+ * @param nip05Username - Optional NIP-05 username (defaults to email local part)
802
+ */
803
+ createForProfile(profile: Profile, email: string, masterSecret: string, nip05Username?: string): Promise<NostrIdentity>;
804
+ /**
805
+ * Link an existing Nostr identity (with encrypted keypair) to a profile
806
+ */
807
+ linkToProfile(profile: Profile, nostrData: {
808
+ pubkey: string;
809
+ encryptedPrivkey: string;
810
+ encryptionIv: string;
811
+ encryptionTag: string;
812
+ email: string;
813
+ nip05Username?: string;
814
+ }): Promise<NostrIdentity>;
815
+ /**
816
+ * Unlink a Nostr identity from a profile
817
+ */
818
+ unlinkFromProfile(profileId: string, pubkey: string): Promise<boolean>;
819
+ /**
820
+ * Get NIP-05 response data for the /.well-known/nostr.json endpoint
821
+ * @param username - Optional username filter, returns all if not provided
822
+ */
823
+ getNip05Response(username?: string): Promise<Nip05Response>;
824
+ /**
825
+ * Check if a NIP-05 username is available
826
+ */
827
+ isNip05Available(username: string): Promise<boolean>;
828
+ /**
829
+ * Update NIP-05 username for an identity
830
+ */
831
+ updateNip05Username(identity: NostrIdentity, newUsername: string): Promise<NostrIdentity>;
832
+ }
833
+
834
+ export declare interface NostrIdentityOptions extends SmrtObjectOptions {
835
+ profileId?: string;
836
+ pubkey?: string;
837
+ encryptedPrivkey?: string;
838
+ encryptionIv?: string;
839
+ encryptionTag?: string;
840
+ email?: string;
841
+ nip05Username?: string;
842
+ lastUsedAt?: Date | null;
843
+ verifiedAt?: Date | null;
844
+ }
845
+
846
+ /**
847
+ * Nostr cryptographic utilities
848
+ *
849
+ * Handles keypair generation, encryption/decryption of private keys,
850
+ * signature verification, and bech32 encoding for Nostr authentication.
851
+ */
852
+ export declare interface NostrKeypair {
853
+ /** Hex-encoded public key (64 characters) */
854
+ pubkey: string;
855
+ /** Hex-encoded private key (64 characters) */
856
+ privkey: string;
857
+ }
858
+
859
+ /**
860
+ * Convert npub (bech32) to hex public key
861
+ */
862
+ export declare function npubToPubkey(npub: string): string;
863
+
864
+ /**
865
+ * Convert nsec (bech32) to hex private key
866
+ */
867
+ export declare function nsecToPrivkey(nsec: string): string;
868
+
869
+ export declare class OidcIdentity extends SmrtObject {
870
+ /**
871
+ * Link to the Profile (Person, Organization, Bot)
872
+ */
873
+ profileId?: string;
874
+ /**
875
+ * Provider name (e.g., 'keycloak', 'google', 'github')
876
+ */
877
+ provider: string;
878
+ /**
879
+ * OIDC issuer URL (e.g., https://keycloak.example.com/realms/bmp)
880
+ */
881
+ issuer: string;
882
+ /**
883
+ * OIDC subject claim - unique identifier from the provider
884
+ */
885
+ subject: string;
886
+ /**
887
+ * Cached email from the IdP (for display/lookup)
888
+ */
889
+ email: string;
890
+ /**
891
+ * Last time this identity was used for authentication
892
+ */
893
+ lastUsedAt: Date | null;
894
+ constructor(options?: OidcIdentityOptions);
895
+ /**
896
+ * Get the linked Profile
897
+ */
898
+ getProfile(): Promise<Profile | null>;
899
+ /**
900
+ * Find identity by issuer and subject
901
+ */
902
+ static findBySubject(issuer: string, subject: string, options?: SmrtObjectOptions): Promise<OidcIdentity | null>;
903
+ /**
904
+ * Find or create identity for a profile
905
+ */
906
+ static findOrCreate(profile: Profile, oidcData: {
907
+ provider: string;
908
+ issuer: string;
909
+ subject: string;
910
+ email?: string;
911
+ }, options?: SmrtObjectOptions): Promise<OidcIdentity>;
912
+ /**
913
+ * Record usage of this identity
914
+ */
915
+ recordUsage(): Promise<void>;
916
+ }
917
+
918
+ export declare class OidcIdentityCollection extends SmrtCollection<OidcIdentity> {
919
+ static readonly _itemClass: typeof OidcIdentity;
920
+ /**
921
+ * Find identities for a profile
922
+ */
923
+ findByProfile(profileId: string): Promise<OidcIdentity[]>;
924
+ /**
925
+ * Find identity by issuer and subject
926
+ */
927
+ findBySubject(issuer: string, subject: string): Promise<OidcIdentity | null>;
928
+ /**
929
+ * Find identities by provider
930
+ */
931
+ findByProvider(provider: string): Promise<OidcIdentity[]>;
932
+ /**
933
+ * Link a new OIDC identity to a profile
934
+ */
935
+ linkToProfile(profile: Profile, oidcData: {
936
+ provider: string;
937
+ issuer: string;
938
+ subject: string;
939
+ email?: string;
940
+ }): Promise<OidcIdentity>;
941
+ /**
942
+ * Unlink an OIDC identity from a profile
943
+ */
944
+ unlinkFromProfile(profileId: string, issuer: string, subject: string): Promise<boolean>;
945
+ }
946
+
947
+ export declare interface OidcIdentityOptions extends SmrtObjectOptions {
948
+ profileId?: string;
949
+ provider?: string;
950
+ issuer?: string;
951
+ subject?: string;
952
+ email?: string;
953
+ lastUsedAt?: Date | null;
954
+ }
955
+
956
+ /**
957
+ * Organization profile type
958
+ *
959
+ * Represents companies, groups, institutions, and other organizational entities.
960
+ */
961
+ export declare class Organization extends Profile {
962
+ constructor(options?: ProfileOptions);
963
+ }
964
+
965
+ /**
966
+ * Parse a NIP-05 identifier into its parts
967
+ * @param identifier - The identifier to parse (e.g., "alice@example.com")
968
+ */
969
+ export declare function parseNip05Identifier(identifier: string): {
970
+ localPart: string;
971
+ domain: string;
972
+ } | null;
973
+
974
+ /**
975
+ * Person profile type
976
+ *
977
+ * Represents individual people/users.
978
+ */
979
+ export declare class Person extends Profile {
980
+ constructor(options?: ProfileOptions);
981
+ }
982
+
983
+ /**
984
+ * Convert hex private key to nsec (bech32)
985
+ */
986
+ export declare function privkeyToNsec(privkey: string): string;
987
+
988
+ export declare class Profile extends SmrtObject {
989
+ tenantId: string | null;
990
+ typeId?: string;
991
+ email?: string;
992
+ name: string;
993
+ description?: string;
994
+ metadata: any[];
995
+ relationshipsFrom: any[];
996
+ relationshipsTo: any[];
997
+ constructor(options?: ProfileOptions);
998
+ /**
999
+ * Get the profile type slug for this profile
1000
+ *
1001
+ * @returns The slug of the profile type
1002
+ */
1003
+ getTypeSlug(): Promise<string>;
1004
+ /**
1005
+ * Set the profile type by slug
1006
+ *
1007
+ * @param slug - The slug of the profile type
1008
+ * @throws Error if profile type not found
1009
+ */
1010
+ setTypeBySlug(slug: string): Promise<void>;
1011
+ /**
1012
+ * Add metadata to this profile
1013
+ *
1014
+ * @param metafieldSlug - The slug of the metafield
1015
+ * @param value - The value to set
1016
+ */
1017
+ addMetadata(metafieldSlug: string, value: any): Promise<void>;
1018
+ /**
1019
+ * Get all metadata for this profile as key-value object
1020
+ *
1021
+ * @returns Object with metafield slugs as keys
1022
+ */
1023
+ getMetadata(): Promise<Record<string, any>>;
1024
+ /**
1025
+ * Update multiple metadata values
1026
+ *
1027
+ * @param metadata - Object with metafield slugs as keys and values
1028
+ */
1029
+ updateMetadata(metadata: Record<string, any>): Promise<void>;
1030
+ /**
1031
+ * Remove metadata by metafield slug
1032
+ *
1033
+ * @param metafieldSlug - The slug of the metafield to remove
1034
+ */
1035
+ removeMetadata(metafieldSlug: string): Promise<void>;
1036
+ private getProfileAssetCollection;
1037
+ getAssets(relationship?: string): Promise<Asset[]>;
1038
+ addAsset(asset: Asset, relationship?: string, sortOrder?: number): Promise<void>;
1039
+ removeAsset(assetId: string, relationship?: string): Promise<void>;
1040
+ /**
1041
+ * Add a relationship to another profile
1042
+ *
1043
+ * @param toProfile - The target profile
1044
+ * @param relationshipSlug - The type of relationship
1045
+ * @param contextProfile - Optional context profile for tertiary relationships
1046
+ */
1047
+ addRelationship(toProfile: Profile, relationshipSlug: string, contextProfile?: Profile): Promise<void>;
1048
+ /**
1049
+ * Get all relationships for this profile
1050
+ *
1051
+ * @param options - Filter options (typeSlug, direction)
1052
+ * @returns Array of ProfileRelationship instances
1053
+ */
1054
+ getRelationships(options?: {
1055
+ typeSlug?: string;
1056
+ direction?: 'from' | 'to' | 'all';
1057
+ }): Promise<ProfileRelationship[]>;
1058
+ /**
1059
+ * Get related profiles
1060
+ *
1061
+ * @param relationshipSlug - Optional filter by relationship type slug
1062
+ * @returns Array of related Profile instances
1063
+ */
1064
+ getRelatedProfiles(relationshipSlug?: string): Promise<Profile[]>;
1065
+ /**
1066
+ * Remove a relationship to another profile
1067
+ *
1068
+ * @param toProfile - The target profile
1069
+ * @param relationshipSlug - The type of relationship to remove
1070
+ */
1071
+ removeRelationship(toProfile: Profile, relationshipSlug: string): Promise<void>;
1072
+ /**
1073
+ * AI-powered: Generate a professional bio for this profile
1074
+ *
1075
+ * Uses the `smrtProfiles.profile.generateBio` prompt registered via
1076
+ * `@happyvertical/smrt-prompts`, allowing tenant- or instance-level
1077
+ * overrides of the template, model, and parameters at runtime.
1078
+ *
1079
+ * @returns Generated bio text
1080
+ */
1081
+ generateBio(): Promise<string>;
1082
+ /**
1083
+ * AI-powered: Check if profile matches criteria
1084
+ *
1085
+ * @param criteria - Criteria to match against
1086
+ * @returns True if matches criteria
1087
+ */
1088
+ matches(criteria: string): Promise<boolean>;
1089
+ /**
1090
+ * Find profiles by metadata key-value pair
1091
+ *
1092
+ * @param metafieldSlug - The metafield slug to search
1093
+ * @param value - The value to match
1094
+ * @returns Array of matching profiles
1095
+ */
1096
+ static findByMetadata(_metafieldSlug: string, _value: any): Promise<Profile[]>;
1097
+ /**
1098
+ * Find profiles by type slug
1099
+ *
1100
+ * @param typeSlug - The profile type slug
1101
+ * @returns Array of matching profiles
1102
+ */
1103
+ static findByType(_typeSlug: string): Promise<Profile[]>;
1104
+ /**
1105
+ * Find related profiles for a given profile
1106
+ *
1107
+ * @param profileId - The profile UUID
1108
+ * @param relationshipSlug - Optional filter by relationship type
1109
+ * @returns Array of related profiles
1110
+ */
1111
+ static findRelated(_profileId: string, _relationshipSlug?: string): Promise<Profile[]>;
1112
+ /**
1113
+ * Search profiles by email
1114
+ *
1115
+ * @param email - The email to search for
1116
+ * @returns Profile or null if not found
1117
+ */
1118
+ static searchByEmail(_email: string): Promise<Profile | null>;
1119
+ /**
1120
+ * Get all API keys for this profile
1121
+ *
1122
+ * @returns Array of API keys
1123
+ */
1124
+ getApiKeys(): Promise<any[]>;
1125
+ /**
1126
+ * Get active (non-revoked, non-expired) API keys for this profile
1127
+ *
1128
+ * @returns Array of active API keys
1129
+ */
1130
+ getActiveApiKeys(): Promise<any[]>;
1131
+ /**
1132
+ * Generate a new API key for this profile
1133
+ *
1134
+ * @param options - Key options (name, scopes, expiration)
1135
+ * @returns The generated key (plaintext) and ApiKey record
1136
+ */
1137
+ generateApiKey(options: {
1138
+ name: string;
1139
+ scopes?: string[];
1140
+ expiresAt?: Date | null;
1141
+ }): Promise<{
1142
+ key: string;
1143
+ apiKey: any;
1144
+ }>;
1145
+ /**
1146
+ * Get all OIDC identities linked to this profile
1147
+ *
1148
+ * @returns Array of OIDC identity records
1149
+ */
1150
+ getOidcIdentities(): Promise<any[]>;
1151
+ /**
1152
+ * Link a new OIDC identity to this profile
1153
+ *
1154
+ * @param oidcData - OIDC provider data
1155
+ * @returns The linked OIDC identity record
1156
+ */
1157
+ linkOidcIdentity(oidcData: {
1158
+ provider: string;
1159
+ issuer: string;
1160
+ subject: string;
1161
+ email?: string;
1162
+ }): Promise<any>;
1163
+ /**
1164
+ * Get all Nostr identities linked to this profile
1165
+ *
1166
+ * @returns Array of Nostr identity records
1167
+ */
1168
+ getNostrIdentities(): Promise<any[]>;
1169
+ /**
1170
+ * Link a new Nostr identity to this profile
1171
+ *
1172
+ * @param nostrData - Nostr identity data (encrypted keypair)
1173
+ * @returns The linked Nostr identity record
1174
+ */
1175
+ linkNostrIdentity(nostrData: {
1176
+ pubkey: string;
1177
+ encryptedPrivkey: string;
1178
+ encryptionIv: string;
1179
+ encryptionTag: string;
1180
+ email: string;
1181
+ nip05Username?: string;
1182
+ }): Promise<any>;
1183
+ /**
1184
+ * Get audit logs for actions performed by this profile
1185
+ *
1186
+ * @param limit - Maximum number of logs to return
1187
+ * @returns Array of audit log entries
1188
+ */
1189
+ getAuditLogs(limit?: number): Promise<any[]>;
1190
+ /**
1191
+ * Record an audit log entry for an action by this profile
1192
+ *
1193
+ * @param options - Audit log options
1194
+ * @returns The created audit log entry
1195
+ */
1196
+ recordAction(options: {
1197
+ action: string;
1198
+ resourceType: string;
1199
+ resourceId: string;
1200
+ source?: 'web' | 'cli' | 'ci' | 'webhook' | 'mcp';
1201
+ metadata?: Record<string, any>;
1202
+ onBehalfOf?: Profile | null;
1203
+ }): Promise<any>;
1204
+ }
1205
+
1206
+ export declare class ProfileAsset extends SmrtObject {
1207
+ tenantId: string | null;
1208
+ profileId: string;
1209
+ assetId: string;
1210
+ relationship: string;
1211
+ sortOrder: number;
1212
+ constructor(options?: ProfileAssetOptions);
1213
+ }
1214
+
1215
+ export declare class ProfileAssetCollection extends SmrtJunction<ProfileAsset> {
1216
+ static readonly _itemClass: typeof ProfileAsset;
1217
+ protected leftField: string;
1218
+ protected rightField: string;
1219
+ private profileCollectionPromise;
1220
+ private getProfileCollection;
1221
+ getAssets(profileId: string, relationship?: string): Promise<Asset[]>;
1222
+ addAsset(profileId: string, asset: Asset, relationship?: string, sortOrder?: number): Promise<void>;
1223
+ removeAsset(profileId: string, assetId: string, relationship?: string): Promise<void>;
1224
+ }
1225
+
1226
+ export declare interface ProfileAssetOptions extends SmrtObjectOptions {
1227
+ profileId?: string;
1228
+ assetId?: string;
1229
+ relationship?: string;
1230
+ sortOrder?: number;
1231
+ tenantId?: string | null;
1232
+ }
1233
+
1234
+ export declare class ProfileCollection extends SmrtCollection<Profile> {
1235
+ static readonly _itemClass: typeof Profile;
1236
+ /**
1237
+ * Find a profile by email address
1238
+ *
1239
+ * @param email - The email address to search for
1240
+ * @returns The matching profile or null
1241
+ */
1242
+ findByEmail(email: string): Promise<Profile | null>;
1243
+ /**
1244
+ * Find profiles by type slug
1245
+ *
1246
+ * @param typeSlug - The profile type slug to filter by
1247
+ * @returns Array of matching profiles
1248
+ */
1249
+ findByType(typeSlug: string): Promise<Profile[]>;
1250
+ /**
1251
+ * Batch get metadata for multiple profiles
1252
+ *
1253
+ * @param profileIds - Array of profile UUIDs
1254
+ * @returns Map of profile ID to metadata object
1255
+ */
1256
+ batchGetMetadata(profileIds: string[]): Promise<Map<string, Record<string, any>>>;
1257
+ /**
1258
+ * Batch update metadata for multiple profiles
1259
+ *
1260
+ * @param updates - Array of { profileId, data } objects
1261
+ */
1262
+ batchUpdateMetadata(updates: Array<{
1263
+ profileId: string;
1264
+ data: Record<string, any>;
1265
+ }>): Promise<void>;
1266
+ /**
1267
+ * Find related profiles for a given profile
1268
+ *
1269
+ * @param profileId - The profile UUID
1270
+ * @param relationshipSlug - Optional filter by relationship type
1271
+ * @returns Array of related profiles
1272
+ */
1273
+ findRelated(profileId: string, relationshipSlug?: string): Promise<Profile[]>;
1274
+ getAssets(profileId: string, relationship?: string): Promise<Asset[]>;
1275
+ addAsset(profileId: string, asset: Asset, relationship?: string, sortOrder?: number): Promise<void>;
1276
+ removeAsset(profileId: string, assetId: string, relationship?: string): Promise<void>;
1277
+ /**
1278
+ * Get the relationship network for a profile up to a maximum depth
1279
+ *
1280
+ * @param profileId - The starting profile UUID
1281
+ * @param options - Configuration options
1282
+ * @returns Map of profile ID to depth level
1283
+ */
1284
+ getRelationshipNetwork(profileId: string, options?: {
1285
+ maxDepth?: number;
1286
+ }): Promise<Map<string, number>>;
1287
+ /**
1288
+ * Find all profiles belonging to a specific tenant
1289
+ *
1290
+ * @param tenantId - The tenant UUID to filter by
1291
+ * @returns Array of profiles for this tenant
1292
+ */
1293
+ findByTenant(tenantId: string): Promise<Profile[]>;
1294
+ /**
1295
+ * Find all global profiles (without a tenant)
1296
+ *
1297
+ * @returns Array of profiles with null tenantId
1298
+ */
1299
+ findGlobal(): Promise<Profile[]>;
1300
+ /**
1301
+ * Find profiles belonging to a tenant plus all global profiles
1302
+ *
1303
+ * @param tenantId - The tenant UUID to include
1304
+ * @returns Array of tenant-specific and global profiles
1305
+ */
1306
+ findWithGlobals(tenantId: string): Promise<Profile[]>;
1307
+ }
1308
+
1309
+ export declare class ProfileMetadata extends SmrtObject {
1310
+ tenantId: string | null;
1311
+ profileId?: string;
1312
+ metafieldId?: string;
1313
+ value: string;
1314
+ constructor(options?: ProfileMetadataOptions);
1315
+ /**
1316
+ * Validate this metadata value against the metafield's validation schema
1317
+ *
1318
+ * @returns True if valid, throws error if invalid
1319
+ */
1320
+ validate(): Promise<boolean>;
1321
+ /**
1322
+ * Get the metafield slug for this metadata
1323
+ *
1324
+ * @returns The slug of the metafield
1325
+ */
1326
+ getMetafieldSlug(): Promise<string>;
1327
+ }
1328
+
1329
+ export declare class ProfileMetadataCollection extends SmrtCollection<ProfileMetadata> {
1330
+ static readonly _itemClass: typeof ProfileMetadata;
1331
+ /**
1332
+ * Get all metadata for a profile
1333
+ *
1334
+ * @param profileId - The profile UUID
1335
+ * @returns Array of ProfileMetadata instances
1336
+ */
1337
+ getByProfile(profileId: string): Promise<ProfileMetadata[]>;
1338
+ /**
1339
+ * Get metadata as key-value object for a profile
1340
+ *
1341
+ * @param profileId - The profile UUID
1342
+ * @returns Object with metafield slugs as keys
1343
+ */
1344
+ getMetadataObject(profileId: string): Promise<Record<string, any>>;
1345
+ /**
1346
+ * Find all profiles with a specific metadata key-value pair
1347
+ *
1348
+ * @param metafieldId - The metafield UUID
1349
+ * @param value - The value to match
1350
+ * @returns Array of profile UUIDs
1351
+ */
1352
+ findProfilesByMetadata(metafieldId: string, value: any): Promise<string[]>;
1353
+ }
1354
+
1355
+ export declare interface ProfileMetadataOptions extends SmrtObjectOptions {
1356
+ profileId?: string;
1357
+ metafieldId?: string;
1358
+ value?: string;
1359
+ tenantId?: string | null;
1360
+ }
1361
+
1362
+ export declare class ProfileMetafield extends SmrtObject {
1363
+ tenantId: string | null;
1364
+ name: string;
1365
+ description?: string;
1366
+ validation?: Record<string, any>;
1367
+ constructor(options?: ProfileMetafieldOptions);
1368
+ /**
1369
+ * Convenience method for slug-based lookup
1370
+ *
1371
+ * @param slug - The slug to search for
1372
+ * @returns ProfileMetafield instance or null if not found
1373
+ */
1374
+ static getBySlug(_slug: string): Promise<ProfileMetafield | null>;
1375
+ /**
1376
+ * Register a custom validator function
1377
+ *
1378
+ * @param name - Name of the validator (used in validation.custom field)
1379
+ * @param validator - The validator function
1380
+ */
1381
+ static registerValidator(name: string, validator: ValidatorFunction): void;
1382
+ /**
1383
+ * Get a registered custom validator
1384
+ *
1385
+ * @param name - Name of the validator
1386
+ * @returns The validator function or undefined
1387
+ */
1388
+ static getValidator(name: string): ValidatorFunction | undefined;
1389
+ /**
1390
+ * Validate a value against this metafield's validation schema
1391
+ *
1392
+ * @param value - The value to validate
1393
+ * @returns True if valid, throws ValidationError if invalid
1394
+ */
1395
+ validateValue(value: any): Promise<boolean>;
1396
+ }
1397
+
1398
+ export declare class ProfileMetafieldCollection extends SmrtCollection<ProfileMetafield> {
1399
+ static readonly _itemClass: typeof ProfileMetafield;
1400
+ /**
1401
+ * Get metafield by slug
1402
+ *
1403
+ * @param slug - The slug to search for
1404
+ * @returns ProfileMetafield instance or null
1405
+ */
1406
+ getBySlug(slug: string): Promise<ProfileMetafield | null>;
1407
+ /**
1408
+ * Get or create a metafield by slug
1409
+ *
1410
+ * @param slug - The slug to search for
1411
+ * @param defaults - Default values if creating
1412
+ * @returns ProfileMetafield instance
1413
+ */
1414
+ getOrCreateBySlug(slug: string, defaults: {
1415
+ name: string;
1416
+ description?: string;
1417
+ validation?: ValidationSchema;
1418
+ }): Promise<ProfileMetafield>;
1419
+ }
1420
+
1421
+ export declare interface ProfileMetafieldOptions extends SmrtObjectOptions {
1422
+ slug?: string;
1423
+ name?: string;
1424
+ description?: string;
1425
+ validation?: ValidationSchema;
1426
+ tenantId?: string | null;
1427
+ }
1428
+
1429
+ export declare interface ProfileOptions extends SmrtObjectOptions {
1430
+ typeId?: string;
1431
+ email?: string;
1432
+ name?: string;
1433
+ description?: string;
1434
+ tenantId?: string | null;
1435
+ }
1436
+
1437
+ export declare class ProfileRelationship extends SmrtObject {
1438
+ tenantId: string | null;
1439
+ fromProfileId?: string;
1440
+ toProfileId?: string;
1441
+ typeId?: string;
1442
+ contextProfileId?: string;
1443
+ terms: any[];
1444
+ constructor(options?: ProfileRelationshipOptions);
1445
+ /**
1446
+ * Get the relationship type slug
1447
+ *
1448
+ * @returns The slug of the relationship type
1449
+ */
1450
+ getTypeSlug(): Promise<string>;
1451
+ /**
1452
+ * Add a term (time period) to this relationship
1453
+ *
1454
+ * @param startedAt - Start date of the term
1455
+ * @param endedAt - Optional end date of the term
1456
+ */
1457
+ addTerm(startedAt: Date, endedAt?: Date): Promise<void>;
1458
+ /**
1459
+ * End the current active term
1460
+ *
1461
+ * @param endedAt - End date for the term
1462
+ */
1463
+ endCurrentTerm(endedAt: Date): Promise<void>;
1464
+ /**
1465
+ * Get all terms for this relationship
1466
+ *
1467
+ * @returns Array of ProfileRelationshipTerm instances
1468
+ */
1469
+ getTerms(): Promise<ProfileRelationshipTerm[]>;
1470
+ /**
1471
+ * Get the active term (no end date)
1472
+ *
1473
+ * @returns Current term or null if none active
1474
+ */
1475
+ getActiveTerm(): Promise<ProfileRelationshipTerm | null>;
1476
+ }
1477
+
1478
+ export declare class ProfileRelationshipCollection extends SmrtCollection<ProfileRelationship> {
1479
+ static readonly _itemClass: typeof ProfileRelationship;
1480
+ /**
1481
+ * Get all relationships from a profile
1482
+ *
1483
+ * @param fromProfileId - The origin profile UUID
1484
+ * @param typeId - Optional filter by relationship type UUID
1485
+ * @returns Array of ProfileRelationship instances
1486
+ */
1487
+ getFromProfile(fromProfileId: string, typeId?: string): Promise<ProfileRelationship[]>;
1488
+ /**
1489
+ * Get all relationships to a profile
1490
+ *
1491
+ * @param toProfileId - The target profile UUID
1492
+ * @param typeId - Optional filter by relationship type UUID
1493
+ * @returns Array of ProfileRelationship instances
1494
+ */
1495
+ getToProfile(toProfileId: string, typeId?: string): Promise<ProfileRelationship[]>;
1496
+ /**
1497
+ * Get all relationships for a profile (both directions)
1498
+ *
1499
+ * @param profileId - The profile UUID
1500
+ * @param typeId - Optional filter by relationship type UUID
1501
+ * @returns Array of ProfileRelationship instances
1502
+ */
1503
+ getForProfile(profileId: string, typeId?: string): Promise<ProfileRelationship[]>;
1504
+ /**
1505
+ * Check if a relationship exists between two profiles
1506
+ *
1507
+ * @param fromProfileId - The origin profile UUID
1508
+ * @param toProfileId - The target profile UUID
1509
+ * @param typeId - The relationship type UUID
1510
+ * @returns True if relationship exists
1511
+ */
1512
+ exists(fromProfileId: string, toProfileId: string, typeId: string): Promise<boolean>;
1513
+ }
1514
+
1515
+ export declare interface ProfileRelationshipOptions extends SmrtObjectOptions {
1516
+ fromProfileId?: string;
1517
+ toProfileId?: string;
1518
+ typeId?: string;
1519
+ contextProfileId?: string;
1520
+ tenantId?: string | null;
1521
+ }
1522
+
1523
+ export declare class ProfileRelationshipTerm extends SmrtObject {
1524
+ relationshipId?: string;
1525
+ startedAt: Date;
1526
+ endedAt?: Date;
1527
+ constructor(options?: ProfileRelationshipTermOptions);
1528
+ /**
1529
+ * Check if this term is currently active
1530
+ *
1531
+ * @returns True if active (no end date or end date in future)
1532
+ */
1533
+ isActive(): boolean;
1534
+ /**
1535
+ * End this term
1536
+ *
1537
+ * @param endedAt - End date for the term (defaults to now)
1538
+ */
1539
+ end(endedAt?: Date): Promise<void>;
1540
+ /**
1541
+ * Get the duration of this term in days
1542
+ *
1543
+ * @returns Duration in days
1544
+ */
1545
+ getDurationDays(): number;
1546
+ }
1547
+
1548
+ export declare class ProfileRelationshipTermCollection extends SmrtCollection<ProfileRelationshipTerm> {
1549
+ static readonly _itemClass: typeof ProfileRelationshipTerm;
1550
+ /**
1551
+ * Get all terms for a relationship
1552
+ *
1553
+ * @param relationshipId - The relationship UUID
1554
+ * @returns Array of ProfileRelationshipTerm instances
1555
+ */
1556
+ getByRelationship(relationshipId: string): Promise<ProfileRelationshipTerm[]>;
1557
+ /**
1558
+ * Get the active term for a relationship (no end date or future end date)
1559
+ *
1560
+ * @param relationshipId - The relationship UUID
1561
+ * @returns Active term or null
1562
+ */
1563
+ getActiveTerm(relationshipId: string): Promise<ProfileRelationshipTerm | null>;
1564
+ /**
1565
+ * Get all historical (ended) terms for a relationship
1566
+ *
1567
+ * @param relationshipId - The relationship UUID
1568
+ * @returns Array of ended ProfileRelationshipTerm instances
1569
+ */
1570
+ getHistoricalTerms(relationshipId: string): Promise<ProfileRelationshipTerm[]>;
1571
+ }
1572
+
1573
+ export declare interface ProfileRelationshipTermOptions extends SmrtObjectOptions {
1574
+ relationshipId?: string;
1575
+ startedAt?: Date;
1576
+ endedAt?: Date;
1577
+ }
1578
+
1579
+ export declare class ProfileRelationshipType extends SmrtObject {
1580
+ name: string;
1581
+ reciprocal: boolean;
1582
+ constructor(options?: ProfileRelationshipTypeOptions);
1583
+ /**
1584
+ * Convenience method for slug-based lookup
1585
+ *
1586
+ * @param slug - The slug to search for
1587
+ * @returns ProfileRelationshipType instance or null if not found
1588
+ */
1589
+ static getBySlug(_slug: string): Promise<ProfileRelationshipType | null>;
1590
+ /**
1591
+ * Register a custom reciprocal handler for a relationship type
1592
+ *
1593
+ * @param slug - The relationship type slug
1594
+ * @param handler - The handler function to execute when creating reciprocal relationship
1595
+ */
1596
+ static registerReciprocalHandler(slug: string, handler: ReciprocalHandler): void;
1597
+ /**
1598
+ * Get the reciprocal handler for a relationship type
1599
+ *
1600
+ * @param slug - The relationship type slug
1601
+ * @returns The handler function or undefined
1602
+ */
1603
+ static getReciprocalHandler(slug: string): ReciprocalHandler | undefined;
1604
+ /**
1605
+ * Check if a relationship type is reciprocal
1606
+ *
1607
+ * @param slug - The relationship type slug
1608
+ * @returns True if reciprocal, false otherwise
1609
+ */
1610
+ static isReciprocal(slug: string): Promise<boolean>;
1611
+ }
1612
+
1613
+ export declare class ProfileRelationshipTypeCollection extends SmrtCollection<ProfileRelationshipType> {
1614
+ static readonly _itemClass: typeof ProfileRelationshipType;
1615
+ /**
1616
+ * Get relationship type by slug
1617
+ *
1618
+ * @param slug - The slug to search for
1619
+ * @returns ProfileRelationshipType instance or null
1620
+ */
1621
+ getBySlug(slug: string): Promise<ProfileRelationshipType | null>;
1622
+ /**
1623
+ * Get or create a relationship type by slug
1624
+ *
1625
+ * @param slug - The slug to search for
1626
+ * @param defaults - Default values if creating
1627
+ * @returns ProfileRelationshipType instance
1628
+ */
1629
+ getOrCreateBySlug(slug: string, defaults: {
1630
+ name: string;
1631
+ reciprocal?: boolean;
1632
+ }): Promise<ProfileRelationshipType>;
1633
+ /**
1634
+ * Get all reciprocal relationship types
1635
+ *
1636
+ * @returns Array of reciprocal ProfileRelationshipType instances
1637
+ */
1638
+ getReciprocal(): Promise<ProfileRelationshipType[]>;
1639
+ /**
1640
+ * Get all directional (non-reciprocal) relationship types
1641
+ *
1642
+ * @returns Array of directional ProfileRelationshipType instances
1643
+ */
1644
+ getDirectional(): Promise<ProfileRelationshipType[]>;
1645
+ }
1646
+
1647
+ export declare interface ProfileRelationshipTypeOptions extends SmrtObjectOptions {
1648
+ slug?: string;
1649
+ name?: string;
1650
+ reciprocal?: boolean;
1651
+ }
1652
+
1653
+ export declare class ProfileType extends SmrtObject {
1654
+ tenantId: string | null;
1655
+ name: string;
1656
+ description?: string;
1657
+ constructor(options?: ProfileTypeOptions);
1658
+ /**
1659
+ * Convenience method for slug-based lookup
1660
+ *
1661
+ * @param slug - The slug to search for
1662
+ * @returns ProfileType instance or null if not found
1663
+ */
1664
+ static getBySlug(_slug: string): Promise<ProfileType | null>;
1665
+ }
1666
+
1667
+ export declare class ProfileTypeCollection extends SmrtCollection<ProfileType> {
1668
+ static readonly _itemClass: typeof ProfileType;
1669
+ /**
1670
+ * Get profile type by slug
1671
+ *
1672
+ * @param slug - The slug to search for
1673
+ * @returns ProfileType instance or null
1674
+ */
1675
+ getBySlug(slug: string): Promise<ProfileType | null>;
1676
+ /**
1677
+ * Get or create a profile type by slug
1678
+ *
1679
+ * @param slug - The slug to search for
1680
+ * @param defaults - Default values if creating
1681
+ * @returns ProfileType instance
1682
+ */
1683
+ getOrCreateBySlug(slug: string, defaults: {
1684
+ name: string;
1685
+ description?: string;
1686
+ }): Promise<ProfileType>;
1687
+ }
1688
+
1689
+ export declare interface ProfileTypeOptions extends SmrtObjectOptions {
1690
+ slug?: string;
1691
+ name?: string;
1692
+ description?: string;
1693
+ tenantId?: string | null;
1694
+ }
1695
+
1696
+ export declare function promptMessageOptions(ai: ResolvedPromptAI): {
1697
+ maxTokens?: number | undefined;
1698
+ temperature?: number | undefined;
1699
+ model?: string | undefined;
1700
+ };
1701
+
1702
+ /**
1703
+ * Convert hex public key to npub (bech32)
1704
+ */
1705
+ export declare function pubkeyToNpub(pubkey: string): string;
1706
+
1707
+ /**
1708
+ * TypeScript type definitions for @have/profiles package
1709
+ */
1710
+ /**
1711
+ * Handler function interface for reciprocal relationships
1712
+ *
1713
+ * @param from - The profile initiating the relationship
1714
+ * @param to - The target profile
1715
+ * @param context - Optional context profile for tertiary relationships
1716
+ * @param options - Additional options for the handler
1717
+ */
1718
+ export declare type ReciprocalHandler = (from: any, to: any, context?: any, options?: any) => Promise<void>;
1719
+
1720
+ /**
1721
+ * Resolve authentication context to a Profile
1722
+ *
1723
+ * @param context - The authentication context from the request
1724
+ * @returns The resolved profile and metadata
1725
+ *
1726
+ * @example
1727
+ * ```typescript
1728
+ * // In SvelteKit hooks.server.ts
1729
+ * import { resolveIdentity } from '@happyvertical/smrt-profiles';
1730
+ *
1731
+ * const identityMiddleware: Handle = async ({ event, resolve }) => {
1732
+ * const session = await event.locals.auth();
1733
+ *
1734
+ * const { profile, source } = await resolveIdentity({
1735
+ * oidcSession: session,
1736
+ * apiKey: event.request.headers.get('X-API-Key'),
1737
+ * actor: event.request.headers.get('X-Actor'),
1738
+ * db: { type: 'postgres', url: DATABASE_URL },
1739
+ * });
1740
+ *
1741
+ * event.locals.profile = profile;
1742
+ * event.locals.authSource = source;
1743
+ *
1744
+ * return resolve(event);
1745
+ * };
1746
+ * ```
1747
+ */
1748
+ export declare function resolveIdentity(context: AuthContext): Promise<ResolveIdentityResult>;
1749
+
1750
+ /**
1751
+ * Result of identity resolution
1752
+ */
1753
+ export declare interface ResolveIdentityResult {
1754
+ /**
1755
+ * The resolved profile, or null if not authenticated
1756
+ */
1757
+ profile: Profile | null;
1758
+ /**
1759
+ * How the identity was resolved
1760
+ */
1761
+ source: 'api_key' | 'oidc' | 'nostr' | 'actor' | 'none';
1762
+ /**
1763
+ * The API key record if authenticated via API key
1764
+ */
1765
+ apiKey?: ApiKey;
1766
+ /**
1767
+ * The OIDC identity record if authenticated via OIDC
1768
+ */
1769
+ oidcIdentity?: OidcIdentity;
1770
+ /**
1771
+ * The Nostr identity record if authenticated via Nostr
1772
+ */
1773
+ nostrIdentity?: NostrIdentity;
1774
+ }
1775
+
1776
+ /**
1777
+ * Sign a Nostr event using Schnorr signatures (BIP-340)
1778
+ * @param event - Event to sign (without id and sig)
1779
+ * @param privkey - Hex-encoded private key
1780
+ */
1781
+ export declare function signEvent(event: Omit<NostrEvent, 'id' | 'sig'>, privkey: string): NostrEvent;
1782
+
1783
+ export declare const smrtProfilesGenerateBioPrompt: PromptDefinition;
1784
+
1785
+ /**
1786
+ * Validation schema structure for profile metadata fields
1787
+ */
1788
+ export declare interface ValidationSchema {
1789
+ /** Type constraint */
1790
+ type?: 'string' | 'number' | 'boolean' | 'date' | 'json';
1791
+ /** Regex pattern for string validation */
1792
+ pattern?: string;
1793
+ /** Minimum string length */
1794
+ minLength?: number;
1795
+ /** Maximum string length */
1796
+ maxLength?: number;
1797
+ /** Minimum numeric value */
1798
+ min?: number;
1799
+ /** Maximum numeric value */
1800
+ max?: number;
1801
+ /** Custom validator function name */
1802
+ custom?: string;
1803
+ /** Custom validation error message */
1804
+ message?: string;
1805
+ }
1806
+
1807
+ /**
1808
+ * Custom validator function type
1809
+ */
1810
+ export declare type ValidatorFunction = (value: any) => boolean | Promise<boolean>;
1811
+
1812
+ /**
1813
+ * Verify an authentication event
1814
+ * @param event - Auth event to verify
1815
+ * @param expectedChallenge - Expected challenge string
1816
+ * @param maxAgeSeconds - Maximum age of the event in seconds (default: 300 = 5 minutes)
1817
+ */
1818
+ export declare function verifyAuthEvent(event: NostrEvent, expectedChallenge: string, maxAgeSeconds?: number): {
1819
+ valid: boolean;
1820
+ error?: string;
1821
+ };
1822
+
1823
+ /**
1824
+ * Verify a Nostr event Schnorr signature (BIP-340)
1825
+ * @param event - Nostr event object with sig field
1826
+ */
1827
+ export declare function verifyNostrSignature(event: NostrEvent): boolean;
1828
+
1829
+ export declare interface VerifyResult {
1830
+ success: boolean;
1831
+ /** The profile */
1832
+ profile?: Profile;
1833
+ /** The Nostr identity */
1834
+ nostrIdentity?: NostrIdentity;
1835
+ /** The decrypted keypair (only on success) */
1836
+ keypair?: {
1837
+ pubkey: string;
1838
+ privkey: string;
1839
+ npub: string;
1840
+ nsec: string;
1841
+ };
1842
+ /** Error message if success is false */
1843
+ error?: string;
1844
+ /** Error code for programmatic handling */
1845
+ errorCode?: 'INVALID_TOKEN' | 'EXPIRED_TOKEN' | 'USED_TOKEN' | 'NOT_FOUND';
1846
+ }
1847
+
1848
+ export { }