@prmichaelsen/remember-core 0.12.0 → 0.15.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.
- package/README.md +33 -3
- package/dist/services/access-control.service.d.ts +140 -0
- package/dist/services/access-control.service.d.ts.map +1 -0
- package/dist/services/access-control.service.js +257 -0
- package/dist/services/access-control.service.js.map +1 -0
- package/dist/services/escalation.service.d.ts +25 -0
- package/dist/services/escalation.service.d.ts.map +1 -0
- package/dist/services/escalation.service.js +119 -0
- package/dist/services/escalation.service.js.map +1 -0
- package/dist/services/ghost-config-handler.service.d.ts +46 -0
- package/dist/services/ghost-config-handler.service.d.ts.map +1 -0
- package/dist/services/ghost-config-handler.service.js +100 -0
- package/dist/services/ghost-config-handler.service.js.map +1 -0
- package/dist/services/ghost-config.service.d.ts +58 -0
- package/dist/services/ghost-config.service.d.ts.map +1 -0
- package/dist/services/ghost-config.service.js +180 -0
- package/dist/services/ghost-config.service.js.map +1 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +7 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/memory.service.d.ts +4 -1
- package/dist/services/memory.service.d.ts.map +1 -1
- package/dist/services/memory.service.js +32 -6
- package/dist/services/memory.service.js.map +1 -1
- package/dist/services/trust-enforcement.service.d.ts +80 -0
- package/dist/services/trust-enforcement.service.d.ts.map +1 -0
- package/dist/services/trust-enforcement.service.js +174 -0
- package/dist/services/trust-enforcement.service.js.map +1 -0
- package/dist/services/trust-validator.service.d.ts +43 -0
- package/dist/services/trust-validator.service.d.ts.map +1 -0
- package/dist/services/trust-validator.service.js +86 -0
- package/dist/services/trust-validator.service.js.map +1 -0
- package/dist/testing/weaviate-mock.d.ts +15 -0
- package/dist/testing/weaviate-mock.d.ts.map +1 -1
- package/dist/testing/weaviate-mock.js +3 -0
- package/dist/testing/weaviate-mock.js.map +1 -1
- package/dist/types/access-result.types.d.ts +48 -0
- package/dist/types/access-result.types.d.ts.map +1 -0
- package/dist/types/access-result.types.js +6 -0
- package/dist/types/access-result.types.js.map +1 -0
- package/dist/types/auth.types.d.ts +11 -0
- package/dist/types/auth.types.d.ts.map +1 -1
- package/dist/types/ghost-config.types.d.ts +36 -0
- package/dist/types/ghost-config.types.d.ts.map +1 -0
- package/dist/types/ghost-config.types.js +19 -0
- package/dist/types/ghost-config.types.js.map +1 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/memory.types.d.ts +7 -0
- package/dist/types/memory.types.d.ts.map +1 -1
- package/dist/types/search.types.d.ts +12 -0
- package/dist/types/search.types.d.ts.map +1 -1
- package/dist/utils/filters.d.ts +14 -0
- package/dist/utils/filters.d.ts.map +1 -1
- package/dist/utils/filters.js +22 -0
- package/dist/utils/filters.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/web/context.d.ts +60 -0
- package/dist/web/context.d.ts.map +1 -0
- package/dist/web/context.js +22 -0
- package/dist/web/context.js.map +1 -0
- package/dist/web/errors.d.ts +23 -0
- package/dist/web/errors.d.ts.map +1 -0
- package/dist/web/errors.js +28 -0
- package/dist/web/errors.js.map +1 -0
- package/dist/web/ghost.d.ts +64 -0
- package/dist/web/ghost.d.ts.map +1 -0
- package/dist/web/ghost.js +152 -0
- package/dist/web/ghost.js.map +1 -0
- package/dist/web/guard.d.ts +6 -0
- package/dist/web/guard.d.ts.map +1 -0
- package/dist/web/guard.js +14 -0
- package/dist/web/guard.js.map +1 -0
- package/dist/web/index.d.ts +16 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +23 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/memories.d.ts +30 -0
- package/dist/web/memories.d.ts.map +1 -0
- package/dist/web/memories.js +92 -0
- package/dist/web/memories.js.map +1 -0
- package/dist/web/preferences.d.ts +6 -0
- package/dist/web/preferences.d.ts.map +1 -0
- package/dist/web/preferences.js +33 -0
- package/dist/web/preferences.js.map +1 -0
- package/dist/web/profiles.d.ts +36 -0
- package/dist/web/profiles.d.ts.map +1 -0
- package/dist/web/profiles.js +152 -0
- package/dist/web/profiles.js.map +1 -0
- package/dist/web/relationships.d.ts +22 -0
- package/dist/web/relationships.d.ts.map +1 -0
- package/dist/web/relationships.js +77 -0
- package/dist/web/relationships.js.map +1 -0
- package/dist/web/result.d.ts +37 -0
- package/dist/web/result.d.ts.map +1 -0
- package/dist/web/result.js +46 -0
- package/dist/web/result.js.map +1 -0
- package/dist/web/spaces.d.ts +44 -0
- package/dist/web/spaces.d.ts.map +1 -0
- package/dist/web/spaces.js +118 -0
- package/dist/web/spaces.js.map +1 -0
- package/dist/web/testing-helpers.d.ts +13 -0
- package/dist/web/testing-helpers.d.ts.map +1 -0
- package/dist/web/testing-helpers.js +41 -0
- package/dist/web/testing-helpers.js.map +1 -0
- package/dist/web/types.d.ts +68 -0
- package/dist/web/types.d.ts.map +1 -0
- package/dist/web/types.js +9 -0
- package/dist/web/types.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -41,13 +41,14 @@ 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 (
|
|
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 |
|
|
48
48
|
| `@prmichaelsen/remember-core/errors` | Typed errors (AppError, 8 subclasses) |
|
|
49
49
|
| `@prmichaelsen/remember-core/utils` | Logger, filters, auth helpers, debug |
|
|
50
50
|
| `@prmichaelsen/remember-core/testing` | Weaviate mock, test data generator |
|
|
51
|
+
| `@prmichaelsen/remember-core/web` | Web SDK — use-case-oriented server-side functions |
|
|
51
52
|
|
|
52
53
|
## Core Services
|
|
53
54
|
|
|
@@ -61,10 +62,39 @@ const results = await memoryService.search({
|
|
|
61
62
|
|
|
62
63
|
**ConfirmationTokenService** — Time-limited one-use tokens for sensitive operations (5-minute expiry).
|
|
63
64
|
|
|
65
|
+
### Trust & Ghost System
|
|
66
|
+
|
|
67
|
+
**TrustEnforcementService** — 5-tier content redaction (Full Access → Existence Only), query-level and prompt-level enforcement modes.
|
|
68
|
+
|
|
69
|
+
**AccessControlService** — Per-memory access checks with 6-step resolution (self → ghost → block → trust → grant), trust escalation prevention, `canRevise()`/`canOverwrite()` permission resolution.
|
|
70
|
+
|
|
71
|
+
**GhostConfigService** — Firestore-backed ghost persona configuration CRUD (trust levels, blocked users, enforcement mode).
|
|
72
|
+
|
|
73
|
+
**EscalationService** — Trust penalty tracking and automatic blocking after repeated unauthorized access attempts.
|
|
74
|
+
|
|
75
|
+
### Web SDK (`@prmichaelsen/remember-core/web`)
|
|
76
|
+
|
|
77
|
+
Server-side, use-case-oriented functions that bundle multi-step business logic into single calls. 31 functions across 6 modules:
|
|
78
|
+
|
|
79
|
+
- **Memories** (6) — CRUD + search with `Result<T>` responses
|
|
80
|
+
- **Relationships** (4) — CRUD + search
|
|
81
|
+
- **Spaces** (7) — publish/retract/revise auto-confirmed (no tokens), moderate, search, query
|
|
82
|
+
- **Ghost/Trust** (8) — config, trust management, `searchAsGhost` (resolves trust internally)
|
|
83
|
+
- **Profiles** (4) — compound operations (create+publish, search, retract, update+republish)
|
|
84
|
+
- **Preferences** (2) — get/update
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { createWebSDKContext, createAndPublishProfile, searchAsGhost } from '@prmichaelsen/remember-core/web';
|
|
88
|
+
|
|
89
|
+
const ctx = createWebSDKContext({ userId, memoryService, spaceService, ... });
|
|
90
|
+
const result = await createAndPublishProfile(ctx, { display_name: 'Jane', bio: 'Engineer' });
|
|
91
|
+
if (result.ok) console.log(result.data.composite_id);
|
|
92
|
+
```
|
|
93
|
+
|
|
64
94
|
## Testing
|
|
65
95
|
|
|
66
96
|
```bash
|
|
67
|
-
npm test # Unit tests (
|
|
97
|
+
npm test # Unit tests (323 tests)
|
|
68
98
|
npm run test:e2e # Integration tests (22 tests)
|
|
69
99
|
npm run typecheck # Type checking
|
|
70
100
|
npm run build # TypeScript compilation
|
|
@@ -85,7 +115,7 @@ remember-core (this package)
|
|
|
85
115
|
├── database/ Weaviate + Firestore initialization
|
|
86
116
|
├── collections/ Weaviate collection utilities
|
|
87
117
|
├── utils/ Logger, filters, auth helpers
|
|
88
|
-
├── services/ Business logic (5 service
|
|
118
|
+
├── services/ Business logic (5 core + 4 trust/ghost service modules)
|
|
89
119
|
└── testing/ Mock infrastructure for consumers
|
|
90
120
|
|
|
91
121
|
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":"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"}
|
|
@@ -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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"}
|