Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,28 @@
"Bash(/Users/nick/.claude/skills/gstack/browse/dist/browse goto *)",
"Bash(/Users/nick/.claude/skills/gstack/browse/dist/browse text *)",
"Bash(/Users/nick/.claude/skills/gstack/browse/dist/browse wait *)",
"Bash(/Users/nick/.claude/skills/gstack/browse/dist/browse useragent *)"
"Bash(/Users/nick/.claude/skills/gstack/browse/dist/browse useragent *)",
"mcp__plugin_firebase_firebase__firebase_get_environment",
"Bash(node -e 'const now=Date.now\\(\\); console.log\\(\"now_ms\",now\\); console.log\\(\"cutoff_ms\",now-7*86400000\\); console.log\\(\"now_iso\",new Date\\(now\\).toISOString\\(\\)\\); console.log\\(\"cutoff_iso\",new Date\\(now-7*86400000\\).toISOString\\(\\)\\);')",
"mcp__plugin_firebase_firebase__firestore_query_collection",
"mcp__plugin_firebase_firebase__functions_get_logs",
"Bash(jq -r .entries[].severity /Users/nick/.claude/projects/-Users-nick-git-metacortex/1e637d4d-3ecc-4a86-9686-ce889b97a544/tool-results/mcp-plugin_firebase_firebase-functions_get_logs-1780688372658.txt)",
"Read(//private/tmp/**)",
"Bash(node -e \"require\\('pptxgenjs'\\); console.log\\('pptxgenjs ok'\\)\")",
"Bash(node -e \"require\\('react-icons/fa'\\); require\\('sharp'\\); console.log\\('icons ok'\\)\")",
"Bash(node -e 'require\\('\\\\''__TRACKED_VAR__'\\\\''\\)')",
"Bash(npm root *)",
"Bash(echo \"npm root: $\\(npm root -g\\)\")",
"Bash(npm install *)",
"Bash(node build.js)",
"Bash(which soffice *)",
"Bash(python -m markitdown MetaCortex-Usage-Report.pptx)",
"Bash(grep -iE \"\\\\bx{3,}\\\\b|lorem|ipsum|undefined|\\\\[insert|placeholder\")",
"Bash(cp /tmp/deck/MetaCortex-Usage-Report.pptx ~/Desktop/MetaCortex-Usage-Report.pptx)",
"Read(//Users/nick/Desktop/**)",
"Bash(cd /tmp/deck && node build.js 2>&1 | tail -3 && cp MetaCortex-Usage-Report.pptx ~/Desktop/MetaCortex-Usage-Report.pptx && echo \"redeployed to Desktop\")",
"Bash(ls /Applications/Keynote.app >/dev/null 2>&1 && echo \"Keynote present\" || echo \"no Keynote\")",
"Bash(node -e ' *)"
]
}
}
1 change: 0 additions & 1 deletion .claude/worktrees/compassionate-lederberg
Submodule compassionate-lederberg deleted from 944e40
1 change: 0 additions & 1 deletion .claude/worktrees/sweet-villani
Submodule sweet-villani deleted from 1596b5
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Test fakes in `functions/test/support/fakes.ts`:
| Variable | Default | Purpose |
|----------|---------|---------|
| `GEMINI_EMBEDDING_MODEL` | `text-embedding-004` | Embedding model name |
| `GEMINI_MULTIMODAL_MODEL` | `gemini-3.1-flash-lite-preview` | Multimodal normalization model |
| `GEMINI_MULTIMODAL_MODEL` | `gemini-3.1-flash-lite` | Multimodal normalization model |
| `GEMINI_EMBEDDING_DIMENSIONS` | `768` | Embedding vector dimensions |
| `MEMORY_COLLECTION` | `memory_vectors` | Firestore collection name |
| `SEARCH_RESULT_LIMIT` | `5` | Max search results returned |
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,6 @@ Typical result:
"id": "abc123",
"summary": "We use Ktor for shared Android and iOS networking.",
"score": 0.92,
"content_preview": "We use Ktor for shared Android and iOS networking.",
"metadata": {
"topic": "kmp-networking",
"branch_state": "active",
Expand Down Expand Up @@ -287,7 +286,7 @@ If nothing matches, the result is:

### `fetch_context`

Preferred input: pass the same `id` returned by `remember_context` or `search_context`.
Preferred input: pass the same `id` returned by `remember_context` or `search_context`. `document_id` is accepted as a compatibility alias for older connector wrappers.

Example input:

Expand All @@ -297,6 +296,14 @@ Example input:
}
```

Compatibility alias:

```json
{
"document_id": "abc123"
}
```

Typical result:

```json
Expand Down Expand Up @@ -401,6 +408,7 @@ After deployment, there are three places to look:
- `event_type`
- `status`
- `timestamp`
- `expires_at`
- `latency_ms`
- a compact `request` summary
- either a compact `response` summary, an `error`, or a request rejection reason
Expand All @@ -427,6 +435,12 @@ What is intentionally not stored in observability events:

Search events do include a short `query_preview`, but the observability collection is designed to track behavior, not duplicate the corpus.

Retention is handled with Firestore TTL policies:

- `memory_events.expires_at` targets 90-day audit retention
- `memory_vectors_write_fingerprints.expires_at` targets 30-day fingerprint retention
- fingerprint documents keep numeric `dedupe_expires_at` for the short duplicate-write window

## Quick start

1. Install dependencies:
Expand Down
65 changes: 57 additions & 8 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ The deploy path in this repo currently assumes:
- Firestore collection `memory_events` for audit and observability
- embedding output pinned to `768` dimensions
- embedding model pinned to `text-embedding-004`
- multimodal normalization model pinned to `gemini-3.1-flash-lite-preview`
- total MCP surface of 4 tools
- multimodal normalization model pinned to `gemini-3.1-flash-lite`
- total MCP surface of 5 tools
- public/browser toolset of 3 tools: `remember_context`, `search_context`, `fetch_context`
- admin-only maintenance tool: `deprecate_context`
- WIP consolidation handled internally, not through a public MCP tool
- admin-only maintenance tools: `deprecate_context`, `consolidate_context`
- WIP consolidation is available only through the admin maintenance surface

For the first production release, if `memory_vectors` is empty, no embedding migration is required.

Expand Down Expand Up @@ -79,7 +79,8 @@ Minimum required production values:
GEMINI_API_KEY=...
MCP_ADMIN_TOKEN=...
GEMINI_EMBEDDING_MODEL=text-embedding-004
GEMINI_MULTIMODAL_MODEL=gemini-3.1-flash-lite-preview
GEMINI_MULTIMODAL_MODEL=gemini-3.1-flash-lite
GEMINI_GENERATION_VERTEX_LOCATION=global
GEMINI_EMBEDDING_DIMENSIONS=768
MEMORY_COLLECTION=memory_vectors
```
Expand Down Expand Up @@ -154,6 +155,13 @@ That script checks:
- full test suite
- TypeScript build

Validate the live Gemini model configuration before production deploy:

```bash
cd /Users/nick/git/metacortex
npm --prefix functions run validate:models
```

If you want a manual local round-trip before production:

```bash
Expand Down Expand Up @@ -192,6 +200,7 @@ The automated tests and build can also be run directly:
cd /Users/nick/git/metacortex
npm --prefix functions test
npm --prefix functions run build
npm --prefix functions run validate:models
```

## Deploy
Expand All @@ -215,6 +224,8 @@ Verify that `functions/.env.prod` or the dotenv file you plan to deploy with inc
- `MCP_ALLOWED_ORIGINS` only if you intentionally want browser access to the admin endpoint
- `MCP_CLIENT_PROFILES_JSON` with both `chatgpt-web` and `claude-web` profiles
- `GEMINI_EMBEDDING_MODEL=text-embedding-004`
- `GEMINI_MULTIMODAL_MODEL=gemini-3.1-flash-lite`
- `GEMINI_GENERATION_VERTEX_LOCATION=global`
- `GEMINI_EMBEDDING_DIMENSIONS=768`
- `MEMORY_COLLECTION=memory_vectors`

Expand All @@ -229,7 +240,30 @@ Also confirm the actual web-client registration values you will use:
- each bearer token comes from the matching client profile, not `MCP_ADMIN_TOKEN`
- each web origin must match the profile's `allowedOrigins`

### 3. Deploy Firestore indexes
### 3. Backfill TTL fields

The hardening release uses Firestore TTL policies for unbounded operational collections:

- `memory_vectors_write_fingerprints.expires_at`: 30-day retention
- `memory_events.expires_at`: 90-day retention

Run a dry run first:

```bash
cd /Users/nick/git/metacortex
npm --prefix functions run backfill:ttl
```

If the counts look correct, apply the backfill:

```bash
cd /Users/nick/git/metacortex
npm --prefix functions run backfill:ttl -- --write --project my-brain-88870
```

The backfill preserves numeric event `timestamp`, copies legacy numeric fingerprint `expires_at` into `dedupe_expires_at` when needed, and writes Date-valued `expires_at` fields for Firestore TTL.

### 4. Deploy Firestore indexes

```bash
cd /Users/nick/git/metacortex
Expand All @@ -244,7 +278,7 @@ Required vector indexes:

Wait until those indexes are fully built before trusting search results.

### 4. Deploy the function
### 5. Deploy the function

```bash
cd /Users/nick/git/metacortex
Expand All @@ -266,6 +300,21 @@ The useful production routes are:
- `<FUNCTION_BASE_URL>/mcp`
- `<FUNCTION_BASE_URL>/clients/<CLIENT_ID>/mcp`

### 6. Enable Firestore TTL policies

Enable TTL policies after the `expires_at` fields exist:

```bash
cd /Users/nick/git/metacortex
./scripts/deploy-firestore-ttl.sh --project my-brain-88870
```

Verify the policies:

```bash
gcloud firestore fields ttls list --project=my-brain-88870
```

## Post-deploy verification

### 1. Health check
Expand Down Expand Up @@ -402,7 +451,7 @@ Expected:

- `remember_context` accepts the image-backed memory
- returned JSON metadata includes `modality=mixed` when both text and image are present
- `search_context` returns a summary with the same `id=...`
- `search_context` returns a summary-only result with the same `id=...`
- `fetch_context` accepts that same `id` and returns the same `artifact_refs`

## Token Management
Expand Down
10 changes: 7 additions & 3 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ Known security warnings for the deployed MetaCortex service. These are documente

---

## WARN-1: `memory_events` collection not in Firestore rules
## FIXED-1: `memory_events` and fingerprint collections explicitly denied

**File:** `firestore.rules`

The `memory_events` audit log collection is not explicitly covered by Firestore security rules. It is currently protected only by Firestore's implicit default-deny behavior. A future rules edit could inadvertently open it.
`memory_events` and `memory_vectors_write_fingerprints` are explicitly covered by deny-all Firestore security rules. They are server-only collections and should remain inaccessible to client SDK traffic.

**Fix:** Add an explicit deny rule for `memory_events`:
Current rule shape:
```
match /memory_events/{document=**} {
allow read, write: if false;
}

match /memory_vectors_write_fingerprints/{document=**} {
allow read, write: if false;
}
```

---
Expand Down
4 changes: 1 addition & 3 deletions firestore-debug.log
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Mar 10, 2026 10:41:40 PM com.google.cloud.datastore.emulator.firestore.websocket.WebSocketServer start
Jun 01, 2026 8:59:26 PM com.google.cloud.datastore.emulator.firestore.websocket.WebSocketServer start
INFO: Started WebSocket server on ws://127.0.0.1:9150
API endpoint: http://127.0.0.1:8080
If you are using a library that supports the FIRESTORE_EMULATOR_HOST environment variable, run:
Expand All @@ -12,7 +12,5 @@ If you are running a Firestore in Datastore Mode project, run:
Note: Support for Datastore Mode is in preview. If you encounter any bugs please file at https://github.com/firebase/firebase-tools/issues.
Dev App Server is now running.

Mar 10, 2026 10:41:48 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
INFO: Detected HTTP/2 connection.
*** shutting down gRPC server since JVM is shutting down
*** server shut down
3 changes: 2 additions & 1 deletion functions/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ MCP_ALLOWED_FILTER_STATES=active,merged,deprecated,wip
# Browser and other scoped clients should use allowedOrigins inside MCP_CLIENT_PROFILES_JSON.
MCP_CLIENT_PROFILES_JSON=[{"id":"chatgpt-web","token":"replace-chatgpt-token","allowedTools":["remember_context","search_context","fetch_context"],"allowedFilterStates":["active"],"allowedOrigins":["https://chatgpt.com"]},{"id":"claude-web","token":"replace-claude-token","allowedTools":["remember_context","search_context","fetch_context"],"allowedFilterStates":["active"],"allowedOrigins":["https://claude.ai"]}]
GEMINI_EMBEDDING_MODEL=text-embedding-004
GEMINI_MULTIMODAL_MODEL=gemini-3.1-flash-lite-preview
GEMINI_MULTIMODAL_MODEL=gemini-3.1-flash-lite
GEMINI_GENERATION_VERTEX_LOCATION=global
GEMINI_EMBEDDING_DIMENSIONS=768
MEMORY_COLLECTION=memory_vectors
SEARCH_RESULT_LIMIT=5
Expand Down
2 changes: 2 additions & 0 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"clean": "node -e \"const fs=require('fs'); fs.rmSync('lib',{recursive:true,force:true}); fs.rmSync('coverage',{recursive:true,force:true});\"",
"backfill:ttl": "node scripts/backfill-firestore-ttl.mjs",
"serve": "cd .. && firebase emulators:start --only functions,firestore",
"shell": "firebase functions:shell",
"smoke": "node scripts/mcp-smoke-test.mjs",
"validate:models": "node scripts/validate-models.mjs",
"test": "vitest run --coverage",
"test:watch": "vitest"
},
Expand Down
Loading
Loading