AI-assisted recruitment: candidates apply with resumes and ATS scoring; admins manage jobs, applicants, and dashboards.
| Path | Role |
|---|---|
client/ |
React 18 + Vite SPA (dev 3000); proxies /api to the server in development |
server/ |
Express API monolith (5000): /api/*, Google auth, jobs, applications, calendar, phone-agent routes |
phone-agent/ |
Twilio Media Streams + Gemini Live (3010); webhooks and WebSocket at /twilio/* |
Docker defaults to an Atlas-first two-container stack: app (Express API + built SPA) and phone-agent. Optional local Mongo is available via docker-compose.mongo.yml.
- Node.js 22+
- A MongoDB Atlas cluster (recommended) and a connection string for
MONGODB_URI - Optional: ngrok (or similar) for public HTTPS to the phone-agent when testing Twilio
- Optional: Docker + Docker Compose
- Copy
.env.exampleto.envat the repository root (next to the rootpackage.json). - Replace every placeholder with real values. Keys are documented only in
.env.example(no duplicate list here); required vs optional behavior followsserver/src/config/envValidation.jsandphone-agent/src/config.js.
Important details:
MONGODB_URI: Use your Atlas SRV URI. In Atlas, allow your IP (or0.0.0.0/0for quick local tests only) under Network Access.PORT: Leave empty in.envunless you know you need it. The server defaults to 5000; the phone-agent defaults to 3010. Setting a singlePORTin.envapplies to both processes and can break one of them.GOOGLE_CLIENT_IDandVITE_GOOGLE_CLIENT_ID: Use the same Google OAuth Web client ID.- Phone / Twilio: Twilio must reach the phone-agent on a public HTTPS URL. For host-based development you can still set
PUBLIC_BASE_URLandPHONE_AGENT_BASE_URLto the same ngrok URL manually. For Docker voice runs, Compose can start anngroksidecar and the phone-agent can auto-discover its public URL fromNGROK_API_URL. The SkillSync server calls the agent usingPHONE_AGENT_BASE_URL. The agent calls the API usingSKILLSYNC_API_BASE_URLandSKILLSYNC_SERVICE_TOKEN(must match on server and agent; sent asAuthorization: Bearer …to/api/phone-agent/*).
From the repository root:
npm install
npm install --prefix client
npm install --prefix server
npm install --prefix phone-agent
npm run devThis runs client, server, and phone-agent together. The Vite dev server serves the SPA on port 3000 and proxies /api to http://localhost:5000, so the browser uses relative /api/... URLs and you avoid hardcoding the API host.
- Start services (
npm run devor run the phone-agent alone withnpm run start:devinphone-agent/). - Expose the agent:
ngrok http 3010(or the port the agent actually listens on). - Set
PUBLIC_BASE_URLandPHONE_AGENT_BASE_URLin.envto the https URL ngrok prints (no trailing slash). Restart server and phone-agent after changing.env.
If you prefer not to copy the ngrok URL manually, set NGROK_API_URL=http://127.0.0.1:4040 in .env; the phone-agent will read the active HTTPS tunnel from the local ngrok API at call time.
- Server only:
npm run dev:server - Client only:
npm run dev:client - Phone-agent only:
npm run dev:phone-agentornpm --prefix phone-agent run start:dev
After the server is up:
npm run smokeThis hits the server /health endpoint (override base URL with SMOKE_SERVER_URL if needed). See Smoke checklist in this file below.
- Server: open or curl
http://127.0.0.1:5000/healthand confirm JSONstatusis OK. - Client: open
http://localhost:3000(Vite) and sign in with Google as a smoke test of auth. - Phone-agent (optional): with the agent running, curl
http://127.0.0.1:3010/and confirm JSONserviceisphone-agent. - Twilio (optional): requires ngrok and Twilio env vars; place a test call only when those are configured.
npm run dev:dockeror detached:
docker compose up --build -d
npm run smoke
npm run docker:downThis starts three containers by default:
app: Express API + built client SPA on http://localhost:5000phone-agent: Twilio/Gemini service on http://localhost:3010ngrok: tunnels https → phone-agent:3010 so Twilio can reach webhooks; local inspector on http://localhost:4040
Notes:
- Default Compose expects
MONGODB_URIin.envto point to Atlas. - The
appcontainer injectsPHONE_AGENT_BASE_URL=http://phone-agent:3010. - The
phone-agentcontainer injectsSKILLSYNC_API_BASE_URL=http://app:5000andNGROK_API_URL=http://ngrok:4040so the agent reads the public tunnel URL from the ngrok sidecar. Override in.envonly if you know you need something different. - Twilio / ngrok: set
NGROK_AUTHTOKENin.envfrom Your Authtoken (free account is enough). Without it the ngrok container logsERR_NGROK_4018. You may omitPUBLIC_BASE_URLin.envfor this stack; the agent resolves HTTPS from ngrok at call time. - To run without the ngrok container (for example no Twilio yet):
docker compose up app phone-agent(or scale/stopngrokafter up). - When you use the SPA served from
app, it talks to/apion the same origin (localhost:5000) in the built image. - If you still run the Vite dev server separately while Docker is up, set
ALLOWED_ORIGINSto include bothhttp://localhost:3000andhttp://localhost:5000.
If you want Compose to run Mongo locally too:
docker compose -f docker-compose.yml -f docker-compose.mongo.yml up --buildThat adds a third mongo container and overrides MONGODB_URI inside app to mongodb://mongo:27017/SkillSync.
- Browser → API: In dev, same-origin via Vite on 3000 with
/apiproxied to the Express app on 5000. In production, serve the SPA and API behind one origin or setVITE_API_BASE_URLto your public API base (including/apipath suffix if your deployment uses it). - User JWT:
Authorization: Bearer …on protected routes; verified withJWT_SECRET. - Phone-agent → server:
Authorization: Bearer <SKILLSYNC_SERVICE_TOKEN>for/api/phone-agent/*(seeserver/src/middleware/requirePhoneAgent.js).
Private application — see your team’s license and deployment policy.