VibecoderMcSwaggins commited on
Commit
0171a09
·
1 Parent(s): e745675

docs(audit): update HuggingFace Spaces integration report with critical issues

Browse files

- Revised the audit report to reflect a comprehensive review of the frontend/backend integration for HuggingFace Spaces.
- Identified and documented high-priority operational and security issues, including cold start handling, CORS regex vulnerabilities, and missing COOP/COEP headers.
- Provided detailed evidence and required fixes for each identified bug, emphasizing the need for immediate attention before production deployment.
- Enhanced documentation on hardware requirements and ephemeral storage risks, ensuring clarity for future deployments.

This update aims to facilitate a smoother deployment process and improve the overall security and performance of the integration.

Files changed (1) hide show
  1. BUGS-HF-SPACES-INTEGRATION.md +320 -144
BUGS-HF-SPACES-INTEGRATION.md CHANGED
@@ -1,14 +1,14 @@
1
  # Bug Report: HuggingFace Spaces Frontend/Backend Integration
2
 
3
  **Date**: 2025-12-12
4
- **Auditor**: Claude Code
5
- **Status**: Pre-deployment audit (UPDATED after URL verification)
6
 
7
  ---
8
 
9
  ## Executive Summary
10
 
11
- Audit of the frontend/backend integration for HuggingFace Spaces deployment. After verifying against the **actual deployed Space URLs**, the core configuration is **CORRECT**. No P0/P1 blockers found. Minor P2/P3 improvements identified.
12
 
13
  ### Actual Space URLs (Verified)
14
  - **Frontend**: https://huggingface.co/spaces/VibecoderMcSwaggins/stroke-viewer-frontend
@@ -18,74 +18,251 @@ Audit of the frontend/backend integration for HuggingFace Spaces deployment. Aft
18
 
19
  ---
20
 
21
- ## Configuration Verification (All PASS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- | Component | Configuration | Status | Evidence |
24
- |-----------|--------------|--------|----------|
25
- | CORS Regex | `r"https://.*stroke-viewer-frontend.*\.hf\.space"` | **PASS** | Matches `vibecodermcswaggins-stroke-viewer-frontend.hf.space` |
26
- | Backend URL in Frontend | `VITE_API_URL=https://vibecodermcswaggins-stroke-deepisles-demo.hf.space` | **PASS** | Correctly points to backend |
27
- | Built dist has production URL | `https://vibecodermcswaggins-stroke-deepisles-demo.hf.space` | **PASS** | Verified in `dist/assets/index-*.js` |
28
- | Proxy headers | `--proxy-headers` in Dockerfile CMD | **PASS** | Ensures HTTPS URLs behind HF proxy |
29
- | Port configuration | 7860 | **PASS** | Matches HF Spaces requirements |
30
- | Results directory | `/tmp/stroke-results` | **PASS** | Writable on HF Spaces |
31
- | Async job queue | Implemented | **PASS** | Avoids 60s gateway timeout |
32
 
33
- ### CORS Regex Verification
 
 
34
 
 
 
 
35
  ```python
36
- # Test performed:
37
- import re
38
- frontend_url = 'https://vibecodermcswaggins-stroke-viewer-frontend.hf.space'
39
- cors_regex = r'https://.*stroke-viewer-frontend.*\.hf\.space'
40
- re.fullmatch(cors_regex, frontend_url) # Returns Match object - SUCCESS
41
 
42
- # Also matches proxy/embed format:
43
- proxy_url = 'https://vibecodermcswaggins--stroke-viewer-frontend--abc123.hf.space'
44
- re.fullmatch(cors_regex, proxy_url) # Returns Match object - SUCCESS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ```
46
 
47
  ---
48
 
49
- ## P2 - MEDIUM PRIORITY (Non-blocking)
50
 
51
- ### ISSUE-001: Hardcoded User in Production Config
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  **Severity**: P2 - MEDIUM
54
- **Impact**: Forks/clones require manual configuration update
55
- **Files**: `frontend/.env.production`, `frontend/dist/`
56
 
57
  #### Problem
58
 
59
- The production URL is hardcoded for a specific user:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  ```
61
- # frontend/.env.production
62
- VITE_API_URL=https://vibecodermcswaggins-stroke-deepisles-demo.hf.space
 
 
 
 
63
  ```
64
 
65
- Anyone forking this repo must:
66
- 1. Update `.env.production` with their backend URL
67
- 2. Rebuild the frontend (`npm run build`)
68
- 3. Re-deploy the dist folder
69
 
70
- #### Recommendation (Optional)
71
 
72
- Add a deployment note to `frontend/README.md`:
73
- ```markdown
74
- ## Fork Deployment
75
 
76
- If you fork this repo, update `.env.production` before building:
77
- \`\`\`
78
- VITE_API_URL=https://{YOUR_USERNAME}-stroke-deepisles-demo.hf.space
79
- \`\`\`
80
- Then rebuild: `npm run build`
81
  ```
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  ---
84
 
85
- ### ISSUE-002: allow_credentials May Be Unnecessary
86
 
87
- **Severity**: P2 - MEDIUM
88
- **Impact**: More permissive than needed
89
  **File**: `src/stroke_deepisles_demo/api/main.py:87`
90
 
91
  #### Problem
@@ -93,160 +270,159 @@ Then rebuild: `npm run build`
93
  ```python
94
  app.add_middleware(
95
  CORSMiddleware,
96
- allow_origins=CORS_ORIGINS,
97
- allow_origin_regex=r"https://.*stroke-viewer-frontend.*\.hf\.space",
98
- allow_credentials=True, # <-- Is this needed?
99
- allow_methods=["*"],
100
- allow_headers=["*"],
101
  )
102
  ```
103
 
104
- The frontend API client doesn't use credentials:
105
  ```typescript
106
- // frontend/src/api/client.ts - no credentials option in fetch calls
107
- const response = await fetch(`${this.baseUrl}/api/cases`, { signal })
108
  ```
109
 
110
- #### Recommendation (Optional)
111
 
112
- If credentials (cookies, auth headers) aren't needed, consider:
113
  ```python
114
  app.add_middleware(
115
  CORSMiddleware,
116
  allow_origins=CORS_ORIGINS,
117
- allow_origin_regex=r"https://.*stroke-viewer-frontend.*\.hf\.space",
118
- allow_credentials=False, # More restrictive
119
- allow_methods=["GET", "POST"], # Only methods actually used
120
- allow_headers=["Content-Type"], # Only headers actually needed
121
  )
122
  ```
123
 
124
- This follows the principle of least privilege.
125
-
126
  ---
127
 
128
- ## P3 - LOW PRIORITY (Nice-to-have)
129
 
130
- ### ISSUE-003: FRONTEND_ORIGIN Env Var Not Used
131
 
132
  **Severity**: P3 - LOW
133
- **Impact**: Works without it, but could be more explicit
134
- **File**: `Dockerfile`, `src/stroke_deepisles_demo/api/main.py:72-78`
135
 
136
  #### Problem
137
 
138
- The code supports `FRONTEND_ORIGIN` environment variable:
139
- ```python
140
- FRONTEND_ORIGIN = os.environ.get("FRONTEND_ORIGIN", "")
141
- if FRONTEND_ORIGIN:
142
- CORS_ORIGINS.append(FRONTEND_ORIGIN)
143
- ```
144
 
145
- But it's not set in the Dockerfile. The regex fallback works, but explicit is better than implicit.
146
 
147
- #### Recommendation (Optional)
148
 
149
- Add to Dockerfile:
150
- ```dockerfile
151
- ENV FRONTEND_ORIGIN=https://vibecodermcswaggins-stroke-viewer-frontend.hf.space
152
- ```
153
 
154
- Or document that users can set it as a Space secret for more explicit configuration.
 
 
 
155
 
156
  ---
157
 
158
- ## P4 - INFO (No Action Required)
159
 
160
- ### INFO-001: Static Space README Configuration
 
 
161
 
162
- **Status**: CORRECT
163
- **File**: `frontend/README.md`
164
 
165
- The Static Space header is properly configured:
166
- ```yaml
167
- ---
168
- title: Stroke Lesion Viewer
169
- emoji: 🧠
170
- sdk: static
171
- app_file: dist/index.html
172
- app_build_command: npm run build
173
- nodejs_version: "20" # Required for Vite 7
174
- ---
175
  ```
 
 
 
 
 
 
176
 
177
- ### INFO-002: Backend Dockerfile Configuration
 
 
 
 
 
 
 
 
 
 
178
 
179
- **Status**: CORRECT
 
 
 
 
 
180
  **File**: `Dockerfile`
181
 
182
- All critical settings are present:
183
- - `FROM isleschallenge/deepisles:latest` - Correct base image
184
- - `USER user` - Non-root user (required by HF Spaces)
185
- - `EXPOSE 7860` - Correct port
186
- - `--proxy-headers` - Trusts X-Forwarded-Proto
187
 
188
- ### INFO-003: Async Job Queue Pattern
 
 
 
189
 
190
- **Status**: CORRECT
191
- **Files**: `src/stroke_deepisles_demo/api/routes.py`, `frontend/src/hooks/useSegmentation.ts`
192
 
193
- The implementation correctly handles HF Spaces' ~60s gateway timeout:
194
- 1. POST `/api/segment` returns immediately with job ID (202 Accepted)
195
- 2. Frontend polls GET `/api/jobs/{id}` every 2 seconds
196
- 3. Progress updates shown via `ProgressIndicator`
197
 
198
  ---
199
 
200
- ## Pre-Deployment Checklist
201
-
202
- Before going live, verify these items:
203
 
204
- | Check | Status | Notes |
205
- |-------|--------|-------|
206
- | Frontend Space created | [ ] | `stroke-viewer-frontend` Static Space |
207
- | Backend Space created | [ ] | `stroke-deepisles-demo` Docker Space |
208
- | Backend Space has GPU | [ ] | T4 or better required for DeepISLES |
209
- | Frontend built with production env | [x] | dist/ contains correct backend URL |
210
- | CORS regex matches frontend URL | [x] | Verified via regex test |
211
- | `--proxy-headers` in Dockerfile | [x] | Ensures HTTPS URLs |
212
- | Port 7860 configured | [x] | Required by HF Spaces |
213
- | Results dir in /tmp | [x] | `/tmp/stroke-results` |
214
 
215
  ---
216
 
217
  ## Runtime Testing Checklist
218
 
219
- Once both Spaces are running:
220
-
221
- | Test | Expected Result | How to Verify |
222
- |------|-----------------|---------------|
223
- | Frontend loads | Page renders without errors | Open frontend URL in browser |
224
- | Backend health check | `{"status": "healthy", ...}` | `curl https://...-stroke-deepisles-demo.hf.space/health` |
225
- | Cases endpoint | JSON array of case IDs | Check Network tab in DevTools |
226
- | CORS on cases | No CORS error | Check Console tab in DevTools |
227
- | Segmentation job created | 202 response with jobId | Click "Run Segmentation" |
228
- | Progress polling | Progress updates in UI | Watch ProgressIndicator |
229
- | Results displayed | NiiVue viewer shows volumes | Verify 3D rendering |
230
- | Static file CORS | NIfTI files load without error | Check Network tab |
231
 
232
  ---
233
 
234
- ## Previously Fixed Issues (Reference)
235
-
236
- These issues from earlier audits are correctly resolved:
237
 
238
- | ID | Issue | Fix Applied |
239
- |----|-------|-------------|
240
- | 001 | CORS regex only matched proxy URL format | Fixed: `.*stroke-viewer-frontend.*` matches both |
241
- | 002 | HTTP URLs returned behind HTTPS proxy | Fixed: `--proxy-headers` in uvicorn CMD |
242
- | 003 | Gateway timeout on long inference | Fixed: Async job queue with polling |
243
 
244
  ---
245
 
246
  ## Sources
247
 
248
- - [HuggingFace Static Spaces](https://huggingface.co/docs/hub/en/spaces-sdks-static)
249
- - [HF Spaces URL Format](https://huggingface.co/docs/hub/spaces-embed)
250
- - [FastAPI CORS Configuration](https://www.stackhawk.com/blog/configuring-cors-in-fastapi/)
251
- - [Deploying FastAPI on HF Spaces](https://huggingface.co/blog/HemanthSai7/deploy-applications-on-huggingface-spaces)
252
- - [HF Spaces Docker Docs](https://huggingface.co/docs/hub/spaces-sdks-docker)
 
 
 
 
1
  # Bug Report: HuggingFace Spaces Frontend/Backend Integration
2
 
3
  **Date**: 2025-12-12
4
+ **Auditor**: Claude Code + External Agent Review
5
+ **Status**: Pre-deployment audit (v3 - externally validated)
6
 
7
  ---
8
 
9
  ## Executive Summary
10
 
11
+ Comprehensive audit of the frontend/backend integration for HuggingFace Spaces deployment, validated by external senior review. **Core CORS and URL configuration is correct**, but critical operational and security issues were identified that require attention before production use.
12
 
13
  ### Actual Space URLs (Verified)
14
  - **Frontend**: https://huggingface.co/spaces/VibecoderMcSwaggins/stroke-viewer-frontend
 
18
 
19
  ---
20
 
21
+ ## P1 - HIGH PRIORITY (Operational Blockers)
22
+
23
+ ### BUG-001: No Cold Start / 503 Error Handling
24
+
25
+ **Severity**: P1 - HIGH
26
+ **Impact**: First user request fails when backend Space is sleeping
27
+ **File**: `frontend/src/hooks/useSegmentation.ts:152-160`
28
+
29
+ #### Problem
30
+
31
+ HF Spaces on free tier (cpu-basic) sleep after ~48h inactivity. When a user hits the frontend while backend is asleep:
32
+ 1. POST `/api/segment` fails with 503 or network error
33
+ 2. Frontend shows generic "Failed to start job" error
34
+ 3. User thinks the app is broken
35
+
36
+ Current code (lines 152-160):
37
+ ```typescript
38
+ } catch (err) {
39
+ if (err instanceof Error && err.name === 'AbortError') return
40
+ const message = err instanceof Error ? err.message : 'Failed to start job'
41
+ setError(message) // <-- No 503 detection, no retry logic
42
+ setIsLoading(false)
43
+ setJobStatus('failed')
44
+ }
45
+ ```
46
+
47
+ #### Evidence
48
+
49
+ - HF Spaces sleep behavior: [HF Docs - spaces-gpus](https://huggingface.co/docs/hub/spaces-gpus)
50
+ - Cold start time: 30-120 seconds typical
51
+
52
+ #### Required Fix
53
+
54
+ Add "waking up" state with exponential backoff retry:
55
+ ```typescript
56
+ } catch (err) {
57
+ if (err instanceof Error && err.name === 'AbortError') return
58
+
59
+ // Detect cold start (503 or network failure)
60
+ const is503 = err instanceof ApiError && err.status === 503
61
+ const isNetworkError = err instanceof TypeError && err.message.includes('fetch')
62
+
63
+ if ((is503 || isNetworkError) && retryCount < MAX_COLD_START_RETRIES) {
64
+ setProgressMessage('Backend is waking up... Please wait.')
65
+ setJobStatus('waking_up')
66
+ await sleep(RETRY_DELAY * Math.pow(2, retryCount))
67
+ return runSegmentation(caseId, fastMode, retryCount + 1)
68
+ }
69
+
70
+ setError(message)
71
+ setIsLoading(false)
72
+ setJobStatus('failed')
73
+ }
74
+ ```
75
+
76
+ ---
77
 
78
+ ### BUG-002: CORS Regex Security Vulnerability
 
 
 
 
 
 
 
 
79
 
80
+ **Severity**: P1 - HIGH (Security)
81
+ **Impact**: Malicious HF Spaces can make cross-origin requests
82
+ **File**: `src/stroke_deepisles_demo/api/main.py:86`
83
 
84
+ #### Problem
85
+
86
+ Current regex is too permissive:
87
  ```python
88
+ allow_origin_regex=r"https://.*stroke-viewer-frontend.*\.hf\.space"
89
+ ```
90
+
91
+ #### Security Test Results
 
92
 
93
+ ```
94
+ Pattern: https://.*stroke-viewer-frontend.*\.hf\.space
95
+
96
+ MATCH: https://vibecodermcswaggins-stroke-viewer-frontend.hf.space ✓ (legitimate)
97
+ MATCH: https://evil-stroke-viewer-frontend.hf.space ✗ (MALICIOUS)
98
+ MATCH: https://attacker.com-stroke-viewer-frontend-fake.hf.space ✗ (MALICIOUS)
99
+ MATCH: https://phishing-stroke-viewer-frontend-clone.hf.space ✗ (MALICIOUS)
100
+ ```
101
+
102
+ An attacker could create a malicious HF Space with `stroke-viewer-frontend` anywhere in the name and make cross-origin requests to your backend.
103
+
104
+ #### Required Fix
105
+
106
+ Anchor the regex to your specific username:
107
+ ```python
108
+ # Option A: Strict regex anchored to username
109
+ allow_origin_regex=r"https://vibecodermcswaggins-stroke-viewer-frontend\.hf\.space"
110
+
111
+ # Option B: Allow username variations but anchor pattern
112
+ allow_origin_regex=r"https://[a-z0-9]+-stroke-viewer-frontend\.hf\.space"
113
+ ```
114
+
115
+ Or use explicit origin via environment variable:
116
+ ```python
117
+ FRONTEND_ORIGIN = os.environ.get("FRONTEND_ORIGIN", "")
118
+ # Set FRONTEND_ORIGIN=https://vibecodermcswaggins-stroke-viewer-frontend.hf.space
119
  ```
120
 
121
  ---
122
 
123
+ ### BUG-003: Missing COOP/COEP Headers for WebGL Performance
124
 
125
+ **Severity**: P1 - HIGH (Performance)
126
+ **Impact**: NiiVue cannot use SharedArrayBuffer, degraded performance with large medical volumes
127
+ **File**: `frontend/README.md` (HF Space config)
128
+
129
+ #### Problem
130
+
131
+ NiiVue uses WebGL2 and benefits from `SharedArrayBuffer` for optimal performance when loading large NIfTI volumes. Modern browsers require Cross-Origin Isolation headers for `SharedArrayBuffer`.
132
+
133
+ Current `frontend/README.md` has no `custom_headers`:
134
+ ```yaml
135
+ ---
136
+ title: Stroke Lesion Viewer
137
+ sdk: static
138
+ app_file: dist/index.html
139
+ # Missing: custom_headers for COOP/COEP
140
+ ---
141
+ ```
142
+
143
+ #### Evidence
144
+
145
+ - [MDN SharedArrayBuffer Security Requirements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
146
+ - [HF Spaces custom_headers](https://huggingface.co/docs/hub/spaces-config-reference) - CONFIRMED supports COOP/COEP/CORP
147
+
148
+ #### Required Fix
149
+
150
+ Add to `frontend/README.md`:
151
+ ```yaml
152
+ ---
153
+ title: Stroke Lesion Viewer
154
+ emoji: 🧠
155
+ colorFrom: blue
156
+ colorTo: purple
157
+ sdk: static
158
+ app_file: dist/index.html
159
+ app_build_command: npm run build
160
+ nodejs_version: "20"
161
+ pinned: false
162
+ custom_headers:
163
+ cross-origin-embedder-policy: require-corp
164
+ cross-origin-opener-policy: same-origin
165
+ cross-origin-resource-policy: cross-origin
166
+ ---
167
+ ```
168
+
169
+ **Note**: After adding COEP `require-corp`, ALL cross-origin resources (including the backend API) must either:
170
+ - Come from same origin, OR
171
+ - Have `Cross-Origin-Resource-Policy: cross-origin` header, OR
172
+ - Be loaded with `crossorigin` attribute
173
+
174
+ The backend already sets CORS headers, but verify NIfTI file responses include proper CORP headers.
175
+
176
+ ---
177
+
178
+ ## P2 - MEDIUM PRIORITY (Operational Risks)
179
+
180
+ ### BUG-004: Hardware Requirements Not Enforced
181
 
182
  **Severity**: P2 - MEDIUM
183
+ **Impact**: App fails silently if deployed on wrong hardware
184
+ **File**: `README.md`
185
 
186
  #### Problem
187
 
188
+ Backend `README.md` specifies `suggested_hardware: t4-small` but:
189
+ 1. This is only a "suggestion" - doesn't enforce GPU
190
+ 2. If deployed on `cpu-basic` (free tier), DeepISLES will fail or be extremely slow
191
+ 3. No runtime check or user-friendly error
192
+
193
+ #### Evidence
194
+
195
+ - `cpu-basic`: 2 vCPU, 16 GB RAM, NO GPU
196
+ - `t4-small`: T4 GPU required for DeepISLES inference
197
+ - [HF Hardware Tiers](https://huggingface.co/docs/hub/spaces-gpus)
198
+
199
+ #### Required Fix
200
+
201
+ Add runtime GPU check in backend startup:
202
+ ```python
203
+ # In lifespan or startup
204
+ import torch
205
+ if not torch.cuda.is_available():
206
+ logger.warning("GPU not available! DeepISLES requires GPU for inference.")
207
+ # Optionally: return error response from /health endpoint
208
  ```
209
+
210
+ Document clearly in README:
211
+ ```markdown
212
+ ## Requirements
213
+ - **GPU Required**: This Space requires `t4-small` or better hardware.
214
+ - Free tier (`cpu-basic`) will NOT work for inference.
215
  ```
216
 
217
+ ---
 
 
 
218
 
219
+ ### BUG-005: Ephemeral Disk - Results Lost on Restart
220
 
221
+ **Severity**: P2 - MEDIUM
222
+ **Impact**: Completed job results disappear after Space restart
223
+ **File**: `src/stroke_deepisles_demo/api/routes.py:38`
224
 
225
+ #### Problem
226
+
227
+ Results are stored in `/tmp/stroke-results`:
228
+ ```python
229
+ RESULTS_BASE = Path("/tmp/stroke-results")
230
  ```
231
 
232
+ HF Spaces have ephemeral filesystems by default:
233
+ - Space restart = all `/tmp` data lost
234
+ - Users with valid job IDs get 404 errors after restart
235
+ - No warning to users that results are temporary
236
+
237
+ #### Evidence
238
+
239
+ - [HF Spaces Storage](https://huggingface.co/docs/hub/spaces-storage)
240
+
241
+ #### Mitigation Options
242
+
243
+ 1. **Document the limitation** (minimum fix):
244
+ ```markdown
245
+ ## Note
246
+ Job results are stored temporarily and will be lost if the Space restarts.
247
+ Download your results promptly.
248
+ ```
249
+
250
+ 2. **Enable persistent storage** (if budget allows):
251
+ - Add `suggested_storage: small` to README.md
252
+ - Move results to persistent volume
253
+
254
+ 3. **Add result expiry warning to API**:
255
+ ```python
256
+ # In job response
257
+ "warning": "Results expire after 1 hour or on Space restart"
258
+ ```
259
+
260
  ---
261
 
262
+ ### BUG-006: allow_credentials=True Unnecessary
263
 
264
+ **Severity**: P2 - MEDIUM (Security hygiene)
265
+ **Impact**: Increases CSRF surface if auth is added later
266
  **File**: `src/stroke_deepisles_demo/api/main.py:87`
267
 
268
  #### Problem
 
270
  ```python
271
  app.add_middleware(
272
  CORSMiddleware,
273
+ allow_credentials=True, # <-- Unnecessary
274
+ allow_methods=["*"], # <-- Overly permissive
275
+ allow_headers=["*"], # <-- Overly permissive
 
 
276
  )
277
  ```
278
 
279
+ Frontend doesn't use credentials:
280
  ```typescript
281
+ // client.ts - no credentials: 'include'
282
+ await fetch(`${this.baseUrl}/api/cases`, { signal })
283
  ```
284
 
285
+ #### Required Fix
286
 
 
287
  ```python
288
  app.add_middleware(
289
  CORSMiddleware,
290
  allow_origins=CORS_ORIGINS,
291
+ allow_origin_regex=r"...", # Fixed as per BUG-002
292
+ allow_credentials=False,
293
+ allow_methods=["GET", "POST"],
294
+ allow_headers=["Content-Type"],
295
  )
296
  ```
297
 
 
 
298
  ---
299
 
300
+ ## P3 - LOW PRIORITY (Documentation/Polish)
301
 
302
+ ### BUG-007: WebGL2 Client Requirement Not Documented
303
 
304
  **Severity**: P3 - LOW
305
+ **Impact**: Users with old browsers see cryptic errors
306
+ **File**: `frontend/README.md`
307
 
308
  #### Problem
309
 
310
+ NiiVue requires WebGL2. Users on:
311
+ - Old browsers (Safari < 15, IE)
312
+ - Locked-down enterprise browsers
313
+ - Some mobile devices
 
 
314
 
315
+ Will see rendering failures with no explanation.
316
 
317
+ #### Required Fix
318
 
319
+ Add to frontend README:
320
+ ```markdown
321
+ ## Browser Requirements
 
322
 
323
+ - **WebGL2 Required**: This viewer uses NiiVue which requires WebGL2 support.
324
+ - Supported: Chrome 56+, Firefox 51+, Safari 15+, Edge 79+
325
+ - Check your browser: https://get.webgl.org/webgl2/
326
+ ```
327
 
328
  ---
329
 
330
+ ### BUG-008: Hardcoded Username in Production Config
331
 
332
+ **Severity**: P3 - LOW
333
+ **Impact**: Forks require manual config update
334
+ **File**: `frontend/.env.production`
335
 
336
+ #### Problem
 
337
 
 
 
 
 
 
 
 
 
 
 
338
  ```
339
+ VITE_API_URL=https://vibecodermcswaggins-stroke-deepisles-demo.hf.space
340
+ ```
341
+
342
+ Anyone forking must update this manually.
343
+
344
+ #### Required Fix
345
 
346
+ Document in README:
347
+ ```markdown
348
+ ## Fork Deployment
349
+
350
+ 1. Update `frontend/.env.production`:
351
+ ```
352
+ VITE_API_URL=https://{YOUR_USERNAME}-stroke-deepisles-demo.hf.space
353
+ ```
354
+ 2. Update CORS in `main.py` to match your frontend URL
355
+ 3. Rebuild: `npm run build`
356
+ ```
357
 
358
+ ---
359
+
360
+ ### BUG-009: FRONTEND_ORIGIN Env Var Not Explicitly Set
361
+
362
+ **Severity**: P3 - LOW
363
+ **Impact**: Relies on regex fallback, less explicit
364
  **File**: `Dockerfile`
365
 
366
+ #### Problem
 
 
 
 
367
 
368
+ Code supports `FRONTEND_ORIGIN` but Dockerfile doesn't set it:
369
+ ```python
370
+ FRONTEND_ORIGIN = os.environ.get("FRONTEND_ORIGIN", "")
371
+ ```
372
 
373
+ #### Optional Fix
 
374
 
375
+ Add to Dockerfile:
376
+ ```dockerfile
377
+ ENV FRONTEND_ORIGIN=https://vibecodermcswaggins-stroke-viewer-frontend.hf.space
378
+ ```
379
 
380
  ---
381
 
382
+ ## Configuration Verification (All PASS)
 
 
383
 
384
+ | Component | Configuration | Status |
385
+ |-----------|--------------|--------|
386
+ | CORS regex matches frontend URL | `.*stroke-viewer-frontend.*` | **PASS** |
387
+ | Backend URL in frontend build | `vibecodermcswaggins-stroke-deepisles-demo.hf.space` | **PASS** |
388
+ | `--proxy-headers` in Dockerfile | Present | **PASS** |
389
+ | Port 7860 | Configured | **PASS** |
390
+ | Results in `/tmp/` | `/tmp/stroke-results` | **PASS** |
391
+ | Async job queue | Implemented | **PASS** |
 
 
392
 
393
  ---
394
 
395
  ## Runtime Testing Checklist
396
 
397
+ | Test | Expected | Status |
398
+ |------|----------|--------|
399
+ | Frontend loads | Page renders | [ ] |
400
+ | Backend health | `{"status": "healthy"}` | [ ] |
401
+ | CORS on /api/cases | No CORS error | [ ] |
402
+ | Cold start handling | Shows "waking up" message | [ ] |
403
+ | Segmentation job | 202 + polling works | [ ] |
404
+ | NIfTI file CORS | Files load in NiiVue | [ ] |
405
+ | WebGL rendering | 3D view displays | [ ] |
 
 
 
406
 
407
  ---
408
 
409
+ ## Priority Summary
 
 
410
 
411
+ | Priority | Count | Issues |
412
+ |----------|-------|--------|
413
+ | **P1** | 3 | Cold start handling, CORS regex security, COOP/COEP headers |
414
+ | **P2** | 3 | Hardware requirements, ephemeral disk, allow_credentials |
415
+ | **P3** | 3 | WebGL2 docs, hardcoded username, FRONTEND_ORIGIN |
416
 
417
  ---
418
 
419
  ## Sources
420
 
421
+ - [HuggingFace Spaces Config Reference](https://huggingface.co/docs/hub/spaces-config-reference)
422
+ - [HF Spaces Hardware Tiers](https://huggingface.co/docs/hub/spaces-gpus)
423
+ - [HF Spaces Storage](https://huggingface.co/docs/hub/spaces-storage)
424
+ - [FastAPI CORS](https://fastapi.tiangolo.com/tutorial/cors/)
425
+ - [MDN SharedArrayBuffer Security](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
426
+ - [MDN CORS Credentials](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
427
+ - [Uvicorn Proxy Settings](https://www.uvicorn.org/settings/)
428
+ - [NiiVue GitHub](https://github.com/niivue/niivue)