Operational reference for teachers, curriculum maintainers, translation reviewers, and contributors of the Microsoft Machine Learning for Beginners curriculum. If you are a student looking for how to run the lessons, read the User Guide instead — this document covers what the learner-facing guide deliberately skips: architecture, authoring contracts, the multi-language translation pipeline, classroom delivery patterns, and the Microsoft contribution workflow.
1. Executive Summary
ML-For-Beginners is a 12-week, 26-lesson curriculum repository published by Microsoft's cloud-advocacy team. It is not a runtime system. It is an authoring and delivery artifact that ships four distinct kinds of content in one repository:
| Artifact | Technology | Purpose |
|---|---|---|
| Lesson content | Markdown + Jupyter notebooks (Python) + R notebooks | Core curriculum: 26 lessons across 7 topic areas |
| Quiz front-end | Vue 3 + vue-router + vue-i18n (Vue CLI 5) in quiz-app/ |
Pre/post-lecture quizzes with i18n |
| Demo web app | Flask + scikit-learn + ufo-model.pkl in 3-Web-App/ |
Lesson 9 teaches how to wrap a trained model in Flask |
| Documentation site | Docsify (zero-build) via root index.html |
Renders the curriculum as a browsable site |
Everything else in the repo exists to support these four artifacts: translations of every Markdown file into 50+ languages (generated by the Co-op Translator GitHub Action), mirrored image assets in translated_images/, GitHub issue/PR templates, a dev container, and housekeeping workflows (stale-issue closure, SLSA provenance on releases, closed-issue locking).
What this means operationally:
- No production runtime to operate. There are no servers to patch, no databases to back up, no queues to drain. "Operating" the project is authoring content and reviewing pull requests.
- The translation pipeline is the most complex subsystem. It runs as a GitHub Action and is the single largest source of repository churn — most of the 17,000+ tracked files are translation artifacts.
- Sub-projects (Flask, Vue) are illustrative, not production. They are pinned to older, lesson-compatible versions and are not security-hardened. Do not use them as templates for production apps.
- Authorship contracts matter more than code quality. Because the project is pedagogical, the authoring conventions (lesson sections, quiz wiring, sketchnote placement) carry more weight than typical software engineering concerns like test coverage.
This guide is organized around those realities.
Note
This documentation is generated from the repository at commit
000d5941. Version numbers, file paths, and configuration values refer to that exact snapshot. Behavior in newer commits may differ.
2. Architecture
The repository has four loosely coupled content systems that share a common top-level layout. There is no service mesh, no inter-process communication, no shared database — each system is a static or self-contained asset. The diagram below shows how they relate.
┌──────────────────────────────────────────┐
│ Source of truth │
│ English Markdown + notebooks in repo │
└────────────────────┬─────────────────────┘
│
┌──────────────────┬───────────────┼──────────────────────┬───────────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌───────────┐ ┌────────────────┐ ┌──────────┐ ┌────────────────┐ ┌─────────┐
│ Docsify │ │ Co-op Trans- │ │ docsify- │ │ Lesson quizzes│ │ PDF │
│ static │ │ lator GitHub │ │ to-pdf │ │ rendered by │ │ output │
│ site │ │ Action │ │ (Pup- │ │ Vue quiz-app │ │ pdf/ │
│ │ │ │ │ peteer) │ │ │ │ │
└───────────┘ └───────┬────────┘ └────┬─────┘ └────────┬───────┘ └─────────┘
│ │ │
▼ ▼ ▼
translations/ pdf/readme.pdf ff-quizzes.netlify.app
translated_images/ (external quiz host)
+ local quiz-app build
2.1 Docsify documentation site (root)
The root index.html is a 27-line Docsify bootstrap that turns the repo into a static documentation site without a build step. Docsify fetches README.md and any linked Markdown on demand from the hosting server. Configuration:
window.$docsify = {
name: 'Machine Learning for Beginners',
repo: 'https://github.com/Microsoft/ML-For-Beginners',
relativePath: true,
auto2top: true,
}
Theming is pulled from the CDN (docsify-themeable@0/dist/css/theme-simple.css). The .nojekyll file prevents GitHub Pages from rewriting underscore-prefixed paths. A sidebar at docs/_sidebar.md provides the navigation tree.
Why this matters: every Markdown file in the repo is potentially linkable from the site. When authoring, assume that any relative link in a lesson README will resolve correctly via Docsify's relativePath: true mode.
2.2 Translation pipeline
Translations live under translations/{lang-code}/... mirroring the English tree, and translated images live under translated_images/. Both are generated artifacts produced by the Co-op Translator GitHub Action (see §6). They are committed to the repo rather than built on demand because Docsify has no build step and the site must serve pre-translated files.
Supported languages at this snapshot include: ar, bg, bn, cs, da, de, el, en, es, fa, fi, fr, he, hi, hk, hr, hu, id, it, ja, ko, lt, mo, mr, ms, nl, ne, no, pa, pl, pt, ro, ru, sk, sl, sr, sv, sw, ta, te, th, tl, tr, tw, uk, ur, vi, zh-CN, zh-TW, among others.
2.3 Quiz system
Quizzes are authored as data in Vue components under quiz-app/src/ and consumed in two places:
- Inline in the lesson README — each lesson links to the pre- and post-lecture quiz on the external host
https://ff-quizzes.netlify.app/en/ml/. This is the path students actually use. - Locally via the quiz-app — teachers or offline classrooms can run
npm run serveinquiz-app/to host the same quizzes on localhost, or deploy them to Azure Static Web Apps (see §11.3).
The quiz-app is a Vue 3 SPA with vue-router for lesson navigation and vue-i18n for in-app translation. It is version-pinned to Vue CLI 5 (@vue/cli-service ~5.0.8) and Vue 3.5.12.
2.4 Demo Flask web app (Lesson 9)
3-Web-App/1-Web-App/solution/web-app/app.py is a 20-line Flask application that loads a pickled scikit-learn model and predicts "likely country of a UFO sighting" from form input:
from flask import Flask, request, render_template
import pickle, numpy as np
app = Flask(__name__)
model = pickle.load(open("../ufo-model.pkl", "rb"))
@app.route("/predict", methods=["POST"])
def predict():
int_features = [int(x) for x in request.form.values()]
final_features = [np.array(int_features)]
prediction = model.predict(final_features)
countries = ["Australia", "Canada", "Germany", "UK", "US"]
return render_template("index.html",
prediction_text="Likely country: {}".format(countries[output]))
requirements.txt: scikit-learn, pandas, numpy, flask. This is a teaching artifact — see §10 for the security properties you must explicitly call out when students use it as a template.
2.5 PDF generation
docsifytopdf.js configures docsify-to-pdf (a Puppeteer wrapper) to crawl docs/_sidebar.md and emit pdf/readme.pdf. The top-level package.json exposes this as npm run convert. Output is checked into the repo under pdf/.
module.exports = {
contents: ['docs/_sidebar.md'],
pathToPublic: 'pdf/readme.pdf',
pdfOptions: { margin: { top: '100px', bottom: '100px' } },
removeTemp: true,
emulateMedia: 'print',
};
3. Repository Layout
ML-For-Beginners/
├── 1-Introduction/ Lessons 1–4: ML intro, history, fairness, tools
├── 2-Regression/ Lessons 1–4: regression, Scikit-learn pipeline
├── 3-Web-App/ Lesson 1: Flask web app wrapping a trained model
├── 4-Classification/ Lessons 1–4: classification, cuisine dataset
├── 5-Clustering/ Lessons 1–2: K-Means, Nigerian music dataset
├── 6-NLP/ Lessons 1–5: NLP tasks, translation, sentiment
├── 7-TimeSeries/ Lessons 1–3: time series, ARIMA, SVR
├── 8-Reinforcement/ Lessons 1–2: Q-learning, Gym environment
├── 9-Real-World/ Post-script: ML in production
├── docs/
│ └── _sidebar.md Docsify navigation, also the TOC for PDF export
├── quiz-app/ Vue 3 pre/post-lecture quiz SPA
│ ├── src/
│ │ ├── App.vue, main.js
│ │ ├── router/ vue-router config
│ │ ├── views/ Lesson quiz views
│ │ ├── components/ Shared UI
│ │ └── assets/ Translation JSON files
│ ├── public/
│ ├── package.json Vue CLI 5 + Vue 3.5.12
│ └── README.md Quiz-app build/deploy docs
├── translations/ {lang-code}/ mirror of English content (generated)
├── translated_images/ Translated image assets (generated)
├── sketchnotes/ Lesson cover sketchnotes (Nitya Narasimhan)
├── images/ Shared imagery, favicon
├── .devcontainer/ VS Code dev container (Python 3.10-bullseye)
├── .github/
│ ├── workflows/ stale.yml, lock.yml, SLSA publish
│ ├── ISSUE_TEMPLATE/
│ ├── PULL_REQUEST_TEMPLATE/
│ └── chatmodes/ Copilot chat modes for curriculum authoring
├── README.md Master curriculum table, sparse-checkout recipe
├── AGENTS.md Pre-structured project overview (for AI agents)
├── TROUBLESHOOTING.md Lesson-by-lesson error recipes
├── for-teachers.md GitHub Classroom integration notes
├── CONTRIBUTING.md Microsoft CLA + community translation note
├── SECURITY.md MSRC reporting boilerplate
├── SUPPORT.md Issue + Discord channels
├── CODE_OF_CONDUCT.md Microsoft Open Source CoC pointer
├── LICENSE MIT
├── package.json Root: docsify-to-pdf devDependency
├── docsifytopdf.js PDF export config
├── index.html Docsify bootstrap
├── .nojekyll Disable Jekyll processing on GitHub Pages
├── pdf/ Checked-in PDF exports
├── PyTorch_Fundamentals.ipynb Supplementary PyTorch notebook (~73 KB)
└── .gitignore
Inside each lesson directory the layout is:
{N}-Topic/{lesson-number}-{lesson-name}/
├── README.md Lesson body (English source of truth)
├── notebook.ipynb Python Jupyter (when applicable)
├── solution/ Reference implementation
│ ├── notebook.ipynb
│ └── R/ Tidymodels equivalent (when applicable)
├── assignment.md End-of-lesson exercise
├── images/ Lesson-local figures
└── translations/ {lang-code}/ per-lesson mirror (generated)
4. System Requirements
4.1 Authoring machine
| Component | Version | Notes |
|---|---|---|
| Git | 2.25+ | Required for sparse-checkout filter |
| Python | 3.9–3.11 | Lessons and solution notebooks; 3.10 matches the devcontainer |
| Jupyter / JupyterLab | any recent | Install via pip install notebook or conda install jupyter |
| Node.js | 18 LTS or 20 LTS | Required for quiz-app and docsify-to-pdf |
| npm | bundled with Node | npm install from repo root pulls docsify-to-pdf |
| R | 4.1+ | Only for R-track lessons; see solution/R/ |
| RStudio | 2022.02+ | Recommended when editing .Rmd files |
4.2 Curriculum Python dependencies
There is no top-level requirements.txt. Each lesson that runs code either imports from the standard scientific-Python stack (scikit-learn, pandas, numpy, matplotlib, seaborn) or declares its own deps inline. The demo Flask app is the only place where a pinned list lives:
# 3-Web-App/1-Web-App/solution/web-app/requirements.txt
scikit-learn
pandas
numpy
flask
For the full curriculum we recommend creating a conda environment with:
conda create -n ml4beginners python=3.10
conda activate ml4beginners
conda install -c conda-forge jupyter scikit-learn pandas numpy matplotlib seaborn
pip install flask
4.3 Optional: dev container
The repo ships a VS Code dev container at .devcontainer/:
- Base image:
mcr.microsoft.com/vscode/devcontainers/python:0-3.10-bullseye - Pylance + Python linting (pylint) preconfigured
- Node version:
"none"— students wanting to run the quiz-app ordocsify-to-pdfmust editdevcontainer.jsonto setNODE_VERSIONtolts/*or rebuild with--build-arg NODE_VERSION=18.
Open the repo in VS Code → Reopen in Container and everything Python-side works out of the box.
4.4 GitHub/org requirements for deployment
- Docsify site hosting — GitHub Pages (free) or any static host. The site needs only
index.html,README.md, and the Markdown tree. - Quiz-app deployment — see §11.3. Requires an Azure subscription and the Static Web Apps service, or any static host.
- Co-op Translator — the Action requires repository secrets for the translation provider; see §6.3.
5. Lesson Authoring Contract
The single most important constraint when editing or adding content: lessons follow a 10-section structure. Deviations break the Docsify sidebar, the quiz wiring, and the expectations set in the root README.
5.1 Lesson Markdown template
# Lesson Title

> Sketchnote by [Tomomi Imura](https://twitter.com/girlie_mac)
## [Pre-lecture quiz](https://ff-quizzes.netlify.app/en/ml/)
### Introduction
Two to four paragraphs framing why the learner should care and
what they will be able to do after.
### What you'll learn
- Bulleted skills list
- Written in outcome language ("You will be able to …")
### Prerequisites
- References to earlier lessons
- Required libraries installed
## {Main body — H2-level sections specific to the topic}
### H3 subsections with code blocks, images, and exercises
## 🚀Challenge
A stretch exercise that goes beyond the lesson.
## [Post-lecture quiz](https://ff-quizzes.netlify.app/en/ml/)
## Review & Self Study
Three to five external links for deeper reading.
## Assignment
[Assignment Title](assignment.md)
5.2 Quiz linking rules
- Pre-lecture quiz link is always immediately after the sketchnote H1 title and before the Introduction.
- Post-lecture quiz link is immediately after the main body, before "Review & Self Study".
- Both links point at the external host
https://ff-quizzes.netlify.app/en/ml/. The quiz-id is resolved from the URL path configured inside the quiz-app. Do not link to localhost or a relative path — translated lessons rely on the external host being resolvable.
5.3 Images and sketchnotes
- Lesson-local images live in
{lesson}/images/and are referenced relatively:. - Cover sketchnotes live in
sketchnotes/at the repo root and are referenced as../../sketchnotes/{name}.jpg. - All images are candidates for translation. The Co-op Translator walks the tree, extracts text, and writes translated copies under
translated_images/{lang}/. Do not hand-edit anything intranslated_images/— it is regenerated.
5.4 Code blocks
- Fenced with a language tag:
```python,```r,```bash,```javascript. - No language tag = no syntax highlighting in Docsify and no translation exemption. Always tag code blocks so Co-op Translator leaves them alone.
- Shell commands prefer POSIX form; call out Windows differences with a blockquote:
> On Windows, use \instead of /.`
5.5 Notebooks
- Notebooks are the actual teaching artifact for any lesson that runs code. The Markdown README explains the why; the notebook delivers the how.
- Use
solution/notebook.ipynbfor the reference implementation and keep the lesson-levelnotebook.ipynbas a scaffolded "fill in the blanks" version for students. - For R-track lessons, mirror under
solution/R/using tidymodels.
5.6 Assignment files
Each lesson's end-of-lesson exercise is a standalone assignment.md with this template:
# Assignment Title
## Instructions
What the student must do.
## Rubric
| Exemplary | Adequate | Needs Improvement |
|-----------|----------|-------------------|
| … | … | … |
The rubric table is expected by graders; omitting it breaks the grading workflow documented in for-teachers.md.
5.7 README master table
README.md at the repo root carries the canonical 26-lesson inventory as a Markdown table. When adding or renumbering a lesson, update this table as the same commit — the sidebar, the quiz IDs in quiz-app/, and the translation outputs all key off lesson ordering.
6. Translation Subsystem
6.1 What Co-op Translator does
Co-op Translator is an Azure-published GitHub Action that:
- Walks the repository for English Markdown, notebooks, and images.
- Sends text to an LLM translator (typically Azure OpenAI).
- Writes translated Markdown/notebooks under
translations/{lang-code}/. - Renders translated images (preserving layout) under
translated_images/{lang-code}/. - Commits the results back to the repo via a workflow-authored commit.
This is why most of the ~17,000 tracked files are translation artifacts — a single lesson plus its image set expands across 50 language codes.
6.2 Languages supported
The language list is driven by a Co-op Translator config (typically co-op-translator-config.yaml or similar). To change the language set, edit that config, not the committed translation trees. The set visible in this snapshot includes Western European, Slavic, East Asian, Middle Eastern, South Asian, and Southeast Asian language groups — a full inventory is in translations/.
6.3 Required secrets and permissions
For the Action to run in your fork you need:
- Azure OpenAI endpoint and API key (repository secrets)
contents: writepermission on the workflow (to commit translated files)pull-requests: writeif you configure the Action to open PRs instead of pushing directly
Consult the Co-op Translator README for the exact secret names at the commit you are using.
6.4 Community translation override
Machine translations can be overridden by human volunteers. CONTRIBUTING.md is explicit:
Important: when translating text in this repo, please ensure that you do not use machine translation. We will verify translations via the community, so please only volunteer for translations in languages where you are proficient.
This means human-translated content should land as direct edits inside translations/{lang}/ and must be protected from being overwritten on the next Action run. The review policy is: a human translation, once merged, is treated as ground truth for that language; re-runs of Co-op Translator must either skip files marked as human-reviewed or be constrained to new content only. If you operate the Action, audit its config for this behavior before scheduling large re-runs.
6.5 Review responsibilities for maintainers
| Task | Owner | Cadence |
|---|---|---|
| Triage translation PRs from volunteers | Curriculum maintainer | Per PR |
| Audit Co-op Translator output on new lessons | Curriculum maintainer | Per new lesson merge |
| Add/remove supported languages | Lead maintainer | Ad hoc |
| Investigate garbled translation reports | Translation reviewer for that language | Per issue |
7. Sub-Project: Flask Web App (3-Web-App/)
7.1 Purpose
Teaches students how to serialize a trained scikit-learn model with pickle and serve predictions through a minimal Flask route. It is intentionally minimal — the lesson goal is "a trained model is just a file; Flask is just a function router" — not production deployment.
7.2 Layout
3-Web-App/
└── 1-Web-App/
├── notebook.ipynb Lesson notebook: train + pickle ufo-model.pkl
├── README.md Lesson walkthrough
├── assignment.md End-of-lesson challenge
└── solution/
├── ufo-model.pkl Pre-trained classifier (checked in)
└── web-app/
├── app.py Flask application (full source in §2.4)
├── requirements.txt scikit-learn, pandas, numpy, flask
├── static/
│ └── css/styles.css
└── templates/
└── index.html Single-page form + prediction display
7.3 Running locally
cd 3-Web-App/1-Web-App/solution/web-app
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python app.py
# Flask dev server on http://127.0.0.1:5000 with debug=True
7.4 Security properties (important to call out when teaching)
| Property | State | Reason | What students must know before reuse |
|---|---|---|---|
pickle.load on a model file |
Used | Teaches the canonical scikit-learn persist pattern | pickle executes arbitrary code on load; never load a .pkl from an untrusted source |
debug=True in app.run() |
Used | Shows tracebacks in the browser when students break things | Never set in production — the Werkzeug debugger is an RCE if exposed |
| Input validation | Absent | int_features = [int(x) for x in request.form.values()] is the only coercion |
Any field that is not an int raises ValueError — which debug=True then exposes. In production, validate and coerce explicitly. |
| Authentication | Absent | Irrelevant to the teaching goal | Add Flask-Login / a reverse proxy with auth for any real deployment |
| CSRF protection | Absent | Form posts use no token | Add Flask-WTF for real forms |
| Content Security Policy | Absent | Templates render directly | Set headers in front of Flask in production |
| HTTPS | Absent | Dev server is HTTP | Terminate TLS at the proxy |
| Rate limiting | Absent | Dev server is single-threaded | Use a production WSGI server + gateway |
All of these are deliberate teaching tradeoffs. When a student forks this as a template, the above table is the conversation to have with them.
7.5 Swapping the model
The lesson's own notebook.ipynb trains the classifier and writes ufo-model.pkl. To replace the model:
- Re-run the notebook (or train in your own script) and emit a
.pklwith a compatiblepredict()signature (takes ann_samples × n_features2D array, returns an array of class indices). - Replace
3-Web-App/1-Web-App/solution/ufo-model.pkl. - Update the
countrieslist inapp.pyso the class index → label mapping stays consistent.
8. Sub-Project: Vue Quiz App (quiz-app/)
8.1 Purpose
Locally hostable version of the pre/post-lecture quizzes. The canonical student experience uses the externally hosted ff-quizzes.netlify.app, but teachers can self-host for air-gapped classrooms, for LMS integration, or when the external host is unreachable.
8.2 Stack at this commit
| Package | Version | Role |
|---|---|---|
vue |
^3.5.12 | UI framework |
vue-router |
^3.5.3 | Client-side routing (note: paired with Vue 3 — intentional lesson-era pinning) |
vue-i18n |
^8.26.7 | In-app language switching |
@vue/cli-service |
~5.0.8 | Build tool (Vue CLI 5) |
eslint |
^9.33.0 | Linter; eslint-plugin-vue ^10.4.0 |
core-js |
^3.6.5 | Polyfill |
The quiz-app also lives under src/ with:
quiz-app/src/
├── App.vue Root component
├── main.js Bootstrap: router + i18n mount
├── router/ Routes per lesson/quiz
├── views/ Per-quiz views
├── components/ Shared UI (Quiz, Question, Result)
└── assets/ Per-language quiz JSON
8.3 Scripts
| Command | Action |
|---|---|
npm install |
Install deps into quiz-app/node_modules/ |
npm run serve |
Vue CLI dev server with HMR (default port 8080) |
npm run build |
Production bundle to quiz-app/dist/ |
npm run lint |
ESLint with plugin:vue/essential + eslint:recommended |
8.4 Adding a quiz
- Add the question objects to the appropriate language JSON under
quiz-app/src/assets/translations/(or wherever the quiz data lives for the version you are on). - If it is a new lesson, add a route in
quiz-app/src/router/and a view inquiz-app/src/views/. - Update the root
README.mdmaster table and the lesson README's quiz links so the quiz-id matches. npm run buildand test the production bundle locally before pushing.
8.5 Deployment to Azure Static Web Apps
The quiz-app's README documents the Azure SWA flow. Summary of the Action config:
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "/quiz-app" # App source code path
api_location: "" # No API
output_location: "dist" # Vue CLI build output
Portal steps (condensed from quiz-app/README.md):
- Fork the repo.
- Azure Portal → Create resource → Static Web App. Select GitHub as source, authorize Azure, pick the fork and branch.
- Build preset: Vue. App location:
/quiz-app. Output location:dist. - Azure writes a workflow file at
.github/workflows/azure-static-web-apps-<name>.ymland runs it on every push to the selected branch.
9. Classroom Delivery Patterns
The for-teachers.md file is short but prescriptive. The core patterns:
9.1 GitHub Classroom (recommended for formal courses)
Because GitHub Classroom expects one repo per assignment, teachers extract each lesson folder into its own repo so Classroom can assign them individually:
- Fork this repo.
- For each lesson you want to assign, extract the lesson folder into a standalone repo.
- In GitHub Classroom, create an assignment pointing at the lesson repo.
- Each student gets an auto-forked copy; submissions are managed through Classroom.
9.2 Repo-as-is (informal or cohort-based delivery)
Simplest pattern — the teacher just directs students to specific lessons in the upstream repo. Quizzes and assignments are submitted via:
- Public submission — students file GitHub Issues on the teacher's classroom repo with their answers.
- Private submission — students fork each lesson to a private repo, grant the teacher access, and submit via issues on those private forks.
9.3 Online classroom (Zoom/Teams/breakout rooms)
The recommended pattern in for-teachers.md:
- Pre-lecture quiz → breakout rooms of 3–4. Each room submits collective answers as an issue on the classroom repo before the lecture starts.
- Lecture → live-coded walkthrough of the lesson notebook.
- Breakout work → students work the assignment in pairs, TAs rotate rooms.
- Post-lecture quiz → back in the main room, quick recap.
9.4 Feedback channel
Teachers are asked to fill the feedback form referenced in for-teachers.md so Microsoft can iterate on the curriculum.
10. Security & Trust Boundary
10.1 Reporting vulnerabilities
SECURITY.md directs all reports to the Microsoft Security Response Center (MSRC):
- Primary: https://msrc.microsoft.com/create-report
- Email fallback:
[email removed](PGP available from Microsoft Security Response Center PGP Key page) - Response SLA: 24 hours
Never report via a public GitHub issue. The curriculum is watched by students — a public disclosure would expose them to copy-paste exploitation before MSRC can respond.
10.2 What counts as a vulnerability here
Because the repo has no production runtime, the attack surface is:
| Surface | Concern | Reporting route |
|---|---|---|
| Flask demo app in student forks | RCE via debug=True Werkzeug console; pickle-load of hostile .pkl; form-value coercion |
MSRC (as the upstream template) |
| Quiz-app dependencies | Supply-chain CVEs in Vue / vue-cli / eslint-plugin-vue | MSRC or Dependabot PR |
docsify-to-pdf |
Puppeteer/Chromium CVEs reachable during PDF generation | MSRC or Dependabot PR |
| Notebooks | Embedded `!curl … | sh` or other remote-execution patterns |
| Translations | Garbage content from MT pipeline that alters meaning in harmful ways | GitHub issue (quality), MSRC (if intentional malicious insertion) |
10.3 Intentional non-security for teaching clarity
The demo Flask app runs with debug=True, loads a local pickle, and has no auth. This is documented as intentional. The authoring contract is that lessons optimize for learner clarity; the operational mitigation is §7.4 — when reusing this code, the security table is the conversation.
10.4 Dependency hygiene
- Dependabot is available (free for public repos on GitHub); enabling it catches CVEs in the quiz-app and
docsify-to-pdfstacks automatically. - The
.github/workflows/generator-generic-ossf-slsa3-publish.ymlworkflow produces SLSA level-3 provenance on releases. It currently shipsartifact1/artifact2placeholders — adapt theBuild artifactsstep before cutting a tagged release if you want provenance to cover real binaries.
10.5 Secret handling
The repo holds no secrets. The Co-op Translator workflow consumes secrets (Azure OpenAI keys) stored in GitHub repository settings. When forking:
- Do not copy secrets from the upstream — they are not accessible anyway.
- Generate your own Azure OpenAI deployment and add its credentials as repo secrets before enabling the Action.
11. Operations Reference
11.1 Building the Docsify site
Docsify has no build step. Verifying the site locally:
# From repo root
npm install -g docsify-cli # one-time
docsify serve . # http://localhost:3000
Deployment: push to GitHub with Pages enabled for the branch. The .nojekyll file is already in place so Pages serves the tree as-is.
11.2 Generating the PDF
npm install # pulls docsify-to-pdf@0.0.5 from root package.json
npm run convert # runs node_modules/.bin/docsify-to-pdf
# Output: pdf/readme.pdf
docsify-to-pdf requires a Chromium binary (bundled via Puppeteer). On Linux CI you need the usual Chromium deps (libnss3, libatk-bridge2.0-0, libcups2, libdrm2, libxcomposite1, libxrandr2, libgbm1, libxkbcommon0, libasound2).
11.3 Deploying the quiz-app
Two supported paths:
| Target | Mechanism | Detail |
|---|---|---|
| Azure Static Web Apps | GitHub Action Azure/static-web-apps-deploy@v1 |
See §8.5. Preset: Vue. app_location /quiz-app, output_location dist. |
| Any static host (Netlify, Vercel, S3+CloudFront, GitHub Pages) | npm run build and upload quiz-app/dist/ |
No SPA routing gotchas — vue-router uses hash mode by default in this config. |
11.4 Running Co-op Translator
Run on demand via the Action UI (workflow_dispatch) or on a schedule. For a full re-translation of the repo:
- Confirm Azure OpenAI quota — a full run hits every English Markdown and every image with text.
- Decide the commit policy (direct push vs. PR). PRs are safer but add review overhead.
- Kick off the workflow.
- Expect a large commit (thousands of files). Use
git log --staton the generated commit to spot-check.
11.5 Repository maintenance workflows
| Workflow | Trigger | Behavior |
|---|---|---|
.github/workflows/stale.yml |
Cron 35 8 * * * daily |
Uses actions/stale@v10 to comment on and close issues/PRs with no activity. Labels: no-issue-activity, no-pr-activity. |
.github/workflows/lock.yml |
On issue closed |
Uses OSDKDev/lock-issues@v1.1 to prevent new comments on closed issues. |
.github/workflows/generator-generic-ossf-slsa3-publish.yml |
workflow_dispatch or release created |
SLSA level-3 provenance on release artifacts. |
Adjusting close timing: edit stale.yml — defaults of actions/stale@v10 apply (typically 60-day warn + 7-day close) unless overridden. Adjusting issue-lock grace: edit the lock.yml step or swap in dessant/lock-threads@v5 for more configuration.
11.6 Sparse-checkout recipe for maintainers
To work with a specific lesson without cloning translations:
git clone --filter=blob:none --no-checkout https://github.com/microsoft/ML-For-Beginners.git
cd ML-For-Beginners
git sparse-checkout init --cone
git sparse-checkout set 2-Regression 3-Web-App docs quiz-app
# Exclude translation noise from the working tree
git sparse-checkout set --no-cone \
'/*' \
'!/translations' \
'!/translated_images'
git checkout main
This trims the working tree from thousands of generated files to only the English source. Cuts clone size by ~95%.
12. Contributing Workflow
12.1 Microsoft CLA (Contributor License Agreement)
Per CONTRIBUTING.md, all non-trivial contributions require a signed Microsoft CLA. The flow:
- Open a PR against
main. - The CLA-bot auto-comments on the PR with a signing link (first-time contributors).
- Sign once at https://cla.microsoft.com — valid across all Microsoft CLA repositories.
- The bot re-verifies and labels the PR accordingly.
12.2 Code of Conduct
The repo adopts the Microsoft Open Source Code of Conduct. Reports go to [email removed]. The Code of Conduct FAQ explains enforcement.
12.3 Pull request categories
| PR type | Expectations | Extra steps |
|---|---|---|
| Typo / copy fix in English Markdown | Single-file PR, no translation update | CLA sign-off (one-time) |
| New lesson content or substantial rewrite | Follows §5 authoring contract; includes sketchnote; updates root README table; updates docs/_sidebar.md |
CLA; re-run Co-op Translator after merge |
| Human translation | Direct edit under translations/{lang}/; marks file as human-reviewed so MT does not overwrite |
CLA; proficiency declaration in PR description |
| Quiz change | Updates quiz-app JSON and lesson README quiz links in the same PR | CLA; npm run lint must pass |
| Dependency bump | quiz-app/package.json or root package.json |
CLA; verify npm run build locally |
| Security fix | Route through MSRC first — do not open a public PR with an exploit description | Per §10.1 |
12.4 Commit signing
Not required by repo config at this commit, but Microsoft repos commonly adopt DCO or signed commits over time. Check the PR template and main branch protection rules before submitting.
12.5 Issue templates
Located in .github/ISSUE_TEMPLATE/. Use them — triagers depend on the structured fields to route issues. The default templates cover bug reports, feature/content requests, and translation issues.
13. Troubleshooting Runbook (Maintainer View)
A full learner-facing troubleshooting list lives in TROUBLESHOOTING.md. The items below are the maintainer failures that recur and how to triage them.
13.1 "Co-op Translator commit broke lesson N"
Symptom: a translation PR or push lands and a lesson's sidebar link 404s, or the post-lecture quiz link is missing in translations/{lang}/.
Triage:
- Diff the generated commit against the English source:
git diff HEAD~1 -- translations/{lang}/{lesson-path}/README.md. - Check whether the English
README.mduses an uncommon Markdown construct (nested code fence, HTML embed) that the translator stripped. - Fix the English source to use the standard constructs, then re-run the Action for that language only.
13.2 Docsify site shows a blank page after a push
Symptom: Docsify hits a parsing error on a Markdown file and stops rendering.
Triage:
- Open browser DevTools → Console. Docsify prints the failing URL.
- Load that Markdown file directly in the browser — invalid YAML front matter or unbalanced fences are common.
- For translated files, look for a dropped closing
```— the Co-op Translator historically has had sporadic issues with fence preservation.
13.3 npm run convert hangs during PDF generation
Symptom: docsify-to-pdf launches Chromium and never completes.
Triage:
- On Linux CI, missing Chromium libs — install per §11.2.
- On large repos (this one qualifies), memory pressure. Increase the CI runner, or scope
docsifytopdf.jscontents:to a subset ofdocs/_sidebar.md. - Network timeouts on CDN-loaded theme — retry, or self-host the Docsify theme.
13.4 Quiz-app npm install fails on a fresh clone
Symptom: peer-dependency warnings escalate to errors with newer npm versions.
Triage:
- The package set was authored for npm 7/8. On npm 9+ you may need
--legacy-peer-deps. - If a devDependency (e.g.
babel-eslint) is deprecated, do not unilaterally bump it — it affects how lesson-era quizzes tokenize. File a PR and testnpm run build+ the generated SPA against the full quiz set.
13.5 Stale workflow closes an active issue
Symptom: a discussion issue is auto-closed by actions/stale@v10 mid-conversation.
Triage:
- Add a non-stale label (e.g.
pinnedorkeep) and configurestale.yml'sexempt-issue-labelsto exclude it. - Reopen the issue. The
lock.ymlworkflow locks issues on close, so a freshly reopened issue is unlocked again.
13.6 SLSA provenance workflow fails on release
Symptom: release job succeeds but the provenance job errors with signing/permission failures.
Triage:
- The
provenancejob needsid-token: writeandcontents: write. Confirm the workflow block still includes them. - The called workflow is pinned to
slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.4.0. If GitHub has deprecated the runner image or the action version, bump both and re-run.
14. Forking Guidance
Common reasons to fork:
| Reason | What to change | What to leave alone |
|---|---|---|
| Running a cohort-specific course | README.md cohort notes, for-teachers.md |
Lesson content, quiz IDs, translations |
| Translating to a new language | Add language in Co-op Translator config; may seed translations/{new-lang}/ with a run |
English source (never branch English lesson content for translation work) |
| Derivative curriculum (different order, extra lessons) | Lesson tree, README.md master table, docs/_sidebar.md |
Microsoft-specific files (CLA ref, SECURITY.md, CODE_OF_CONDUCT.md) unless you are replacing them with your own |
| Training-only material for an org | Private fork, add org-specific material alongside | Upstream lesson folders — makes future rebasing easier |
Attribution: The root README and LICENSE (MIT) document attribution. If you use the curriculum under MIT, keep LICENSE and the author credits in AGENTS.md / README.md. Translations retain the upstream authorship notice at the top (the Co-op Translator adds a standard attribution block).
What to strip before re-publishing under your own brand:
SECURITY.md— replace with your org's reporting channel; MSRC is not appropriate outside Microsoft.CONTRIBUTING.md— the Microsoft CLA does not apply to a non-Microsoft fork.SUPPORT.md— swap in your support channels..github/ISSUE_TEMPLATE/andPULL_REQUEST_TEMPLATE/— adjust to match your processes.
15. External Dependencies and References
15.1 Build-time dependencies
| Package | Scope | Role | Pin location |
|---|---|---|---|
docsify-to-pdf@0.0.5 |
root devDependency | PDF export | package.json |
vue@^3.5.12 |
quiz-app dependency |
SPA framework | quiz-app/package.json |
vue-router@^3.5.3 |
quiz-app dependency |
Routing | quiz-app/package.json |
vue-i18n@^8.26.7 |
quiz-app dependency |
i18n | quiz-app/package.json |
@vue/cli-service@~5.0.8 |
quiz-app devDependency |
Build | quiz-app/package.json |
eslint@^9.33.0 |
quiz-app devDependency |
Linting | quiz-app/package.json |
eslint-plugin-vue@^10.4.0 |
quiz-app devDependency |
Vue lint rules | quiz-app/package.json |
flask |
Lesson 9 runtime | Web framework | 3-Web-App/1-Web-App/solution/web-app/requirements.txt |
scikit-learn, pandas, numpy |
Lesson 9 + most Python lessons | ML stack | same requirements.txt |
15.2 Runtime external hosts
| Host | Purpose |
|---|---|
https://ff-quizzes.netlify.app/en/ml/ |
External quiz host referenced from every lesson README |
https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js |
Docsify runtime for the docs site |
https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css |
Docs site theme |
https://aka.ms/ml-beginners |
Canonical short URL for the curriculum |
https://aka.ms/foundry/discord |
Community Discord (#ml-for-beginners channel) |
https://cla.microsoft.com |
Microsoft CLA signing |
https://msrc.microsoft.com/create-report |
MSRC vulnerability reporting |
15.3 Upstream references
- Curriculum landing page
- Co-op Translator source
- GitHub Classroom setup guide
- Microsoft Open Source Code of Conduct
- Azure Static Web Apps documentation
- SLSA specification
16. Quick Reference
16.1 Key paths
| Path | What it is |
|---|---|
/README.md |
Master curriculum table — update on any lesson add/remove |
/AGENTS.md |
AI-agent-readable project overview — keep in sync with README |
/docs/_sidebar.md |
Docsify nav + PDF TOC source |
/index.html |
Docsify bootstrap; change site name/repo URL here |
/docsifytopdf.js |
PDF export config |
/quiz-app/src/ |
Vue quiz SPA source |
/3-Web-App/1-Web-App/solution/web-app/app.py |
Demo Flask app |
/translations/{lang}/ |
Generated translations — do not hand-edit MT output |
/translated_images/{lang}/ |
Generated image translations |
/.github/workflows/ |
stale, lock, SLSA |
/.devcontainer/ |
VS Code dev container |
16.2 Commands cheat sheet
# Author and preview site
docsify serve .
# PDF export
npm install && npm run convert # writes pdf/readme.pdf
# Quiz-app dev
cd quiz-app && npm install && npm run serve # http://localhost:8080
cd quiz-app && npm run build # dist/
cd quiz-app && npm run lint # ESLint
# Flask demo
cd 3-Web-App/1-Web-App/solution/web-app
pip install -r requirements.txt && python app.py # http://127.0.0.1:5000
# Sparse-checkout without translation noise
git clone --filter=blob:none --no-checkout https://github.com/microsoft/ML-For-Beginners.git
cd ML-For-Beginners
git sparse-checkout init --cone
git sparse-checkout set --no-cone '/*' '!/translations' '!/translated_images'
git checkout main
16.3 Decision matrix: where to report or ask
| Situation | Channel |
|---|---|
| Bug in lesson content or broken link | GitHub Issue (use the issue template) |
| Security vulnerability | MSRC: https://msrc.microsoft.com/create-report |
| Translation quality problem | GitHub Issue; tag the language |
| Code-of-conduct concern | [email removed] |
| Feature request | GitHub Issue with enhancement label |
| Community help / classroom questions | Discord #ml-for-beginners at aka.ms/foundry/discord |
| Teacher feedback | Form linked in for-teachers.md |
| Tooling/translation pipeline | Co-op Translator repo: github.com/Azure/co-op-translator |