Commit Graph
59 Commits
Author SHA1 Message Date
Lucas WintherandClaude Opus 5.5 2854c26a7d merge: studio
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:42:51 +01:00
Lucas WintherandClaude Opus 5.5 627ebe0029 merge: import
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:41:36 +01:00
Lucas WintherandClaude Opus 5.5 d8d62696da merge: learner
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:40:29 +01:00
Lucas WintherandClaude Opus 5.5 0da35ec6f8 fix(learner): remove the SPA proxy; CSP as a meta tag in index.html (ruling R16)
The proxy, its internal server, Unix socket and NO_PROXY changes are gone; main.ts serves the HTML
import directly again. apps/web/index.html carries the SPA policy as <meta http-equiv=CSP> ahead of every
script (script-src 'self' plus the hash of Bun's dev-server helper, no unsafe-inline/eval; no
frame-ancestors, which meta cannot carry). Dev HMR connects under it. The runner frame keeps its header CSP.

Tests: the served index.html (prod and dev, in a subprocess) has the meta policy and every inline script
is hash-covered; the XSS browser test fails with the sanitizer disabled and passes with it, and the meta
CSP alone blocks unsanitized handlers (with a no-CSP control); no source assigns proxy env vars, and the
feature leaves them untouched at runtime. Contract request 5 asks core for frame-ancestors/X-Frame-Options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:38:04 +01:00
Lucas WintherandClaude Opus 5.5 213f2247b4 fix(learner): SPA proxy forwards no credentials and never goes through HTTP_PROXY
spaWithCsp proxies GET and HEAD only (405 otherwise, no body buffering) and forwards only accept,
if-none-match, if-modified-since and range. The internal server listens on a Unix socket in a private
0700 temp dir. Bun 1.3.14 ignores proxy: false for this hop, and over a socket with a proxy set it sends
an absolute-form target that breaks asset routing, so localhost, 127.0.0.1 and ::1 are appended to
NO_PROXY/no_proxy (existing entries kept). accept-encoding is no longer forced; fetch decompresses and
the encoding headers are dropped.

Test: in a subprocess with HTTP(S)_PROXY and ALL_PROXY set to a dead proxy, the SPA and an asset load
with the CSP, POST gets 405, and the upstream sees no Cookie, Authorization or X-Forwarded-For.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 14:27:56 +01:00
Lucas WintherandClaude Opus 5.5 c7b9e1f98e fix(learner): review round 1: sanitize-html, sandboxed runner frame, one visibility rule, no Tab capture
C1: lesson HTML goes through a sanitize-html allowlist on the fully rendered output (no on* attributes, no
style, no svg/math/iframe/object/embed/form; URLs limited to http(s), mailto, relative and anchors). Raw tags
outside the allowlist are escaped as text. The SPA document is served with a CSP (script-src 'self', no inline
script) through spaWithCsp in main.ts; contract request §5 asks core to own it.

C2: exercises run in <iframe sandbox="allow-scripts"> (opaque origin, no cookies) loaded from
/api/learner/runner-frame, whose CSP allows only its own script (by hash) and blob: workers, and connect-src
'none'. postMessage only, both sides check event.source; the timeout removes the frame. The worker stubs now
also cover Worker, SharedWorker and caches.

I1: learner visibility calls core canView with per-course authorship only, like /api/media (contract request §6).
I2: Tab and Shift+Tab move focus; Enter keeps the previous line's indentation.

Tests: XSS unit cases, headless-Chrome checks for the exploit lesson and for fetch, import, Cache.add, nested
Worker, img, EventSource, WebSocket, XHR and sendBeacon from learner and test code (plus a negative control),
visibility on both dialects, SPA and runner-frame CSP headers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 05:20:43 +01:00
Lucas WintherandClaude Opus 5.5 061ae7a2d9 feat(learner): search results show lesson matches cleanly; contract notes for the browser entry and LessonView; plan marked done
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:54:53 +01:00
Lucas WintherandClaude Opus 5.5 89c51f013d feat(learner): web shell, library, lesson views (LessonView), decks, code files, exercises, notes, account and admin pages
Tasks 10-12 of the WP-A plan in one commit (the route modules import each other):
- header extras portalled into the Phase 0 header: course switcher, search, Reading/Work, account
- course frame with outline sidebar (mobile overlay, Escape closes), progress, gating locks
- library: featured course, continue row, New and collection rows, course list, filters, search
- LessonView: text (track switcher, folds, ordered hint tiers), video (resume, done near the end,
  timestamp notes, transcript), companion decks rendered with pdf.js (thumbs, arrows, fullscreen,
  per-slide notes), code files (tree, tabs, copy, zip, command), exercises (Web Worker runner or
  local command with optional pasted evidence), floating action bar
- notes list, account (email/password, providers, SSO by email) and owner admin pages
- pdf.js worker served from /api/learner/pdf.worker.mjs
- browser entry @stc/feature-learner/web (the package index also carries the server half)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:53:36 +01:00
Lucas WintherandClaude Opus 5.5 2d162a4282 fix(studio): review round 1 (symlink-safe writes, remote allowlist, upload cap/types/no-overwrite, owner rule at publish/sync/resolve, studio/repos layout and read-only imports, side-effect-free list)
C1: writes lstat every path component and refuse symlinks (dangling included),
check the parent's real path after mkdir, and open with O_NOFOLLOW.
I1: only https://, ssh:// and git@host: remotes; protocol.file/ext/fd=never on
every fetch and push; global/system git config ignored.
I2: STC_UPLOAD_MAX_MB cap (default 100) by Content-Length and while streaming;
extension allowlist (415); stcourse.yaml refused; 409 unless overwrite=true;
sandboxing CSP on draft raw files.
I3: owner-only visibility/source.kind rule checked on the merged result at
publish and sync, and at resolve; publish also validates the merged tree.
I4: bare repos in <dataDir>/studio/repos/<id>.git; imported courses read-only.
I5: list status comes from the bare repo; drafts are created lazily.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 04:43:53 +01:00
Lucas WintherandClaude Opus 5.5 99c0e4929f feat(learner): lesson markdown pipeline with track variants, folds, hint tiers and safe HTML
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:29:14 +01:00
Lucas WintherandClaude Opus 5.5 667f3e814b fix(studio): forbid ext:: remotes; type the per-dialect test table
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:27:30 +01:00
Lucas WintherandClaude Opus 5.5 2849d3c911 docs(studio): contract requests (ctx feature factories, worktrees course dir, LessonView swap, web subpath, nav roles, DraftWriter)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:26:58 +01:00
Lucas WintherandClaude Opus 5.5 9ff5862382 feat(learner): JS/TS exercise runner in a Web Worker with timeout and network blocked
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:26:32 +01:00
Lucas WintherandClaude Opus 5.5 7adf9765b1 fix(import): allowed import roots, hostile zip symlink removal, tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:26:22 +01:00
Lucas WintherandClaude Opus 5.5 0992df7257 feat(studio): studio web UI (course list, manifest, lessons with live preview, decks, attachments, exercises, conflicts, sync) and dev entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:26:18 +01:00
Lucas WintherandClaude Opus 5.5 752e44696f fix(learner): zip buffer types
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:25:30 +01:00
Lucas WintherandClaude Opus 5.5 34826762a8 feat(learner): section code listing and zip download
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:25:18 +01:00
Lucas WintherandClaude Opus 5.5 478ebeb255 fix(learner): narrow lesson target result for the type checker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:24:28 +01:00
Lucas WintherandClaude Opus 5.5 96c147e3a2 feat(learner): progress, notes and exercise attempt API
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:24:16 +01:00
Lucas WintherandClaude Opus 5.5 5f85ce1d4b feat(learner): library, search, course outline with gating, lesson payload API
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:23:30 +01:00
Lucas WintherandClaude Opus 5.5 c4df51f7ca feat(import): UI polish, contract requests, report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:22:22 +01:00
Lucas WintherandClaude Opus 5.5 a8a8e32bcb feat(learner): owner admin API for roles, groups, course access and library curation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:20:49 +01:00
Lucas WintherandClaude Opus 5.5 90a477691b feat(import): admin UI at /admin/import, dev server, extraction cleanup
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:20:40 +01:00
Lucas WintherandClaude Opus 5.5 668ae379bf feat(learner): Better Auth sessions, owner bootstrap, claim groups; server takes feature factories
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:20:06 +01:00
Lucas WintherandClaude Opus 5.5 9f96e293e1 feat(import): wire feature into server and web shell; browser-safe web entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:18:26 +01:00
Lucas WintherandClaude Opus 5.5 497ce91216 feat(studio): /api/studio routes, author access, acceptance tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:18:25 +01:00
Lucas WintherandClaude Opus 5.5 9aa475329a feat(learner): Better Auth adapter over Bun.SQL for SQLite and Postgres
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:17:44 +01:00
Lucas WintherandClaude Opus 5.5 596099a13f feat(import): owner-only API routes and importFeature
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:17:31 +01:00
Lucas WintherandClaude Opus 5.5 7c3f613feb feat(import): optional transcription job
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:17:07 +01:00
Lucas WintherandClaude Opus 5.5 5a215744e5 feat(learner): package scaffold, auth tables migration, migrations runner
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:16:31 +01:00
Lucas WintherandClaude Opus 5.5 06b6ea6168 feat(studio): published-worktree watcher and DraftWriter API for the AI package
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:16:13 +01:00
Lucas WintherandClaude Opus 5.5 0e090a0e15 feat(import): code-repo job
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:16:12 +01:00
Lucas WintherandClaude Opus 5.5 524b1b4645 feat(import): code-repo job
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:16:00 +01:00
Lucas WintherandClaude Opus 5.5 0b117159cd docs: WP-A learner package plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:15:48 +01:00
Lucas WintherandClaude Opus 5.5 9738da458d feat(import): video-folder and pdf-deck jobs, deck cache
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:15:44 +01:00
Lucas WintherandClaude Opus 5.5 c2385b472c feat(studio): publish as merge, update draft, conflict resolution, explicit sync
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:15:20 +01:00
Lucas WintherandClaude Opus 5.5 07e2223c8a feat(import): Packt parser and packt-video job
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:15:09 +01:00
Lucas WintherandClaude Opus 5.5 a6f887f31c feat(studio): git wrapper and StudioStore (create course, drafts, save as commit)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:14:47 +01:00
Lucas WintherandClaude Opus 5.5 34f27d05c9 feat(import): utilities, media tooling, PyMuPDF helper, synthetic fixtures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:13:22 +01:00
Lucas WintherandClaude Opus 5.5 10cf93c27e docs(import): WP-B plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:12:03 +01:00
Lucas WintherandClaude Opus 5.5 fc37da9789 docs(studio): WP-C implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:11:41 +01:00
Lucas WintherandClaude Opus 5.5 3ca05fcfbc merge: Phase 0 contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:07:26 +01:00
Lucas WintherandClaude Opus 5.5 05eee4cd40 feat: server entry and web shell with design tokens
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:05:49 +01:00
Lucas WintherandClaude Opus 5.5 7f33169487 feat(core): config, job queue, range media serving, feature composition
Adds aclRepo to @stc/db for canView.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:04:46 +01:00
Lucas WintherandClaude Opus 5.5 ea18f18b4f fix(db): use DOUBLE PRECISION for position and progress columns
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:01:31 +01:00
Lucas WintherandClaude Opus 5.5 f061e36092 fix(db): jobs.update writes only patched columns; late progress cannot resurrect a finished job
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 03:01:05 +01:00
Lucas WintherandClaude Opus 5.5 4636273d55 feat(db): progress, notes, attempts, library, jobs repositories
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:59:09 +01:00
Lucas WintherandClaude Opus 5.5 738d813fb8 feat(db): Bun.SQL connection and portable migrations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:57:34 +01:00
Lucas WintherandClaude Opus 5.5 f71a31a5f1 fix(course-format): validate anchor file via requireFile; real throw test for loadCourse catch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:55:38 +01:00
Lucas WintherandClaude Opus 5.5 a82ab18bee fix(course-format): directories are not lesson files; a failing course never rejects loadAll
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:54:23 +01:00