Skip to content

[codex] harden auth and production security - #21

Merged
tomnguyen103 merged 2 commits into
mainfrom
codex/security-auth-hardening
Jun 5, 2026
Merged

[codex] harden auth and production security#21
tomnguyen103 merged 2 commits into
mainfrom
codex/security-auth-hardening

Conversation

@tomnguyen103

@tomnguyen103 tomnguyen103 commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds no-cost single-owner auth across personal-data APIs, with destructive data-ops requiring both the normal API bearer and a separate admin header.
  • Adds frictionless web capture, citation-safe streaming, stricter citation-support validation, MCP mutation opt-in, and safer research URL fetching.
  • Hardens production Compose/runbooks/env examples around secrets, backups, Caddy, Redis rate-limit posture, and container dependency scope while keeping Docker Compose as production runtime.

Security Notes

  • Admin token no longer passes the normal API gate; data-ops use X-Second-Brain-Admin-Token in addition to Authorization: Bearer <SECOND_BRAIN_API_TOKEN>.
  • MCP durable mutations are disabled unless SECOND_BRAIN_MCP_ENABLE_MUTATIONS=true for trusted local clients.
  • /chat/stream buffers generated chunks until citation validation passes, and weak or unsupported cited answers are replaced before persistence/emission.
  • Research URL fetching validates public DNS/redirects and restricts fetches to default HTTP(S) ports.

Verification

  • SECOND_BRAIN_TEST_DATABASE_URL=postgresql+psycopg://second_brain:second_brain@localhost:5433/second_brain python -m pytest backend/tests -q -> 226 passed, 6 warnings
  • focused auth/data-ops/MCP/config/research/chat/API tests -> 31 passed, 21 skipped, 1 warning
  • npm run lint -> passed
  • npm run build -> passed, with existing Next.js multiple-lockfile warning
  • npm audit --audit-level=moderate -> 0 vulnerabilities
  • docker compose -f deploy/docker-compose.prod.yml config --quiet with dummy required env -> passed
  • git diff --check -> only CRLF normalization warnings

Summary by CodeRabbit

  • New Features

    • Added /capture endpoint and web UI for saving bookmarks from the browser
    • Introduced streaming chat responses via /chat/stream with real-time delta events
    • Implemented API token authentication for personal-data endpoints
  • Security & Operations

    • Separate admin token header for destructive data operations
    • Enhanced URL validation for captured content
    • Strengthened citation validation in chat responses
    • Added automated backup and restore documentation with security hardening guidance
  • Infrastructure

    • Optimized container images for production deployments
    • Updated database connectivity and pooling architecture
    • Simplified Kubernetes configuration for core services

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tomnguyen103, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 53 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9cc0d252-8c1e-4d11-9471-49127dc0c16d

📥 Commits

Reviewing files that changed from the base of the PR and between fad7394 and 02f1b35.

📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • backend/app/api/chat.py
  • backend/app/capture/service.py
  • backend/app/llm/ollama.py
  • backend/tests/integration/test_capture_api.py
  • backend/tests/integration/test_dataops_api.py
  • deploy/Dockerfile.caddy
  • deploy/docker-compose.prod.yml
  • deploy/docker-compose.vps.yml.example
  • deploy/k8s/README.md
  • deploy/k8s/redis.yaml
  • docs/PROGRESS.md
  • docs/implementation-notes.md
  • frontend/app/capture/page.tsx
  • frontend/lib/api/client.ts
📝 Walkthrough

Walkthrough

Adds API bearer gating to most routers, introduces SSE streaming chat with validation, implements a new /capture endpoint/service and frontend page, updates rate-limit behavior, token storage in UI, and broad CI/Docker/K8s/Compose/docs hardening including Caddy headers and pgvector builds.

Changes

Authenticated APIs, streaming chat, capture, and deployment/runtime updates

Layer / File(s) Summary
Backend auth/streaming/capture + Frontend streaming UI + Ops manifests/docs
.github/workflows/*, backend/app/**/*, frontend/**/*, deploy/**/*, docs/**/*, .dockerignore, .gitignore, docker-compose.*, backend/requirements*
Routers gated by bearer token, SSE /chat/stream and validation, new /capture flow (schemas/service/API/UI), rate-limit/client identity tweaks, UI token storage and streaming, and CI/K8s/Compose/Dockerfile/security/docs/cron updates including pgvector builds and Caddy headers.

Sequence Diagram(s)

sequenceDiagram
  participant Frontend
  participant API
  participant ChatService
  participant Redis
  participant DB
  participant LLM
  Frontend->>API: POST /chat/stream (Bearer)
  API->>Redis: rate limit check
  API->>ChatService: stream_chat(...)
  ChatService->>DB: load history/context
  ChatService->>LLM: generate_stream(messages)
  LLM-->>ChatService: chunks
  ChatService-->>API: validated events
  API-->>Frontend: SSE delta/complete
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

A rabbit taps the stream with gentle paw,
SSE whispers, citations keep the law.
Tokens guard the burrow, safe and bright,
Capture bookmarks hop into the night.
Caddy shields the warren’s sunny door—
Ship, sip, and stream some wisdom more. ✨🐇

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/security-auth-hardening

@tomnguyen103
tomnguyen103 marked this pull request as ready for review June 5, 2026 04:16
@tomnguyen103

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (9)
.github/workflows/ci.yml (1)

56-70: 💤 Low value

Consider adding explicit container cleanup.

The second-brain-ci-db container is started but never explicitly stopped or removed. While GitHub Actions will clean it up during runner teardown, adding an explicit cleanup step (e.g., in an if: always() block) would make the workflow more maintainable and portable.

🧹 Suggested cleanup step

Add this step after the test steps:

- name: Cleanup database container
  if: always()
  run: docker rm -f second-brain-ci-db || true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 56 - 70, The workflow step "Start
cleaned pgvector database" launches a container named second-brain-ci-db but
never stops or removes it; add a separate cleanup step that runs unconditionally
(use if: always()) after your tests to force-remove the container (targeting
second-brain-ci-db) so it won't linger (e.g., run docker rm -f
second-brain-ci-db || true in that step); ensure the cleanup step is placed
after the test steps and uses the same container name to reliably remove the
instance.
backend/tests/integration/test_capture_api.py (1)

11-21: 💤 Low value

Duplicate tag in test payload.

Line 20 includes "capture" twice in the tags list. This is likely intentional to test tag deduplication, but if not, it should be cleaned up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/integration/test_capture_api.py` around lines 11 - 21, The test
payload builder function _capture_payload has a duplicated "capture" entry in
the tags list; either remove the duplicate so tags becomes ["capture", "inbox"]
or, if the test intentionally verifies tag deduplication, add an inline comment
clarifying that intent and ensure the corresponding test asserts dedup behavior
(update tests referencing _capture_payload accordingly).
deploy/Dockerfile.caddy (1)

18-24: ⚡ Quick win

Consider adding a non-root USER directive.

The runtime stage runs as root, which increases the attack surface. While Caddy needs to bind to privileged ports 80 and 443, modern Caddy can drop privileges after binding. Consider adding a non-root user and letting Caddy handle privilege dropping, or use Docker's capability system to grant CAP_NET_BIND_SERVICE.

🔒 Proposed fix to run as non-root
 FROM alpine:3.23.4
 
-RUN apk add --no-cache ca-certificates mailcap
+RUN apk add --no-cache ca-certificates mailcap \
+    && adduser -D -u 1000 caddy
 COPY --from=builder /out/caddy /usr/bin/caddy
 
+USER caddy
 EXPOSE 80 443
 CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

Then in docker-compose, add capability:

caddy:
  cap_add:
    - NET_BIND_SERVICE
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/Dockerfile.caddy` around lines 18 - 24, The Dockerfile.caddy runtime
stage runs all processes as root, which increases the attack surface. Add a
non-root USER directive in the Dockerfile before the CMD instruction that starts
Caddy, and then update the docker-compose configuration to grant the
NET_BIND_SERVICE capability to the caddy service. This allows the non-root user
to bind to the privileged ports 80 and 443 while maintaining security best
practices.
deploy/k8s/README.md (1)

76-82: ⚡ Quick win

Clarify how to build the monitoring images.

The monitoring section mentions that templates require "scanned-clean local images" with specific tags like second-brain-grafana:phase7-clean-required, but the README doesn't provide build instructions for these images. Users who want to enable monitoring won't have a clear path forward without Dockerfiles or build commands for Grafana, Prometheus, and PgBouncer.

Consider adding a brief note pointing to where these Dockerfiles live, or documenting the expected build workflow (e.g., base image selection, scanning tools, tag conventions).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/k8s/README.md` around lines 76 - 82, The README entry under "Optional
Monitoring Templates" references local scanned-clean image tags (e.g.,
second-brain-grafana:phase7-clean-required) and imagePullPolicy: Never but lacks
build instructions; update deploy/k8s/README.md to add a short section that
points to where the Dockerfiles live (deploy/grafana/, deploy/prometheus/,
deploy/pgbouncer/ or the repo paths), and document the expected build-and-scan
workflow: which Dockerfile to use, the docker build and tag convention (use the
*-clean-required tag format), the scanning step to run (e.g., SCA or image
scanner), and that images must be built locally before applying manifests that
reference those tags; mention the tag naming convention and remind that
imagePullPolicy: Never requires local images.
backend/app/capture/service.py (1)

53-56: 💤 Low value

Clarify the port validation intent.

Accessing parsed.port solely for its side effect (raising ValueError on invalid ports) works but triggers static analysis warnings and obscures intent. Consider assigning to _ or adding a brief comment.

♻️ Suggested clarification
     try:
-        parsed.port
+        _ = parsed.port  # access triggers ValueError on invalid port
     except ValueError as exc:
         raise ValueError("capture URL includes an invalid port") from exc
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/capture/service.py` around lines 53 - 56, The try/except that
accesses parsed.port only for its side effect should be made explicit to avoid
static-analysis warnings: replace the bare access with an explicit assignment
like "_ = parsed.port" (or add a short comment above it) and keep the existing
except ValueError as exc: raise ValueError("capture URL includes an invalid
port") from exc so intent is clear; update the code around parsed.port in the
capture service where parsed is used.
backend/tests/integration/test_dataops_api.py (1)

55-66: Clarify the two-token intent of test_admin_token_alone_is_not_api_access. (backend/tests/integration/test_dataops_api.py:55-66)

  • /data/export requires both: Authorization: Bearer ... validated by require_api_access (must equal api_token) and X-Second-Brain-Admin-Token validated by require_admin (must equal admin_token).
  • In this test, TOKEN is test-admin-token, and the bearer is set to that value while the admin header is also set correctly—so the expected 401 is consistent; the bearer header at line 61 already uses the admin token (no need to change it to test-api-token).
  • Optional: add a short comment (or tweak the test name) explicitly stating that the admin token must not substitute for the API bearer token.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/integration/test_dataops_api.py` around lines 55 - 66, The test
test_admin_token_alone_is_not_api_access should clarify that /data/export
requires two distinct tokens validated by require_api_access (Authorization
Bearer == api_token) and require_admin (X-Second-Brain-Admin-Token ==
admin_token); update the test by adding a short inline comment (or rename the
test to something like test_admin_token_does_not_substitute_api_token) stating
that the admin token must not substitute for the API bearer token, and ensure
the existing headers keep TOKEN (test-admin-token) for both Authorization and
X-Second-Brain-Admin-Token to show the bearer being the admin token is
intentional.
backend/app/llm/ollama.py (1)

29-32: ⚡ Quick win

Consider defensive error handling around JSON parsing.

The json.loads(line) call on Line 32 will raise if Ollama sends malformed data. While the caller in stream_chat() has a try/except wrapper (Lines 325-327 in service.py), adding a try/except here with a more specific error message would improve debuggability and fail faster on unexpected Ollama responses.

🛡️ Proposed defensive handling
             for line in r.iter_lines():
                 if not line:
                     continue
-                data = json.loads(line)
+                try:
+                    data = json.loads(line)
+                except json.JSONDecodeError as e:
+                    raise RuntimeError(f"Ollama returned invalid JSON: {line!r}") from e
                 if data.get("done"):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app/llm/ollama.py` around lines 29 - 32, The json.loads(line) inside
the for loop that iterates r.iter_lines() can raise on malformed JSON from
Ollama; wrap the json.loads(line) call in a try/except that catches
json.JSONDecodeError (and optionally ValueError), log a clear, contextual error
including the raw line and any request identifiers, and either continue or raise
a more specific exception so callers (e.g., stream_chat) get a clearer failure;
update the block in ollama.py where the for line in r.iter_lines(): loop and
json.loads(line) occur (reference json.loads and the iter_lines loop) to
implement this defensive parsing and logging.
frontend/lib/api/client.ts (1)

99-111: ⚡ Quick win

Consider handling malformed JSON gracefully in SSE parsing.

The parseSseBlock function calls JSON.parse(dataLines.join("\n")) without error handling. If the server emits a malformed data payload, this will throw a synchronous exception that may not surface cleanly to the caller. Consider wrapping the parse in a try-catch and returning null or logging the error.

🛡️ Suggested defensive parsing
 function parseSseBlock(block: string): { event: string; data: unknown } | null {
   let event = "message";
   const dataLines: string[] = [];
   for (const line of block.split("\n")) {
     if (line.startsWith("event:")) {
       event = line.slice("event:".length).trim();
     } else if (line.startsWith("data:")) {
       dataLines.push(line.slice("data:".length).trimStart());
     }
   }
   if (dataLines.length === 0) return null;
-  return { event, data: JSON.parse(dataLines.join("\n")) as unknown };
+  try {
+    return { event, data: JSON.parse(dataLines.join("\n")) as unknown };
+  } catch {
+    console.warn("Failed to parse SSE data block:", dataLines.join("\n"));
+    return null;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/lib/api/client.ts` around lines 99 - 111, The parseSseBlock function
currently calls JSON.parse without protection; wrap the parse of
dataLines.join("\n") in a try-catch inside parseSseBlock, and on parse failure
return null (or optionally log the error) instead of letting the exception
propagate so malformed SSE payloads are handled gracefully; update references in
parseSseBlock where it returns parsed data to handle the null case accordingly.
frontend/app/capture/page.tsx (1)

109-116: ⚡ Quick win

Consider wrapping in a <form> element for better semantics.

The current implementation uses a button with onClick to trigger submission. While this works, wrapping the inputs in a <form> element and handling onSubmit would provide better semantics, keyboard support (Enter key submission), and accessibility.

♻️ Suggested refactor
-          <div className="grid gap-3 p-4">
+          <form className="grid gap-3 p-4" onSubmit={(e) => { e.preventDefault(); capture.mutate(); }}>
             {capture.error && (
               <InlineError message={capture.error instanceof Error ? capture.error.message : "Capture failed"} />
             )}
             {/* ...inputs... */}
             <div className="flex justify-end border-t border-border pt-3">
               <button
-                type="button"
-                onClick={() => capture.mutate()}
+                type="submit"
                 disabled={!canSubmit}
                 className="inline-flex h-9 items-center gap-1.5 rounded-lg bg-amber-500 px-3 text-sm font-semibold text-white shadow-sm shadow-amber-200/60 transition-colors hover:bg-amber-600 disabled:cursor-not-allowed disabled:opacity-40 dark:shadow-none"
               >
                 {capture.isPending ? "Capturing" : "Capture"} <ArrowRight size={14} weight="bold" />
               </button>
             </div>
+          </form>
-          </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/capture/page.tsx` around lines 109 - 116, Wrap the capture UI in
a semantic <form> and move the submit logic from the button onClick to the
form's onSubmit handler: call capture.mutate() from an onSubmit that calls
event.preventDefault(), change the button to type="submit" (keep
disabled={!canSubmit}) and keep using capture.isPending for the label; this will
enable Enter-key submission and better accessibility while preserving the
existing canSubmit and capture.* behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/app/api/chat.py`:
- Around line 104-105: The except block that yields the generic SSE error
currently swallows the exception; update the except Exception handler in
backend/app/api/chat.py (the block that currently does "except Exception: yield
_format_sse(\"error\", {\"message\": \"streaming chat failed\"})") to log the
caught exception before yielding—use the module/logger instance (e.g.,
logger.exception or logger.error with exc_info=True) to record the stacktrace
and context, then yield the same generic SSE error to the client.

In `@deploy/docker-compose.prod.yml`:
- Around line 91-92: The docker-compose.prod.yml stack currently only defines
volumes: db_data and omits the Prometheus/Grafana services found under
deploy/prometheus/ and deploy/grafana/; update docker-compose.prod.yml to re-add
prometheus and grafana service definitions (and their volumes) and mount the
provided configs from deploy/prometheus/ and deploy/grafana/, ensuring service
names match references used elsewhere (prometheus, grafana) and that any network
and volume entries (e.g., db_data plus prometheus_data/grafana_data) are
declared, or alternatively add a README note in the repo root explaining where
VPS observability is handled (pointing to deploy/k8s/monitoring/ if using
Kubernetes instead) so reviewers know whether Compose intentionally omits
monitoring.

In `@deploy/k8s/redis.yaml`:
- Line 41: The Kubernetes manifest currently references the floating image tag
"redis:7.4-alpine" in deploy/k8s/redis.yaml; audit Redis 7.4 release notes for
Lua VM/jemalloc/LRU eviction, ACL LOAD, BITCOUNT/BITPOS, and hash field
expiration behavior and run integration tests covering Lua scripts, ACL loading,
bit operations, and persistence/RDB/INFO before upgrading; if any behavior
breaks, either pin to a known-good digest (use the multi-arch sha256 digest) or
roll back to the previous 7.3.x image tag, and update the manifest's image field
to the chosen immutable digest/tag and document the verification steps.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 56-70: The workflow step "Start cleaned pgvector database"
launches a container named second-brain-ci-db but never stops or removes it; add
a separate cleanup step that runs unconditionally (use if: always()) after your
tests to force-remove the container (targeting second-brain-ci-db) so it won't
linger (e.g., run docker rm -f second-brain-ci-db || true in that step); ensure
the cleanup step is placed after the test steps and uses the same container name
to reliably remove the instance.

In `@backend/app/capture/service.py`:
- Around line 53-56: The try/except that accesses parsed.port only for its side
effect should be made explicit to avoid static-analysis warnings: replace the
bare access with an explicit assignment like "_ = parsed.port" (or add a short
comment above it) and keep the existing except ValueError as exc: raise
ValueError("capture URL includes an invalid port") from exc so intent is clear;
update the code around parsed.port in the capture service where parsed is used.

In `@backend/app/llm/ollama.py`:
- Around line 29-32: The json.loads(line) inside the for loop that iterates
r.iter_lines() can raise on malformed JSON from Ollama; wrap the
json.loads(line) call in a try/except that catches json.JSONDecodeError (and
optionally ValueError), log a clear, contextual error including the raw line and
any request identifiers, and either continue or raise a more specific exception
so callers (e.g., stream_chat) get a clearer failure; update the block in
ollama.py where the for line in r.iter_lines(): loop and json.loads(line) occur
(reference json.loads and the iter_lines loop) to implement this defensive
parsing and logging.

In `@backend/tests/integration/test_capture_api.py`:
- Around line 11-21: The test payload builder function _capture_payload has a
duplicated "capture" entry in the tags list; either remove the duplicate so tags
becomes ["capture", "inbox"] or, if the test intentionally verifies tag
deduplication, add an inline comment clarifying that intent and ensure the
corresponding test asserts dedup behavior (update tests referencing
_capture_payload accordingly).

In `@backend/tests/integration/test_dataops_api.py`:
- Around line 55-66: The test test_admin_token_alone_is_not_api_access should
clarify that /data/export requires two distinct tokens validated by
require_api_access (Authorization Bearer == api_token) and require_admin
(X-Second-Brain-Admin-Token == admin_token); update the test by adding a short
inline comment (or rename the test to something like
test_admin_token_does_not_substitute_api_token) stating that the admin token
must not substitute for the API bearer token, and ensure the existing headers
keep TOKEN (test-admin-token) for both Authorization and
X-Second-Brain-Admin-Token to show the bearer being the admin token is
intentional.

In `@deploy/Dockerfile.caddy`:
- Around line 18-24: The Dockerfile.caddy runtime stage runs all processes as
root, which increases the attack surface. Add a non-root USER directive in the
Dockerfile before the CMD instruction that starts Caddy, and then update the
docker-compose configuration to grant the NET_BIND_SERVICE capability to the
caddy service. This allows the non-root user to bind to the privileged ports 80
and 443 while maintaining security best practices.

In `@deploy/k8s/README.md`:
- Around line 76-82: The README entry under "Optional Monitoring Templates"
references local scanned-clean image tags (e.g.,
second-brain-grafana:phase7-clean-required) and imagePullPolicy: Never but lacks
build instructions; update deploy/k8s/README.md to add a short section that
points to where the Dockerfiles live (deploy/grafana/, deploy/prometheus/,
deploy/pgbouncer/ or the repo paths), and document the expected build-and-scan
workflow: which Dockerfile to use, the docker build and tag convention (use the
*-clean-required tag format), the scanning step to run (e.g., SCA or image
scanner), and that images must be built locally before applying manifests that
reference those tags; mention the tag naming convention and remind that
imagePullPolicy: Never requires local images.

In `@frontend/app/capture/page.tsx`:
- Around line 109-116: Wrap the capture UI in a semantic <form> and move the
submit logic from the button onClick to the form's onSubmit handler: call
capture.mutate() from an onSubmit that calls event.preventDefault(), change the
button to type="submit" (keep disabled={!canSubmit}) and keep using
capture.isPending for the label; this will enable Enter-key submission and
better accessibility while preserving the existing canSubmit and capture.*
behavior.

In `@frontend/lib/api/client.ts`:
- Around line 99-111: The parseSseBlock function currently calls JSON.parse
without protection; wrap the parse of dataLines.join("\n") in a try-catch inside
parseSseBlock, and on parse failure return null (or optionally log the error)
instead of letting the exception propagate so malformed SSE payloads are handled
gracefully; update references in parseSseBlock where it returns parsed data to
handle the null case accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f2e53d5-f3c4-4961-86b7-942299047b8d

📥 Commits

Reviewing files that changed from the base of the PR and between 9bc93d7 and fad7394.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (90)
  • .dockerignore
  • .github/workflows/ci.yml
  • .github/workflows/k8s.yml
  • .gitignore
  • README.md
  • backend/.env.example
  • backend/README.md
  • backend/app/api/briefing.py
  • backend/app/api/capture.py
  • backend/app/api/chat.py
  • backend/app/api/conversations.py
  • backend/app/api/dataops.py
  • backend/app/api/ingest.py
  • backend/app/api/research_jobs.py
  • backend/app/api/search.py
  • backend/app/api/sources.py
  • backend/app/api/tasks.py
  • backend/app/cache/rate_limit.py
  • backend/app/cache/redis_client.py
  • backend/app/capture/__init__.py
  • backend/app/capture/service.py
  • backend/app/chat/prompt.py
  • backend/app/chat/service.py
  • backend/app/config.py
  • backend/app/deps.py
  • backend/app/llm/base.py
  • backend/app/llm/fake.py
  • backend/app/llm/gemini.py
  • backend/app/llm/ollama.py
  • backend/app/main.py
  • backend/app/mcp_server.py
  • backend/app/research/service.py
  • backend/app/schemas/capture.py
  • backend/eval/corpus/04-docker-compose-runtime.md
  • backend/requirements.prod.txt
  • backend/requirements.txt
  • backend/tests/conftest.py
  • backend/tests/integration/conftest.py
  • backend/tests/integration/test_api.py
  • backend/tests/integration/test_briefing.py
  • backend/tests/integration/test_capture_api.py
  • backend/tests/integration/test_chat.py
  • backend/tests/integration/test_dataops_api.py
  • backend/tests/unit/test_api_auth.py
  • backend/tests/unit/test_chat_stream.py
  • backend/tests/unit/test_config.py
  • backend/tests/unit/test_mcp_server.py
  • backend/tests/unit/test_prompt.py
  • backend/tests/unit/test_redis_paths.py
  • backend/tests/unit/test_research_prompt.py
  • deploy/.env.prod.example
  • deploy/Dockerfile.backend
  • deploy/Dockerfile.caddy
  • deploy/Dockerfile.frontend
  • deploy/Dockerfile.pgvector
  • deploy/caddy/Caddyfile
  • deploy/cron/second-brain-backup
  • deploy/docker-compose.prod.yml
  • deploy/docker-compose.vps.yml.example
  • deploy/k8s/README.md
  • deploy/k8s/api.yaml
  • deploy/k8s/kustomization.yaml
  • deploy/k8s/monitoring/grafana.yaml
  • deploy/k8s/monitoring/prometheus.yaml
  • deploy/k8s/pgbouncer.yaml
  • deploy/k8s/postgres-statefulset.yaml
  • deploy/k8s/redis.yaml
  • deploy/k8s/secret.example.yaml
  • deploy/k8s/worker.yaml
  • deploy/pgbouncer/pgbouncer.ini
  • deploy/pgbouncer/userlist.txt.example
  • docker-compose.yml
  • docs/PROGRESS.md
  • docs/USAGE.md
  • docs/adr/0014-kubernetes-learning-track.md
  • docs/implementation-notes.md
  • docs/query-optimization.md
  • docs/runbooks/backup-restore.md
  • docs/runbooks/deploy-checklist.md
  • docs/runbooks/incident-response.md
  • frontend/.env.example
  • frontend/.gitignore
  • frontend/app/admin/page.tsx
  • frontend/app/capture/page.tsx
  • frontend/app/chat/page.tsx
  • frontend/components/ConversationSidebar.tsx
  • frontend/components/MessageList.tsx
  • frontend/lib/api/client.ts
  • frontend/lib/api/types.ts
  • frontend/package.json
💤 Files with no reviewable changes (2)
  • deploy/pgbouncer/userlist.txt.example
  • deploy/pgbouncer/pgbouncer.ini

Comment thread backend/app/api/chat.py
Comment thread deploy/docker-compose.prod.yml
Comment thread deploy/k8s/redis.yaml Outdated
@tomnguyen103

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomnguyen103
tomnguyen103 merged commit 1bc096f into main Jun 5, 2026
9 checks passed
@tomnguyen103
tomnguyen103 deleted the codex/security-auth-hardening branch June 5, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant