-
Notifications
You must be signed in to change notification settings - Fork 0
ci: harden NeuralOS verification workflow #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7569b6d
839d561
2cb44cf
0eca8d3
ca5efc1
1521aa7
79c731e
aad9733
2f781c8
f0b975c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,83 @@ | ||||||
| name: Release | ||||||
|
|
||||||
| on: | ||||||
| push: | ||||||
| tags: | ||||||
| - "v*" | ||||||
|
|
||||||
| permissions: | ||||||
| contents: write | ||||||
|
|
||||||
| jobs: | ||||||
| release: | ||||||
| runs-on: ubuntu-latest | ||||||
|
|
||||||
| steps: | ||||||
| - uses: actions/checkout@v4 | ||||||
|
|
||||||
| - name: Use Node.js | ||||||
| uses: actions/setup-node@v4 | ||||||
| with: | ||||||
| node-version: "20" | ||||||
| cache: "npm" | ||||||
|
|
||||||
| - name: Install dependencies | ||||||
| run: npm ci | ||||||
|
|
||||||
| - name: Generate CI architect key | ||||||
| run: | | ||||||
| node -e " | ||||||
| const crypto = require('crypto'); | ||||||
| const fs = require('fs'); | ||||||
| const pair = crypto.generateKeyPairSync('ed25519'); | ||||||
| const pem = pair.privateKey.export({ type: 'pkcs8', format: 'pem' }); | ||||||
| fs.writeFileSync('core/architect.key', pem, { encoding: 'utf8' }); | ||||||
| console.log('[CI] architect.key generated for release signing'); | ||||||
| " | ||||||
|
|
||||||
| - name: Install Wine for cross-platform build | ||||||
| run: | | ||||||
| sudo dpkg --add-architecture i386 | ||||||
| sudo apt-get update -qq | ||||||
| sudo apt-get install -y -qq wine64 > /dev/null 2>&1 | ||||||
|
|
||||||
| - name: Refresh manifest and build portable | ||||||
| run: npm run build:portable | ||||||
|
|
||||||
| - name: Generate release manifest | ||||||
| run: node scripts/release_portable.cjs | ||||||
|
|
||||||
| - name: Verify artifact | ||||||
| run: npm run verify:portable | ||||||
|
|
||||||
| - name: Verify release hash chain | ||||||
| run: npm run verify:release | ||||||
|
|
||||||
| - name: Identify build artifact | ||||||
| id: artifact | ||||||
| run: | | ||||||
| EXE=$(ls dist/*.exe 2>/dev/null | head -1) | ||||||
| if [ -z "$EXE" ]; then | ||||||
| echo "No .exe found in dist/" | ||||||
| exit 1 | ||||||
| fi | ||||||
| echo "path=$EXE" >> "$GITHUB_OUTPUT" | ||||||
| echo "name=$(basename $EXE)" >> "$GITHUB_OUTPUT" | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Unquoted The artifact name pattern in
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||
| SHA=$(sha256sum "$EXE" | cut -d' ' -f1) | ||||||
| echo "sha256=$SHA" >> "$GITHUB_OUTPUT" | ||||||
|
|
||||||
| - name: Create GitHub Release | ||||||
| uses: softprops/action-gh-release@v2 | ||||||
| with: | ||||||
| generate_release_notes: true | ||||||
| files: | | ||||||
| ${{ steps.artifact.outputs.path }} | ||||||
| dist/release-manifest.json | ||||||
| dist/*.sha256.txt | ||||||
| body: | | ||||||
| ## ForgeCore OS ${{ github.ref_name }} | ||||||
|
|
||||||
| **Artifact:** `${{ steps.artifact.outputs.name }}` | ||||||
| **SHA-256:** `${{ steps.artifact.outputs.sha256 }}` | ||||||
|
|
||||||
| See `release-manifest.json` for full integrity chain. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| npx lint-staged |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "singleQuote": true, | ||
| "trailingComma": "none", | ||
| "tabWidth": 4, | ||
| "semi": true, | ||
| "printWidth": 120, | ||
| "arrowParens": "always", | ||
| "endOfLine": "lf" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Next Todo List - Phase 2 | ||
|
|
||
| Updated: 2026-05-08 | ||
|
|
||
| Previous cycle completed: repo hygiene, CI command alignment, docs, release notes, full E2E, lint, asset build, Windows packaging fallback, and packaged smoke test. | ||
|
|
||
| ## Priority 1 - Commit And PR Readiness | ||
|
|
||
| - [x] Review the current diff from the repository root at `C:/Users/KickA`. | ||
| - [x] Stage the intended files only: `.gitignore`, `.github/workflows/forgecore-ci.yml`, root `README.md`, `NeuralOS_Master_Build/README.md`, `NeuralOS_Master_Build/NEXT_TODO.md`, `NeuralOS_Master_Build/docs/RELEASE_NOTES.md`, packaged QA evidence, `NeuralOS_Master_Build/modules/window.js`, and the removal of `NeuralOS_Master_Build/test-results/.last-run.json` from tracking. | ||
| - [x] Confirm ignored local artifacts stay out of Git status: stale workflow drafts, subproject `.husky/`, `packages/sample.txt`, Playwright report output, and packaged build output. | ||
| - [x] Commit the change set on `codex/next-15-ci-hooks`. Outcome: committed as `ci: harden neuralos verification workflow`. | ||
| - [x] Push the branch and open or update the PR with test results. Outcome: pushed branch and updated PR #7. | ||
|
|
||
| ## Priority 2 - Native Build Environment | ||
|
|
||
| - [x] Install Visual Studio C++ Build Tools with MSVC, Windows SDK, and Desktop C++ workload. | ||
| - [x] Rerun `npm run rebuild` and confirm `node-pty` rebuilds without the fallback. | ||
| - [x] Rerun default `npm run build:win` and confirm the package builds without `--config.npmRebuild=false`. | ||
| - [x] Rerun `npm run test:packaged` against the default build artifact. | ||
| - [x] Decide whether to add a `postinstall` script for `electron-builder install-app-deps`. Outcome: leave it out for now to keep `npm ci` lighter; explicit rebuild/build steps cover native alignment. | ||
|
|
||
| ## Priority 3 - Repository Boundary Decision | ||
|
|
||
| - [x] Decide whether `NeuralOS_Master_Build` should remain a subdirectory of the `C:/Users/KickA` repository. Outcome: keep the current root for this branch. | ||
| - [x] If keeping the current root, add a short root-level map that explains where NeuralOS work lives. | ||
| - [x] If splitting the project, plan a clean repo extraction path that preserves needed history. Outcome: no split now; defer extraction planning until the current CI branch lands. | ||
| - [x] Update CI paths and package commands to match the chosen repository boundary. Outcome: current CI paths already match the retained root. | ||
|
|
||
| ## Priority 4 - Release QA Evidence | ||
|
|
||
| - [x] Run the manual QA checklist for WinShadow, NeuralMac, NeuralLinux, XXXplorer, and the system layer. | ||
| - [x] Verify shell switching and PTY behavior inside the packaged app, not only the dev app. | ||
| - [x] Capture screenshots or notes from the packaged-app QA pass. | ||
| - [x] Add the QA evidence summary to `docs/RELEASE_NOTES.md`. | ||
| - [x] Confirm the portable EXE launches from a clean temporary directory. | ||
|
|
||
| ## Follow-Up Watchlist | ||
|
|
||
| - [ ] Consider adding a dedicated packaged shell-switch Playwright test so this lifecycle regression is caught before manual QA. | ||
| - [ ] Triage XXXplorer visual glitching separately from packaged startup smoke. | ||
| - [ ] Decide later whether `NeuralOS_Master_Build` should become its own Git repository after the CI/hooks branch is merged. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| ## NeuralOS Master Build | ||
|
|
||
| Sovereign Desktop Runtime built on Electron 41. Provides a triple-shell architecture (WinShadow, NeuralMac, NeuralLinux) with hot-swap switching, a hardware-sealed identity system, and capability-based IPC. | ||
|
|
||
| ### Quick Start | ||
|
|
||
| ```bash | ||
| cd NeuralOS_Master_Build | ||
| npm install | ||
| npm run rebuild # rebuild node-pty for Electron ABI | ||
| npm start # launch in development mode | ||
| ``` | ||
|
|
||
| ### Architecture | ||
|
|
||
| The main process is split into focused modules under `modules/`: | ||
|
|
||
| | Module | Purpose | | ||
| | ------------- | ------------------------------------------------------------------------- | | ||
| | `config.js` | Paths, constants, `DEFAULT_SYSTEM_STATE`, native launch allowlist | | ||
| | `context.js` | Shared mutable singleton (`mainWindow`, `ptyProcess`) | | ||
| | `state.js` | Deep-merge state engine — load, persist, normalize | | ||
| | `hardware.js` | Ed25519 hardware seal generation | | ||
| | `logging.js` | Session memory log + proof-of-operation log | | ||
| | `pty.js` | Pseudo-terminal lifecycle management | | ||
| | `window.js` | BrowserWindow creation, CSP headers, permission handler, shell resolution | | ||
| | `ipc.js` | All IPC handlers (system, fs, vpn, pod, shell, state) | | ||
|
|
||
| The entry point `main.desktop.js` is a 51-line orchestrator that wires these modules together and manages the app lifecycle. | ||
|
|
||
| ### Renderer API | ||
|
|
||
| `preload.js` exposes `window.neuralos` with eight namespaces via `contextBridge`: | ||
|
|
||
| - **shell** — `getMode()`, `switch(mode)`, `execute(cmd)`, `onMemoryUpdate(cb)` | ||
| - **core** — `getSeal()` | ||
| - **fs** — `ls(dir)`, `verify(path)`, `vaultMove(src, dest)` | ||
| - **vpn** — `start(config)`, `stop()`, `status()` | ||
| - **pod** — `start()`, `stop()`, `status()` | ||
| - **state** — `get()`, `set(patch)`, `onUpdate(cb)` | ||
| - **pty** — `send(data)`, `onData(cb)`, `resize(cols, rows)` | ||
| - **system** — `audit()`, `launch(appPath)`, `metrics()` | ||
|
|
||
| ### Security | ||
|
|
||
| - `sandbox: true`, `contextIsolation: true`, `nodeIntegration: false`, `webSecurity: true` | ||
| - Content-Security-Policy injected via `session.webRequest.onHeadersReceived` — restricts to `self`/`file:` origins | ||
| - `setPermissionRequestHandler` denies all permissions except clipboard | ||
| - Native launch targets are allowlisted (no shell injection) | ||
|
|
||
| ### Scripts | ||
|
|
||
| | Script | Description | | ||
| | ---------------------- | ----------------------------------------- | | ||
| | `npm start` | Launch the app in development mode | | ||
| | `npm run proof:lint` | Run ESLint with auto-fix | | ||
| | `npm run test:e2e` | Run single smoke test (xxxplorer) | | ||
| | `npm run test:e2e:all` | Run all 9 spec files (23 tests) | | ||
| | `npm run build:win` | Build portable Windows exe | | ||
| | `npm run rebuild` | Rebuild node-pty for current Electron ABI | | ||
|
|
||
| ### Shell Hot-Swap Behavior | ||
|
|
||
| Shell mode is persisted through the state engine and resolved by `modules/window.js` before the renderer loads. `window.neuralos.shell.switch(mode)` accepts `winshadow`, `neuralmac`, and `neurallinux`; invalid modes should fail without mutating persisted state. When a shell bundle is missing or fails to load, the expected recovery path is to keep the last valid shell mode and surface the failure through the renderer/devtools rather than silently switching modes. | ||
|
|
||
| ### Tests | ||
|
|
||
| Nine Playwright E2E spec files in `tests/`: | ||
|
|
||
| | Spec | Coverage | | ||
| | ------------------------------- | --------------------------------------------- | | ||
| | `xxxplorer.spec.ts` | Vault move integrity | | ||
| | `xxxplorer-preferences.spec.ts` | Theme and root persistence | | ||
| | `state-persistence.spec.ts` | State persistence across launches | | ||
| | `winshadow-draft.spec.ts` | Command draft persistence | | ||
| | `native-launch.spec.ts` | Allowlist enforcement + injection blocking | | ||
| | `shell-hotswap.spec.ts` | Shell mode, execute, switch, hot-swap | | ||
| | `system-metrics.spec.ts` | RAM/CPU/battery metrics, audit lifecycle | | ||
| | `vpn-pod-ipc.spec.ts` | VPN and Pod start/stop/status | | ||
| | `memory-engine.spec.ts` | State get/set/deep-merge, event subscriptions | | ||
|
|
||
| ### Native Modules | ||
|
|
||
| Five Rust NAPI addons (`.node` binaries) ship pre-built: | ||
|
|
||
| - `packages/core/neuralpod_core/index.node` — Pod lifecycle | ||
| - `packages/core/seal_pulse/index.node` — Seal pulse engine | ||
| - `packages/core/trustctl/index.node` — Hash verification | ||
| - `packages/core/vaultfs/index.node` — Vault filesystem + TPM enclave | ||
| - `packages/modules/vipn/rust/index.node` — VPN bridge | ||
|
|
||
| These use N-API (stable ABI) and don't require per-Electron-version rebuilds. `node-pty` is the only native dependency that needs `npm run rebuild` after Electron upgrades. | ||
|
|
||
| ### Native Module Rebuild Troubleshooting | ||
|
|
||
| - If Electron starts but PTY features fail, run `npm run rebuild` from `NeuralOS_Master_Build` and restart the app. | ||
| - If rebuild fails on Windows, confirm the Visual Studio C++ build tools and Python are available on `PATH`. | ||
| - If the failure only appears after an Electron upgrade, delete `node_modules`, reinstall with `npm install`, then run `npm run rebuild`. | ||
| - If packaging on a Windows machine without Visual Studio C++ build tools and verified `node-pty` prebuilds already exist, use `npx electron-builder --windows portable --config.npmRebuild=false`, then run `npm run test:packaged`. | ||
| - The Rust NAPI addons are pre-built and should not need per-Electron rebuilds; investigate missing `.node` files or packaging paths before rebuilding those crates. | ||
|
|
||
| ### Manual QA Checklist | ||
|
|
||
| - WinShadow: launch with `npm start`, verify command draft persistence, native launch allowlist behavior, and shell switching controls. | ||
| - NeuralMac: switch from WinShadow, confirm the desktop renders, state persists, and switching back does not reset the shell mode. | ||
| - NeuralLinux: switch from WinShadow or NeuralMac, verify terminal UI load, PTY input/output, and state persistence across restart. | ||
| - XXXplorer: verify vault move integrity, root preference persistence, and theme preference persistence. | ||
| - System layer: verify `system.audit()`, `system.metrics()`, VPN status transitions, and Pod status transitions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Release workflow uses Node 20 while CI tests with Node 22.12.0
The CI workflow (
forgecore-ci.yml:18) consistently usesnode-version: "22.12.0"for all three jobs, but the new release workflow (release.yml:21) usesnode-version: "20". The release workflow runs the same scripts (build:portable,verify:portable,verify:release,release_portable.cjs) that CI tests with Node 22. If any of these scripts use Node 22-specific APIs or behavior, the release build will fail even though CI passes. The release pipeline should use the same Node version that CI validates against.Was this helpful? React with 👍 or 👎 to provide feedback.