ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
SKS 앱으로 온라인 이론시험 준비, 전 문제·모범답안·학습진도 제공, 무료
Die SKS App, mit der du dich online auf die SKS-Theorieprüfung vorbereitest: alle Fragen des amtlichen Katalogs mit Musterantworten und Lernfortschritt – kostenlos.
교육TypeScript · PythonGitHub @berndloeffeld온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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).