ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Web app to prepare for the German SKS sailing license theory exam — free-text answers graded by an LLM against the official model answer. React + FastAPI, deployed on Render.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A web app to prepare for the theoretical exam of the German SKS (Sportküstenschifferschein) sailing license. The official exam catalog is free-text, not multiple choice — the learner writes an answer and has to judge for themselves whether it's close enough to the model answer. SKS Lotse tracks what a learner has actually retained, simulates the exam, and — on request — has an LLM compare the learner's free-text answer with the official one, suggest a grade and explain what was missing or wrong; the learner always confirms the grade.檢視完整 README ↗
A web app to prepare for the theoretical exam of the German SKS (Sportküstenschifferschein) sailing license. The official exam catalog is free-text, not multiple choice — the learner writes an answer and has to judge for themselves whether it's close enough to the model answer. SKS Lotse tracks what a learner has actually retained, simulates the exam, and — on request — has an LLM compare the learner's free-text answer with the official one, suggest a grade and explain what was missing or wrong; the learner always confirms the grade.
What's different from existing apps (SKS-Buddy, the official SKS App — both already offer AI-graded free text): web-only (no app store), a learning status based on an estimated memory half-life rather than a streak, and the official Kartenaufgaben worked through task by task. Monetization is freemium without ads — pay per use for the AI check with a token balance — see docs/adr/0043 and docs/adr/0060 for the reasoning.
Fully AI-built, human-directed, live in production. Code, tests, migrations, workflows and docs were written by Claude through Claude Code (apart from Dependabot's bumps). The human part: what the product is, which option to take, what counts as done and goes live, plus what only a person can settle — sailing domain, catalog and chart rights, prices, operations. Live on Render; first commit 2026-09-16.
What keeps the AI on course:
Commits name the model that wrote them (Co-Authored-By):
git log --format='%(trailers:key=Co-Authored-By,valueonly)' | sort | uniq -c | sort -rn
Live: email+OTP login, learning by topic with self-assessment against the official answers, Fokus and Auffrischen, the exam simulation (Fragebogen) with history and statistics, the Lotsen-Check (an LLM that suggests a grade, paid with tokens) with token packages bought through Stripe, and the admin tools. Also live: the Kartenaufgaben (sheets 1 to 7 so far), open to guests too, with their own Lotsen-Check. Not built yet: SSO, speech-to-text. What learners and operators can do today, incl. prices: docs/FEATURES.md; the technical current state and the full "not yet built" list: docs/ARCHITECTURE.md.
| Layer | Technology |
|---|---|
| Frontend | React (Vite) + TypeScript, Zustand, Tailwind CSS |
| Backend | Python 3.12 / FastAPI |
| Database | PostgreSQL 18 |
| Auth | Email + one-time code (OTP), JWT sessions (SSO not yet built) |
| Transactional email | Resend |
| Answer check (LLM) | Anthropic API (Claude Haiku 5.5 for catalog questions, Claude Sonnet for Kartenaufgaben), pay-per-use with tokens — ADR-0031, ADR-0066, ADR-0043, ADR-0058 |
| Payments | Stripe Hosted Checkout, fulfilled by webhook — ADR-0048 |
| Analytics | Umami Cloud (cookieless; hosting region: see RUNBOOK) |
| Monitoring | Better Stack (uptime, status page, logs) |
| Hosting | Render (Frankfurt EU) |
| CI/CD | GitHub Actions |
Everything that remembers needs an account (only the topic learning and the Kartenaufgaben run in the browser without one), so handling personal data properly is part of the design, not an afterthought. The user-facing statement is the in-app Datenschutzerklärung (/privacy); this is the technical side.
localStorage (ADR-0012). Every /api/v1 route requires it except the login (POST /auth/otp/request and /auth/otp/verify), GET /pricing and the Stripe webhook; per-IP rate limiting applies throughout, with tighter limits on login and email change.Requires Docker, Python 3.12 (.python-version), and Node 22 (frontend/.nvmrc). All commands assume the repo root as the working directory unless noted.
# 1. Start local Postgres
docker compose up -d
# 2. Set up the backend
cd backend
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
sed "s/^JWT_SECRET=$/JWT_SECRET=$(openssl rand -hex 32)/" .env.example > .env # .env with a fresh JWT secret
alembic upgrade head # also seeds the full question catalog (data migration)
# 3. Run the API
uvicorn app.main:app --reload --port 8000
In a second terminal, the frontend:
cd frontend
npm install
cp .env.example .env # points the app at the local API on :8000
npm run dev # http://localhost:5173
First login locally: with RESEND_API_KEY left empty no email goes out, so read the code from the dev-only peek endpoint after requesting one on http://localhost:5173/login:
curl "http://localhost:8000/api/v1/auth/otp/[email protected]"
To reach /admin, put your address into ADMIN_EMAILS in backend/.env and restart the API; /admin then stays closed until you have set up 2FA there, see RUNBOOK → Admin 2FA. To try the Lotsen-Check, set ANTHROPIC_GRADING_API_KEY and credit your account some tokens on /admin (without the key the check answers 503).
API docs (Swagger UI): http://localhost:8000/docs. A Postman collection is also generated from the live OpenAPI schema — see below.
Backend — run from backend/, with the venv active:
pytest # tests + line/branch coverage gate (threshold in backend/pyproject.toml)
ruff check . # lint
ruff format --check . # formatting
mypy app # type check
Frontend — run from frontend/:
npx tsc -b # type check
npx vitest run --coverage # tests + line/branch coverage gate (thresholds in vite.config.ts)
npm run lint # ESLint
npm run format:check # Prettier
npm run build # production build incl. prerender (what Render runs)
VITE_SITE=sbf VITE_SBF=off npm run build && node scripts/check-sbf-build.mjs # SBF Lotse's build (ADR-0067), checked for API calls
npm run dev and npm run build build SKS Lotse; with VITE_SITE=sbf they build SBF Lotse (sbf-lotse.de) from the same code instead (ADR-0067). Its own page, routes and public files are frontend/index.sbf.html, frontend/src/sbf/ and frontend/public-sbf/.
These run in CI on every push to main and every PR (.github/workflows/backend-ci.yml, .github/workflows/frontend-ci.yml). Backend CI additionally checks the Alembic migrations against a real Postgres, checks that the committed Postman collection and frontend API types are still in sync with the API (scripts/generate_postman_collection.sh), and runs the integration tests below.
Optional but recommended: pre-commit install -t pre-commit -t pre-push (from the venv). On commit it runs ruff, ESLint, Prettier and a few hygiene checks (merge markers, YAML/JSON syntax, private keys, large files) on staged files, and blocks commits directly on main. On push it also runs mypy app and tsc -b.
A separate, hand-written Postman collection black-box tests every endpoint of a running local server (auth flow, profile/email change/account deletion, admin tools, CORS, security headers, rate limiting) without needing Python: start the API as above with [email protected] and CHART_EXERCISES=admins on a freshly started server, then run ./scripts/run_integration_tests.sh from the repo root. The script's header lists the full server requirements.
The repo is also connected to Aikido Security for dependency/SAST scanning — scripts/check_aikido.sh queries open findings directly (needs a local .env.aikido and a plan with API access — on the free plan it fails, check the dashboard by hand; see docs/RUNBOOK.md). Aikido rescans about every three days, so it is no merge gate and there is no Aikido job in CI; an alert it raises is handled right away (docs/RUNBOOK.md → Security alerts).
Backend dependencies are declared in backend/requirements*.in and locked — every transitive package, with hashes — in the matching requirements*.txt, generated by pip-compile (pip-tools, part of the dev requirements). Render, CI and local setup install only the .txt locks, so every environment gets byte-identical packages, and pip refuses a download whose hash doesn't match. To add or bump a package, edit the .in file, then regenerate all three locks (runtime, dev, mutation, in that order, since each one constrains the next) and commit both:
./scripts/lock_backend_requirements.sh
Dependabot regenerates the locks itself for its bump PRs. A package some dependency needs only on certain platforms must be listed in the .in file explicitly when its absence on macOS would drop it from the lock (see greenlet in requirements.in) — Render and CI are Linux. Frontend dependencies are locked by frontend/package-lock.json (npm ci).
Trunk-based: main is the single source of truth, every change, however small, goes on a feature/* branch merged back via PR — see CLAUDE.md for the full set of conventions (branch strategy, working directory, security scanning, coverage, linting, ADRs).
/profile. Access, rectification and export requests go to the operator by email and are fulfilled with the admin tools (ADR-0019).