{"id":49535367,"url":"https://github.com/giftedloser/runway","last_synced_at":"2026-05-02T10:03:23.406Z","repository":{"id":349849141,"uuid":"1203017867","full_name":"giftedloser/Runway","owner":"giftedloser","description":"Runway: local-first Windows Autopilot, Intune, Entra, and SCCM readiness triage.","archived":false,"fork":false,"pushed_at":"2026-04-27T23:04:02.000Z","size":1699,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-27T23:18:26.416Z","etag":null,"topics":["autopilot","endpoint-management","entra-id","intune","runway","sccm","tauri","windows"],"latest_commit_sha":null,"homepage":"https://github.com/giftedloser/PilotCheck#readme","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/giftedloser.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-06T16:31:40.000Z","updated_at":"2026-04-27T23:04:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/giftedloser/Runway","commit_stats":null,"previous_names":["giftedloser/pilotcheck","giftedloser/runway"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/giftedloser/Runway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giftedloser%2FRunway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giftedloser%2FRunway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giftedloser%2FRunway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giftedloser%2FRunway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giftedloser","download_url":"https://codeload.github.com/giftedloser/Runway/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giftedloser%2FRunway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32530178,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autopilot","endpoint-management","entra-id","intune","runway","sccm","tauri","windows"],"created_at":"2026-05-02T10:03:17.400Z","updated_at":"2026-05-02T10:03:23.398Z","avatar_url":"https://github.com/giftedloser.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Runway\n\nRunway is the local-first Windows endpoint triage console in this repository.\n\n![CI](https://github.com/giftedloser/Runway/actions/workflows/ci.yml/badge.svg)\n![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Platform: Windows](https://img.shields.io/badge/platform-Windows-0078d4.svg)\n\n\u003e A local-first triage console for **Windows** Autopilot, Intune, Entra ID,\n\u003e and Graph-derived ConfigMgr / SCCM client presence.\n\u003e Windows-only by design.\n\nRunway correlates Windows device identities across Microsoft Autopilot,\nIntune, Entra ID, and the ConfigMgr/SCCM signal exposed by Intune, computes\nprovisioning health with a transparent rule engine, and presents\n**problem-first** diagnostics — so an operator opening the app on a Monday\nmorning can tell within seconds which devices are broken, _why_, and _what to\ndo about it_.\n\nIt's built for the small-but-real internal IT team running a fleet of a few\nhundred to a few thousand Windows endpoints — the band where SCCM console\nhopping is too heavy for quick triage, the Intune admin centre is too slow to\nwork from all day, and PowerShell scripts are how everyone is currently getting\nby.\n\nRunway is intentionally **not** a full Intune replacement, **not** a\nmulti-tenant tool, and does **not** attempt to manage iOS, Android, or macOS.\nThose are non-goals — the product is laser-focused on the Windows triage loop\nand stays honest about that scope.\n\n## 1.0 Readiness\n\nRunway is ready for controlled live testing when:\n\n- Graph app credentials are configured and `SEED_MODE=none` is used for a\n  clean live-data validation pass.\n- `SESSION_SECRET` is present in `.env` (Runway auto-generates one on first\n  boot if missing; verify it has not been wiped).\n- The Entra app access gate is enabled, or an explicit local/dev exception is\n  documented. Note: every `/api/*` route now requires the desktop token,\n  a delegated session, or an app-access session even when the gate is\n  disabled — there is no anonymous access path on the loopback API.\n- At least five known devices match expected Autopilot, Intune, Entra, and\n  ConfigMgr/SCCM states.\n- Admin sign-in, sign-out, and one low-risk delegated action such as Intune\n  device sync have been validated.\n- Tauri updater signing is configured, and Windows Authenticode signing has\n  either been wired with a real certificate or accepted as the remaining\n  SmartScreen risk for a controlled pilot. See\n  [`docs/release-signing.md`](docs/release-signing.md).\n- The security review in [`docs/security-report.md`](docs/security-report.md)\n  and the preflight in\n  [`docs/live-testing-checklist.md`](docs/live-testing-checklist.md) have been\n  accepted.\n\n---\n\n## What it does\n\n- **Cross-system identity correlation** — joins Autopilot hardware records,\n  Intune managed devices, and Entra device objects on serial / device ID /\n  ZTDID, surfacing identity conflicts and orphans.\n- **Provisioning health engine** — pure rule evaluation against the joined\n  state, producing typed flags (`no_profile_assigned`, `not_in_target_group`,\n  `provisioning_stalled`, `compliance_drift`, …) with severity, summary, and\n  the raw evidence behind each.\n- **Assignment-path inspector** — for any device, walks the chain\n  _Autopilot record → group membership → assigned profile → effective\n  deployment mode_ and tells you exactly which link is broken.\n- **Drift history** — transition-only `device_state_history` table, plus a\n  14-day fleet-wide health trend on the dashboard.\n- **Remote actions** (delegated auth) — sync, reboot, rename, rotate LAPS,\n  Autopilot reset, retire, factory wipe — all logged to a cross-device action\n  audit timeline.\n- **LAPS retrieval** with reveal toggle, copy-to-clipboard, and 30-second\n  auto-rehide.\n- **Custom rules** — small predicate DSL for site-specific health checks,\n  scoped globally / per-property / per-profile.\n- **Group inspector** — Entra group membership, dynamic membership rule, the\n  profiles each group resolves to, and member-level health filtering.\n- **Profile audit** — health breakdown per Autopilot profile with click-through\n  to the device queue.\n- **Tag mapping dictionary** — maps Autopilot group tags to expected profiles\n  and target groups, with JSON import/export for versioning.\n- **ConfigMgr / SCCM client presence** — optional, presence-only signal that\n  appears alongside Graph, Intune, and Entra on device detail pages. Derived\n  from Intune's `managementAgent` value. Tells you whether a ConfigMgr client\n  is reported on the device — not whether it is healthy, on-site, or pulling\n  the right update channel.\n- **Tenant access gate** — Entra sign-in gate locks the app before operators\n  can see fleet data once Graph is configured; delegated admin consent remains\n  separate.\n- **Desktop polish** — Tauri shell with Runway branding, custom title bar,\n  drag region, minimize/maximize/close controls, and Windows installers.\n- **Mock mode** — seeds realistic data when Graph credentials aren't\n  configured, so the entire UI is explorable offline.\n\n---\n\n## Stack\n\n| Layer   | Tech                                                                   |\n| ------- | ---------------------------------------------------------------------- |\n| Client  | React 19, TypeScript, Vite, Tailwind CSS v4                            |\n| Routing | TanStack Router (URL-as-state), TanStack Query                         |\n| UI      | shadcn-style primitives, lucide-react icons                            |\n| Server  | Express 5, better-sqlite3, pino                                        |\n| Auth    | MSAL Node - app-only sync, delegated actions, Entra app access gate    |\n| Desktop | Tauri 2, custom Windows title bar, NSIS/MSI installers                 |\n| Tests   | Vitest (unit / api / e2e), Testing Library, supertest                  |\n\n---\n\n## Quick start\n\n```bash\n# 1. Install\nnpm install\n\n# 2. Initialise the database (creates ./data/pilotcheck.sqlite)\nnpm run db:migrate\nnpm run db:seed:mock     # optional — seeds realistic mock data\n\n# 3. Run client + server\nnpm run dev\n```\n\n- Client: \u003chttp://localhost:5173\u003e\n- API: \u003chttp://localhost:3001\u003e\n\nIf `AZURE_TENANT_ID` / `AZURE_CLIENT_ID` / `AZURE_CLIENT_SECRET` are not set\nand the database is empty, Runway automatically falls back to **mock mode**\non first start so the entire UI is explorable without a tenant.\n\nFor local development, either set `NODE_ENV=development` or replace\n`SESSION_SECRET` in `.env` with a long random value before starting the server.\n\n### As a desktop app\n\n```bash\nnpm run tauri:dev      # dev build\nnpm run tauri:build    # production installer\n```\n\n---\n\n## Configuration\n\nRunway is **self-hosted, bring-your-own-Entra-app-registration**. The\ninstaller never contains Microsoft Graph credentials — each operator\nregisters Runway as an application in their own tenant and places the\ncredentials in their per-user app data folder. See\n[`docs/graph-auth.md`](docs/graph-auth.md) for the full design and the\nplanned migration to a PKCE public-client model.\n\nFor local development, copy `.env.example` to `.env` at the repo root and fill in:\n\nFor the installed desktop app, place the same `.env` file in the app data folder:\n\n- Windows: `%LOCALAPPDATA%\\com.giftedloser.pilotcheck\\.env`\n\n```ini\n# Microsoft Graph (read-only ingestion)\nAZURE_TENANT_ID=\nAZURE_CLIENT_ID=\nAZURE_CLIENT_SECRET=\nAZURE_REDIRECT_URI=http://localhost:3001/api/auth/callback\n\n# Session signing for delegated auth\n# Required in production. The server refuses to start outside\n# NODE_ENV=development|test if this is left at the built-in default.\nSESSION_SECRET=change-me-in-production\n\n# Server\nPORT=3001\nCLIENT_PORT=5173\nDATABASE_PATH=./data/pilotcheck.sqlite\n\n# Engine tunables\nSYNC_INTERVAL_MINUTES=15\nPROFILE_ASSIGNED_NOT_ENROLLED_HOURS=2\nPROVISIONING_STALLED_HOURS=8\n\n# Force mock data even when credentials are present\nSEED_MODE=mock\n\n# Entra app-access gate. Enforced by default once Graph is configured.\nAPP_ACCESS_MODE=entra\nAPP_ACCESS_ALLOWED_USERS=\n\n# SCCM/ConfigMgr detection is toggled in Settings.\n# It is read from Intune's managementAgent field; no SCCM connector\n# credentials or SCCM actions are configured in Runway.\n```\n\n### Mock mode vs live data\n\nRunway ships with mock mode because it makes local development and demos safe.\nFor live tenant validation:\n\n- Use `SEED_MODE=none`.\n- Start with a clean database or a database you know does not contain seeded\n  demo records.\n- Confirm Settings shows Graph configured and the mock banner is gone.\n- Run a sync, then validate a small known device set before trusting fleet-wide\n  counts.\n\nMock data is realistic, but it is still fake. Do not mix mock and live records\nwhen producing screenshots or reports for leadership.\n\n### SCCM / ConfigMgr visibility\n\nRunway does **not** connect to a Configuration Manager site server, does\n**not** store SCCM credentials, and does **not** run SCCM actions.\n\nFor v1.0, the SCCM check answers exactly one question:\n\n\u003e Does Microsoft Graph / Intune report this Windows device as having a\n\u003e Configuration Manager client?\n\nThis is a **presence-only** signal. It does not confirm site assignment,\npolicy retrieval, last-policy-request time, inventory freshness, MP/DP\nreachability, software update group membership, or which authority\n(ConfigMgr vs Windows Update for Business) is driving updates on the device.\n\nRunway reads `managedDevice.managementAgent` from Microsoft Graph. If the\nvalue contains `configurationManager`, Runway shows the ConfigMgr client as\nreported.\n\n| Runway status                   | Meaning                                                                          |\n| ------------------------------- | -------------------------------------------------------------------------------- |\n| `ConfigMgr client reported`     | Intune's `managementAgent` contains `configurationManager`.                      |\n| `ConfigMgr client not reported` | Intune reports a management agent, but it does not contain `configurationManager`. |\n| `Not reported by Intune`        | The Intune record exists, but Graph did not return `managementAgent`.            |\n| `Cannot determine`              | No Intune managed-device record exists for the correlated device.                |\n| `Signal disabled`               | The optional ConfigMgr presence flag is off in Settings.                         |\n\nIf you need true ConfigMgr client health — site assignment, policy retrieval,\ninventory freshness, MP/DP reachability, software update deployment status,\nor console record status — that requires a direct ConfigMgr connector via\nAdminService, read-only SQL, or a trusted ConfigMgr PowerShell host. That\nwork is **not in v1.0** and is on the roadmap as an optional, opt-in\nconnector.\n\n### Required Graph permissions\n\n**Application (read-only sync)**\n\n- `DeviceManagementServiceConfig.Read.All`\n- `DeviceManagementManagedDevices.Read.All`\n- `DeviceManagementConfiguration.Read.All`\n- `Device.Read.All`\n- `Group.Read.All`\n\n**Delegated (remote actions + LAPS / BitLocker / group checks)**\n\n- `DeviceManagementManagedDevices.ReadWrite.All`\n- `DeviceManagementManagedDevices.PrivilegedOperations.All`\n- `DeviceLocalCredential.Read.All`\n- `BitLockerKey.Read.All`\n- `Group.ReadWrite.All`\n- `DeviceManagementServiceConfig.ReadWrite.All`\n- `User.Read`\n\nGrant admin consent in the tenant after assigning these.\n\n---\n\n## Scripts\n\n| Script                 | What it does                                      |\n| ---------------------- | ------------------------------------------------- |\n| `npm run dev`          | Vite client + tsx server, hot reload              |\n| `npm run build`        | Build client and server bundles into `dist/`      |\n| `npm run start`        | Run the built server                              |\n| `npm run db:migrate`   | Apply schema migrations to the SQLite database    |\n| `npm run db:seed:mock` | Seed realistic mock devices, groups, profiles     |\n| `npm run test`         | Vitest unit + api projects                        |\n| `npm run test:e2e`     | Vitest e2e project                                |\n| `npm run lint`         | ESLint                                            |\n| `npm run check`        | Lint + unit/API + e2e tests                       |\n| `npm run tauri:dev`    | Run as a Tauri desktop app                        |\n| `npm run tauri:build`  | Build the Tauri executable and Windows installers |\n\n---\n\n## Project layout\n\n```\nsrc/\n├── client/           # React SPA\n│   ├── routes/       # Top-level pages (Dashboard, DeviceList, …)\n│   ├── components/   # Feature-grouped components\n│   ├── hooks/        # TanStack Query hooks\n│   ├── lib/          # API client, types, helpers\n│   └── styles/       # Tailwind v4 globals + tokens\n├── server/           # Express API\n│   ├── routes/       # HTTP route handlers\n│   ├── db/           # better-sqlite3 schema, queries, migrations, seed\n│   ├── engine/       # State / rule evaluation (pure)\n│   ├── sync/         # Graph ingestion + sync orchestrator\n│   └── auth/         # MSAL app-only + delegated flows\n├── shared/           # Types shared across client and server\n└── test/             # Vitest unit / api / e2e suites\n```\n\n---\n\n## How the engine works\n\n1. **Sync** (`src/server/sync/`) pulls Autopilot, Intune, and Entra resources\n   from Microsoft Graph and writes them into `raw_*` tables verbatim. Intune's\n   `managementAgent` field is retained for the ConfigMgr/SCCM visibility\n   signal.\n2. **Compute** (`src/server/engine/compute-all-device-states.ts`) joins those\n   tables into a per-device snapshot, evaluates the built-in rules and any\n   user-defined rules, and writes the result to `device_state`.\n3. **History** writes a row to `device_state_history` only when a device's\n   health or flag set actually changes — keeping the table small while still\n   supporting transition-driven views like the 14-day trend chart.\n4. **Read paths** are pure SQL queries against `device_state` joined with\n   `tag_config` and the raw tables for the diagnostic detail panels.\n\nThe engine is a pure function from raw state → typed flags. There is no\n\"magic\" anywhere — every diagnostic in the UI traces back to a named rule\nfile you can read.\n\n---\n\n## Documentation\n\n- **[docs/architecture.md](./docs/architecture.md)** — system architecture, data flow, schema overview\n- **[docs/user-guide.md](./docs/user-guide.md)** — comprehensive technician guide for day-to-day Runway use\n- **[docs/engine.md](./docs/engine.md)** — health flags, rule DSL, custom rules\n- **[docs/graph-setup.md](./docs/graph-setup.md)** — app registration, permissions, redirect URIs\n- **[docs/troubleshooting.md](./docs/troubleshooting.md)** — live Graph troubleshooting and symptom-to-fix tables\n- **[docs/live-testing-checklist.md](./docs/live-testing-checklist.md)** — pre-flight checks before connecting a tenant\n- **[docs/release-signing.md](./docs/release-signing.md)** — updater signing, Authenticode status, release artifact shape\n- **[docs/security-report.md](./docs/security-report.md)** — security posture summary for review/approval\n- **[CONTRIBUTING.md](./CONTRIBUTING.md)** — development workflow, conventions\n- **[CHANGELOG.md](./CHANGELOG.md)** — release notes\n\n---\n\n## Security\n\nRunway is **local-first** — all device state and action logs stay on the\noperator's machine in a local SQLite file. There is no Runway cloud, no\ntelemetry, no analytics. The only external network calls are to\n`graph.microsoft.com` and `login.microsoftonline.com`.\n\nLAPS passwords are fetched on-demand via delegated auth, displayed with a\n30-second auto-rehide, and never persisted to disk.\n\nRaw source JSON and conditional-access policy detail are only returned to\nan authenticated admin session.\n\nSCCM/ConfigMgr support is visibility-only and Graph-derived. The app does not\nhold SCCM credentials, open a site-server connection, or dispatch ConfigMgr\nclient actions.\n\nIf you discover a security issue, please see [SECURITY.md](./SECURITY.md).\n\n---\n\n## License\n\nMIT © Runway contributors — see [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiftedloser%2Frunway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiftedloser%2Frunway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiftedloser%2Frunway/lists"}