On this page
wesktop application for PixelWeaver collaboration server.
#server.src.pixelweaver.main
#server.src.pixelweaver.main
wesktop application for PixelWeaver collaboration server.
Provides REST endpoints for project management and a WebSocket endpoint for the real-time server-authoritative protocol.
#lifespan
async def lifespan(scope)Startup/shutdown lifecycle: load projects from disk, start auto-saver.
#health_check
async def health_check(req: Request)Health check endpoint.
The payload names the application and its version, not just liveness: any HTTP service can answer {"status": "ok"}, so a client that merely finds something listening on the expected port cannot tell a PixelWeaver backend from an unrelated app that happens to hold it. Tooling (the e2e suite) probes here and refuses to drive a server that does not identify itself.
#CreateProjectRequest
#name_must_be_safe
def name_must_be_safe(cls, v: str) -> strReject names that could escape the project data directory.
#list_all_projects
async def list_all_projects(req: Request)List all projects.
#create_project
async def create_project(req: Request)Create a new project.
#get_project
async def get_project(req: Request)Get project metadata.
#delete_project
async def delete_project(req: Request)Delete a project from memory (does not delete files on disk).
#activate_project
async def activate_project(req: Request)Make the named project active and repaint every connected client.
Switching the active project is navigation, not a content mutation: it goes through the same validate-and-set path as the MCP open_project tool (state.set_active_project), then broadcasts a full-state patch over the live WebSocket path so all clients repaint to the newly-active project (pixels, layers, canvas).
#export_png
async def export_png(req: Request)Export a frame as a composited PNG.
#export_part_manifest
async def export_part_manifest(req: Request)Export the part-manifest document (G8) as JSON.
#export_part_strip
async def export_part_strip(req: Request)Export a single per-part PNG strip (kind = indexed | rgba).
#export_parts_zip
async def export_parts_zip(req: Request)Export part-manifest.json + all strips as a deterministic zip.
#get_full_state
async def get_full_state(req: Request)Return the full serialized server state for MCP sync.
The MCP server calls this before executing a tool so it operates on the latest state held by the collab server.
#sync_state_from_mcp
async def sync_state_from_mcp(req: Request)Accept full state from the MCP server and broadcast changes.
After the MCP server executes a tool, it pushes the modified state here. The collab server overwrites its in-memory state and broadcasts a full-state patch to every connected WebSocket client so the frontend sees the mutation.
#_on_dirty
def _on_dirty() -> NoneCallback passed to message handler to mark state as dirty.
#websocket_endpoint
async def websocket_endpoint(ws: WebSocket)WebSocket endpoint for the server-authoritative protocol.
#make_app
def make_app(*, serve_static: bool=True)Build the PixelWeaver ASGI app.
Production, desktop, and plain serve mode serve the built SPA from dist/ on the same origin as the API. Dev mode (pixelweaver dev) instead lets Vite serve the sources and wraps this app in fastware's ViteDevProxy, which routes /api and /ws to the backend and proxies everything else (assets, HMR) to Vite. That proxy uses a backend-first strategy: a non-API request only falls through to Vite if the backend returns 404. If static/SPA serving were left on, the app would answer every non-API GET with the stale dist/index.html (a 200), so the fallthrough would never reach Vite and HMR would break. Pass serve_static=False for the dev app to disable static/SPA serving.