├── .prettierignore ├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ ├── publish.yml │ └── ci.yml ├── src ├── bin.ts ├── platforms │ ├── windows.ts │ ├── linux.ts │ └── macos.ts └── index.ts ├── test └── hwid.test.ts ├── tsdown.config.ts ├── eslint.config.mjs ├── LICENSE ├── README.md ├── tsconfig.json └── package.json /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | pnpm-lock.yaml 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | *.log 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ["luludotdev"] 2 | custom: 3 | - "https://monzo.me/jackbaron" 4 | -------------------------------------------------------------------------------- /src/bin.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { hwid } from "./index"; 3 | 4 | void hwid().then(console.log).catch(console.error); 5 | -------------------------------------------------------------------------------- /test/hwid.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { hwid } from "../src/index"; 3 | 4 | test("is a function", () => { 5 | expect(typeof hwid).eq("function"); 6 | }); 7 | 8 | test("returns a string with length > 0", async () => { 9 | const id = await hwid(); 10 | expect(typeof id).eq("string"); 11 | expect(id.length).greaterThan(0); 12 | }); 13 | -------------------------------------------------------------------------------- /src/platforms/windows.ts: -------------------------------------------------------------------------------- 1 | import * as util from "node:util"; 2 | import Registry from "winreg"; 3 | 4 | export async function hwid(): Promise { 5 | const regKey = new Registry({ 6 | hive: Registry.HKLM, 7 | key: "\\SOFTWARE\\Microsoft\\Cryptography", 8 | }); 9 | 10 | const getKey = util.promisify(regKey.get.bind(regKey)); 11 | const key = await getKey("MachineGuid"); 12 | 13 | return key.value.toLowerCase(); 14 | } 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { platform } from "node:process"; 2 | import * as linux from "./platforms/linux"; 3 | import * as macos from "./platforms/macos"; 4 | import * as windows from "./platforms/windows"; 5 | 6 | export async function hwid(): Promise { 7 | if (platform === "win32") return windows.hwid(); 8 | if (platform === "linux") return linux.hwid(); 9 | if (platform === "darwin") return macos.hwid(); 10 | 11 | throw new Error(`unsupported platform: ${platform}`); 12 | } 13 | -------------------------------------------------------------------------------- /src/platforms/linux.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from "node:child_process"; 2 | import * as util from "node:util"; 3 | 4 | const exec = util.promisify(child_process.exec); 5 | 6 | export async function hwid(): Promise { 7 | const { stdout } = await exec( 8 | "cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || true" 9 | ); 10 | 11 | const array = stdout.trim().split("\n"); 12 | const first = array[0]; 13 | if (!first) throw new Error("failed to find hwid"); 14 | 15 | return first; 16 | } 17 | -------------------------------------------------------------------------------- /src/platforms/macos.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from "node:child_process"; 2 | import * as util from "node:util"; 3 | 4 | const exec = util.promisify(child_process.exec); 5 | 6 | export async function hwid(): Promise { 7 | const { stdout } = await exec("ioreg -rd1 -c IOPlatformExpertDevice"); 8 | const uuid = stdout 9 | .trim() 10 | .split("\n") 11 | .find((line) => line.includes("IOPlatformUUID")) 12 | ?.replaceAll(/=|\s+|"/gi, "") 13 | .replaceAll("IOPlatformUUID", ""); 14 | 15 | if (!uuid) throw new Error("failed to find hwid"); 16 | return uuid; 17 | } 18 | -------------------------------------------------------------------------------- /tsdown.config.ts: -------------------------------------------------------------------------------- 1 | import type { UserConfig } from "tsdown"; 2 | import { defineConfig } from "tsdown"; 3 | 4 | const config: UserConfig = defineConfig({ 5 | entry: { 6 | index: "./src/index.ts", 7 | bin: "./src/bin.ts", 8 | "*": "./src/platforms/*.ts", 9 | }, 10 | exports: { 11 | enabled: true, 12 | customExports: (pkg, context) => { 13 | context.pkg.bin = pkg["./bin"]; 14 | delete pkg["./bin"]; 15 | return pkg; 16 | }, 17 | }, 18 | platform: "node", 19 | tsconfig: true, 20 | skipNodeModulesBundle: true, 21 | }); 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import common from "@luludev/eslint-config/common"; 2 | import node from "@luludev/eslint-config/node"; 3 | import prettier from "@luludev/eslint-config/prettier"; 4 | import typescript from "@luludev/eslint-config/typescript"; 5 | 6 | const config = [ 7 | ...common, 8 | ...node, 9 | ...typescript, 10 | ...prettier, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | projectService: true, 15 | tsconfigRootDir: import.meta.dirname, 16 | }, 17 | }, 18 | rules: { 19 | "n/shebang": 0, 20 | }, 21 | }, 22 | ]; 23 | 24 | export default config; 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2026, Lulu 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package 2 | on: 3 | release: 4 | types: [published] 5 | 6 | env: 7 | NODE_VERSION: 24.x 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | id-token: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v6 18 | - name: Install pnpm 19 | uses: pnpm/action-setup@v4 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v6 22 | with: 23 | cache: pnpm 24 | node-version: ${{ env.NODE_VERSION }} 25 | registry-url: "https://registry.npmjs.org" 26 | - name: Install packages 27 | run: pnpm install 28 | - name: Publish to NPM 29 | run: npm publish --provenance --access public 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | if: contains(github.ref, 'refs/tags/') == false 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, windows-latest, macos-latest] 11 | node-version: [24.x] 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v6 16 | - name: Install pnpm 17 | uses: pnpm/action-setup@v4 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v6 20 | with: 21 | cache: pnpm 22 | node-version: ${{ matrix.node-version }} 23 | - name: Install packages 24 | run: pnpm install 25 | - name: Check Types 26 | run: pnpm run typecheck 27 | - name: Test 28 | run: pnpm run test 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hwid 2 | 3 | [![Node.js CI](https://github.com/luludotdev/hwid/actions/workflows/ci.yml/badge.svg)](https://github.com/luludotdev/hwid/actions/workflows/ci.yml) 4 | [![NPM version](https://img.shields.io/npm/v/hwid.svg?maxAge=3600)](https://www.npmjs.com/package/hwid) 5 | [![NPM downloads](https://img.shields.io/npm/dt/hwid.svg?maxAge=3600)](https://www.npmjs.com/package/hwid) 6 | 7 | > Get the current system's unique hardware ID 8 | 9 | ## Install 10 | 11 | ```sh 12 | pnpm i hwid 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```ts 18 | import { hwid } from "hwid"; 19 | 20 | // detects platform automatically 21 | const id: string = await hwid(); 22 | ``` 23 | 24 | ### Platform-specific 25 | 26 | Supported platforms: `windows`, `macos`, and `linux` 27 | 28 | ```ts 29 | // will only work on macos 30 | import { hwid } from "hwid/macos"; 31 | 32 | const id: string = await hwid(); 33 | ``` 34 | 35 | ## CLI 36 | 37 | ```sh 38 | pnpm dlx hwid@latest 39 | ``` 40 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | // Type Checking 5 | "allowUnreachableCode": false, 6 | "allowUnusedLabels": false, 7 | "exactOptionalPropertyTypes": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitOverride": true, 10 | "noImplicitReturns": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "strict": true, 14 | "useUnknownInCatchVariables": true, 15 | "noUncheckedIndexedAccess": true, 16 | "isolatedDeclarations": true, 17 | "skipLibCheck": true, 18 | "types": ["node"], 19 | 20 | // Modules 21 | "module": "preserve", 22 | "moduleResolution": "bundler", 23 | "moduleDetection": "force", 24 | "isolatedModules": true, 25 | "resolveJsonModule": true, 26 | "verbatimModuleSyntax": true, 27 | "esModuleInterop": true, 28 | 29 | // Emit 30 | "declaration": true, 31 | "emitDeclarationOnly": true, 32 | "newLine": "lf", 33 | "forceConsistentCasingInFileNames": true, 34 | 35 | // Language and Environment 36 | "emitDecoratorMetadata": false, 37 | "experimentalDecorators": false, 38 | "lib": ["es2023"], 39 | "target": "esnext", 40 | "useDefineForClassFields": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hwid", 3 | "version": "0.6.1", 4 | "type": "module", 5 | "main": "./dist/index.mjs", 6 | "module": "./dist/index.mjs", 7 | "types": "./dist/index.d.mts", 8 | "bin": "./dist/bin.mjs", 9 | "exports": { 10 | ".": "./dist/index.mjs", 11 | "./linux": "./dist/linux.mjs", 12 | "./macos": "./dist/macos.mjs", 13 | "./windows": "./dist/windows.mjs", 14 | "./package.json": "./package.json" 15 | }, 16 | "sideEffects": false, 17 | "files": [ 18 | "dist" 19 | ], 20 | "scripts": { 21 | "build": "tsdown", 22 | "dev": "tsdown --watch", 23 | "lint": "prettier --check . && eslint src test", 24 | "format": "prettier --write . && eslint src test --fix", 25 | "test": "vitest", 26 | "typecheck": "tsc --noEmit", 27 | "prepublishOnly": "pnpm run build" 28 | }, 29 | "author": "Lulu (https://lulu.dev)", 30 | "license": "ISC", 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/luludotdev/hwid.git" 34 | }, 35 | "devDependencies": { 36 | "@luludev/eslint-config": "^0.8.0", 37 | "@types/node": "^25.0.3", 38 | "@types/winreg": "^1.2.36", 39 | "eslint": "^9.39.2", 40 | "prettier": "^3.7.4", 41 | "tsdown": "^0.18.1", 42 | "typescript": "^5.9.3", 43 | "vitest": "^4.0.16" 44 | }, 45 | "optionalDependencies": { 46 | "winreg": "^1.2.5" 47 | }, 48 | "packageManager": "pnpm@10.26.0", 49 | "pnpm": { 50 | "overrides": { 51 | "@stylistic/eslint-plugin": "^5.0.0", 52 | "@stylistic/eslint-plugin-jsx": "npm:@stylistic/eslint-plugin", 53 | "@stylistic/eslint-plugin-ts": "npm:@stylistic/eslint-plugin", 54 | "@angular-eslint/eslint-plugin": "-", 55 | "@angular-eslint/eslint-plugin-template": "-", 56 | "@angular-eslint/template-parser": "-", 57 | "astro-eslint-parser": "-", 58 | "eslint-plugin-astro": "-", 59 | "eslint-plugin-cypress": "-", 60 | "eslint-plugin-rxjs": "-", 61 | "eslint-plugin-rxjs-angular": "-", 62 | "eslint-plugin-svelte3": "-", 63 | "eslint-plugin-vue": "-", 64 | "vue-eslint-parser": "-" 65 | }, 66 | "onlyBuiltDependencies": [ 67 | "esbuild", 68 | "registry-js", 69 | "unrs-resolver" 70 | ] 71 | } 72 | } 73 | --------------------------------------------------------------------------------