@prmichaelsen/remember-core 0.12.0 → 0.13.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 (54) hide show
  1. package/README.md +13 -3
  2. package/dist/services/access-control.service.d.ts +140 -0
  3. package/dist/services/access-control.service.d.ts.map +1 -0
  4. package/dist/services/access-control.service.js +257 -0
  5. package/dist/services/access-control.service.js.map +1 -0
  6. package/dist/services/escalation.service.d.ts +25 -0
  7. package/dist/services/escalation.service.d.ts.map +1 -0
  8. package/dist/services/escalation.service.js +119 -0
  9. package/dist/services/escalation.service.js.map +1 -0
  10. package/dist/services/ghost-config-handler.service.d.ts +46 -0
  11. package/dist/services/ghost-config-handler.service.d.ts.map +1 -0
  12. package/dist/services/ghost-config-handler.service.js +100 -0
  13. package/dist/services/ghost-config-handler.service.js.map +1 -0
  14. package/dist/services/ghost-config.service.d.ts +58 -0
  15. package/dist/services/ghost-config.service.d.ts.map +1 -0
  16. package/dist/services/ghost-config.service.js +180 -0
  17. package/dist/services/ghost-config.service.js.map +1 -0
  18. package/dist/services/index.d.ts +6 -0
  19. package/dist/services/index.d.ts.map +1 -1
  20. package/dist/services/index.js +7 -0
  21. package/dist/services/index.js.map +1 -1
  22. package/dist/services/trust-enforcement.service.d.ts +80 -0
  23. package/dist/services/trust-enforcement.service.d.ts.map +1 -0
  24. package/dist/services/trust-enforcement.service.js +174 -0
  25. package/dist/services/trust-enforcement.service.js.map +1 -0
  26. package/dist/services/trust-validator.service.d.ts +43 -0
  27. package/dist/services/trust-validator.service.d.ts.map +1 -0
  28. package/dist/services/trust-validator.service.js +86 -0
  29. package/dist/services/trust-validator.service.js.map +1 -0
  30. package/dist/types/access-result.types.d.ts +48 -0
  31. package/dist/types/access-result.types.d.ts.map +1 -0
  32. package/dist/types/access-result.types.js +6 -0
  33. package/dist/types/access-result.types.js.map +1 -0
  34. package/dist/types/auth.types.d.ts +11 -0
  35. package/dist/types/auth.types.d.ts.map +1 -1
  36. package/dist/types/ghost-config.types.d.ts +36 -0
  37. package/dist/types/ghost-config.types.d.ts.map +1 -0
  38. package/dist/types/ghost-config.types.js +19 -0
  39. package/dist/types/ghost-config.types.js.map +1 -0
  40. package/dist/types/index.d.ts +4 -1
  41. package/dist/types/index.d.ts.map +1 -1
  42. package/dist/types/index.js +1 -0
  43. package/dist/types/index.js.map +1 -1
  44. package/dist/types/memory.types.d.ts +7 -0
  45. package/dist/types/memory.types.d.ts.map +1 -1
  46. package/dist/utils/filters.d.ts +14 -0
  47. package/dist/utils/filters.d.ts.map +1 -1
  48. package/dist/utils/filters.js +22 -0
  49. package/dist/utils/filters.js.map +1 -1
  50. package/dist/utils/index.d.ts +1 -1
  51. package/dist/utils/index.d.ts.map +1 -1
  52. package/dist/utils/index.js +1 -1
  53. package/dist/utils/index.js.map +1 -1
  54. package/package.json +1 -1
package/README.md CHANGED
@@ -41,7 +41,7 @@ const results = await memoryService.search({
41
41
  | `@prmichaelsen/remember-core/types` | All type definitions and interfaces |
42
42
  | `@prmichaelsen/remember-core/services` | Service classes and input/output types |
43
43
  | `@prmichaelsen/remember-core/collections` | Composite IDs, tracking arrays, dot notation |
44
- | `@prmichaelsen/remember-core/constants` | Content types (41 types across 8 categories) |
44
+ | `@prmichaelsen/remember-core/constants` | Content types (43 types across 11 categories) |
45
45
  | `@prmichaelsen/remember-core/config` | Environment config, debug levels |
46
46
  | `@prmichaelsen/remember-core/database/weaviate` | Weaviate client, schema, space collections |
47
47
  | `@prmichaelsen/remember-core/database/firestore` | Firestore init, path helpers |
@@ -61,10 +61,20 @@ const results = await memoryService.search({
61
61
 
62
62
  **ConfirmationTokenService** — Time-limited one-use tokens for sensitive operations (5-minute expiry).
63
63
 
64
+ ### Trust & Ghost System
65
+
66
+ **TrustEnforcementService** — 5-tier content redaction (Full Access → Existence Only), query-level and prompt-level enforcement modes.
67
+
68
+ **AccessControlService** — Per-memory access checks with 6-step resolution (self → ghost → block → trust → grant), trust escalation prevention, `canRevise()`/`canOverwrite()` permission resolution.
69
+
70
+ **GhostConfigService** — Firestore-backed ghost persona configuration CRUD (trust levels, blocked users, enforcement mode).
71
+
72
+ **EscalationService** — Trust penalty tracking and automatic blocking after repeated unauthorized access attempts.
73
+
64
74
  ## Testing
65
75
 
66
76
  ```bash
67
- npm test # Unit tests (120 tests)
77
+ npm test # Unit tests (270 tests)
68
78
  npm run test:e2e # Integration tests (22 tests)
69
79
  npm run typecheck # Type checking
70
80
  npm run build # TypeScript compilation
@@ -85,7 +95,7 @@ remember-core (this package)
85
95
  ├── database/ Weaviate + Firestore initialization
86
96
  ├── collections/ Weaviate collection utilities
87
97
  ├── utils/ Logger, filters, auth helpers
88
- ├── services/ Business logic (5 service classes)
98
+ ├── services/ Business logic (5 core + 4 trust/ghost service modules)
89
99
  └── testing/ Mock infrastructure for consumers
90
100
 
91
101
  remember-mcp-server (consumer)
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Access control service — per-memory access checks with escalation prevention.
3
+ *
4
+ * In ghost mode (default), query-level filtering handles trust at the Weaviate layer.
5
+ * This service is needed for:
6
+ * 1. Trust escalation penalty tracking
7
+ * 2. Block management
8
+ * 3. Prompt/hybrid enforcement modes (per-memory access checks)
9
+ * 4. Future direct access tools
10
+ *
11
+ * Ported from remember-mcp/src/services/access-control.ts
12
+ */
13
+ import type { Memory } from '../types/memory.types.js';
14
+ import type { AccessResult } from '../types/access-result.types.js';
15
+ import type { GhostConfig } from '../types/ghost-config.types.js';
16
+ import type { WriteMode, UserCredentials } from '../types/auth.types.js';
17
+ /** Block record for a specific (accessor, memory) pair */
18
+ export interface MemoryBlock {
19
+ blocked_at: string;
20
+ reason: string;
21
+ attempt_count: number;
22
+ }
23
+ /** Attempt record for escalation tracking */
24
+ export interface AttemptRecord {
25
+ count: number;
26
+ last_attempt_at: string;
27
+ }
28
+ /**
29
+ * Provider interface for GhostConfig lookups.
30
+ * In-memory stub included, Firestore implementation in ghost-config.service.ts.
31
+ */
32
+ export interface GhostConfigProvider {
33
+ getGhostConfig(ownerUserId: string): Promise<GhostConfig | null>;
34
+ }
35
+ /**
36
+ * Provider interface for block and attempt tracking.
37
+ * In-memory stub included, Firestore implementation in escalation.service.ts.
38
+ */
39
+ export interface EscalationStore {
40
+ getBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<MemoryBlock | null>;
41
+ setBlock(ownerUserId: string, accessorUserId: string, memoryId: string, block: MemoryBlock): Promise<void>;
42
+ removeBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<void>;
43
+ getAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord | null>;
44
+ incrementAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord>;
45
+ }
46
+ /** Stub GhostConfig provider — returns null (ghost not configured) */
47
+ export declare class StubGhostConfigProvider implements GhostConfigProvider {
48
+ private configs;
49
+ getGhostConfig(ownerUserId: string): Promise<GhostConfig | null>;
50
+ /** Test helper: set a GhostConfig for a user */
51
+ setGhostConfig(ownerUserId: string, config: GhostConfig): void;
52
+ }
53
+ /** In-memory escalation store for development/testing */
54
+ export declare class InMemoryEscalationStore implements EscalationStore {
55
+ private blocks;
56
+ private attempts;
57
+ private key;
58
+ getBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<MemoryBlock | null>;
59
+ setBlock(ownerUserId: string, accessorUserId: string, memoryId: string, block: MemoryBlock): Promise<void>;
60
+ removeBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<void>;
61
+ getAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord | null>;
62
+ incrementAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord>;
63
+ }
64
+ /** Trust penalty applied per failed access attempt (after repeated attempts) */
65
+ export declare const TRUST_PENALTY = 0.1;
66
+ /** Number of failed attempts before blocking */
67
+ export declare const MAX_ATTEMPTS_BEFORE_BLOCK = 3;
68
+ /**
69
+ * Check if an accessor has permission to access a specific memory.
70
+ *
71
+ * Flow:
72
+ * 1. Self-access → always granted (owner)
73
+ * 2. Ghost not enabled → no_permission
74
+ * 3. Accessor blocked by owner → no_permission
75
+ * 4. Memory-specific block → blocked
76
+ * 5. Insufficient trust → insufficient_trust (+ penalty on repeated attempts, possible block)
77
+ * 6. Sufficient trust → granted (trusted; trust 1.0 memories capped to existence-only by formatting layer)
78
+ */
79
+ export declare function checkMemoryAccess(accessorUserId: string, memory: Memory, ghostConfigProvider: GhostConfigProvider, escalationStore: EscalationStore): Promise<AccessResult>;
80
+ /**
81
+ * Handle an insufficient trust access attempt.
82
+ * Tracks attempts; applies -0.1 penalty on repeated attempts, blocks after 3.
83
+ */
84
+ export declare function handleInsufficientTrust(ownerUserId: string, accessorUserId: string, memoryId: string, requiredTrust: number, actualTrust: number, escalationStore: EscalationStore): Promise<AccessResult>;
85
+ /**
86
+ * Check if access to a specific memory is blocked.
87
+ */
88
+ export declare function isMemoryBlocked(ownerUserId: string, accessorUserId: string, memoryId: string, escalationStore: EscalationStore): Promise<boolean>;
89
+ /**
90
+ * Reset a memory-specific block (e.g., via grant_access).
91
+ */
92
+ export declare function resetBlock(ownerUserId: string, accessorUserId: string, memoryId: string, escalationStore: EscalationStore): Promise<void>;
93
+ /**
94
+ * Resolve the trust level for an accessor from GhostConfig.
95
+ *
96
+ * Priority: per_user_trust → default_public_trust → 0
97
+ *
98
+ * Note: "friend" vs "public" distinction will be determined by the calling
99
+ * context when friend list/social graph is available. For now, non-per_user
100
+ * accessors fall through to default_public_trust.
101
+ */
102
+ export declare function resolveAccessorTrustLevel(ghostConfig: GhostConfig, accessorUserId: string): number;
103
+ /**
104
+ * Format an AccessResult into a human-readable message.
105
+ */
106
+ export declare function formatAccessResultMessage(result: AccessResult): string;
107
+ /**
108
+ * Minimal type for published memory ACL fields used by permission resolution.
109
+ * Task 19 will add these to the Weaviate schema; this type covers the fields
110
+ * needed by canRevise/canOverwrite without coupling to the full schema.
111
+ */
112
+ export interface PublishedMemoryACL {
113
+ author_id: string;
114
+ owner_id?: string | null;
115
+ write_mode?: WriteMode | null;
116
+ overwrite_allowed_ids?: string[];
117
+ group_ids?: string[];
118
+ }
119
+ /**
120
+ * Check if a user can revise (edit content of) a published memory.
121
+ *
122
+ * Resolution:
123
+ * - Owner always can revise
124
+ * - write_mode 'owner_only' (default): owner only
125
+ * - write_mode 'group_editors': users with can_revise permission in a shared group
126
+ * - write_mode 'anyone': any authenticated user
127
+ */
128
+ export declare function canRevise(userId: string, memory: PublishedMemoryACL, credentialsFetcher?: () => Promise<UserCredentials>): Promise<boolean>;
129
+ /**
130
+ * Check if a user can overwrite (replace/destructively edit) a published memory.
131
+ *
132
+ * Resolution:
133
+ * - Owner always can overwrite
134
+ * - Explicit per-memory grant via overwrite_allowed_ids
135
+ * - write_mode 'owner_only' (default): owner + overwrite_allowed_ids only
136
+ * - write_mode 'group_editors': users with can_overwrite permission in a shared group
137
+ * - write_mode 'anyone': any authenticated user
138
+ */
139
+ export declare function canOverwrite(userId: string, memory: PublishedMemoryACL, credentialsFetcher?: () => Promise<UserCredentials>): Promise<boolean>;
140
+ //# sourceMappingURL=access-control.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-control.service.d.ts","sourceRoot":"","sources":["../../src/services/access-control.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzE,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3G,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC1G,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC1G;AAID,sEAAsE;AACtE,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,OAAO,CAAC,OAAO,CAAuC;IAEhD,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAItE,gDAAgD;IAChD,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;CAG/D;AAED,yDAAyD;AACzD,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,QAAQ,CAAyC;IAEzD,OAAO,CAAC,GAAG;IAIL,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAIpG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1G,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzF,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAIzG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CAU/G;AAID,gFAAgF;AAChF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAI3C;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,mBAAmB,EACxC,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,YAAY,CAAC,CA6CvB;AAID;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,YAAY,CAAC,CA0BvB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,IAAI,CAAC,CAEf;AAID;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAQlG;AAID;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAiBtE;AAID;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAID;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,kBAAkB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CAsBlB"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Access control service — per-memory access checks with escalation prevention.
3
+ *
4
+ * In ghost mode (default), query-level filtering handles trust at the Weaviate layer.
5
+ * This service is needed for:
6
+ * 1. Trust escalation penalty tracking
7
+ * 2. Block management
8
+ * 3. Prompt/hybrid enforcement modes (per-memory access checks)
9
+ * 4. Future direct access tools
10
+ *
11
+ * Ported from remember-mcp/src/services/access-control.ts
12
+ */
13
+ import { isTrustSufficient } from './trust-enforcement.service.js';
14
+ // ─── In-Memory Implementations ────────────────────────────────────────────
15
+ /** Stub GhostConfig provider — returns null (ghost not configured) */
16
+ export class StubGhostConfigProvider {
17
+ configs = new Map();
18
+ async getGhostConfig(ownerUserId) {
19
+ return this.configs.get(ownerUserId) ?? null;
20
+ }
21
+ /** Test helper: set a GhostConfig for a user */
22
+ setGhostConfig(ownerUserId, config) {
23
+ this.configs.set(ownerUserId, config);
24
+ }
25
+ }
26
+ /** In-memory escalation store for development/testing */
27
+ export class InMemoryEscalationStore {
28
+ blocks = new Map();
29
+ attempts = new Map();
30
+ key(ownerUserId, accessorUserId, memoryId) {
31
+ return `${ownerUserId}:${accessorUserId}:${memoryId}`;
32
+ }
33
+ async getBlock(ownerUserId, accessorUserId, memoryId) {
34
+ return this.blocks.get(this.key(ownerUserId, accessorUserId, memoryId)) ?? null;
35
+ }
36
+ async setBlock(ownerUserId, accessorUserId, memoryId, block) {
37
+ this.blocks.set(this.key(ownerUserId, accessorUserId, memoryId), block);
38
+ }
39
+ async removeBlock(ownerUserId, accessorUserId, memoryId) {
40
+ this.blocks.delete(this.key(ownerUserId, accessorUserId, memoryId));
41
+ }
42
+ async getAttempts(ownerUserId, accessorUserId, memoryId) {
43
+ return this.attempts.get(this.key(ownerUserId, accessorUserId, memoryId)) ?? null;
44
+ }
45
+ async incrementAttempts(ownerUserId, accessorUserId, memoryId) {
46
+ const k = this.key(ownerUserId, accessorUserId, memoryId);
47
+ const existing = this.attempts.get(k);
48
+ const record = {
49
+ count: (existing?.count ?? 0) + 1,
50
+ last_attempt_at: new Date().toISOString(),
51
+ };
52
+ this.attempts.set(k, record);
53
+ return record;
54
+ }
55
+ }
56
+ // ─── Constants ─────────────────────────────────────────────────────────────
57
+ /** Trust penalty applied per failed access attempt (after repeated attempts) */
58
+ export const TRUST_PENALTY = 0.1;
59
+ /** Number of failed attempts before blocking */
60
+ export const MAX_ATTEMPTS_BEFORE_BLOCK = 3;
61
+ // ─── Core Access Control ───────────────────────────────────────────────────
62
+ /**
63
+ * Check if an accessor has permission to access a specific memory.
64
+ *
65
+ * Flow:
66
+ * 1. Self-access → always granted (owner)
67
+ * 2. Ghost not enabled → no_permission
68
+ * 3. Accessor blocked by owner → no_permission
69
+ * 4. Memory-specific block → blocked
70
+ * 5. Insufficient trust → insufficient_trust (+ penalty on repeated attempts, possible block)
71
+ * 6. Sufficient trust → granted (trusted; trust 1.0 memories capped to existence-only by formatting layer)
72
+ */
73
+ export async function checkMemoryAccess(accessorUserId, memory, ghostConfigProvider, escalationStore) {
74
+ const ownerUserId = memory.user_id;
75
+ const memoryId = memory.id;
76
+ // 1. Self-access always granted
77
+ if (accessorUserId === ownerUserId) {
78
+ return { status: 'granted', memory, access_level: 'owner' };
79
+ }
80
+ // 2. Check if ghost is enabled for owner
81
+ const ghostConfig = await ghostConfigProvider.getGhostConfig(ownerUserId);
82
+ if (!ghostConfig || !ghostConfig.enabled) {
83
+ return { status: 'no_permission', owner_user_id: ownerUserId, accessor_user_id: accessorUserId };
84
+ }
85
+ // 3. Check if accessor is user-wide blocked
86
+ if (ghostConfig.blocked_users.includes(accessorUserId)) {
87
+ return { status: 'no_permission', owner_user_id: ownerUserId, accessor_user_id: accessorUserId };
88
+ }
89
+ // 4. Check memory-specific block
90
+ const block = await escalationStore.getBlock(ownerUserId, accessorUserId, memoryId);
91
+ if (block) {
92
+ return {
93
+ status: 'blocked',
94
+ memory_id: memoryId,
95
+ reason: block.reason,
96
+ blocked_at: block.blocked_at,
97
+ };
98
+ }
99
+ // 5. Check trust level
100
+ const accessorTrust = resolveAccessorTrustLevel(ghostConfig, accessorUserId);
101
+ const memoryTrust = memory.trust;
102
+ if (!isTrustSufficient(memoryTrust, accessorTrust)) {
103
+ // Apply escalation
104
+ const result = await handleInsufficientTrust(ownerUserId, accessorUserId, memoryId, memoryTrust, accessorTrust, escalationStore);
105
+ return result;
106
+ }
107
+ // 6. All checks pass (trust 1.0 memories capped to existence-only by formatting layer)
108
+ return { status: 'granted', memory, access_level: 'trusted' };
109
+ }
110
+ // ─── Trust Escalation ──────────────────────────────────────────────────────
111
+ /**
112
+ * Handle an insufficient trust access attempt.
113
+ * Tracks attempts; applies -0.1 penalty on repeated attempts, blocks after 3.
114
+ */
115
+ export async function handleInsufficientTrust(ownerUserId, accessorUserId, memoryId, requiredTrust, actualTrust, escalationStore) {
116
+ const attempt = await escalationStore.incrementAttempts(ownerUserId, accessorUserId, memoryId);
117
+ // Block after MAX_ATTEMPTS_BEFORE_BLOCK
118
+ if (attempt.count >= MAX_ATTEMPTS_BEFORE_BLOCK) {
119
+ const block = {
120
+ blocked_at: new Date().toISOString(),
121
+ reason: `Access blocked after ${attempt.count} unauthorized attempts`,
122
+ attempt_count: attempt.count,
123
+ };
124
+ await escalationStore.setBlock(ownerUserId, accessorUserId, memoryId, block);
125
+ return {
126
+ status: 'blocked',
127
+ memory_id: memoryId,
128
+ reason: block.reason,
129
+ blocked_at: block.blocked_at,
130
+ };
131
+ }
132
+ return {
133
+ status: 'insufficient_trust',
134
+ memory_id: memoryId,
135
+ required_trust: requiredTrust,
136
+ actual_trust: Math.max(0, actualTrust - TRUST_PENALTY),
137
+ attempts_remaining: MAX_ATTEMPTS_BEFORE_BLOCK - attempt.count,
138
+ };
139
+ }
140
+ /**
141
+ * Check if access to a specific memory is blocked.
142
+ */
143
+ export async function isMemoryBlocked(ownerUserId, accessorUserId, memoryId, escalationStore) {
144
+ const block = await escalationStore.getBlock(ownerUserId, accessorUserId, memoryId);
145
+ return block !== null;
146
+ }
147
+ /**
148
+ * Reset a memory-specific block (e.g., via grant_access).
149
+ */
150
+ export async function resetBlock(ownerUserId, accessorUserId, memoryId, escalationStore) {
151
+ await escalationStore.removeBlock(ownerUserId, accessorUserId, memoryId);
152
+ }
153
+ // ─── Trust Resolution ──────────────────────────────────────────────────────
154
+ /**
155
+ * Resolve the trust level for an accessor from GhostConfig.
156
+ *
157
+ * Priority: per_user_trust → default_public_trust → 0
158
+ *
159
+ * Note: "friend" vs "public" distinction will be determined by the calling
160
+ * context when friend list/social graph is available. For now, non-per_user
161
+ * accessors fall through to default_public_trust.
162
+ */
163
+ export function resolveAccessorTrustLevel(ghostConfig, accessorUserId) {
164
+ // 1. Per-user override
165
+ if (accessorUserId in ghostConfig.per_user_trust) {
166
+ return ghostConfig.per_user_trust[accessorUserId];
167
+ }
168
+ // 2. Fall through to public trust (friend detection deferred)
169
+ return ghostConfig.default_public_trust ?? 0;
170
+ }
171
+ // ─── Message Formatting ───────────────────────────────────────────────────
172
+ /**
173
+ * Format an AccessResult into a human-readable message.
174
+ */
175
+ export function formatAccessResultMessage(result) {
176
+ switch (result.status) {
177
+ case 'granted':
178
+ return result.access_level === 'owner'
179
+ ? 'Access granted (owner).'
180
+ : 'Access granted (trusted).';
181
+ case 'insufficient_trust':
182
+ return `Insufficient trust level. Required: ${result.required_trust.toFixed(2)}, actual: ${result.actual_trust.toFixed(2)}. ${result.attempts_remaining} attempt(s) remaining before access is blocked.`;
183
+ case 'blocked':
184
+ return `Access blocked: ${result.reason}`;
185
+ case 'no_permission':
186
+ return 'No permission to access this user\'s memories.';
187
+ case 'not_found':
188
+ return `Memory ${result.memory_id} not found.`;
189
+ case 'deleted':
190
+ return `Memory ${result.memory_id} was deleted on ${result.deleted_at}.`;
191
+ }
192
+ }
193
+ // ─── Permission Resolution (Write ACL) ───────────────────────────────────
194
+ /**
195
+ * Check if a user can revise (edit content of) a published memory.
196
+ *
197
+ * Resolution:
198
+ * - Owner always can revise
199
+ * - write_mode 'owner_only' (default): owner only
200
+ * - write_mode 'group_editors': users with can_revise permission in a shared group
201
+ * - write_mode 'anyone': any authenticated user
202
+ */
203
+ export async function canRevise(userId, memory, credentialsFetcher) {
204
+ const owner = memory.owner_id ?? memory.author_id;
205
+ if (userId === owner)
206
+ return true;
207
+ const writeMode = memory.write_mode ?? 'owner_only';
208
+ switch (writeMode) {
209
+ case 'owner_only':
210
+ return false;
211
+ case 'group_editors': {
212
+ if (!credentialsFetcher)
213
+ return false;
214
+ const credentials = await credentialsFetcher();
215
+ return (memory.group_ids ?? []).some(gid => {
216
+ const membership = credentials.group_memberships.find(m => m.group_id === gid);
217
+ return membership?.permissions.can_revise === true;
218
+ });
219
+ }
220
+ case 'anyone':
221
+ return true;
222
+ }
223
+ }
224
+ /**
225
+ * Check if a user can overwrite (replace/destructively edit) a published memory.
226
+ *
227
+ * Resolution:
228
+ * - Owner always can overwrite
229
+ * - Explicit per-memory grant via overwrite_allowed_ids
230
+ * - write_mode 'owner_only' (default): owner + overwrite_allowed_ids only
231
+ * - write_mode 'group_editors': users with can_overwrite permission in a shared group
232
+ * - write_mode 'anyone': any authenticated user
233
+ */
234
+ export async function canOverwrite(userId, memory, credentialsFetcher) {
235
+ const owner = memory.owner_id ?? memory.author_id;
236
+ if (userId === owner)
237
+ return true;
238
+ if ((memory.overwrite_allowed_ids ?? []).includes(userId))
239
+ return true;
240
+ const writeMode = memory.write_mode ?? 'owner_only';
241
+ switch (writeMode) {
242
+ case 'owner_only':
243
+ return false;
244
+ case 'group_editors': {
245
+ if (!credentialsFetcher)
246
+ return false;
247
+ const credentials = await credentialsFetcher();
248
+ return (memory.group_ids ?? []).some(gid => {
249
+ const membership = credentials.group_memberships.find(m => m.group_id === gid);
250
+ return membership?.permissions.can_overwrite === true;
251
+ });
252
+ }
253
+ case 'anyone':
254
+ return true;
255
+ }
256
+ }
257
+ //# sourceMappingURL=access-control.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-control.service.js","sourceRoot":"","sources":["../../src/services/access-control.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Firestore-backed Escalation Store
3
+ *
4
+ * Persists trust escalation tracking (attempt counts, blocks) to Firestore.
5
+ * Replaces InMemoryEscalationStore for production use.
6
+ *
7
+ * Firestore path: {BASE}.users/{ownerUserId}/ghost_escalation/{accessorUserId}:{memoryId}
8
+ *
9
+ * Ported from remember-mcp/src/services/escalation.service.ts
10
+ */
11
+ import type { Logger } from '../utils/logger.js';
12
+ import type { EscalationStore, MemoryBlock, AttemptRecord } from './access-control.service.js';
13
+ /**
14
+ * Firestore-backed escalation store for production use.
15
+ */
16
+ export declare class FirestoreEscalationStore implements EscalationStore {
17
+ private logger?;
18
+ constructor(logger?: Logger | undefined);
19
+ getBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<MemoryBlock | null>;
20
+ setBlock(ownerUserId: string, accessorUserId: string, memoryId: string, block: MemoryBlock): Promise<void>;
21
+ removeBlock(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<void>;
22
+ getAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord | null>;
23
+ incrementAttempts(ownerUserId: string, accessorUserId: string, memoryId: string): Promise<AttemptRecord>;
24
+ }
25
+ //# sourceMappingURL=escalation.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalation.service.d.ts","sourceRoot":"","sources":["../../src/services/escalation.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAmB/F;;GAEG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAClD,OAAO,CAAC,MAAM,CAAC;gBAAP,MAAM,CAAC,EAAE,MAAM,YAAA;IAE7B,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IA4BpG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB1G,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAczF,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA2BzG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CAmB/G"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Firestore-backed Escalation Store
3
+ *
4
+ * Persists trust escalation tracking (attempt counts, blocks) to Firestore.
5
+ * Replaces InMemoryEscalationStore for production use.
6
+ *
7
+ * Firestore path: {BASE}.users/{ownerUserId}/ghost_escalation/{accessorUserId}:{memoryId}
8
+ *
9
+ * Ported from remember-mcp/src/services/escalation.service.ts
10
+ */
11
+ import { getDocument, setDocument, deleteDocument } from '../database/firestore/init.js';
12
+ import { BASE } from '../database/firestore/paths.js';
13
+ const SERVICE = 'EscalationService';
14
+ /**
15
+ * Get Firestore collection path for escalation data.
16
+ */
17
+ function getEscalationPath(ownerUserId) {
18
+ return `${BASE}.users/${ownerUserId}/ghost_escalation`;
19
+ }
20
+ /**
21
+ * Build a document ID from accessor and memory.
22
+ * Uses colon separator (safe in Firestore doc IDs).
23
+ */
24
+ function docId(accessorUserId, memoryId) {
25
+ return `${accessorUserId}:${memoryId}`;
26
+ }
27
+ /**
28
+ * Firestore-backed escalation store for production use.
29
+ */
30
+ export class FirestoreEscalationStore {
31
+ logger;
32
+ constructor(logger) {
33
+ this.logger = logger;
34
+ }
35
+ async getBlock(ownerUserId, accessorUserId, memoryId) {
36
+ try {
37
+ const doc = await getDocument(getEscalationPath(ownerUserId), docId(accessorUserId, memoryId));
38
+ if (!doc || !doc.blocked) {
39
+ return null;
40
+ }
41
+ return {
42
+ blocked_at: doc.blocked_at,
43
+ reason: doc.reason || 'Trust escalation limit reached',
44
+ attempt_count: doc.attempt_count || 0,
45
+ };
46
+ }
47
+ catch (error) {
48
+ this.logger?.error('Failed to get block', {
49
+ service: SERVICE,
50
+ ownerUserId,
51
+ accessorUserId,
52
+ memoryId,
53
+ error: error instanceof Error ? error.message : String(error),
54
+ });
55
+ return null;
56
+ }
57
+ }
58
+ async setBlock(ownerUserId, accessorUserId, memoryId, block) {
59
+ await setDocument(getEscalationPath(ownerUserId), docId(accessorUserId, memoryId), {
60
+ blocked: true,
61
+ blocked_at: block.blocked_at,
62
+ reason: block.reason,
63
+ attempt_count: block.attempt_count,
64
+ accessor_user_id: accessorUserId,
65
+ memory_id: memoryId,
66
+ }, { merge: true });
67
+ this.logger?.info('User blocked from memory', {
68
+ service: SERVICE,
69
+ ownerUserId,
70
+ accessorUserId,
71
+ memoryId,
72
+ reason: block.reason,
73
+ });
74
+ }
75
+ async removeBlock(ownerUserId, accessorUserId, memoryId) {
76
+ await deleteDocument(getEscalationPath(ownerUserId), docId(accessorUserId, memoryId));
77
+ this.logger?.info('Block removed', {
78
+ service: SERVICE,
79
+ ownerUserId,
80
+ accessorUserId,
81
+ memoryId,
82
+ });
83
+ }
84
+ async getAttempts(ownerUserId, accessorUserId, memoryId) {
85
+ try {
86
+ const doc = await getDocument(getEscalationPath(ownerUserId), docId(accessorUserId, memoryId));
87
+ if (!doc || doc.attempt_count === undefined) {
88
+ return null;
89
+ }
90
+ return {
91
+ count: doc.attempt_count,
92
+ last_attempt_at: doc.last_attempt_at,
93
+ };
94
+ }
95
+ catch (error) {
96
+ this.logger?.error('Failed to get attempts', {
97
+ service: SERVICE,
98
+ ownerUserId,
99
+ accessorUserId,
100
+ memoryId,
101
+ error: error instanceof Error ? error.message : String(error),
102
+ });
103
+ return null;
104
+ }
105
+ }
106
+ async incrementAttempts(ownerUserId, accessorUserId, memoryId) {
107
+ const existing = await this.getAttempts(ownerUserId, accessorUserId, memoryId);
108
+ const newCount = (existing?.count ?? 0) + 1;
109
+ const now = new Date().toISOString();
110
+ await setDocument(getEscalationPath(ownerUserId), docId(accessorUserId, memoryId), {
111
+ attempt_count: newCount,
112
+ last_attempt_at: now,
113
+ accessor_user_id: accessorUserId,
114
+ memory_id: memoryId,
115
+ }, { merge: true });
116
+ return { count: newCount, last_attempt_at: now };
117
+ }
118
+ }
119
+ //# sourceMappingURL=escalation.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalation.service.js","sourceRoot":"","sources":["../../src/services/escalation.service.ts"],"names":[],"mappings":"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"}