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
16 changes: 14 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ supabase start # Local Supabase stack
supabase db push # Apply migrations from supabase/migrations/
supabase gen types typescript --local > src/types/database.ts # Regenerate DB types after schema changes

npm test # Vitest unit tests (172 tests across 9 files)
npm test # Vitest unit tests
npm run test:watch # Vitest in watch mode
npm run test:coverage # Vitest with V8 coverage
npm run e2e # Playwright E2E tests (requires dev server)
Expand All @@ -102,6 +102,7 @@ npm run e2e:ui # Playwright with interactive UI
### Test Structure

- **Unit tests (Vitest):** `src/**/__tests__/*.test.ts` — constants, dev-mock, mock-data, worker-client, PDF certificate builder, Stripe client/products/webhooks, API validation schemas
- **API route tests (Vitest):** `src/app/api/__tests__/` — route handler unit tests
- **E2E tests (Playwright):** `e2e/*.spec.ts` — auth flows, verification flows, certificate flows. Config in `playwright.config.ts`.
- **Worker tests (Python):** `worker/tests/` — callback handling, response structure, format validation

Expand All @@ -119,11 +120,13 @@ See `edgeproof/.env.example` for the full list. Key groups:
- Prefer server components; use `"use client"` only when needed (hooks, event handlers, browser APIs)
- Validate all API inputs with Zod schemas at the top of route handlers
- Use Supabase generated types from `src/types/database.ts` — don't manually define DB types
- API keys: SHA-256 hash stored in DB, only `ep_live_` prefix displayed to users
- API keys: SHA-256 hash stored in DB, only `ep_live_` prefix displayed to users. Validation helpers in `src/lib/auth/api-keys.ts`: `readApiKeyFromAuthHeader()` → `validateApiKey()`. Both the hash and first-16-char prefix are stored for efficient lookup.
- Path alias: `@/` maps to `src/`
- Tailwind v4 with `@theme inline` in `globals.css` (no tailwind.config.ts) and OKLCh color system
- shadcn/ui components in `src/components/ui/`, added via `npx shadcn@latest add <component>`
- Toast notifications via `sonner` (not shadcn toast)
- Audit trail writes go through `src/lib/audit/actions.ts`
- `src/lib/mock/` provides shared test data (`data.ts`) and a test logger (`logger.ts`) used across unit tests

## Database Schema

Expand All @@ -150,6 +153,15 @@ Returns JSON with: `status` (authentic/tampered/unsigned/inconclusive/error), `d

Full contract with example response in `EDGEPROOF_BUILD_PLAN.md` § Step 2.2.

## V1 Enterprise API

Public REST API for Enterprise-tier customers. All endpoints require `Authorization: Bearer ep_live_<key>` header. Auth validated via `src/lib/auth/api-keys.ts`.

- **`POST /api/v1/verify`** — multipart/form-data with `file` field. Creates a verification record, uploads file to Supabase Storage, dispatches to worker. Returns `{ verification_id, status: "processing", poll_url }`. Requires `VERIFICATION_WORKER_URL` (not available in mock mode).
- **`GET /api/v1/verifications/{id}`** — poll a specific verification. Returns full `VerificationPollResponse` (see `src/types/api.ts`).

The V1 API does NOT use the session-based flow — it inserts directly into `verifications` with `user_id` from the API key row, bypassing quota enforcement (MVP; quota to be wired in later).

## Signed Video Technical Context

- **Signing UUID:** `5369676e-6564-2056-6964-656f2e2e2e30` (identifies signed video SEI NALUs)
Expand Down
5 changes: 4 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { NextConfig } from "next";
import path from "path";

const nextConfig: NextConfig = {
/* config options here */
turbopack: {
root: path.resolve(__dirname),
},
};

export default nextConfig;
3 changes: 3 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"framework": "nextjs"
}
1 change: 1 addition & 0 deletions worker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vercel
16 changes: 16 additions & 0 deletions worker/Dockerfile.mock
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt

COPY certs/ /app/certs/
COPY app/ /app/app/

EXPOSE 8000

ENV USE_MOCK_RESULTS=true

# Use Railway's injected $PORT if set, otherwise fall back to 8000
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
Comment on lines +1 to +16
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Run the container as a non-root user.

The image currently runs as root, which weakens container isolation and increases blast radius if compromised.

🔒 Proposed hardening patch
 FROM python:3.11-slim
 
 WORKDIR /app
 
 COPY requirements.txt .
 RUN pip3 install --no-cache-dir -r requirements.txt
 
 COPY certs/ /app/certs/
 COPY app/ /app/app/
+
+RUN addgroup --system app && adduser --system --ingroup app app \
+  && chown -R app:app /app
 
 EXPOSE 8000
 
 ENV USE_MOCK_RESULTS=true
+USER app
 
 # Use Railway's injected $PORT if set, otherwise fall back to 8000
 CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
COPY certs/ /app/certs/
COPY app/ /app/app/
EXPOSE 8000
ENV USE_MOCK_RESULTS=true
# Use Railway's injected $PORT if set, otherwise fall back to 8000
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
COPY certs/ /app/certs/
COPY app/ /app/app/
RUN addgroup --system app && adduser --system --ingroup app app \
&& chown -R app:app /app
EXPOSE 8000
ENV USE_MOCK_RESULTS=true
USER app
# Use Railway's injected $PORT if set, otherwise fall back to 8000
CMD ["sh", "-c", "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
🧰 Tools
🪛 Trivy (0.69.1)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@worker/Dockerfile.mock` around lines 1 - 16, Create and switch to a non-root
user in the Dockerfile: add steps to create a dedicated user (e.g., "appuser"),
create or set a group if desired, set appropriate ownership for /app and
/app/certs (chown to appuser), set HOME and switch to that user with USER
appuser before the CMD so uvicorn runs unprivileged; ensure any files created
earlier (installed packages, copied files) are accessible by that user and avoid
running pip as root at runtime by doing installation during build as root but
ensuring the app directory permissions are changed for the non-root user.

13 changes: 13 additions & 0 deletions worker/railway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile.mock"
},
"deploy": {
"healthcheckPath": "/health",
"healthcheckTimeout": 300,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}