@cybermem/cli 0.6.11 → 0.7.7

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.
@@ -97,7 +97,7 @@ function isLocalRequest(req) {
97
97
  }
98
98
 
99
99
  // ForwardAuth handler
100
- const server = http.createServer((req, res) => {
100
+ const server = http.createServer(async (req, res) => {
101
101
  // Health check
102
102
  if (req.url === "/health") {
103
103
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -108,9 +108,22 @@ const server = http.createServer((req, res) => {
108
108
  const authHeader = req.headers["authorization"];
109
109
  const apiKeyHeader = req.headers["x-api-key"];
110
110
 
111
- // 1. Check JWT (Authorization: Bearer <token>)
111
+ // 1. Check Bearer token (JWT or API Key)
112
112
  if (authHeader?.startsWith("Bearer ")) {
113
113
  const token = authHeader.substring(7);
114
+
115
+ // 1a. Check if Bearer token is actually an API key (MCP clients like Claude Desktop)
116
+ const expectedKey = loadApiKey();
117
+ if (expectedKey && token === expectedKey) {
118
+ console.log("Auth OK: Bearer API Key");
119
+ res.writeHead(200, {
120
+ "X-Auth-Method": "bearer-api-key",
121
+ });
122
+ res.end();
123
+ return;
124
+ }
125
+
126
+ // 1b. Try JWT RS256 validation
114
127
  const payload = validateJwt(token);
115
128
 
116
129
  if (payload) {
@@ -124,6 +137,45 @@ const server = http.createServer((req, res) => {
124
137
  res.end();
125
138
  return;
126
139
  }
140
+
141
+ // 1c. Try GitHub OAuth token verification
142
+ try {
143
+ const https = require("https");
144
+ const ghRes = await new Promise((resolve, reject) => {
145
+ const req = https.get(
146
+ "https://api.github.com/user",
147
+ {
148
+ headers: {
149
+ Authorization: `Bearer ${token}`,
150
+ "User-Agent": "CyberMem-Auth-Sidecar/1.0",
151
+ Accept: "application/vnd.github+json",
152
+ },
153
+ },
154
+ (res) => {
155
+ let data = "";
156
+ res.on("data", (chunk) => (data += chunk));
157
+ res.on("end", () => resolve({ status: res.statusCode, data }));
158
+ },
159
+ );
160
+ req.on("error", reject);
161
+ req.end();
162
+ });
163
+
164
+ if (ghRes.status === 200) {
165
+ const user = JSON.parse(ghRes.data);
166
+ console.log(`Auth OK: GitHub OAuth (${user.login})`);
167
+ res.writeHead(200, {
168
+ "X-User-Id": String(user.id),
169
+ "X-User-Email": user.email || "",
170
+ "X-User-Name": user.login,
171
+ "X-Auth-Method": "github-oauth",
172
+ });
173
+ res.end();
174
+ return;
175
+ }
176
+ } catch (err) {
177
+ // GitHub verification failed, continue to other methods
178
+ }
127
179
  }
128
180
 
129
181
  // 2. Check API Key (deprecated fallback)
@@ -1,3 +1,4 @@
1
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json
1
2
  # CyberMem - OpenMemory + DevOps monitoring stack
2
3
  # For local development only
3
4
  # Production deployment: use Helm chart (charts/cybermem/)
@@ -24,21 +25,53 @@ services:
24
25
  - traefik-logs:/var/log/traefik
25
26
  labels:
26
27
  - traefik.enable=true
28
+ - traefik.http.middlewares.auth-check.forwardauth.address=http://auth-sidecar:3001/auth
29
+ - traefik.http.middlewares.auth-check.forwardauth.authResponseHeaders=X-User-Id,X-User-Email,X-User-Name,X-Auth-Method
27
30
  restart: unless-stopped
28
31
 
29
- # Workaround: responds 200 on GET /mcp for Perplexity validation
30
- mcp-responder:
32
+ # MCP Server (Node.js/SDK)
33
+ mcp-server:
31
34
  build:
32
- context: ./mcp-responder
35
+ context: ../../mcp
33
36
  dockerfile: Dockerfile
34
- container_name: cybermem-mcp-responder
37
+ image: ghcr.io/mikhailkogan17/cybermem-mcp:latest
38
+ # Note: platform omitted - docker auto-detects (amd64 for CI, arm64 for RPi)
39
+ restart: unless-stopped
40
+ container_name: cybermem-mcp
41
+ environment:
42
+ OM_TIER: "hybrid"
43
+ PORT: "8080"
44
+ OM_PORT: "0"
45
+ OM_DB_PATH: /data/openmemory.sqlite
46
+ volumes:
47
+ - ${CYBERMEM_ENV_PATH:-${HOME}/.cybermem/.env}:/.env
48
+ - ${HOME}/.cybermem/data:/data
49
+ depends_on:
50
+ - traefik
51
+ - auth-sidecar
35
52
  labels:
36
53
  - traefik.enable=true
37
- - traefik.http.routers.mcp-get.entrypoints=web
38
- - traefik.http.routers.mcp-get.rule=Method(`GET`) && Path(`/mcp`)
39
- - traefik.http.routers.mcp-get.priority=200
40
- - traefik.http.services.mcp-get.loadbalancer.server.port=8081
41
- restart: unless-stopped
54
+ # Middleware definitions (must be on a service that starts BEFORE routes reference them)
55
+ - traefik.http.middlewares.strip-cybermem.stripprefix.prefixes=/cybermem
56
+ # Authenticated routes
57
+ - traefik.http.routers.mcp.rule=PathPrefix(`/cybermem/mcp`) || PathPrefix(`/cybermem/sse`) || PathPrefix(`/mcp`) || PathPrefix(`/sse`)
58
+ - traefik.http.routers.mcp.entrypoints=web
59
+ - traefik.http.routers.mcp.priority=100
60
+ - traefik.http.routers.mcp.service=mcp-service
61
+ - traefik.http.routers.mcp.middlewares=auth-check,strip-cybermem
62
+ # Public routes (Health/Metrics)
63
+ - traefik.http.routers.mcp-public.rule=PathPrefix(`/cybermem/health`) || PathPrefix(`/cybermem/metrics`) || PathPrefix(`/health`) || PathPrefix(`/metrics`)
64
+ - traefik.http.routers.mcp-public.entrypoints=web
65
+ - traefik.http.routers.mcp-public.priority=100
66
+ - traefik.http.routers.mcp-public.service=mcp-service
67
+ - traefik.http.routers.mcp-public.middlewares=strip-cybermem
68
+ - traefik.http.services.mcp-service.loadbalancer.server.port=8080
69
+ # Legacy API support (for simple REST clients)
70
+ - traefik.http.routers.legacy-api.rule=PathPrefix(`/cybermem/add`) || PathPrefix(`/cybermem/query`) || PathPrefix(`/cybermem/all`) || PathPrefix(`/add`) || PathPrefix(`/query`) || PathPrefix(`/all`)
71
+ - traefik.http.routers.legacy-api.entrypoints=web
72
+ - traefik.http.routers.legacy-api.priority=100
73
+ - traefik.http.routers.legacy-api.service=mcp-service
74
+ - traefik.http.routers.legacy-api.middlewares=auth-check,strip-cybermem
42
75
 
43
76
  # Auth sidecar for JWT/API key validation (ForwardAuth)
44
77
  auth-sidecar:
@@ -48,12 +81,9 @@ services:
48
81
  PORT: "3001"
49
82
  API_KEY_FILE: /.env
50
83
  volumes:
51
- - ${CYBERMEM_ENV_PATH}:/.env:ro
84
+ - ${CYBERMEM_ENV_PATH:-${HOME}/.cybermem/.env}:/.env:ro
52
85
  labels:
53
86
  - traefik.enable=true
54
- # ForwardAuth middleware
55
- - traefik.http.middlewares.auth-check.forwardauth.address=http://auth-sidecar:3001/auth
56
- - traefik.http.middlewares.auth-check.forwardauth.authResponseHeaders=X-User-Id,X-User-Email,X-User-Name,X-Auth-Method
57
87
  healthcheck:
58
88
  test:
59
89
  [
@@ -69,9 +99,8 @@ services:
69
99
  retries: 3
70
100
  restart: unless-stopped
71
101
 
72
- # NOTE: openmemory container REMOVED
73
- # Memory now handled by @cybermem/mcp with embedded openmemory-js SDK
74
- # SQLite stored at ~/.cybermem/data/openmemory.sqlite
102
+ # Memory engine (MCP + Persistence)
103
+ # Uses embedded SQLite stored at ~/.cybermem/data/openmemory.sqlite
75
104
 
76
105
  db-exporter:
77
106
  image: ghcr.io/mikhailkogan17/cybermem-db_exporter:latest
@@ -84,7 +113,7 @@ services:
84
113
  - "8000:8000"
85
114
  volumes:
86
115
  # Mount host openmemory data dir (created by SDK)
87
- - ${HOME}/.cybermem/data:/data:ro
116
+ - ${HOME}/.cybermem/data:/data
88
117
  restart: unless-stopped
89
118
 
90
119
  log-exporter:
@@ -97,7 +126,7 @@ services:
97
126
  DB_PATH: /data/openmemory.sqlite
98
127
  volumes:
99
128
  - traefik-logs:/var/log/traefik:ro
100
- - ${HOME}/.cybermem/data:/data:ro
129
+ - ${HOME}/.cybermem/data:/data
101
130
  - ./monitoring/log_exporter/exporter.py:/app/exporter.py:ro
102
131
  restart: unless-stopped
103
132
  depends_on:
@@ -123,22 +152,6 @@ services:
123
152
  profiles:
124
153
  - postgres
125
154
 
126
- postgres-exporter:
127
- image: prometheuscommunity/postgres-exporter:v0.15.0
128
- container_name: cybermem-postgres-exporter
129
- environment:
130
- DATA_SOURCE_NAME: postgresql://${PG_USER:-openmemory}:${PG_PASSWORD:-postgres}@postgres:5432/${PG_DB:-openmemory}?sslmode=disable
131
- PG_EXPORTER_EXTEND_QUERY_PATH: /queries.yml
132
- ports:
133
- - "9187:9187"
134
- volumes:
135
- - ./monitoring/postgres_exporter/queries.yml:/queries.yml:ro
136
- restart: unless-stopped
137
- depends_on:
138
- - postgres
139
- profiles:
140
- - postgres
141
-
142
155
  ollama:
143
156
  image: ollama/ollama:latest
144
157
  container_name: cybermem-ollama
@@ -150,55 +163,37 @@ services:
150
163
  profiles:
151
164
  - ollama
152
165
 
153
- prometheus:
154
- image: prom/prometheus:v2.48.0
155
- container_name: cybermem-prometheus
156
- command:
157
- - --config.file=/etc/prometheus/prometheus.yml
158
- - --storage.tsdb.path=/prometheus
159
- - --storage.tsdb.retention.time=${PROM_RETENTION:-7d}
160
- - --web.console.libraries=/usr/share/prometheus/console_libraries
161
- - --web.console.templates=/usr/share/prometheus/consoles
162
- ports:
163
- - "9092:9090"
164
- volumes:
165
- - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
166
- - prometheus-data:/prometheus
167
- restart: unless-stopped
168
- depends_on:
169
- - db-exporter
170
-
171
166
  dashboard:
172
167
  image: ghcr.io/mikhailkogan17/cybermem-dashboard:latest
168
+ # Note: platform omitted - docker auto-detects (amd64 for CI, arm64 for RPi)
173
169
  container_name: cybermem-dashboard
174
170
  environment:
175
171
  DB_EXPORTER_URL: http://db-exporter:8000
176
- NEXT_PUBLIC_PROMETHEUS_URL: http://prometheus:9090
177
- PROMETHEUS_URL: http://prometheus:9090
172
+ # Required for Health Check to find the MCP API internally
173
+ CYBERMEM_URL: http://mcp-server:8080
174
+ OM_DB_PATH: /data/openmemory.sqlite
178
175
  OM_API_KEY: ${OM_API_KEY:-dev-secret-key}
179
- # WATCHPACK_POLLING: "true" # Enable if hot reload blocks (high CPU usage)
180
176
  ports:
181
177
  - "3000:3000"
182
178
  volumes:
183
- - openmemory-data:/data
179
+ - ${HOME}/.cybermem/data:/data
184
180
  - /var/run/docker.sock:/var/run/docker.sock
185
- - ${CYBERMEM_ENV_PATH}:/app/shared.env
181
+ - ${CYBERMEM_ENV_PATH:-${HOME}/.cybermem/.env}:/app/shared.env
186
182
  labels:
187
183
  - traefik.enable=true
188
184
  # Dashboard route: /cybermem -> dashboard on port 3000
189
185
  - traefik.http.routers.dashboard.entrypoints=web
190
- - traefik.http.routers.dashboard.rule=PathPrefix(`/cybermem`)
191
- - traefik.http.routers.dashboard.middlewares=strip-cybermem
192
- - traefik.http.middlewares.strip-cybermem.stripprefix.prefixes=/cybermem
186
+ - traefik.http.routers.dashboard.rule=PathPrefix(`/cybermem`) && !PathPrefix(`/cybermem/mcp`) && !PathPrefix(`/cybermem/health`)
187
+ - traefik.http.routers.dashboard.priority=10
188
+ - traefik.http.routers.dashboard.middlewares=auth-check,strip-cybermem
193
189
  - traefik.http.services.dashboard.loadbalancer.server.port=3000
194
190
  restart: unless-stopped
195
191
  depends_on:
196
- - prometheus
197
192
  - db-exporter
198
193
 
199
194
  volumes:
200
195
  openmemory-data:
201
- name: cybermem-openmemory-data
196
+ name: cybermem-data
202
197
  driver: local
203
198
  postgres-data:
204
199
  name: cybermem-postgres-data
@@ -206,9 +201,6 @@ volumes:
206
201
  ollama-models:
207
202
  name: cybermem-ollama-models
208
203
  driver: local
209
- prometheus-data:
210
- name: cybermem-prometheus-data
211
- driver: local
212
204
  traefik-logs:
213
205
  name: cybermem-traefik-logs
214
206
  driver: local
@@ -0,0 +1,17 @@
1
+ # Environment: LOCAL (Dev)
2
+ # No API key required for localhost access.
3
+ # Used by: npx @cybermem/cli init --local
4
+
5
+ # Application Settings
6
+ LOG_LEVEL=info
7
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
8
+
9
+ # Database (SQLite)
10
+ # OM_DB_PATH is mounted to /data/openmemory.sqlite in Docker
11
+
12
+ # Security
13
+ # OM_API_KEY is intentionally empty for local development to bypass auth.
14
+ OM_API_KEY=
15
+
16
+ # Optional: Add your OpenAI Key if using cloud embeddings
17
+ # OPENAI_API_KEY=
@@ -0,0 +1,23 @@
1
+ # Environment: RPi + Tailscale (Remote Access)
2
+ # Secured via Tailscale Funnel.
3
+ # Used by: npx @cybermem/cli init --rpi --remote-access
4
+
5
+ # Application Settings
6
+ LOG_LEVEL=info
7
+
8
+ # Database (SQLite)
9
+ # OM_DB_PATH is mounted to /data/openmemory.sqlite in Docker
10
+
11
+ # Security
12
+ # GENERATED AUTOMATICALLY BY CLI
13
+ OM_API_KEY=
14
+
15
+ # Tailscale Configuration
16
+ # Ensure "tailscale serve" is running (handled by CLI)
17
+ TS_HOSTNAME=raspberrypi
18
+
19
+ # Dashboard
20
+ NEXT_PUBLIC_APP_URL=https://raspberrypi.tailnet-name.ts.net
21
+
22
+ # Optional: Local LLM
23
+ OLLAMA_URL=http://ollama:11434
@@ -0,0 +1,22 @@
1
+ # Environment: RPi (Local Network)
2
+ # Internal API key required.
3
+ # Used by: npx @cybermem/cli init --rpi
4
+
5
+ # Application Settings
6
+ LOG_LEVEL=info
7
+
8
+ # Database (SQLite)
9
+ # OM_DB_PATH is mounted to /data/openmemory.sqlite in Docker
10
+
11
+ # Security
12
+ # GENERATED AUTOMATICALLY BY CLI
13
+ # DO NOT SHARE THIS KEY. It is for internal service communication.
14
+ OM_API_KEY=
15
+
16
+ # Dashboard
17
+ # Clients connecting via MCP must use OAuth or this key (if configured for direct access).
18
+ # OAUTH_CLIENT_ID=...
19
+ # OAUTH_CLIENT_SECRET=...
20
+
21
+ # Optional: Local LLM
22
+ OLLAMA_URL=http://ollama:11434
@@ -0,0 +1,29 @@
1
+ # Environment: VPS (Public Cloud)
2
+ # High security setup.
3
+ # Used by: npx @cybermem/cli init --vps
4
+
5
+ # Application Settings
6
+ LOG_LEVEL=warn
7
+ # Set your domain here
8
+ NEXT_PUBLIC_APP_URL=https://cybermem.yourdomain.com
9
+
10
+ # Database (PostgreSQL recommended for VPS, but SQLite default)
11
+ # To use Postgres:
12
+ # DB_TYPE=postgres
13
+ # POSTGRES_URL=postgresql://user:pass@db:5432/cybermem
14
+
15
+ # Security
16
+ # GENERATED AUTOMATICALLY BY CLI
17
+ OM_API_KEY=
18
+
19
+ # Auth Sidecar (JWT)
20
+ # Generate a random secret: openssl rand -hex 32
21
+ JWT_SECRET=
22
+ JWT_PRIVATE_KEY=
23
+
24
+ # GitHub OAuth (Recommended for dashboard access)
25
+ OAUTH_CLIENT_ID=
26
+ OAUTH_CLIENT_SECRET=
27
+
28
+ # OpenAI (Recommended for VPS performance)
29
+ OPENAI_API_KEY=
@@ -123,11 +123,16 @@ def collect_metrics():
123
123
  FROM memories
124
124
  GROUP BY user_id
125
125
  """)
126
- for row in cursor.fetchall():
127
- client = row["client"] or "anonymous"
128
- memories_total.labels(client=client).set(row["count"])
126
+ rows = cursor.fetchall()
127
+ if not rows:
128
+ # Emit a default anonymous:0 if no data
129
+ memories_total.labels(client="anonymous").set(0)
130
+ else:
131
+ for row in rows:
132
+ client = row["client"] or "anonymous"
133
+ memories_total.labels(client=client).set(row["count"])
129
134
 
130
- logger.debug(f"Collected total memories for {cursor.rowcount} clients")
135
+ logger.debug(f"Collected total memories for {len(rows)} clients")
131
136
 
132
137
  # Metric 2: Recent memories (24h)
133
138
  # Note: created_at is stored as milliseconds since epoch
@@ -1,27 +0,0 @@
1
- # Example environment configuration
2
- # Copy to .env and customize
3
-
4
- # Embeddings provider: ollama (local) or openai (cloud)
5
- EMBEDDINGS_PROVIDER=ollama
6
- OLLAMA_URL=http://ollama:11434
7
- OPENAI_API_KEY=
8
-
9
- # Database backend: sqlite (local/rpi) or postgres (vps)
10
- DB_BACKEND=sqlite
11
- DB_PATH=/data/openmemory.sqlite
12
- VECTOR_BACKEND=sqlite
13
-
14
- # PostgreSQL settings (only for DB_BACKEND=postgres)
15
- PG_HOST=postgres
16
- PG_PORT=5432
17
- PG_DB=openmemory
18
- PG_USER=openmemory
19
- PG_PASSWORD=change-me
20
-
21
- # OpenMemory API key (Optional for local mode)
22
- # OM_API_KEY=
23
-
24
- # Monitoring
25
- PROM_RETENTION=7d
26
- GRAFANA_USER=admin
27
- GRAFANA_PASSWORD=admin
@@ -1,27 +0,0 @@
1
- # Raspberry Pi environment
2
- # Optimized for low memory (1GB total)
3
- DOCKER_PLATFORM=linux/arm64
4
-
5
-
6
- # Embeddings (use Ollama with small models)
7
- EMBEDDINGS_PROVIDER=ollama
8
- OLLAMA_URL=http://ollama:11434
9
- OPENAI_API_KEY=
10
-
11
- # Database (SQLite for RPi - low memory footprint)
12
- DB_BACKEND=sqlite
13
- DB_PATH=/data/openmemory.sqlite
14
-
15
- # PostgreSQL (not used)
16
- PG_HOST=postgres
17
- PG_DB=openmemory
18
- PG_USER=openmemory
19
- PG_PASSWORD=not-used
20
-
21
- # OpenMemory
22
- OM_API_KEY=key-change-me
23
-
24
- # Monitoring (short retention for disk space)
25
- PROM_RETENTION=3d
26
- GRAFANA_USER=admin
27
- GRAFANA_PASSWORD=admin
@@ -1,25 +0,0 @@
1
- # VPS production environment
2
- # For Hetzner CX22 or similar (2 vCPU, 4GB RAM)
3
-
4
- # Embeddings (use OpenAI for production)
5
- EMBEDDINGS_PROVIDER=openai
6
- OPENAI_API_KEY=sk-change-me-in-production
7
- OLLAMA_URL=
8
-
9
- # Database (PostgreSQL for production)
10
- DB_BACKEND=postgres
11
- VECTOR_BACKEND=postgres
12
-
13
- # PostgreSQL
14
- PG_HOST=postgres
15
- PG_DB=openmemory
16
- PG_USER=openmemory
17
- PG_PASSWORD=change-me-in-production-use-secrets
18
-
19
- # OpenMemory
20
- OM_API_KEY=change-me-in-production-use-secrets
21
-
22
- # Monitoring
23
- PROM_RETENTION=30d
24
- GRAFANA_USER=admin
25
- GRAFANA_PASSWORD=change-me-in-production-use-secrets