@prmichaelsen/remember-mcp 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.
Files changed (95) hide show
  1. package/.env.example +65 -0
  2. package/AGENT.md +840 -0
  3. package/README.md +72 -0
  4. package/agent/design/.gitkeep +0 -0
  5. package/agent/design/access-control-result-pattern.md +458 -0
  6. package/agent/design/action-audit-memory-types.md +637 -0
  7. package/agent/design/common-template-fields.md +282 -0
  8. package/agent/design/complete-tool-set.md +407 -0
  9. package/agent/design/content-types-expansion.md +521 -0
  10. package/agent/design/cross-database-id-strategy.md +358 -0
  11. package/agent/design/default-template-library.md +423 -0
  12. package/agent/design/firestore-wrapper-analysis.md +606 -0
  13. package/agent/design/llm-provider-abstraction.md +691 -0
  14. package/agent/design/location-handling-architecture.md +523 -0
  15. package/agent/design/memory-templates-design.md +364 -0
  16. package/agent/design/permissions-storage-architecture.md +680 -0
  17. package/agent/design/relationship-storage-strategy.md +361 -0
  18. package/agent/design/remember-mcp-implementation-tasks.md +417 -0
  19. package/agent/design/remember-mcp-progress.yaml +141 -0
  20. package/agent/design/requirements-enhancements.md +468 -0
  21. package/agent/design/requirements.md +56 -0
  22. package/agent/design/template-storage-strategy.md +412 -0
  23. package/agent/design/template-suggestion-system.md +853 -0
  24. package/agent/design/trust-escalation-prevention.md +343 -0
  25. package/agent/design/trust-system-implementation.md +592 -0
  26. package/agent/design/user-preferences.md +683 -0
  27. package/agent/design/weaviate-collection-strategy.md +461 -0
  28. package/agent/milestones/.gitkeep +0 -0
  29. package/agent/milestones/milestone-1-project-foundation.md +121 -0
  30. package/agent/milestones/milestone-2-core-memory-system.md +150 -0
  31. package/agent/milestones/milestone-3-relationships-graph.md +116 -0
  32. package/agent/milestones/milestone-4-user-preferences.md +103 -0
  33. package/agent/milestones/milestone-5-template-system.md +126 -0
  34. package/agent/milestones/milestone-6-auth-multi-tenancy.md +124 -0
  35. package/agent/milestones/milestone-7-trust-permissions.md +133 -0
  36. package/agent/milestones/milestone-8-testing-quality.md +137 -0
  37. package/agent/milestones/milestone-9-deployment-documentation.md +147 -0
  38. package/agent/patterns/.gitkeep +0 -0
  39. package/agent/patterns/bootstrap.md +1271 -0
  40. package/agent/patterns/firebase-admin-sdk-v8-usage.md +950 -0
  41. package/agent/patterns/firestore-users-pattern-best-practices.md +347 -0
  42. package/agent/patterns/library-services.md +454 -0
  43. package/agent/patterns/testing-colocated.md +316 -0
  44. package/agent/progress.yaml +395 -0
  45. package/agent/tasks/.gitkeep +0 -0
  46. package/agent/tasks/task-1-initialize-project-structure.md +266 -0
  47. package/agent/tasks/task-2-install-dependencies.md +199 -0
  48. package/agent/tasks/task-3-setup-weaviate-client.md +330 -0
  49. package/agent/tasks/task-4-setup-firestore-client.md +362 -0
  50. package/agent/tasks/task-5-create-basic-mcp-server.md +114 -0
  51. package/agent/tasks/task-6-create-integration-tests.md +195 -0
  52. package/agent/tasks/task-7-finalize-milestone-1.md +363 -0
  53. package/agent/tasks/task-8-setup-utility-scripts.md +382 -0
  54. package/agent/tasks/task-9-create-server-factory.md +404 -0
  55. package/dist/config.d.ts +26 -0
  56. package/dist/constants/content-types.d.ts +60 -0
  57. package/dist/firestore/init.d.ts +14 -0
  58. package/dist/firestore/paths.d.ts +53 -0
  59. package/dist/firestore/paths.spec.d.ts +2 -0
  60. package/dist/server-factory.d.ts +40 -0
  61. package/dist/server-factory.js +1741 -0
  62. package/dist/server-factory.spec.d.ts +2 -0
  63. package/dist/server.d.ts +3 -0
  64. package/dist/server.js +1690 -0
  65. package/dist/tools/create-memory.d.ts +94 -0
  66. package/dist/tools/delete-memory.d.ts +47 -0
  67. package/dist/tools/search-memory.d.ts +88 -0
  68. package/dist/types/memory.d.ts +183 -0
  69. package/dist/utils/logger.d.ts +7 -0
  70. package/dist/weaviate/client.d.ts +39 -0
  71. package/dist/weaviate/client.spec.d.ts +2 -0
  72. package/dist/weaviate/schema.d.ts +29 -0
  73. package/esbuild.build.js +60 -0
  74. package/esbuild.watch.js +25 -0
  75. package/jest.config.js +31 -0
  76. package/jest.e2e.config.js +17 -0
  77. package/package.json +68 -0
  78. package/src/.gitkeep +0 -0
  79. package/src/config.ts +56 -0
  80. package/src/constants/content-types.ts +454 -0
  81. package/src/firestore/init.ts +68 -0
  82. package/src/firestore/paths.spec.ts +75 -0
  83. package/src/firestore/paths.ts +124 -0
  84. package/src/server-factory.spec.ts +60 -0
  85. package/src/server-factory.ts +215 -0
  86. package/src/server.ts +243 -0
  87. package/src/tools/create-memory.ts +198 -0
  88. package/src/tools/delete-memory.ts +126 -0
  89. package/src/tools/search-memory.ts +216 -0
  90. package/src/types/memory.ts +276 -0
  91. package/src/utils/logger.ts +42 -0
  92. package/src/weaviate/client.spec.ts +58 -0
  93. package/src/weaviate/client.ts +114 -0
  94. package/src/weaviate/schema.ts +288 -0
  95. package/tsconfig.json +26 -0
@@ -0,0 +1,680 @@
1
+ # Permissions & Trust Storage Architecture
2
+
3
+ **Concept**: Storage strategy for user permissions and trust relationships
4
+ **Created**: 2026-02-11
5
+ **Status**: Design Specification
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ We need to store:
12
+ 1. **User Permissions**: Which user_id can access which user's persona/memories
13
+ 2. **Trust Relationships**: Trust levels between users
14
+ 3. **Trust Context**: Why trust was granted, history, etc.
15
+
16
+ ---
17
+
18
+ ## Storage Options Analysis
19
+
20
+ ### Option 1: Separate Database (e.g., Firestore)
21
+
22
+ **Pros**:
23
+ - ✅ Optimized for relational queries
24
+ - ✅ Real-time updates and subscriptions
25
+ - ✅ Built-in security rules
26
+ - ✅ Easy to query "who can access my memories?"
27
+ - ✅ Separate concerns (permissions vs memories)
28
+ - ✅ Better for complex permission logic
29
+ - ✅ Easier to audit and manage
30
+ - ✅ Can use Firebase Auth integration
31
+
32
+ **Cons**:
33
+ - ❌ Additional database to manage
34
+ - ❌ Cross-database queries more complex
35
+ - ❌ Potential consistency issues
36
+ - ❌ Extra network hop for permission checks
37
+
38
+ **Best For**: Complex permission scenarios, frequent permission queries, real-time updates
39
+
40
+ ---
41
+
42
+ ### Option 2: Weaviate Database (Structured)
43
+
44
+ **Pros**:
45
+ - ✅ Single database for everything
46
+ - ✅ No cross-database queries
47
+ - ✅ Simpler architecture
48
+ - ✅ Consistent data model
49
+ - ✅ Can leverage vector search for trust patterns
50
+
51
+ **Cons**:
52
+ - ❌ Weaviate not optimized for relational queries
53
+ - ❌ No built-in security rules
54
+ - ❌ Harder to query complex permission graphs
55
+ - ❌ Less flexible for permission logic
56
+ - ❌ Mixing concerns (permissions + memories)
57
+
58
+ **Best For**: Simple permission scenarios, minimal permission queries
59
+
60
+ ---
61
+
62
+ ## Recommended Approach: Hybrid Strategy
63
+
64
+ **Use Firestore for permissions, Weaviate for memories**
65
+
66
+ ### Why Hybrid?
67
+
68
+ 1. **Separation of Concerns**
69
+ - Firestore: User relationships, permissions, trust
70
+ - Weaviate: Memories, content, semantic search
71
+
72
+ 2. **Optimized for Use Case**
73
+ - Firestore excels at relational data
74
+ - Weaviate excels at vector search
75
+
76
+ 3. **Scalability**
77
+ - Permission checks are fast (Firestore)
78
+ - Memory search is fast (Weaviate)
79
+ - Each database does what it's best at
80
+
81
+ 4. **Security**
82
+ - Firestore security rules for permissions
83
+ - Weaviate collection isolation for memories
84
+
85
+ ---
86
+
87
+ ## Architecture Design
88
+
89
+ ### System Overview
90
+
91
+ ```
92
+ ┌─────────────────────────────────────────────────────────────┐
93
+ │ agentbase.me Platform │
94
+ │ - Firebase Auth (user authentication) │
95
+ │ - Firestore (permissions & trust relationships) │
96
+ └────────────────┬────────────────────────────────────────────┘
97
+
98
+ │ MCP Request with auth token
99
+
100
+ ┌────────────────▼────────────────────────────────────────────┐
101
+ │ remember-mcp Server │
102
+ │ │
103
+ │ ┌──────────────────────────────────────────────────────┐ │
104
+ │ │ Permission Layer │ │
105
+ │ │ 1. Validate Firebase token → user_id │ │
106
+ │ │ 2. Check Firestore for permissions │ │
107
+ │ │ 3. Get trust relationships │ │
108
+ │ └────────────────┬─────────────────────────────────────┘ │
109
+ │ │ │
110
+ │ ┌────────────────▼─────────────────────────────────────┐ │
111
+ │ │ Memory Layer │ │
112
+ │ │ 1. Query Weaviate with user_id scope │ │
113
+ │ │ 2. Apply trust filtering │ │
114
+ │ │ 3. Format with trust context │ │
115
+ │ └──────────────────────────────────────────────────────┘ │
116
+ └─────────────────────────────────────────────────────────────┘
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Firestore Schema
122
+
123
+ ### Collection: `user_permissions`
124
+
125
+ ```typescript
126
+ // Document path: user_permissions/{owner_user_id}/allowed_accessors/{accessor_user_id}
127
+ interface UserPermission {
128
+ // Identity
129
+ owner_user_id: string; // User whose memories can be accessed
130
+ accessor_user_id: string; // User who can access
131
+
132
+ // Permission
133
+ can_access: boolean; // Is access allowed?
134
+ access_level: string; // "read", "read_write", "admin"
135
+
136
+ // Trust
137
+ trust_level: number; // 0-1, continuous trust score
138
+ trust_summary: string; // Brief explanation of trust
139
+ trust_reason: string; // Detailed reason for trust level
140
+
141
+ // Scope
142
+ allowed_memory_types: string[]; // Which memory types can be accessed
143
+ allowed_tags: string[]; // Which tags can be accessed
144
+ excluded_tags: string[]; // Which tags are forbidden
145
+
146
+ // Temporal
147
+ granted_at: Timestamp;
148
+ expires_at: Timestamp | null; // Optional expiration
149
+ last_accessed: Timestamp;
150
+ access_count: number;
151
+
152
+ // Metadata
153
+ granted_by: string; // Who granted this permission
154
+ revoked: boolean;
155
+ revoked_at: Timestamp | null;
156
+ revoked_reason: string | null;
157
+ }
158
+ ```
159
+
160
+ **Example Document**:
161
+ ```javascript
162
+ // user_permissions/alice_123/allowed_accessors/bob_456
163
+ {
164
+ owner_user_id: "alice_123",
165
+ accessor_user_id: "bob_456",
166
+ can_access: true,
167
+ access_level: "read",
168
+ trust_level: 0.7,
169
+ trust_summary: "Close friend, can see most memories",
170
+ trust_reason: "Bob is a trusted friend. We've known each other for 5 years...",
171
+ allowed_memory_types: ["note", "event", "location"],
172
+ allowed_tags: ["travel", "food", "movies"],
173
+ excluded_tags: ["medical", "financial", "private"],
174
+ granted_at: Timestamp.now(),
175
+ expires_at: null,
176
+ last_accessed: Timestamp.now(),
177
+ access_count: 42,
178
+ granted_by: "alice_123",
179
+ revoked: false
180
+ }
181
+ ```
182
+
183
+ ### Collection: `trust_history`
184
+
185
+ ```typescript
186
+ // Document path: trust_history/{owner_user_id}/history/{history_id}
187
+ interface TrustHistoryEntry {
188
+ owner_user_id: string;
189
+ accessor_user_id: string;
190
+
191
+ // Change
192
+ previous_trust: number;
193
+ new_trust: number;
194
+ change_reason: string;
195
+
196
+ // Context
197
+ changed_at: Timestamp;
198
+ changed_by: string;
199
+ conversation_id: string | null;
200
+
201
+ // Evidence
202
+ evidence: {
203
+ type: string; // "positive_interaction", "violation", "manual_adjustment"
204
+ description: string;
205
+ severity: number; // 0-1
206
+ }[];
207
+ }
208
+ ```
209
+
210
+ ### Collection: `persona_access`
211
+
212
+ ```typescript
213
+ // Document path: persona_access/{persona_id}/allowed_users/{user_id}
214
+ interface PersonaAccess {
215
+ persona_id: string; // Which persona/agent
216
+ user_id: string; // Which user can talk to it
217
+
218
+ // Access
219
+ can_interact: boolean;
220
+ interaction_level: string; // "basic", "full", "admin"
221
+
222
+ // Limits
223
+ rate_limit: number; // Requests per hour
224
+ daily_limit: number; // Requests per day
225
+
226
+ // Temporal
227
+ granted_at: Timestamp;
228
+ expires_at: Timestamp | null;
229
+ last_interaction: Timestamp;
230
+ interaction_count: number;
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Weaviate Schema
237
+
238
+ ### Collection: `Memory_{user_id}`
239
+
240
+ ```yaml
241
+ Memory:
242
+ # Core fields (as defined in requirements)
243
+ id: uuid
244
+ user_id: string
245
+ content: text
246
+ # ... other memory fields ...
247
+
248
+ # Trust metadata (stored but not used for filtering)
249
+ default_trust: float # Default trust for this memory
250
+ trust_override: object # Per-user trust overrides
251
+ user_id: string
252
+ trust: float
253
+ reason: string
254
+ ```
255
+
256
+ **Note**: Trust relationships are primarily in Firestore, but memories can have default trust levels and per-user overrides stored in Weaviate for performance.
257
+
258
+ ---
259
+
260
+ ## Permission Check Flow
261
+
262
+ ### 1. Request Arrives
263
+
264
+ ```typescript
265
+ async function handleMCPRequest(
266
+ tool: string,
267
+ args: any,
268
+ context: RequestContext
269
+ ): Promise<any> {
270
+ // 1. Validate Firebase token
271
+ const authResult = await firebaseAuth.verifyToken(context.auth_token);
272
+ const accessor_user_id = authResult.uid;
273
+
274
+ // 2. Determine target user
275
+ const target_user_id = args.user_id || accessor_user_id;
276
+
277
+ // 3. Check permissions (if accessing another user's memories)
278
+ if (accessor_user_id !== target_user_id) {
279
+ const permission = await checkPermission(target_user_id, accessor_user_id);
280
+
281
+ if (!permission.can_access) {
282
+ throw new Error('Access denied');
283
+ }
284
+
285
+ // Store permission context for trust filtering
286
+ context.permission = permission;
287
+ }
288
+
289
+ // 4. Execute tool with permission context
290
+ return await executeTool(tool, args, context);
291
+ }
292
+ ```
293
+
294
+ ### 2. Check Permission (Firestore)
295
+
296
+ ```typescript
297
+ async function checkPermission(
298
+ owner_user_id: string,
299
+ accessor_user_id: string
300
+ ): Promise<UserPermission | null> {
301
+ // Query Firestore
302
+ const doc = await firestore
303
+ .collection('user_permissions')
304
+ .doc(owner_user_id)
305
+ .collection('allowed_accessors')
306
+ .doc(accessor_user_id)
307
+ .get();
308
+
309
+ if (!doc.exists) {
310
+ return null; // No permission granted
311
+ }
312
+
313
+ const permission = doc.data() as UserPermission;
314
+
315
+ // Check if revoked
316
+ if (permission.revoked) {
317
+ return null;
318
+ }
319
+
320
+ // Check if expired
321
+ if (permission.expires_at && permission.expires_at < Timestamp.now()) {
322
+ return null;
323
+ }
324
+
325
+ // Update last accessed
326
+ await doc.ref.update({
327
+ last_accessed: Timestamp.now(),
328
+ access_count: FieldValue.increment(1)
329
+ });
330
+
331
+ return permission;
332
+ }
333
+ ```
334
+
335
+ ### 3. Query Memories with Trust Context
336
+
337
+ ```typescript
338
+ async function searchMemories(
339
+ query: string,
340
+ owner_user_id: string,
341
+ context: RequestContext
342
+ ): Promise<Memory[]> {
343
+ // 1. Query Weaviate (user's collection)
344
+ const memories = await weaviateClient.searchDocuments(
345
+ query,
346
+ {}, // filters
347
+ 10, // limit
348
+ owner_user_id // collection scope
349
+ );
350
+
351
+ // 2. Apply trust filtering
352
+ const permission = context.permission;
353
+
354
+ if (permission) {
355
+ // Accessing another user's memories
356
+ return memories
357
+ .filter(m => isMemoryAllowed(m, permission))
358
+ .map(m => applyTrustLevel(m, permission.trust_level));
359
+ }
360
+
361
+ // Accessing own memories - full access
362
+ return memories;
363
+ }
364
+ ```
365
+
366
+ ### 4. Filter by Permission Scope
367
+
368
+ ```typescript
369
+ function isMemoryAllowed(
370
+ memory: Memory,
371
+ permission: UserPermission
372
+ ): boolean {
373
+ // Check memory type
374
+ if (permission.allowed_memory_types.length > 0) {
375
+ if (!permission.allowed_memory_types.includes(memory.type)) {
376
+ return false;
377
+ }
378
+ }
379
+
380
+ // Check excluded tags
381
+ if (permission.excluded_tags.length > 0) {
382
+ const hasExcludedTag = memory.tags.some(tag =>
383
+ permission.excluded_tags.includes(tag)
384
+ );
385
+ if (hasExcludedTag) {
386
+ return false;
387
+ }
388
+ }
389
+
390
+ // Check allowed tags (if specified)
391
+ if (permission.allowed_tags.length > 0) {
392
+ const hasAllowedTag = memory.tags.some(tag =>
393
+ permission.allowed_tags.includes(tag)
394
+ );
395
+ if (!hasAllowedTag) {
396
+ return false;
397
+ }
398
+ }
399
+
400
+ return true;
401
+ }
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Firestore Security Rules
407
+
408
+ ```javascript
409
+ rules_version = '2';
410
+ service cloud.firestore {
411
+ match /databases/{database}/documents {
412
+
413
+ // User permissions
414
+ match /user_permissions/{owner_user_id}/allowed_accessors/{accessor_user_id} {
415
+ // Owner can read/write their own permissions
416
+ allow read, write: if request.auth.uid == owner_user_id;
417
+
418
+ // Accessor can read their permission (but not write)
419
+ allow read: if request.auth.uid == accessor_user_id;
420
+
421
+ // Prevent self-permission escalation
422
+ allow write: if request.auth.uid == owner_user_id
423
+ && accessor_user_id != owner_user_id;
424
+ }
425
+
426
+ // Trust history
427
+ match /trust_history/{owner_user_id}/history/{history_id} {
428
+ // Owner can read their trust history
429
+ allow read: if request.auth.uid == owner_user_id;
430
+
431
+ // System can write (via admin SDK)
432
+ allow write: if false; // Only via admin SDK
433
+ }
434
+
435
+ // Persona access
436
+ match /persona_access/{persona_id}/allowed_users/{user_id} {
437
+ // User can read their own access
438
+ allow read: if request.auth.uid == user_id;
439
+
440
+ // Persona owner can manage access
441
+ allow write: if request.auth.uid == getPersonaOwner(persona_id);
442
+ }
443
+ }
444
+ }
445
+ ```
446
+
447
+ ---
448
+
449
+ ## API Examples
450
+
451
+ ### Grant Permission
452
+
453
+ ```typescript
454
+ // User Alice grants Bob access to her memories
455
+ async function grantPermission(
456
+ owner_user_id: string,
457
+ accessor_user_id: string,
458
+ trust_level: number,
459
+ options: PermissionOptions
460
+ ): Promise<void> {
461
+ const permission: UserPermission = {
462
+ owner_user_id,
463
+ accessor_user_id,
464
+ can_access: true,
465
+ access_level: options.access_level || "read",
466
+ trust_level,
467
+ trust_summary: options.trust_summary,
468
+ trust_reason: options.trust_reason,
469
+ allowed_memory_types: options.allowed_memory_types || [],
470
+ allowed_tags: options.allowed_tags || [],
471
+ excluded_tags: options.excluded_tags || [],
472
+ granted_at: Timestamp.now(),
473
+ expires_at: options.expires_at || null,
474
+ last_accessed: Timestamp.now(),
475
+ access_count: 0,
476
+ granted_by: owner_user_id,
477
+ revoked: false,
478
+ revoked_at: null,
479
+ revoked_reason: null
480
+ };
481
+
482
+ await firestore
483
+ .collection('user_permissions')
484
+ .doc(owner_user_id)
485
+ .collection('allowed_accessors')
486
+ .doc(accessor_user_id)
487
+ .set(permission);
488
+ }
489
+ ```
490
+
491
+ ### Update Trust Level
492
+
493
+ ```typescript
494
+ async function updateTrustLevel(
495
+ owner_user_id: string,
496
+ accessor_user_id: string,
497
+ new_trust: number,
498
+ reason: string
499
+ ): Promise<void> {
500
+ const permissionRef = firestore
501
+ .collection('user_permissions')
502
+ .doc(owner_user_id)
503
+ .collection('allowed_accessors')
504
+ .doc(accessor_user_id);
505
+
506
+ const doc = await permissionRef.get();
507
+ const current = doc.data() as UserPermission;
508
+
509
+ // Update permission
510
+ await permissionRef.update({
511
+ trust_level: new_trust,
512
+ trust_reason: reason
513
+ });
514
+
515
+ // Log history
516
+ await firestore
517
+ .collection('trust_history')
518
+ .doc(owner_user_id)
519
+ .collection('history')
520
+ .add({
521
+ owner_user_id,
522
+ accessor_user_id,
523
+ previous_trust: current.trust_level,
524
+ new_trust,
525
+ change_reason: reason,
526
+ changed_at: Timestamp.now(),
527
+ changed_by: owner_user_id,
528
+ conversation_id: null,
529
+ evidence: []
530
+ });
531
+ }
532
+ ```
533
+
534
+ ### Revoke Permission
535
+
536
+ ```typescript
537
+ async function revokePermission(
538
+ owner_user_id: string,
539
+ accessor_user_id: string,
540
+ reason: string
541
+ ): Promise<void> {
542
+ await firestore
543
+ .collection('user_permissions')
544
+ .doc(owner_user_id)
545
+ .collection('allowed_accessors')
546
+ .doc(accessor_user_id)
547
+ .update({
548
+ revoked: true,
549
+ revoked_at: Timestamp.now(),
550
+ revoked_reason: reason
551
+ });
552
+ }
553
+ ```
554
+
555
+ ### List Who Can Access My Memories
556
+
557
+ ```typescript
558
+ async function listAccessors(owner_user_id: string): Promise<UserPermission[]> {
559
+ const snapshot = await firestore
560
+ .collection('user_permissions')
561
+ .doc(owner_user_id)
562
+ .collection('allowed_accessors')
563
+ .where('revoked', '==', false)
564
+ .orderBy('trust_level', 'desc')
565
+ .get();
566
+
567
+ return snapshot.docs.map(doc => doc.data() as UserPermission);
568
+ }
569
+ ```
570
+
571
+ ---
572
+
573
+ ## Performance Optimization
574
+
575
+ ### 1. Cache Permissions
576
+
577
+ ```typescript
578
+ // In-memory cache for frequently checked permissions
579
+ const permissionCache = new Map<string, UserPermission>();
580
+
581
+ async function checkPermissionCached(
582
+ owner_user_id: string,
583
+ accessor_user_id: string
584
+ ): Promise<UserPermission | null> {
585
+ const cacheKey = `${owner_user_id}:${accessor_user_id}`;
586
+
587
+ // Check cache
588
+ if (permissionCache.has(cacheKey)) {
589
+ const cached = permissionCache.get(cacheKey)!;
590
+
591
+ // Validate cache (5 minute TTL)
592
+ if (Date.now() - cached.last_accessed.toMillis() < 300000) {
593
+ return cached;
594
+ }
595
+ }
596
+
597
+ // Fetch from Firestore
598
+ const permission = await checkPermission(owner_user_id, accessor_user_id);
599
+
600
+ if (permission) {
601
+ permissionCache.set(cacheKey, permission);
602
+ }
603
+
604
+ return permission;
605
+ }
606
+ ```
607
+
608
+ ### 2. Batch Permission Checks
609
+
610
+ ```typescript
611
+ async function checkPermissionsBatch(
612
+ owner_user_id: string,
613
+ accessor_user_ids: string[]
614
+ ): Promise<Map<string, UserPermission>> {
615
+ const results = new Map();
616
+
617
+ // Batch read from Firestore
618
+ const refs = accessor_user_ids.map(id =>
619
+ firestore
620
+ .collection('user_permissions')
621
+ .doc(owner_user_id)
622
+ .collection('allowed_accessors')
623
+ .doc(id)
624
+ );
625
+
626
+ const docs = await firestore.getAll(...refs);
627
+
628
+ docs.forEach((doc, index) => {
629
+ if (doc.exists) {
630
+ results.set(accessor_user_ids[index], doc.data() as UserPermission);
631
+ }
632
+ });
633
+
634
+ return results;
635
+ }
636
+ ```
637
+
638
+ ---
639
+
640
+ ## Migration Strategy
641
+
642
+ ### Phase 1: Firestore Setup
643
+ 1. Create Firestore collections
644
+ 2. Define security rules
645
+ 3. Set up indexes
646
+ 4. Create admin tools for permission management
647
+
648
+ ### Phase 2: Integration
649
+ 1. Add Firestore client to remember-mcp
650
+ 2. Implement permission check layer
651
+ 3. Add caching
652
+ 4. Test with sample permissions
653
+
654
+ ### Phase 3: UI
655
+ 1. Add permission management UI to agentbase.me
656
+ 2. Allow users to grant/revoke access
657
+ 3. Show trust history
658
+ 4. Display who can access memories
659
+
660
+ ---
661
+
662
+ ## Recommendation
663
+
664
+ **Use Firestore for permissions** because:
665
+
666
+ 1. ✅ **Optimized for relational queries** - "Who can access my memories?"
667
+ 2. ✅ **Real-time updates** - Permission changes take effect immediately
668
+ 3. ✅ **Security rules** - Built-in access control
669
+ 4. ✅ **Firebase integration** - Works seamlessly with Firebase Auth
670
+ 5. ✅ **Scalable** - Handles millions of permission records
671
+ 6. ✅ **Auditable** - Easy to track permission changes
672
+ 7. ✅ **Flexible** - Can add complex permission logic without affecting Weaviate
673
+
674
+ **Cost**: Minimal - permission checks are infrequent and can be cached
675
+
676
+ ---
677
+
678
+ **Status**: Design Specification
679
+ **Recommendation**: Hybrid approach - Firestore for permissions, Weaviate for memories
680
+ **Next Step**: Implement Firestore schema and permission check layer