Skip to content

Fix build, deploy, and P2P discovery gaps found in P0/P1 audit - #2

Merged
XxHugheadxX merged 17 commits into
mainfrom
chore/p0-p1-fixes
Aug 25, 2026
Merged

XxHugheadxX merged 17 commits into
mainfrom
chore/p0-p1-fixes

Conversation

@XxHugheadxX

Copy link
Copy Markdown
Contributor

What changes

  • Build/CI pipeline moved fully to pnpm (npm dropped everywhere, including pear-build packaging); release artifact/binary name corrected from a leftover hello-pear-bare template name to towerbell.
  • bare-events and bare-fs declared as explicit dependencies (were resolving only by accident via hoisting).
  • The real fix for pnpm silently ignoring node-linker in .npmrc (pnpm >=10.6 only reads it from pnpm-workspace.yaml now) -- this was causing a streamx module-not-found failure at runtime, in both dev and the compiled binary.
  • peer-lost is now emitted by the real backend (on Hyperswarm connection close) and simulated by both mocks; the CLI TUI already expected it and had no source for it until now.
  • The OTA worker (workers/main.js) is a real implementation instead of the untouched hello-pear-worker boilerplate, with an explicit low delay (the boilerplate never set one, so it defaulted to up to 1h before checking an update) and correct error/progress event wiring, verified against the installed pear-runtime/pear-runtime-updater source.
  • pear.stage.ignore added to package.json: staging the repo root with no ignore list ships .git and node_modules whole (~1.8GB); trimmed to ~620MB. Also documented (docs/RELEASES.md) that this app's real distribution needs the by-arch deployment folder built by bare-build + pear-build, not a raw source stage -- confirmed live, staging the repo root does not work with pear install.
  • Scanner.stop() added (only Beacon had a stop method) and wired to graceful-goodbye, which was declared as a dependency but never used, so Ctrl+C now closes the swarm instead of leaving it for the OS.
  • English contract frozen explicitly in AGENTS.md; added FRONTEND_INTEGRATION.md (referenced but missing).
  • Fixed literal ? characters (confirmed plain ASCII corruption, not a display issue) in the CLI TUI, widened lint to cover the Bare-run frontend files it was missing.
  • Replaced the test/index.js placeholder with 4 real tests against Corestore/Hyperbee.

What was verified

  • pnpm run lint (prettier + lunte): clean.
  • pnpm test: 4/4 pass.
  • bare bin.mjs --fake scan/beacon: both run, peer-found and peer-lost fire end to end.
  • Two real instances (scan + beacon, separate storage, no --fake) discovered each other over real Hyperswarm.
  • pnpm run make: compiles to towerbell.exe, runs correctly, all modules resolve including streamx.
  • Full deploy cycle on a throwaway link (never the project's real upgrade link): bare-build + pear-build -> pear stage -> pear seed -> pear install -> ran the installed binary. Staging raw source was also tried and confirmed to fail the same way, before finding the by-arch fix.

CI and the build/release workflows ran on npm while .npmrc's node-linker=hoisted only means anything under pnpm, plus both package-lock.json and pnpm-lock.yaml were committed. Standardize everything (ci.yaml, build.yaml, scripts/make.js) on pnpm and drop the npm lockfile. Also fixes the release artifact/binary name in build.yaml, which still pointed at hello-pear-bare from the template instead of towerbell.
Both are required directly by backend/ and bin.mjs but were only resolving via hoisted transitive deps, which node-linker=hoisted can mask until the dependency graph shifts.
peer-lost was documented in AGENTS.md and already listened for in the CLI TUI, but no backend ever emitted it. DiscoveryDHT now tracks the announced key per connection and fires peer-left on close; Scanner turns that into peer-lost with the same hex id peer-found uses. Both mocks (backend/mock.js, frontend/backend/mock.js) simulate a beacon going out of range so --fake and the Expo demo exercise the same path.
workers/main.js only required the unmodified hello-pear-worker boilerplate, which never passes a delay to PearRuntime and so falls back to its library default of up to one hour before checking a detected update -- unusable for a live demo. Replaced it with a small worker written directly against the documented PearRuntime/IPC API (dir, version, upgrade, name, app, updates, delay), keeping the same 'updating'/'updated'/pear:applyUpdate message protocol App.js already expects. delay is set to 5s; raise it back up for a real multi-peer production deploy. Drops the now-unused hello-pear-worker dependency.
traveler.mjs had literal ASCII '?' where status glyphs/separators should be (confirmed the file itself is plain ASCII, not a rendering issue). Swapped in plain ASCII glyphs consistent with style.mjs, which never uses Unicode either. trade.mjs was checked too and was already fine. lunte now also covers frontend/cli and the frontend/*.mjs barrels, since those run under Bare via bin.mjs and were the only Bare-executed files not linted.
AGENTS.md now says explicitly that the Spanish names in planning docs are for human readability only and never go into code -- the implemented contract (scan/beacon, peer-found/peer-lost/status/visitor) is the frozen one. Added FRONTEND_INTEGRATION.md (referenced by AGENTS.md but missing) covering the contract, the two consumers (Pear CLI vs Expo mock), and the boundary rules. Also notes that frontend/src/store/app.store.js doesn't listen for peer-lost yet, for whoever owns frontend/src next.
Verified against the actually-installed pear-runtime@1.3.1 and pear-runtime-updater@3.4.0 source: PearRuntime swallows its own ready() rejection, so update-time errors (failed drive update, failed prefetch) are emitted on pear.updater, not on pear itself. The previous commit only listened on pear.
pnpm install after declaring the two missing deps explicitly. pnpm-workspace.yaml is new: pnpm's supply-chain policy flagged bare-fs@4.8.1 as too recently published and this records the explicit allow, not a manual choice.
pnpm >=10.6 stopped reading node-linker from .npmrc -- only auth/registry settings are still read there. The .npmrc setting was silently dead, pnpm fell back to isolated linking, and Bare's module resolver doesn't walk pnpm's .pnpm symlink structure the way Node's require() does. Result: streamx (a transitive dep of framed-stream) failed to resolve at runtime with MODULE_NOT_FOUND, both under bare bin.mjs and in the compiled binary -- reproduced and confirmed fixed by hand. Real fix is nodeLinker: hoisted in pnpm-workspace.yaml; kept .npmrc for anyone on an older pnpm.
Found live while testing the deploy pipeline end to end (stage -> seed -> install -> run): pear stage has no default ignore, so staging the repo root ships .git and node_modules whole (~1.8GB, confirmed by actually running it). package.json's pear.stage.ignore trims that to ~620MB by dropping .git, out/, the Expo app, and devDependencies (bare-build's cross-compile toolchains alone account for most of the removed size).

Bigger finding: staging the repo root does not work for pear install at all. towerbell has "bin": "bin.mjs" and OTA swaps the installed binary, which makes pear install look for /by-arch/<host>/app/<name> on the staged drive -- confirmed by reproducing the 'Not found' failure, then confirming a deployment folder built with bare-build + pear-build installs and runs correctly. Documented the real recipe in docs/RELEASES.md and linked it from README; the previous 'pear stage <link>' one-liner there was untested and wrong for this app's distribution model.
bin.mjs has had an app.on('updating-delta', ...) listener the whole time, but nothing ever sent that message -- confirmed pear.updater does emit it (one entry per mirrored file during an update) by reading the installed pear-runtime-updater source. Worker now forwards it as JSON on the same ad hoc string-message protocol the other control messages use; app.js parses it back out.
graceful-goodbye was declared in package.json but never required anywhere, and Scanner had no way to close its swarm on exit -- only Beacon did. Added stop() to the real Scanner and both mocks (mirroring Beacon.stop()), and wired graceful-goodbye in bin.mjs so Ctrl+C/SIGTERM closes the DHT swarm right away instead of leaving it for the OS to tear down.
Replaces the REMOVE ME placeholder with four real tests against Corestore/Hyperbee (not mocked): put/get roundtrip, overwrite, null when nothing was written, and getRemoteDb caching by key. All four run and pass under brittle-bare.
@XxHugheadxX
XxHugheadxX merged commit 235431f into main Aug 25, 2026
7 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.

1 participant