Player Deploy Plan
Deploy PathMX sites to Cloudflare at *.path.app using:
SpaceDOper subdomain for mutable site state, deploy history, rollback, and live events- Worker for request routing and publish endpoints
R2for immutable content-addressed artifacts
Architecture
Components
SpaceDO
Owns all mutable per-site concerns:
- active site state
- deploy history
- publish commit serialization
- rollback
/_eventswebsocket clientsChangeEventbroadcast
Worker
Owns stateless request handling:
- parse host / subdomain
- route
/_eventsto the correctSpaceDO - load active state from
SpaceDO - cache parsed routes and manifests by hash
- fetch blobs from
R2 - expose publish and rollback APIs
R2
Stores immutable blobs by content hash:
- HTML
- JSON
- CSS
- JS
- images
- routes JSON
- manifest JSON
CLI
Owns local build and remote publish operations:
pmx publishpmx publish --rollback
State
type SpaceState = {
deployId: string
roots: BuildRootsJSON
updatedAt: string
}
type DeployRecord = {
deployId: string
roots: BuildRootsJSON
createdAt: string
meta?: {
commit?: string
branch?: string
}
}
Event Contract
Production should emit the same event shapes the player already expects:
type ChangeEvent =
| { type: "sources-changed"; sources: string[] }
| { type: "artifacts-changed"; paths: string[] }
Endpoint:
/_events
Events are scoped by subdomain through SpaceDO(subdomain).
Serving Flow
- Worker parses subdomain from host.
- Worker resolves
SpaceDO(subdomain). - Worker reads active
SpaceState. - Worker loads routes and manifest from
R2using hashes fromroots. - Worker reuses or creates a cached
PathMXServerkeyed byroutesHash + manifestHash. PathMXServerresolves the request and fetches the target blob fromR2.- Worker returns the response with the correct headers.
Cache Rules
- cache parsed routes and manifests in memory by hash
- do not cache mutable site state by subdomain without checking
deployId - stable route URLs like
/lecture-1:ETag+Cache-Control: public, max-age=0, must-revalidate - hashed asset URLs:
Cache-Control: public, max-age=31536000, immutable
Publish Flow
pmx publishbuilds locally.- CLI reads manifest and roots.
- CLI calls
POST /api/publish/beginwithsubdomainand referenced hashes. - Worker checks which hashes already exist in
R2. - CLI uploads only missing blobs.
- CLI calls
POST /api/publish/commitwithsubdomainand newBuildRootsJSON. - Worker validates auth and artifact existence.
- Worker forwards the commit to
SpaceDO(subdomain). SpaceDOstores a newDeployRecord, updates activeSpaceState, and broadcasts change events.- Worker returns the live URL.
Publish Rules
- upload content blobs before commit
- upload routes/manifest blobs before commit
- make the site live only in
SpaceDO.commitPublish() - emit websocket events only after the active state changes
Rollback Flow
pmx publish --rollback <deployId>callsPOST /api/publish/rollback.- Worker validates auth and resolves
SpaceDO(subdomain). SpaceDOloads the targetDeployRecord.SpaceDOmakes that deploy active.SpaceDObroadcasts the same change events used for publish.- Worker returns the live URL.
Rollback does not rebuild or re-upload artifacts.
API Surface
POST /api/publish/begin
POST /api/publish/commit
POST /api/publish/rollback
GET /api/deploys?subdomain=...
GET /_events
SpaceDO Interface
type SpaceDOApi = {
getActiveState(): Promise<SpaceState | null>
commitPublish(input: {
roots: BuildRootsJSON
meta?: DeployRecord["meta"]
events: ChangeEvent[]
}): Promise<SpaceState>
listDeploys(): Promise<DeployRecord[]>
rollbackDeploy(input: {
deployId: string
events: ChangeEvent[]
}): Promise<SpaceState>
connectEvents(request: Request): Promise<Response>
}
Package Layout
Worker-safe shared exports
Worker-safe subpaths that avoid Bun/Node dependencies:
@pathmx/core/shared -> packages/core/src/shared/index.ts
@pathmx/server/shared -> packages/server/src/shared/index.ts
@pathmx/core/shared exports:
Router,ResolvedRoute- route types (
Routes,SourceRoute,Redirect, etc.) ManifestData,ManifestFileBuildRootsJSON,BuildRootChangeEvent,SourceGraphData,GraphSourceEntry- base types (
SourceLink,SourceMeta, etc.)
@pathmx/server/shared exports:
PathMXServer,Server,ServerConfigPathMXStorageresolveRequest,notFound,redirect
Worker project
apps/server/
wrangler.toml -- R2 + SpaceDO bindings
worker-configuration.d.ts -- generated Env types (wrangler types)
tsconfig.json -- extends root, includes worker-configuration.d.ts
src/
worker.ts -- Worker fetch entry, subdomain parsing, routing
space-do.ts -- SpaceDO Durable Object
server.ts -- CloudflareServer (PathMXServer wrapper)
storage.ts -- R2Storage (PathMXStorage backed by R2)
publish.ts -- publish API handlers
Tasks
See pathmx-app-server.tasks.md for implementation checklist.
Later
- preview deploys
- custom domains
- Cloudflare Images-backed image routes
- richer deploy metadata