Skip to content

Pre-launch readiness: publish the docs site, fix onboarding blockers, cut the README - #229

Merged
Sev7eNup merged 3 commits into
mainfrom
chore/pre-launch-readiness
Aug 19, 2026
Merged

Pre-launch readiness: publish the docs site, fix onboarding blockers, cut the README#229
Sev7eNup merged 3 commits into
mainfrom
chore/pre-launch-readiness

Conversation

@Sev7eNup

Copy link
Copy Markdown
Owner

Prepares the repository for being promoted. Everything here answers one question: can a stranger find NodePilot, trust it, install it, and reach a first login without asking anyone?

I walked that path as an outsider — GitHub page → release → install → first login — and separately looked at what a lawyer, a security reviewer and a sceptical corporate admin would find.

Blockers this fixes

Desktop first run breaks under a separate admin account. The setup token was written to the elevated account's profile while the app looked in the interacting user's. When both are the same person — clicking "Yes" on the UAC prompt — it works, which is why it never showed up in the lab. Enter a different admin's credentials, the norm in any managed environment, and the token lands in the admin's profile, the app never finds it, and you face a login form for an account that does not exist. The only remaining copy is owned by SYSTEM, and opening its ACL carelessly invalidates the token permanently.

A failed desktop provisioning step reported success. The installer discarded the script's exit code. First symptom was an error from the app. The app also gave up after 120s while the provisioning script waits up to 180s.

The documented trust ceremony could not be performed. The deployment guide tells you to compare the publisher certificate's thumbprint — "that comparison is the trust decision" — but the .cer stopped being attached after 1.2.8, and even when present it was never covered by SHA256SUMS. It is now a build output, listed in the checksums. The install scripts ship as their own checksum-covered zip, so verifying no longer requires extracting the unverified archive first.

A real SSRF hole, found while cleaning up the roadmap. I had reported roadmap item 2 as "probably already fixed". Before striking it I read NormalizeAddress and found 0.0.0.0 and :: falling through every branch. Both reach the local host on Windows, and IPAddress.IsLoopback does not recognise them, so http://0.0.0.0:5000/ walked straight through RestApi:BlockPrivateNetworks. Fixed, with tests.

Legal and disclosure

  • Two easter eggs removed. One shipped a third-party photo of a real person in every build — server zip and desktop installer — with no license. The other was a tombstone/obituary for a Microsoft product, reachable anywhere in the app via the Konami code.
  • The sparkasse skin is renamed to bank. Visible labels were already neutral; the internal name and a "deliberate corporate fidelity" note were not.
  • The public roadmap no longer names file and class locations for open security work, and its open "is a password in the git history?" question is answered — all 433 commits scanned, only dummy fixtures.
  • SECURITY.md added, including a scope section so intended behaviour (operator trust, the localhost bypass, self-signed signing) is not reported as a vulnerability.

Documentation site

Published to GitHub Pages, and bilingual: all 42 pages exist in English and German, language in the route (#/en/…, #/de/…), a switcher, browser-language detection, and search over the active language. A parity test fails the build if a page or nav title lands in only one language — the two half-states fail differently and neither is obvious to whoever wrote the page.

The site had no test suite. It now has 23 tests covering language detection, the router's path split and corpus parity, wired into CI.

Only content/en feeds the in-product AI knowledge assistant. Embedding both languages would double its token cost for no gain — the model answers in the asker's language regardless.

Public surface

README: 1428 lines → 545. Its feature inventory duplicated the documentation site, so it links there instead. The SCOrch import path — the strongest reason for a System Center Orchestrator user to look at this at all — moves from undocumented to a section of its own with a comparison table. It reads .ois_export from 2012/2016/2019, maps 13 activity types, carries links and conditions across, and rewrites Published Data references into the data bus. Nothing is dropped silently.

Also: CHANGELOG.md, CODE_OF_CONDUCT.md, RELEASING.md, CODEOWNERS; 56 broken links fixed; a stale branch-name column removed; E2ETests.md moved out of the repo root (with its gitleaks allowlist path); eight pinned action SHAs relabelled with the version they actually are (# v4 on a v7.0.1 commit); screenshots retaken in the default dark skin.

Desktop build

The bundled PostgreSQL major version is asserted by reading the binary rather than trusting a path — a 17.x payload produced an installer that failed against every existing database. The desktop shortcut is now an opt-out task.

Supersedes #227

#227 documents that the desktop package ships tools\np and tools\mcp. It touches content/deployment/desktop.md, a path this branch restructured into content/{de,en}/, so it cannot merge cleanly. Its content is folded in here, in both languages. #227 can be closed.

Verification

23/23 docs-site tests · 13/13 DocumentationCountsTests · 3/3 PackageVersionParityTests · deployment template contracts · lint/tsc/build clean · 0 broken relative links repo-wide (from 56) · the built site served under the real /NodePilot/ prefix and clicked through in a browser, both languages, zero failed resources.

One manual step remains

Pages source is already set to GitHub Actions, so the deploy fires on merge. Repository topics, the social preview image and homepageUrl still need setting.

…ers, cut the README

Prepares the repository for being promoted. Everything here is aimed at one question:
can a stranger find NodePilot, trust it, install it, and get to a first login without
asking anyone?

Legal and disclosure
- Remove two easter eggs, one of which shipped a third-party photo in every build
  (server zip and desktop installer alike) under no license.
- Rename the `sparkasse` skin to `bank` across the codebase. The user-visible labels were
  already neutral; the internal name and a "corporate fidelity" note were not.
- Stop naming file and class locations for open security work in the public roadmap, and
  close its open "is a password in the git history?" question — all 433 commits scanned,
  only dummy fixtures.
- Add SECURITY.md with a private reporting path and an explicit scope section, so
  intended behaviour (operator trust, the localhost bypass, self-signed signing) is not
  reported as a finding.

Security fix found while doing the above
- NetworkGuard treated the unspecified addresses 0.0.0.0 and :: as public. Both reach the
  local host on Windows, and IPAddress.IsLoopback does not recognise them, so
  http://0.0.0.0:5000/ walked straight through RestApi:BlockPrivateNetworks.

Onboarding blockers
- Desktop first run under a separate admin account: the setup token went to the elevated
  account's profile while the app looked in the interacting user's. Anyone installing with
  different admin credentials — the norm in managed environments — reached a login form for
  an account that did not exist, with no documented way out.
- A failed desktop provisioning step reported success; the installer now fails and names
  the log. The app's readiness timeout (120s) was below the provisioning script's own
  (180s) and now sits above it.
- The publisher certificate is a build output covered by SHA256SUMS instead of a manual
  upload that was forgotten after 1.2.8 — and was never checksum-covered even when present.
- The install scripts ship as their own checksum-covered zip, so verifying no longer
  requires extracting the unverified archive first.
- Version placeholders replace hardcoded versions in copy-paste commands; the three npm
  manifests move from 1.2.5 to the product version, guarded by a new parity test.

Documentation site
- Published to GitHub Pages, and made bilingual: all 42 pages exist in English and German
  with the language in the route, a switcher, browser-language detection, and search over
  the active language. A parity test fails the build if a page or nav title lands in only
  one language. The site had no test suite; it now has 23 tests, wired into CI.
- Only content/en feeds the in-product AI knowledge assistant — embedding both languages
  would double its token cost for no gain.

Public surface
- README: 1428 lines to 545. Its feature inventory duplicated the documentation site, so
  it links there instead. The SCOrch import path — the strongest reason for a System Center
  Orchestrator user to look at this at all — moves from undocumented to a section with a
  comparison table.
- Add CHANGELOG.md, CODE_OF_CONDUCT.md, RELEASING.md and CODEOWNERS.
- Fix 56 broken relative links, retire a stale branch-name column, move E2ETests.md out of
  the repository root, and label eight pinned action SHAs with the version they actually are.
- Screenshots retaken in the default dark skin; the old ones showed a non-default one.

Desktop build
- Assert the bundled PostgreSQL major version by reading the binary rather than trusting a
  path. A 17.x payload produced an installer that failed against every existing database.
- The desktop shortcut is now an opt-out task rather than unconditional.
…al path

The secret scan walks every reachable commit. E2ETests.md sat at the repository root from
1.0.0 until this branch moved it under docs/testing/, and history keeps the old path
forever — so re-anchoring the allowlist to the new path alone un-allowlisted the documented
X-Api-Key placeholder in every historical commit and failed the scan. Match both.
{
public static TheoryData<string> NpmManifests() => new()
{
Path.Combine("src", "nodepilot-ui", "package.json"),
public static TheoryData<string> NpmManifests() => new()
{
Path.Combine("src", "nodepilot-ui", "package.json"),
Path.Combine("src", "nodepilot-desktop", "package.json"),
{
Path.Combine("src", "nodepilot-ui", "package.json"),
Path.Combine("src", "nodepilot-desktop", "package.json"),
Path.Combine("src", "nodepilot-docs-ui", "package.json"),
public void NpmManifest_DeclaresTheProductVersion(string relativePath)
{
var root = FindRepoRoot();
var manifestPath = Path.Combine(root, relativePath);

private static string ProductVersion(string root)
{
var props = File.ReadAllText(Path.Combine(root, "Directory.Build.props"));
private static string FindRepoRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "NodePilot.slnx")))
MonitoringDeploymentSecurityTests requires the root README to state that the monitoring
Compose stack refuses to start without NODEPILOT_GRAFANA_ADMIN_PASSWORD rather than coming
up on a default credential. Trimming the README dropped the sentence carrying that; the
observability highlight now carries it, which keeps the claim where a casual reader meets
the Grafana stack instead of only in grafana/README.md.
@Sev7eNup
Sev7eNup merged commit 332cc30 into main Aug 19, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants