Synthex is a multi-service cloud IDE and project execution platform. It lets a user:
- sign up or log in with email/password or GitHub OAuth
- create projects from templates, blank language presets, raw workspaces, GitHub repos, or ZIP uploads
- provision an isolated Docker-backed runtime for each project
- edit files through the browser with autosave-backed object storage
- run one-shot commands and stream terminal output live
- start long-running preview servers and expose them through the gateway
- keep project file state, snapshots, execution history, and runtime metadata in sync across services
This repository is a PNPM/Turbo monorepo with two apps, five backend services, and several shared packages.
.
├── apps
│ ├── api_gateway # HTTP/WebSocket edge, auth enforcement, docs aggregation, preview proxying
│ └── web # React frontend
├── services
│ ├── user-service # auth, user profile, GitHub OAuth token management
│ ├── project-service # project CRUD, import orchestration, lifecycle state
│ ├── storage-service # file index, file content persistence, snapshots, ZIP validation/manifests
│ ├── container-service # Docker container lifecycle, terminal, execution, preview runtime
│ └── execution-service # execution records, preview records, replay buffers, cleanup
├── packages
│ ├── database # Prisma client, Redis/MinIO helpers, shared repositories
│ ├── templates # language registry, template registry, import detection helpers
│ ├── openapi # shared OpenAPI components
│ ├── eslint-config
│ └── typescript-config
├── scripts
│ └── docker # runtime preparation helpers
├── docker-compose.yml # local/full stack orchestration
└── package.json # workspace scripts
Browser client built with React, Vite, TanStack Router, Zustand, Axios, Socket.IO, Monaco, and Xterm.
Single public edge for HTTP APIs, Swagger docs aggregation, WebSocket fan-out, terminal proxying, and dynamic preview reverse proxy registration.
Owns users, credential auth, refresh token flow, and GitHub OAuth account linkage/token retrieval.
Owns project records, import metadata, runtime config, start/stop orchestration, and project lifecycle status.
Owns project file metadata/content persistence, MinIO-backed file storage, snapshot indexing, and ZIP manifest generation.
Owns Docker containers, startup/install sequences, terminal sessions, execution runtime, preview runtime, snapshots, and reconciliation of storage mutations into containers.
Owns execution history and preview session metadata, and coordinates replay/buffer lifecycle around running commands.
Source of truth for:
- users
- OAuth accounts
- projects
- project files
- project snapshots
- container logs
- execution logs
Used for:
- pub/sub event bus between services
- execution output buffers
- execution ownership metadata
- setup log buffers and setup progress
- transient ZIP upload ownership/session keys
- preview target registration
- cleanup timers and other short-lived coordination state
Used for:
user-filesproject-filesproject-zipsexecution-outputsproject-snapshots
The ZIP import flow uses direct browser upload with presigned URLs. The browser uploads to MinIO, then the storage service validates the uploaded object and generates the manifest used for import detection.
The container service talks to the host Docker daemon through /var/run/docker.sock and provisions per-project containers named synthex-<projectId>.
The Prisma schema in packages/database/prisma/schema.prisma defines the main records:
User: identity and profileOAuthAccount: external provider linkage and stored GitHub access token/scopeProject: lifecycle state, import metadata, runtime config, template/language selectionProjectFile: per-file metadata plus inline content for small filesProjectSnapshot: snapshot index for tar/tar.gz captures of project stateContainerLog: container/system log storageExecutionLog: one-shot command and dev-server execution history
All browser API calls go through the gateway:
/api/auth/*→ user-service/api/users/*→ user-service/api/projects/*→ project-service/api/execution/*→ execution-service/api/storage/*→ storage-service/terminal/*→ proxied WebSocket path to container-service/preview/:projectId/*→ dynamic runtime preview proxy registered by the gateway
Important channels used across the system:
project:created,project:start,project:stop,project:deletecontainer:status,container:timeoutcontainer:setup:log,container:setup:stagefiles:snapshot,fs:change,storage:file:mutation,storage:file:list-changedexecution:start,execution:input,execution:kill,execution:output,execution:status,execution:donepreview:start,preview:ready,preview:error,preview:status,preview:output,preview:stopuser:cleanup
- Frontend calls
/api/auth/signupor/api/auth/login. - Gateway forwards to user-service.
- User-service validates credentials, returns access token, and sets refresh token cookie for login flow.
- Frontend stores the access token in the auth store and uses it for subsequent requests and socket auth.
- Browser opens
/api/auth/github. - User-service drives the GitHub OAuth handshake.
- Callback stores/updates the user and
OAuthAccount. - Refresh token is set as an HTTP-only cookie.
- Browser is redirected to
/auth/callback#token=<accessToken>. - Frontend hydrates auth state and loads the dashboard.
- Frontend posts to
/api/projects. - Project-service creates the project with
containerStatus=pending. - Project-service publishes
project:created. - Container-service receives the event, creates/starts the runtime, scaffolds the workspace, optionally installs dependencies, snapshots the result, and publishes container status/setup log events.
- Project-service updates the project’s
containerStatuswhencontainer:statusevents arrive. - Gateway fans the status/log events to the user’s socket rooms.
- Frontend calls
/api/projects/import/github/detect. - Project-service reads GitHub metadata/tree and uses
@synthex/templatesdetection helpers. - Frontend submits
/api/projects/import/github. - Project-service creates the project with GitHub import metadata and publishes
project:created. - Container-service clones the repo during setup and completes the rest of the runtime flow.
- Frontend requests
/api/storage/upload/zip/init. - Storage-service returns a
zipKeyand presignedPUTURL. - Browser uploads the ZIP directly to MinIO.
- Frontend calls
/api/storage/upload/zip/complete. - Storage-service validates the object, enforces ZIP safety limits, extracts manifest info, and returns
filePathsplus selected config file contents. - Frontend posts the manifest to
/api/projects/import/zip/detect. - Frontend creates the imported project with
/api/projects/import/zip. - Project-service stores
zipKey, publishesproject:created. - Container-service streams the ZIP from MinIO directly into the container filesystem and unzips it without buffering the full archive in Node memory.
- Editor updates local state in the web app.
- File save calls go to
/api/storage/files/:projectId. - Storage-service writes file content to MinIO and updates
ProjectFilerows. - Storage-service publishes mutation events.
- Container-service consumes those events and applies the changes inside the running container.
- Gateway emits filesystem refresh/mutation events to the browser so UI state stays aligned.
- Frontend posts
/api/execution. - Execution-service creates an execution record and publishes
execution:start. - Container-service starts the command in the project container.
- Output is streamed over pub/sub and buffered in Redis.
- Gateway relays chunks to socket clients in the execution room.
- Container-service publishes
execution:done. - Execution-service finalizes the execution record and clears locks/buffers as needed.
- Frontend posts
/api/execution/preview. - Execution-service creates the preview record and publishes
preview:start. - Container-service launches the dev server inside the project container and discovers the host port.
- Container-service publishes
preview:ready. - Execution-service stores preview metadata and publishes
preview:status. - Gateway registers a dynamic reverse proxy and emits preview status to the browser.
- Browser opens the preview via gateway-managed
/preview/:projectId/....
- Gateway tracks active sockets per user.
- When the last socket disconnects, a delayed cleanup timer starts.
- After the delay, the gateway publishes
user:cleanup. - Container-service tears down the user’s containers.
- Project-service marks affected projects as stopped.
Important root scripts from package.json:
pnpm dev: run package/service dev scripts through Turbopnpm build: build everythingpnpm check-types: type-check all workspacespnpm db:up,pnpm db:down,pnpm db:reset: local infra lifecyclepnpm db:generate,pnpm db:migrate,pnpm db:studio: Prisma workflowspnpm docker:build:language-images: build runtime language images
- Node.js 18+
- PNPM 10+
- Docker and Docker Compose
This repo uses PNPM workspaces, so install dependencies with:
pnpm installIf PNPM is not installed yet:
npm install -g pnpmIf you have not created a local env file yet:
cp .env.example .envReview .env and adjust values if needed. For most local setups, the defaults are intended to work as-is.
To start only the shared infrastructure services used for local development:
docker compose --profile infra up -d --buildThis starts:
- PostgreSQL
- Redis
- MinIO
- MinIO bucket setup
After infra is up:
pnpm run db:generate
pnpm run db:migrateIf you want the one-command infra/bootstrap path instead, you can also use:
pnpm run db:setupRun the monorepo dev command:
pnpm devThis uses Turbo to start the workspace dev scripts for apps and services.
Once the dev processes are running:
- frontend/gateway entry:
http://localhost:5173for the Vite app during frontend dev - API gateway:
http://localhost:3000 - Swagger/docs entry:
http://localhost:3000/docs
If you want the full containerized stack instead of local dev processes:
docker compose --profile full up -d --buildThis starts infra plus all backend services, gateway, migration job, and the web app container.
Some runtime flows depend on language/base images. Build them with:
pnpm run docker:build:language-imagesBring infra down:
docker compose --profile infra downReset infra volumes:
pnpm run db:resetTail compose logs:
pnpm run db:logsRun type checks:
pnpm run check-typesdocker-compose.yml provisions:
postgresredisminiominio-setupmigrateproject-serviceuser-servicecontainer-serviceexecution-servicestorage-serviceapi-gatewayweb
The compose file also configures:
- MinIO buckets and ZIP upload CORS
- service-to-service internal URLs
- health checks
- shared network
- Docker socket mount for container-service