Smartnotes is a Vite + React 19 single-page app backed by Convex. It runs a code-gated study flow with identified access grants:
- Upload class material (PDFs, slides, Word/docs, notes).
- Generate and answer exam-style questions with explanations.
- Analyze topic readiness, then deep dive weak topics or start a new session.
The AI pipeline uses the Vercel AI SDK with Google Vertex AI. PostHog is configured so this app repo can share the correct environment-specific PostHog project with the separate website repo.
- Frontend: React 19, Vite, Tailwind CSS
- Backend: Convex (database, server functions) + Cloudflare R2 (Dateispeicher)
- AI:
ai+@ai-sdk/google-vertex - Document processing: native Vertex file input for PDF/JPG/JPEG/PNG/WEBP, plus
officeparserextraction for PPT/PPTX/DOC/DOCX and text-based formats
- Node.js 20+
pnpm(required)- A configured Convex project/deployment
- Google Vertex AI credentials (API key in Express Mode, or project/location auth)
- Cloudflare R2 bucket plus access credentials
Environment variable reference: docs/environment.md.
PostHog routing and proxy behavior: docs/posthog-proxy.md.
Validation model:
- Frontend +
vite.config.ts: T3 Env (@t3-oss/env-core) - Convex backend: runtime helpers in
convex/env.ts(Convex-specific runtime)
- Install dependencies:
pnpm install- Configure Convex and generate types:
pnpm exec convex devConvex agent skills for common tasks can be installed by running npx convex ai-files install.
- Add frontend env vars in
.env.local:
VITE_CONVEX_URL=<your-convex-url>
VITE_POSTHOG_KEY=<your-posthog-project-key>
VITE_POSTHOG_HOST=/snph
VITE_POSTHOG_UI_HOST=https://eu.posthog.comTip: start from .env.example and keep canonical names (VITE_POSTHOG_*).
The file intentionally keeps Convex env names commented so .env.local is not
mistaken for backend configuration.
Recommended PostHog project layout:
- development: one shared PostHog project for local website + local app
- preview: one shared PostHog project for website previews + app previews
- production: one shared PostHog project for
smartnotes.tech+app.smartnotes.tech
PostHog frontend routing notes:
/snphis the recommended setting. In production, Vercel rewrites that path to the EU PostHog hosts.- In local
pnpm devandpnpm preview, Vite proxies the same path to the same EU hosts. - If you set
VITE_POSTHOG_HOSTto an absolute URL instead, the proxy is bypassed in all environments. VITE_POSTHOG_UI_HOSTshould remain the real PostHog app host, not the proxy path.
- Configure backend env vars in Convex:
pnpm exec convex env set GOOGLE_VERTEX_API_KEY <your-api-key>
# optional alternative auth path
pnpm exec convex env set GOOGLE_VERTEX_PROJECT <your-project-id>
pnpm exec convex env set GOOGLE_VERTEX_LOCATION us-central1
pnpm exec convex env set ACCESS_CODE_ADMIN_SECRET <admin-secret>
# Balanced observability mode (Langfuse + retention)
pnpm exec convex env set LANGFUSE_PUBLIC_KEY <your-langfuse-public-key>
pnpm exec convex env set LANGFUSE_SECRET_KEY <your-langfuse-secret-key>
pnpm exec convex env set LANGFUSE_BASEURL https://cloud.langfuse.com
pnpm exec convex env set OBSERVABILITY_MODE balanced
pnpm exec convex env set OBSERVABILITY_ALLOW_SENSITIVE_CAPTURE false
pnpm exec convex env set OBSERVABILITY_HASH_SALT <random-32+-chars>
pnpm exec convex env set OBSERVABILITY_FLUSH_ON_EXIT true
pnpm exec convex env set OBSERVABILITY_FLUSH_TIMEOUT_MS 300
pnpm exec convex env set RETENTION_DAYS_RAW_CONTENT 14
pnpm exec convex env set RETENTION_DAYS_ANALYTICS 180
pnpm exec convex env set RETENTION_DAYS_POSTHOG_OUTBOX 30
# Storage provider (defaults to "r2" when unset)
# only set this if you explicitly want Convex-managed storage instead of R2
# pnpm exec convex env set FILE_STORAGE_PROVIDER convex
pnpm exec convex env set R2_ACCOUNT_ID <your-r2-account-id>
pnpm exec convex env set R2_ACCESS_KEY_ID <your-r2-access-key-id>
pnpm exec convex env set R2_SECRET_ACCESS_KEY <your-r2-secret-access-key>
pnpm exec convex env set R2_BUCKET_NAME <your-r2-bucket-name>
# PostHog AI bridge
pnpm exec convex env set POSTHOG_ENABLED true
pnpm exec convex env set POSTHOG_PROJECT_KEY <your-posthog-project-key>
pnpm exec convex env set POSTHOG_HOST https://eu.i.posthog.com
# optional explicit backend environment tag
pnpm exec convex env set POSTHOG_APP_ENV productionImportant:
POSTHOG_HOSThere is for the Convex backend bridge, not the browser.- Do not set
POSTHOG_HOST=/snph. Relative proxy paths only work for browser traffic viaVITE_POSTHOG_HOST. FILE_STORAGE_PROVIDERis optional. If you leave it unset, Smartnotes uses R2 by default and therefore still requires theR2_*credentials above.
- Optional: source map upload for PostHog Error Tracking (build-time env vars):
# shell env vars before pnpm build
POSTHOG_SOURCEMAPS_API_KEY=<your-posthog-personal-api-key>
POSTHOG_SOURCEMAPS_PROJECT_ID=<your-posthog-project-id>
POSTHOG_SOURCEMAPS_HOST=https://eu.i.posthog.com
# optional release metadata
POSTHOG_SOURCEMAPS_RELEASE_NAME=smartnotes
POSTHOG_SOURCEMAPS_RELEASE_VERSION=0.0.0The source-map upload variables are validated as a pair:
- both missing -> upload disabled
- only one set -> build fails
- Start the app:
pnpm devLocal development URLs:
pnpm devis always running onhttp://localhost:5173pnpm previewis always running onhttp://localhost:4173- File uploads always go over a Vite proxy on the same origin so that the browser doesn't run against external storage CORS rules
- The app has no classic account system, but access is no longer anonymous.
- Access codes and magic links are tied to a stored Nutzerkennung.
- Users enter a one-time code or magic link to receive a temporary grant token.
- Redeemed codes remain administratively traceable through the stored access-code record and linked grant.
- With a stored email address, analytics stay stable across multiple grants for the same person.
- Without an email address, analytics stay limited to the single redeemed grant.
For local development, SMARTNOTES-DEMO-2026 auto-seeds if no code exists yet.
To create production codes:
pnpm exec convex run access:createAccessCodes "{adminSecret:'<admin-secret>',codes:['YOUR-CODE-1','YOUR-CODE-2'],identityLabel:'Max Mustermann'}"pnpm dev- start Vite + Convex dev server onhttp://localhost:5173pnpm build- typecheck + production buildpnpm lint- run ESLintpnpm format- format code with Prettierpnpm format:check- check code formattingpnpm preview- preview the production build locally onhttp://localhost:4173pnpm test:once- run the Vitest suite oncepnpm observability:debug-window:start -- --minutes 45- set debug-window env vars for bounded troubleshootingpnpm observability:debug-window:stop- clear debug-window env vars immediately
- Neue Uploads laufen über die zentrale Storage-Abstraktion in Cloudflare R2.
sessionDocuments.storageIdist dauerhaft ein String-Feld.sessionDocuments.storageProviderist verpflichtend und kennzeichnet den aktiven Provider.
- A
studySessionis the top-level container for one learner's material, quiz state, responses, and analysis. studySessions.roundis the active quiz batch inside that session, not a cosmetic counter.- Initial quiz generation keeps the current round.
- A focused deep dive writes a fresh question batch and increments the round so the app can treat those questions as a new active set.
quizResponses.roundlinks each stored answer to the quiz batch it belongs to.- The UI uses the current round to decide which questions are still unanswered, while analysis can still inspect responses across multiple rounds in the same session.
- AI calls are traced with Langfuse telemetry in
balancedmode. - Langfuse captures full AI inputs and outputs for every traced backend call.
- Telemetry stores operational metadata plus unredacted request and response context.
aiAnalyticsEvents.metadataJsonstores the raw backend metadata for each AI-event.- AI actions flush telemetry on exit (
OBSERVABILITY_FLUSH_ON_EXIT) with a bounded timeout (OBSERVABILITY_FLUSH_TIMEOUT_MS) to reduce trace loss. - A daily Convex cron redacts/deletes older sensitive data:
- Redacts
sessionDocuments.extractedTextandquizResponses.userAnswer. - Deletes old
aiAnalyticsEventsrows.
- Redacts
- Frontend funnel events from access-code redemption to analysis/deep-dive completion.
- UX events: stage transitions (
study_stage_viewed), theme changes, consent update hook. - Backend AI operation bridge event:
ai_operation_completedplus$ai_generationwith unredacted AI payloads. - Correlation fields for AI observability:
traceId,documentIds,readyDocumentIds.
- Sensitive UI fields are marked with
ph-no-captureanddata-ph-sensitive="true". - PostHog frontend starts as early as possible and uses
persistence: 'localStorage+cookie'. - Production uses
.smartnotes.techas the cookie domain sosmartnotes.techandapp.smartnotes.techcan share the anonymous browser journey. - Local development stays on
localhostonly. Do not mixlocalhostand127.0.0.1if you expect one shared local browser identity across repos. - Preview deployments on separate Vercel-generated hosts keep full per-repo PostHog functionality, but they do not share anonymous browser continuity across repos.
- Identified PostHog persons use a backend-issued
analyticsDistinctId. - With an email address,
analyticsDistinctIdis stable per normalized email. - Without an email address,
analyticsDistinctIdis stable only for the individual grant. - Base frontend properties are registered automatically:
app_area,environment,source_surface,host,path,referrer,landing_url,utm_*,identity_quality. - Session replay masking is configured selectively via
blockSelector,maskTextSelectorandmaskTextFn. - Only elements explicitly marked with
ph-no-captureordata-ph-sensitive="true"are masked in replay. - There is no frontend
before_sendscrubber anymore. Frontend events are sent to PostHog without property-level sanitization or client-side sampling.
defaults: '2026-01-30'api_host: '/snph'by default unlessVITE_POSTHOG_HOSToverrides itui_host: 'https://eu.posthog.com'by default unlessVITE_POSTHOG_UI_HOSToverrides itperson_profiles: 'identified_only'persistence: 'localStorage+cookie'capture_pageview: 'history_change'capture_pageleave: truecapture_dead_clicks: truecapture_performance: { web_vitals: true, network_timing: true }disable_session_recording: falseenable_recording_console_log: falseautocaptureenabled with conservative allowlist and copied-text capture disabled
- Session Replay ON.
- Heatmaps ON.
- Error Tracking ON (exception autocapture enabled in project settings).
- Web Vitals ON.
- Initial replay sampling recommendation: 30%, minimum recording duration 10s, trigger failed flows on exceptions.
- Product analytics billing guardrails: set per-product limits before production traffic.
- Feature flag key:
analysis_cta_variant(used inQuizStageCTA copy). - Suggested variants:
- control: default copy (
Weiter zur Lernanalyse) - kompakt: shorter copy (
Analyse erstellen)
- control: default copy (
- Experiment suggestion: primary metric
analysis_succeededrate afterquiz_answer_submitted. - Survey suggestion: trigger after
analysis_succeeded, German copy, repeat cadence capped (e.g. max once per 14 days).
-- 1) Funnel conversion (auth -> upload -> quiz -> analysis)
SELECT
distinct_id,
max(event = 'auth_code_redeem_succeeded') AS auth_ok,
max(event = 'document_upload_succeeded') AS upload_ok,
max(event = 'quiz_generation_succeeded') AS quiz_ok,
max(event = 'analysis_succeeded') AS analysis_ok
FROM events
WHERE timestamp >= now() - interval 30 day
GROUP BY distinct_id;-- 2) AI reliability by scope/status
SELECT
properties.scope AS scope,
properties.status AS status,
count(*) AS events,
avg(toFloat64(properties.latencyMs)) AS avg_latency_ms,
quantile(0.95)(toFloat64(properties.latencyMs)) AS p95_latency_ms,
avg(toFloat64(properties.totalTokens)) AS avg_total_tokens,
avg(if(properties.fallbackUsed = true, 1, 0)) AS fallback_rate
FROM events
WHERE event = 'ai_operation_completed'
AND timestamp >= now() - interval 30 day
GROUP BY properties.scope, properties.status
ORDER BY scope, status;- Start with one destination only (free-tier friendly), for example:
- HTTP webhook destination for daily reliability snapshots, or
- Batch export to your BI warehouse when available.
- Keep payload minimal and based on the real PostHog event contract.
- Product funnel dashboard: redeem, upload, quiz generation, answer completion, analysis completion, deep-dive uptake.
- Reliability dashboard: frontend exceptions, AI errors by scope, latency percentiles, fallback trend, replay links.
- Web UX dashboard: p90 web vitals, dead/rage clicks, heatmap hotspots.
- Alerts:
- error spike
- analysis failure rate threshold
- quiz generation failure threshold
See docs/observability.md for implementation details and admin operations.
See docs/posthog-proxy.md for the full routing matrix, supported env combinations, and failure modes.