Train with your Copilot. Reach new heights.
Flight School is a sample implementation showing how to build AI-powered developer tools using the GitHub Copilot SDK. It's a learning platform where developers practice coding challenges, receive real-time AI evaluation, and get personalized guidance all powered by GitHub Copilot.
Warning
Exploratory project — not a reference, not production-ready.
Flight School is a single-developer side project used to explore the latest capabilities across GitHub, the Copilot SDK, Aspire, and related tooling. It is mid-flight: actively iterated on, intentionally noisy in places, and almost certainly contains antipatterns, half-finished refactors, and decisions I'd make differently next time.
- Do not treat this codebase as a reference example or a recommended architecture.
- Do not copy patterns from here into production systems without independent review.
- Issues, PRs, and observations are welcome, but there is no SLA, roadmap commitment, or stability guarantee on anything in this repo.
The Copilot SDK integration provides per-user identity isolation by
passing each authenticated user's GitHub token into every SDK session, and
public chat execution is routed through a mandatory private Copilot
worker with a per-user runtime pool. Each user gets a separate
SDK-spawned Copilot CLI child process and a separate COPILOT_HOME.
Public-facing routes do not execute the SDK in-process — if
COPILOT_WORKER_URL is unset, AI routes fail fast. See
Multi-tenant Architecture for details.
Tip
New here? Start with docs/architecture.md — a
story-level walkthrough of the system, with diagrams for the chat-turn,
background-job, and local Aspire dev-loop flows, plus links into the code.
Flight School is multi-tenant: it authenticates each developer via a
GitHub App OAuth flow (Auth.js v5) and
threads the resulting user-to-server (ghu_) token through every GitHub API
call and Copilot SDK session. To run it locally you need to register a
GitHub App and supply AUTH_* credentials.
-
Register a GitHub App at https://github.com/settings/apps/new:
- Homepage URL:
http://localhost:3000 - Callback URL:
http://localhost:3000/api/auth/callback/github - Enable "Request user authorization (OAuth) during installation".
- Generate a client secret.
- Homepage URL:
-
Configure environment. Copy
.env.exampleto.env.localand fill in:AUTH_SECRET= # openssl rand -base64 32 AUTH_GITHUB_ID= # GitHub App client id AUTH_GITHUB_SECRET= # GitHub App client secret AUTH_TRUST_HOST=true AUDIT_SALT= # openssl rand -hex 32
-
Install and run:
git clone https://github.com/chrisreddington/flight-school.git cd flight-school npm install npm run dev -
Open http://localhost:3000 — you'll be redirected to GitHub to authorize the app, then back to your personalized dashboard. This starts the Aspire AppHost, including the required Copilot worker.
Local AI chat requires the Copilot worker. npm run dev is the recommended
path because it starts both the web app and worker through Aspire. To run the
two processes manually, start a worker in one terminal and the web app in
another:
npm run dev:worker
COPILOT_WORKER_URL=http://localhost:3001 \
COPILOT_WORKER_SECRET=local-dev-worker-secret \
npm run dev:web-workernpm run aspire:run starts both resources and injects the local worker URL into
the web app automatically. In worker mode, each GitHub user gets a separate
SDK-spawned Copilot CLI child process and a separate COPILOT_HOME.
For UI-only work that does not exercise Copilot chat, use npm run dev:web-only.
AI chat routes intentionally fail fast in that mode unless you configure
COPILOT_WORKER_URL.
Runtime controls:
# Defaults shown
COPILOT_RUNTIME_IDLE_TTL_MS=600000
COPILOT_RUNTIME_MAX_ACTIVE=3
COPILOT_RUNTIME_HOME_ROOT=$FLIGHT_SCHOOL_DATA_DIR/copilot-runtimesPrerequisites: Node.js 22+, npm, Git, and a GitHub Copilot subscription (Individual, Business, or Enterprise) for the AI features.
Experimental ACA deployment notes:
docs/deployment-aca.md— Container image build, ACA deployment checklist for lab/test environments (env vars, Key Vault secrets, monitoring, rate-limit tuning).infra/README.md— Bicep modules, GitHub App setup against the ACA FQDN, deploy / rotate / cleanup recipes.docs/architecture-multitenant.md— Multi-tenant design (Auth.js → per-request Octokit → Copilot execution boundary → required worker → per-user runtime).
Click Code → Codespaces → Create codespace on main. The dev container
installs dependencies automatically; you still need to register a GitHub App
(callback https://<codespace-host>/api/auth/callback/github) and populate
.env.local before running npm run dev.
AI can be a learning partner, not just a tool that helps generate solutions. Flight School explores how the Copilot SDK can create educational experiences that adapt to each developer's existing experience, their skill level, provide constructive feedback, and guide learners toward understanding.
Flight School gathers data from your GitHub profile and repositories through the Octokit package and uses the Copilot SDK to tailor a personalized learning experience:
- Daily Focus — A challenge, goal, and learning topics tailored to your skill gaps
- Interactive Challenges — Practice in a sandbox with real-time AI evaluation
- Progressive Hints — Get guidance without spoilers when you're stuck
- Learning Conversations — Chat with an AI coach about your code and repositories
flowchart LR
subgraph Flight School
A[GitHub Profile<br>& Repos] --> B[Copilot SDK<br>+ MCP Tools]
B --> C[Your Learning<br>Dashboard]
B --> D[AI-Powered:<br>• Evaluation<br>• Hints<br>• Coaching<br>• Focus Gen]
end
Flight School demonstrates several Copilot SDK patterns that you can adapt for your own applications:
The SDK's CopilotClient manages authenticated sessions. To optimize performance, Flight School creates different session types for different use cases:
- Lightweight sessions for fast responses (hints, quick chats)
- MCP-enabled sessions when GitHub exploration is needed (repo search, file contents, etc.)
- Conversation caching for multi-turn chats that maintain context
Flight School connects to GitHub's Remote MCP Server, giving the AI access to GitHub tools like get_me, list_user_repositories, and search_code. This enables context-aware responses based on your actual repositories in the chat experience.
The chat and challenge evaluation experiences stream feedback in real-time using the SDK's streaming mode, showing results as they're generated rather than waiting for the full response.
| Command | Description |
|---|---|
npm run dev |
Start Aspire AppHost with web + required Copilot worker |
npm run dev:web-only |
Start only the web app; AI chat requires COPILOT_WORKER_URL |
npm run dev:worker |
Start only the local Copilot worker on port 3001 |
npm run dev:web-worker |
Start web app pointed at a manually started local worker |
npm run build |
Create production build |
npm run lint |
Run ESLint |
npm test |
Run tests |
npm run test:watch |
Run tests in watch mode |
npm run check:otel-emitted -- <trace-export.json> |
Assert trace export includes web + worker service names |
npm run dashboard |
Start Aspire Dashboard (standalone OTLP receiver) |
npm run aspire:run |
Run the Aspire TypeScript AppHost |
npm run aspire:mcp |
Start Aspire MCP server against the dashboard |
npm run aspire:deploy |
Deploy AppHost resources to Azure Container Apps |
npm run aspire:destroy |
Tear down deployed Aspire Azure resources |
Flight School registers OpenTelemetry from two processes: the web tier uses
@vercel/otel in src/instrumentation.ts; the worker tier (standalone
Hono/Node) uses @opentelemetry/sdk-node in src/worker/lifecycle/otel.ts,
started from src/worker/bootstrap.ts before any handler loads. Both export
to the same OTLP endpoint Aspire injects. When running with Aspire Dashboard,
traces and metrics include:
- Next.js API request timelines
- GitHub API request spans (including rate-limit headers when available)
- Copilot SDK session spans (
createSession,sendAndWait, streaming duration) - Trace IDs injected into structured logger payloads
Start the local dashboard and app:
npm run dashboard
npm run dev:web-onlyBy default, the dashboard listens on:
- UI:
http://localhost:18888 - OTLP/HTTP:
http://localhost:4318
npm run dashboarduses--allow-anonymous; keep this local-only.
This repository includes a TypeScript Aspire AppHost:
apphost.tsorchestrates the Next.js web app withaddNextJsApp(...)and the standalone Copilot worker (Hono/Node) withaddExecutable(...)aspire.config.jsonconfigures the TypeScript AppHost profile- AppHost includes an Azure Container Apps environment (
addAzureContainerAppEnvironment("aca-env"))
aspire init --language typescript generates the .modules/ runtime helpers used by the AppHost. These files are local setup artifacts and should not be committed.
Run Aspire MCP in dashboard mode so coding agents can inspect traces/logs:
npm run aspire:mcpYou can also use the included skill file:
.github/skills/aspire-debugging/SKILL.md
- Install Aspire CLI and Azure CLI
- Authenticate to Azure:
az login- Initialize Aspire TypeScript AppHost modules (one-time per setup):
aspire init --language typescript- Add Azure App Containers integration (one-time per setup):
aspire add azure-appcontainers- Deploy:
npm run aspire:deployWe welcome contributions! Flight School is both a learning platform and a reference implementation. Improving either helps the community.
- Report bugs — Open an issue with reproduction steps
- Suggest features — Describe your use case in an issue
- Improve documentation — Help others understand the SDK patterns
- Submit code — Fix bugs or implement features
- Fork the repository
- Create a feature branch (
feature/your-feature-name) - Make your changes with clear commits
- Run tests (
npm test) and linting (npm run lint) - Open a Pull Request
See CONTRIBUTING.md for commit conventions, testing practices, and code review process.
Flight School stores your data outside the repository in your user data directory to prevent accidental commits:
| Platform | Location |
|---|---|
| Linux/macOS | ~/.local/share/flight-school/ |
| Windows | %LOCALAPPDATA%\flight-school\ |
| Custom | Set FLIGHT_SCHOOL_DATA_DIR environment variable |
| File | Contents |
|---|---|
profile-cache.json |
Cached GitHub profile (username, avatar, bio, repos) |
focus-storage.json |
Daily challenges, goals, learning topics, completion states |
habits.json |
Your custom learning habits and progress tracking |
threads.json |
Chat conversation history with the AI coach |
workspaces/{id}/ |
Challenge solution code and metadata |
~/.local/share/flight-school/ # or %LOCALAPPDATA%\flight-school\ on Windows
├── profile-cache.json # GitHub profile cache
├── focus-storage.json # Daily focus content + state
├── habits.json # Learning habits
├── threads.json # Chat history
└── workspaces/ # Challenge workspaces
└── {challengeId}/
├── _workspace.json # Metadata
└── solution.ts # Your code files
- Chat history may contain sensitive info based on whatever you discuss with the AI coach.
- Workspace files contain your actual solution code for challenges.
- Back up before clearing — Delete the data folder to reset all local state
- Framework: Next.js with App Router
- UI: GitHub Primer React Components
- AI: GitHub Copilot SDK with MCP tool integration
- Data: Octokit for GitHub API access
- Storage: Local JSON files in user data directory (see above)
- Testing: Vitest
This project is licensed under the MIT License—see the LICENSE file for details.