BIJAY'S Toolkit

Technical / Build Reference — how the project is put together: the GitHub repository, the Vercel API proxy, routing, the data model, security, deployment and how to extend it. Launcher v3.23.0

1. Overview & architecture

BIJAY'S Toolkit is a static site + serverless functions on Vercel in front of a private GitHub repository. The repo holds all content (scripts, app catalogs, config, docs). Small Node functions in api/ talk to GitHub using a token that lives only on the server, so client PCs never see the token or the private repo — they just receive text over HTTPS.

Client PC (PowerShell) Browser (Admin) | | | irm https://domain | iex | https://domain/admin v v +----------------------------------------------------------------+ | Vercel (this project) | | | | public/launcher.ps1 public/admin/index.html public/docs | | | | api/root api/console api/list api/get | | api/tree api/write api/delete (Node serverless) | +----------------------------------------------------------------+ | Authorization: Bearer <GITHUB_TOKEN> (server-side only) v +----------------------------------------------------------------+ | GitHub private repo (Contents & Git Trees API) | | data/scripts data/software data/shared data/templates ... | +----------------------------------------------------------------+

Two independent front doors:

2. The GitHub repository

How the repo side was set up (and how to recreate it):

  1. Create a private repository on GitHub (e.g. waytobijay/Msp-Toolkit). Private is important — push access equals code-execution on every client PC.
  2. Push the project (api/, public/, data/, vercel.json, package.json). No build step and no runtime dependencies — package.json just pins the Node engine.
  3. Create a fine-grained Personal Access Token scoped to this repository only, with Contents: Read and write (write is needed for the admin panel). Give it an expiry and rotate it periodically.
  4. The token is never committed. It's stored as a Vercel environment variable (see §9). Locally, secrets go in a git-ignored .env; example.env is a template only.
The .gitignore keeps real secrets out of the repo. Never place a token in launcher.ps1, the admin page, or any committed file — the whole security model depends on it staying server-side.

3. GitHub as the data store

There is no database. GitHub is the store, accessed through two REST APIs:

Every write is an ordinary commit, so the full history and audit trail live in git. Admin commits are messaged admin: <create|update|delete> <path>.

4. The Vercel API proxy

Each file in api/ is one serverless function. They share a pattern: read env config, check the caller's key, call GitHub with the server-side token, return text/JSON. Reads for client content send Cache-Control: no-store.

EndpointMethodGatePurpose
api/root.jsGETServes launcher.ps1 to PowerShell; redirects browsers to /admin.
api/linux-root.jsGETServes the Linux bash launcher public/linux.sh to curl/wget/bash (the /linux route); redirects browsers to /admin.
api/console.jsGETServes the classic text launcher console.ps1.
api/list.jsGETlauncher key or admin keyFolder contents (Contents API) as {name,type}[].
api/get.jsGETlauncher key or admin keyRaw file text; restricted to data/; no-store.
api/tree.jsGETadmin keyFull recursive tree (Git Trees API) for the admin sidebar.
api/write.jsPOSTadmin keyCreate/update a file (Contents PUT, base64 + sha); path-validated.
api/delete.jsPOSTadmin keyDelete a file (Contents DELETE with sha).

The launcher sends its access password as the x-launcher-key header on every call; the admin panel sends x-admin-key. When a launcher key is configured, list/get accept either key so the admin UI keeps reading files. api/write.js rejects paths containing .., leading slashes, or segments with trailing spaces/dots (which can't be checked out on Windows).

5. Serving the launcher

api/root.js sniffs the User-Agent:

The launcher itself is UI-only: it pulls listings and file text through the API and never contains a token. Its version is shown in the window header ($script:Version).

6. Routing (vercel.json)

{
  "rewrites": [
    { "source": "/",         "destination": "/api/root" },
    { "source": "/launcher", "destination": "/api/root" },
    { "source": "/linux",    "destination": "/api/linux-root" },
    { "source": "/console",  "destination": "/api/console" },
    { "source": "/admin",    "destination": "/admin/index.html" }
  ],
  "functions": { "api/*.js": { "maxDuration": 10 } }
}

Static assets under public/ are served directly, so the docs live at /documentation/…. Rewrites map the friendly paths to functions/pages.

7. Data model

data/ scripts/ "SCRIPTS" — one folder per PowerShell script set (client or general) <set>/config.json per-set values (apps, printers, wifi) -> passed as -Config <set>/<section>/NN-name.ps1 a script inside a section linux/ "LINUX" — bash script sets (mirror of scripts/); run by the Linux launcher <set>/config.json per-set values <set>/<category>/NN-name.sh a bash script inside a category shared/ sections offered to EVERY set ([Shared]); config.json merged under each software/ "SOFTWARE" apps.json the default App Catalog (winget checkbox list) <name>/apps.json an additional named catalog (a folder with apps.json = a catalog) <folder>/NN-*.ps1 custom install scripts (folders without apps.json) templates/ blueprint config.json, software-install.ps1, example script documents/ Doc Storage - admin-only; never shown in the launcher

Naming & conventions

Config merge: a script set's config.json is shallow-merged over data/shared/config.json (set values win), and passed to any script that declares a Config parameter.
Terminology note: the per-client tree used to be called "Companies" / data/companies. It was renamed to Scripts / data/scripts (with git mv, history preserved). If you find old references anywhere, they mean the same thing.

8. App Catalog & winget

A catalog is a JSON file — an array of categories, each with an array of apps:

{
  "categories": [
    { "name": "Browsers", "apps": [
      { "name": "Google Chrome", "winget": "Google.Chrome", "icon": "chrome",
        "desc": "Fast web browser from Google." }
    ] }
  ]
}

The launcher renders it as a categorised checkbox grid (with a per-category Select all). Install Selected opens one elevated window that installs each ticked app:

winget install --id <Id> --exact --source winget --silent \
  --accept-package-agreements --accept-source-agreements

Multiple catalogs are supported: the root apps.json plus any data/software/<name>/apps.json. The launcher caches each by path and clears the selection when you switch catalogs. In the admin panel these are managed by the form editor (openCatalogEditor(path)), and + → New app catalog seeds a new folder catalog with one sample category and app.

9. Environment variables

Set in Vercel → Settings → Environment Variables (Production). Changes apply on the next deploy.

VariableRequiredPurpose
GITHUB_TOKENyesFine-grained PAT, this repo only, Contents: Read & write.
GITHUB_REPOyesowner/repo, e.g. waytobijay/Msp-Toolkit.
GITHUB_REFnoBranch to serve; defaults to main.
ADMIN_PASSWORDyesPassword for /admin and all write/delete APIs.
LAUNCHER_KEYnoAccess password for the PowerShell launcher; if set, the launcher prompts for it.

10. Security model

11. Deploy & how updates go live

  1. Push the repo to private GitHub.
  2. Import it into Vercel (vercel.com/new) — no build settings; functions in api/, static public/.
  3. Add the environment variables (§9) and deploy.
  4. (Optional) Point a custom domain at the project, then set $script:BaseUrl at the top of public/launcher.ps1 to that domain and push once.
What's live-on-push vs needs-a-deploy:
  • Instant (no rebuild): anything read fresh from GitHub — the launcher, the console, all data (scripts, catalogs, config). A push (or an admin-panel commit) is live on the next run/refresh.
  • Needs a Vercel deploy: the admin page itself (static public/admin/index.html), the API functions, vercel.json, and env-var changes. Pushing to main triggers the deploy automatically; hard-refresh the admin page afterwards.

12. Maintaining & extending

13. Version history (highlights)

VersionWhat changed
Linux v1.0.0Linux (bash) script sets. New admin Linux tab (mirror of Scripts, backed by data/linux) and a Linux launcher — curl -fsSL …/linux | bash — a terminal menu that runs / runs-as-root / views / schedules bash scripts, authenticated with the same x-launcher-key. Adds api/linux-root.js, the /linux route, and an LF .gitattributes pin for *.sh.
AdminCommand catalog (folders → categories → entries with copyable command/link items, searchable); theme-aware split login with animated icons; sharper HiDPI PDF preview; pane-aware (container-query) toolbar wrapping; window drag while the password gate is up.
v3.23.0Fluent (Windows 11-style) redesign: DynamicResource theme tokens with a Dark / Light / Auto selector (Auto follows Windows; preference saved to %APPDATA%\BijayToolkit\theme.txt; switches live), a Home / Welcome page (quick actions + info), rounded corners, refined spacing/dividers, and a friendlier empty state. Layout, navigation and functionality unchanged.
v3.23.0Top Scripts/Software tabs; tidy first-open loading; compact right-aligned per-category Select all; clearer window drag handle.
v3.23.0Multiple App Catalogs (any data/software/<name>/apps.json auto-detected); admin "+ → New app catalog / New script folder"; removed the redundant admin "+ Client".
v3.23.0Renamed Companies → Scripts (folder data/companies → data/scripts and all code/labels); per-category Select all in the App Catalog.
v3.1.xWinUtil-style App Catalog with one-click winget install; robust Winget-ID handling (--source winget, Id normalisation); admin form-based catalog editor with three-dot menus and an app Edit modal.
v3.23.0Modern dark WPF GUI launcher (sidebar, search, breadcrumb, cards, Run / Run-as-Admin), async loading; classic text menu kept at /console; added the Software section.
v2.xRestructured content under a single data/ root; responsive admin panel; documentation folder.