SoloSheet turns uploaded lecture PDFs and images into dense, printable study sheets. The application uses Next.js, Supabase, Google Gemini, and Stripe.
This repository is licensed under the MIT License. Please read the security policy before reporting a vulnerability and contribution guide before opening a pull request.
Install dependencies and start the local Supabase stack:
npm ci
npx supabase start
supabase status -o envCopy .env.example to .env.local, then replace its placeholders with the
matching local or hosted values. Never mix credentials from different Supabase
targets.
Run supabase status -o env and copy the values from its output into .env.local.
Use API_URL for the URL, ANON_KEY for the anon key, and SERVICE_ROLE_KEY
for the service-role key:
NEXT_PUBLIC_SUPABASE_URL=<value from API_URL>
NEXT_PUBLIC_SUPABASE_ANON_KEY=<value from ANON_KEY>
SUPABASE_SERVICE_ROLE_KEY=<value from SERVICE_ROLE_KEY>
NEXT_PUBLIC_ENABLE_TEST_AUTH=true
# Required only for local Stripe test-mode integration:
STRIPE_SECRET_KEY=<test-mode secret key>
STRIPE_WEBHOOK_SECRET=<Stripe CLI or test endpoint signing secret>
STRIPE_PRICE_ID=<one-time $3.00 USD Price ID>
APP_URL=http://127.0.0.1:3000
EXTRACTION_DISPATCH_SECRET=<at-least-32-random-bytes>Start the local app for routine UI work:
npm run dev -- --hostname 127.0.0.1Production generation queues a durable Supabase job, dispatches a protected
Netlify Background Function, and polls an owner-scoped status route. Local
end-to-end generation requires netlify dev, not plain next dev, so the
background function is available. Apply phase 19 before deploying the matching
application/functions, and keep EXTRACTION_DISPATCH_SECRET available to both
runtimes. See the generation incident record
for rollout and recovery details. Routine checks use synthetic fixtures; live
Gemini or hosted-database checks require explicit authorization.
Restart Next.js whenever .env.local changes. For a fresh database, apply the
generated supabase/bootstrap.sql in one execution. Do not apply only an early
prefix of the historical phase files: the intermediate schema can be insecure.
See database bootstrap and migration safety for
fresh and existing database procedures.
npx supabase start runs the local backend services (Auth, Postgres, Storage, and
REST). npm run dev runs only the Next.js frontend/API. Full local integration
testing requires both commands. With local Supabase, Google OAuth is not enabled;
the login page uses the development test account. With hosted Supabase, use the
normal Google login. The automated checks below can run without either service.
Stripe is card-only for the MVP. The configured Price must be a one-time $3.00
USD price for 10 credits. Subscribe the webhook endpoint
/api/webhooks/stripe to checkout.session.completed,
checkout.session.expired, charge.refunded, charge.dispute.created, and
charge.dispute.closed. See
docs/stripe-payments.md before test-mode verification.
The Stripe CLI is optional and is needed only to forward sandbox webhooks to a
localhost server; a deployed HTTPS endpoint receives webhooks directly.
Run automated checks. The database suite requires Docker and uses only a fresh, unpublished disposable PostgreSQL database:
npm run check
docker run --name solosheet-hardening-db -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:16
npm run test:database
npm run build
git diff --checkFor an authenticated local browser pass, start local Supabase and Next.js with
NEXT_PUBLIC_ENABLE_TEST_AUTH=true, then create the disposable account and run
the Playwright flow:
node scripts/create-playwright-user.mjs
PLAYWRIGHT_HEADLESS=true node scripts/playwright-auth.mjs
npm run test:e2e:localThe browser test uses the self-authored synthetic PDF, uploads it to local Storage, and mocks only the Gemini extraction response so no hosted provider or production data is contacted. It verifies the same request ID is retained for an ambiguous retry and that a terminal restart cleans up the temporary upload.
For the open-signup release, npm run verify:open-signup runs focused
application/CLI coverage plus the complete disposable-database suite. A
separate loopback-only browser runner covers .edu and non-.edu accounts
under both flag states without calling Stripe or Gemini. See
open-signup local acceptance for setup,
the acceptance matrix, cleanup behavior, and deployment-evidence boundaries.
See administrator operations for first-admin
bootstrap, allowlist changes, hold release, and the non-.edu trial-credit
flag. These operations are never available to browser clients.
To have an agent run the authenticated browser test, ask: Use the local-testing skill, start the local Supabase and Next.js services, authenticate the local Playwright account, and verify dashboard access, guide modes, overflow reporting, and printed PDF page counts.
Keep .env.local, auth state, screenshots, and generated PDFs out of version control. To use hosted Supabase, set the hosted URL, anon key, and server-only service-role key in .env.local, set NEXT_PUBLIC_ENABLE_TEST_AUTH=false, and restart Next.js.
Only the self-authored PDF under Test_Files/synthetic/ may be committed as an
upload fixture. Regenerate it with npm run fixtures:generate; never commit real
lecture notes, annotations, assignments, or student documents.
Next.js loads .env.local before .env, so only one Supabase target should be
active at a time. npm run dev starts the Next.js app; it does not start or stop
Supabase.
For hosted Supabase, keep the hosted values in .env.local (or remove .env.local
to use the hosted values already in .env), set:
NEXT_PUBLIC_ENABLE_TEST_AUTH=falseThen run:
npm run dev -- --hostname 127.0.0.1The hosted setup uses the normal Google login. It does not require npx supabase start.
For local Supabase, start the local services and restore or create .env.local
with the local values:
npx supabase start
supabase status -o env
mv .env.local.local-backup .env.local # if you previously saved the local file
npm run dev -- --hostname 127.0.0.1The local setup uses the development test account because Google OAuth is not enabled by default in the local stack. Apply the atomic bootstrap once per fresh database. Existing deployments must use only unapplied forward migrations as described in the database guide.
Production builds intentionally disable Turbopack's filesystem cache through
turbopackFileSystemCacheForBuild: false in next.config.ts. Next.js 16.3 can
serialize server-only environment values into .next/cache/turbopack, which
Netlify scans as generated build output. Do not re-enable the build cache until
a clean Netlify build confirms those values are no longer written there; keep
secret scanning enabled rather than excluding server credential names.
The in-app Terms and Privacy Policy are operational starter documents, not a substitute for review based on the operator's jurisdiction, contact details, retention promises, and production vendor settings. Review them before accepting real users or payments.
Before making a previously private repository public, follow the public release checklist. Deleting a private file from the current tree does not remove it from Git history.