Skip to content

feat: offline mode and static GitHub Pages build#158

Closed
madjin wants to merge 21 commits intohyperfy-xyz:devfrom
madjin:dev
Closed

feat: offline mode and static GitHub Pages build#158
madjin wants to merge 21 commits intohyperfy-xyz:devfrom
madjin:dev

Conversation

@madjin
Copy link
Copy Markdown
Contributor

@madjin madjin commented Mar 18, 2026

Summary

  • Add offline mode (?mode=offline) for fully client-side operation
  • Add static build for GitHub Pages deployment (npm run static:build)
  • Add connection policy layer with direct mode (?connect=ws://...) for localhost
  • Make PUBLIC_WS_URL optional on server (HTTP-only mode with warning)
  • Sanitize WebSocket URLs to prevent auth token leakage
  • Add connection UI in sidebar with status display

Non-breaking

Existing deployments with current .env settings behave identically. All new features are additive.

Depends on #159 (maintenance/docs changes split out for easier review).

Test plan

  • npm run dev — server starts, world loads, multiplayer works
  • ?mode=offline — client boots without WebSocket
  • npm run static:build — produces working offline client in build/static/
  • Remove PUBLIC_WS_URL — server starts HTTP-only with warning
  • ?connect=ws://localhost:3000/ws — direct connect works on localhost

🤖 Generated with Claude Code

madjin and others added 19 commits February 5, 2026 16:20
Introduce a runtime connection policy resolver so networking mode
(direct-connect vs offline) is decided at boot without touching any
shared system callsites. OfflineNetwork is a null-object that
satisfies the ~70 world.network access sites, and PUBLIC_WS_URL
is now optional on the server side.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a subtle status dot (bottom-right) that expands into a panel
showing connection mode, network ID, server URL input, and
connect/offline/reset actions. Register /connect, /offline, and
/reconnect chat slash commands via a new ClientConnection system.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add `npm run static:build` for self-contained GitHub Pages deployment
- Copy default world assets (avatar, emotes, animations) into static output
- GitHub Actions workflow for auto-deploying to Pages
- Offline bootstrap now spawns a local PlayerLocal with avatar, camera,
  and full controls instead of an empty black screen
- Set assetsUrl so asset:// protocol resolves in offline mode
- Load base-environment.glb and HDR sky in offline mode
- ConnectionBadge: contextual buttons (hide "Go Offline" when already
  offline, highlight primary action green)
- ConnectionBadge: auto-expand on connection failure so user can retry
- CoreUI: hide loading overlay on pre-ready disconnect instead of
  staying stuck forever
- CoreUI: gate player-dependent UI on `ready && player` to prevent
  null crashes in offline mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse connect URLs with URL constructor and rebuild as origin+pathname
only, stripping any query params or fragments. A crafted URL like
ws://evil.com/ws?steal= would cause ClientNetwork.init() to append
?authToken=<token> to the attacker's query string.

Applied consistently in all three input paths: resolveConnectionMode,
ConnectionBadge UI, and /connect chat command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the bottom-right floating ConnectionBadge dot with a wifi
button in the sidebar icon column. Panel pops out to the right like
other panes, shows server URL input, online/offline status with ping,
and a connect/disconnect toggle. Emits ping event from ClientNetwork
so the panel can display live latency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…atic build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ng files

- Merge /connect, /offline, /reconnect commands into ClientNetwork.init()
- Inline resolveConnectionMode logic directly in client/index.js
- Wire navigateToServer helper into Sidebar.handleConnect
- Delete ClientConnection.js and resolveConnectionMode.js

Net result: 3 new files → 1 (OfflineNetwork.js remains).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminate the null-object class by handling the no-wsUrl case directly
in ClientNetwork.init(). Four small guards replace the entire separate
file, and the NetworkSystem override param is removed from createClientWorld.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Triggers on pushes to main (live preview) and feat/connection-policy-layer
(in-flight preview). Uses actions/deploy-pages — configure Pages source via
Settings → Pages → Source: GitHub Actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switches from actions/deploy-pages artifact approach to peaceiris/actions-gh-pages,
which commits only the built files to the gh-pages branch. Trigger: push to dev.

Configure GitHub Pages: Settings → Pages → Deploy from branch → gh-pages → / (root)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: connection policy layer for offline/solo mode
* ci: bump all GitHub Actions dependencies to latest major versions

actions/checkout v4 → v6
actions/setup-node v4 → v6
docker/login-action v3 → v4
docker/metadata-action v5 → v6
docker/setup-qemu-action v3 → v4
docker/setup-buildx-action v3 → v4
docker/build-push-action v6 → v7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: resolve static assets correctly on GitHub Pages sub-paths

Compute an absolute base URL (new URL('./', location.href)) for offline
mode so bare filenames and /absolute paths don't break when the site is
served at a sub-path like /hyperfy/. Passes assetsUrl into world.init so
updateSky fires with the correct HDR URL. Removes redundant explicit HDR
load since ClientEnvironment handles it via baseEnvironment.hdr.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.css: url(/rubik.woff2) → url(rubik.woff2) — absolute path broke
  on sub-paths like /hyperfy/
- world-client.js: restore explicit scene.background = hdr for offline mode;
  ClientEnvironment.updateSky only sets scene.environment (reflections),
  not the visible sky background

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…st-xml-parser)

- npm audit fix: updated fastify, flatted, minimatch, ajv, brace-expansion
- Added fast-xml-parser override to ^5.5.6 to fix AWS SDK transitive vuln
- Remaining: esbuild dev-server advisory (requires breaking upgrade, skipped)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Not intended for upstream; skill lives outside the project.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…line)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@madjin madjin changed the base branch from main to dev March 18, 2026 04:47
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@madjin madjin changed the title feat: connection policy layer, security hardening, and static GH Pages deploy feat: offline mode and static GitHub Pages build Mar 31, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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