Skip to content

Self-hosting: editor silently fails with "Invalid share token" when COLLAB_EMBEDDED_WS is unset; missing dist/ static route #43

@laurencenorton-tc-markets

Description

Summary

When running the SDK standalone via npm run serve (single-process mode), the
editor surfaces [initFromShare] Failed: Error: Invalid share token and the
document area stays blank after the display-name dialog. Two underlying issues
contribute:

  1. The COLLAB_EMBEDDED_WS=1 environment variable is required for correct
    websocket URL generation in single-process deployments, but this isn't
    documented in the README and isn't the default. Without it, the client
    constructs a websocket URL for a port that nothing is listening on, the
    WS handshake fails, the collab runtime can't establish, and the UI surfaces
    this as "Invalid share token" — which sent me on an extended journey
    debugging token validation that had nothing to do with the problem.

  2. The server serves public/ as static content but not dist/, so
    /assets/editor.js 404s after npm run build. The HTML loads, references
    an absent bundle, and renders "Loading editor..." indefinitely. The
    server/share-web-routes.ts handler does a dev-friendly "Editor not
    built. Run: npm run build" error when it can't find dist/index.html,
    but once the build exists, the companion static route for assets is
    missing. I believe this is a gap from the open-source extraction from
    your hosted product, where a CDN or reverse proxy presumably serves
    /assets/* separately.

Environment

  • macOS 26.4.1, Node v25.8.1, fresh npm install on commit
    fb2578758f1c62776301209131181643c5f4a19a
  • Single-machine deployment, no reverse proxy, no separate collab process

Suggested resolutions

For (1): either default COLLAB_EMBEDDED_WS to 1 when no external
collab host is configured (auto-detect mode), or document the requirement
prominently in the README's self-host section. The error surface could also
distinguish "websocket handshake failed" from "token rejected" in the UI —
the current generic "Invalid share token" message is misleading when the
actual issue is networking.

For (2): add app.use(express.static(path.join(__dirname, '..', 'dist')))
alongside the existing public/ static mount in server/index.ts. Single
line change.

Related ergonomic note

server/index.ts currently calls server.listen(PORT) with no host argument
and logs [proof-sdk] listening on http://127.0.0.1:${PORT}. Per the Node
docs, omitting the host binds to :: (all interfaces) on dual-stack systems,
not loopback. Self-hosters wanting to restrict the bind have no supported
knob, and the log message doesn't match reality. Adding a HOST env var
parallel to the existing PORT pattern would address both. I have this
change in my fork and am happy to PR it alongside a fix for (2) if useful.

Local fork with patches

laurencenorton-tc-markets@8083282

Single commit addressing issue (2) and the bind host ergonomic note. Not
addressing issue (1) yet — that one has a design question about whether to
change the default or document the flag, and I'd value your take first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions