- Critical 1 (CSRF): core wrap refuses every POST/PUT/PATCH/DELETE whose Origin (Referer as fallback) is not publicUrl's origin or STC_ALLOWED_ORIGINS, and any body that is not application/json (multipart only on declared upload routes). Better Auth routes go through the same guard; only declared identity-provider callbacks (SAML ACS/SLO, OAuth callbacks) are exempt. Better Auth trustedOrigins use the same list. Session cookie stays Lax (Strict breaks OAuth/OIDC/SAML returns); documented. - Important 4 / learner minor 4: the first-owner claim and the role row are one transaction guarded by "no owner yet"; only a unique violation means learner; any other error fails the sign-up with 500 and removes the half-created user. Serialised per DB handle (Bun's SQLite connection). - Important 2 / learner minor 12: code zips stream entry by entry (large files stored in 256 KB chunks with a data descriptor and incremental CRC-32), so memory stays at one chunk; the cap is now 1 GiB (ZIP32). - Important 3 / learner minor 10: the e2e runs the production wiring (productFeatures order, AI drafts through the real sink) against a restricted course and asserts 404/401 for learner and anonymous on course, lesson, media, cover, code, zip, notes, progress, attempts, AI chat, library, search and AI status; a real Range request (206, 416); forged writes (foreign Origin, text/plain, no Origin) are 403 and the MCP test command never runs. - Minors 7-9: compose passes every documented variable; the health check uses STC_PORT; productFeatures checks the studio-before-AI order. Co-Authored-By: Claude Opus 5.5 <[email protected]>
73 lines
2.9 KiB
YAML
73 lines
2.9 KiB
YAML
# STCourses with Docker Compose. Project name "stc", so containers are stc-app-1 and stc-postgres-1.
|
|
#
|
|
# SQLite (default): STC_SECRET=... docker compose up -d
|
|
# Postgres: STC_SECRET=... STC_DATABASE_URL=postgres://stc:stc@postgres:5432/stc \
|
|
# docker compose --profile postgres up -d
|
|
#
|
|
# Course repos go in ./courses (or set STC_COURSES_HOST); files to import go in ./import-inbox (or STC_IMPORT_HOST).
|
|
# Both are mounted read-only: originals are never changed. See docs/operations.md.
|
|
name: stc
|
|
|
|
services:
|
|
app:
|
|
build: .
|
|
image: stc-app
|
|
restart: unless-stopped
|
|
ports:
|
|
- "${STC_HOST_PORT:-3000}:3000"
|
|
environment:
|
|
STC_SECRET: ${STC_SECRET:?set STC_SECRET to a long random string}
|
|
STC_PUBLIC_URL: ${STC_PUBLIC_URL:-http://localhost:3000}
|
|
STC_DATABASE_URL: ${STC_DATABASE_URL:-sqlite:///data/stcourses.db}
|
|
STC_UPLOAD_MAX_MB: ${STC_UPLOAD_MAX_MB:-100}
|
|
STC_AI_ALLOWED_BASE_URLS: ${STC_AI_ALLOWED_BASE_URLS:-}
|
|
STC_TRANSCRIBE_URL: ${STC_TRANSCRIBE_URL:-}
|
|
STC_TRANSCRIBE_KEY: ${STC_TRANSCRIBE_KEY:-}
|
|
STC_TRANSCRIBE_MODEL: ${STC_TRANSCRIBE_MODEL:-}
|
|
STC_ALLOWED_ORIGINS: ${STC_ALLOWED_ORIGINS:-}
|
|
# Sign-in providers (unset ones stay off). Social: STC_AUTH_<GITHUB|GOOGLE|MICROSOFT|GITLAB|DISCORD>_ID/_SECRET.
|
|
STC_AUTH_GITHUB_ID: ${STC_AUTH_GITHUB_ID:-}
|
|
STC_AUTH_GITHUB_SECRET: ${STC_AUTH_GITHUB_SECRET:-}
|
|
STC_AUTH_GOOGLE_ID: ${STC_AUTH_GOOGLE_ID:-}
|
|
STC_AUTH_GOOGLE_SECRET: ${STC_AUTH_GOOGLE_SECRET:-}
|
|
STC_AUTH_MICROSOFT_ID: ${STC_AUTH_MICROSOFT_ID:-}
|
|
STC_AUTH_MICROSOFT_SECRET: ${STC_AUTH_MICROSOFT_SECRET:-}
|
|
STC_AUTH_GITLAB_ID: ${STC_AUTH_GITLAB_ID:-}
|
|
STC_AUTH_GITLAB_SECRET: ${STC_AUTH_GITLAB_SECRET:-}
|
|
STC_AUTH_DISCORD_ID: ${STC_AUTH_DISCORD_ID:-}
|
|
STC_AUTH_DISCORD_SECRET: ${STC_AUTH_DISCORD_SECRET:-}
|
|
STC_AUTH_OIDC_ISSUER: ${STC_AUTH_OIDC_ISSUER:-}
|
|
STC_AUTH_OIDC_ID: ${STC_AUTH_OIDC_ID:-}
|
|
STC_AUTH_OIDC_SECRET: ${STC_AUTH_OIDC_SECRET:-}
|
|
STC_AUTH_OIDC_NAME: ${STC_AUTH_OIDC_NAME:-}
|
|
STC_AUTH_OIDC_SCOPES: ${STC_AUTH_OIDC_SCOPES:-}
|
|
STC_AUTH_OIDC_DISCOVERY_URL: ${STC_AUTH_OIDC_DISCOVERY_URL:-}
|
|
STC_AUTH_GROUPS_CLAIM: ${STC_AUTH_GROUPS_CLAIM:-}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
required: false # only with --profile postgres
|
|
volumes:
|
|
- stc-data:/data
|
|
- ${STC_COURSES_HOST:-./courses}:/courses:ro
|
|
- ${STC_IMPORT_HOST:-./import-inbox}:/import:ro
|
|
|
|
postgres:
|
|
image: postgres:17
|
|
profiles: ["postgres"]
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_USER: stc
|
|
POSTGRES_PASSWORD: ${STC_PG_PASSWORD:-stc}
|
|
POSTGRES_DB: stc
|
|
volumes:
|
|
- stc-pg:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U stc -d stc"]
|
|
interval: 5s
|
|
retries: 20
|
|
|
|
volumes:
|
|
stc-data:
|
|
stc-pg:
|