Evergreen is Fission Labs' internal resume builder. Employees maintain
their professional profile in small pieces — projects, skills,
certifications — as their work happens, and generate a Fission-Labs
template-perfect Word document on demand. AI assist polishes bullets,
drafts STAR-style accomplishments from a rough description, and
auto-categorizes new skills. Stack: Spring Boot 3.2 / Java 21 backend,
React + Vite + Tailwind frontend, PostgreSQL via Flyway, Apache POI for
.docx generation, optional Anthropic Claude (claude-sonnet-4-5)
integration that gracefully degrades to deterministic stubs without a key.
- Backend — Spring Boot 3.2, Java 21, PostgreSQL 16, JPA, Flyway, Apache POI 5.2, JJWT
- Frontend — React 18, Vite 5, TypeScript, Tailwind CSS, react-router-dom, axios
- Auth — email/password (bcrypt) + stateless JWT (HS384)
- AI — Anthropic Claude (
claude-sonnet-4-5) via SpringRestClient; deterministic stub responses when no API key is set
cp .env.example .env # adjust if you want (ANTHROPIC_API_KEY is optional)
docker compose up --build- Frontend: http://localhost:5173
- Backend: http://localhost:8080
- Postgres: localhost:5433 (host port; container still listens on 5432)
The DB host port is
5433, not5432, so it coexists with a developer's locally-installed Postgres. The backend container talks todb:5432inside the docker network.
The Fission Labs template uses Poppins. Install it system-wide so the
generated .docx renders correctly:
- macOS:
brew install --cask font-poppins - Linux:
sudo apt install fonts-poppins(or download from https://fonts.google.com/specimen/Poppins) - Windows: download from Google Fonts and install
Without Poppins, Word substitutes another sans-serif and the brand look breaks (colors, sizes, layout are still correct).
See docs/architecture.md for component, sequence,
and entity-relationship diagrams (Mermaid; renders inline on GitHub).
evergreen/
├── backend/ Spring Boot service
│ ├── pom.xml Java 21, Spring Boot 3.2.5
│ └── src/main/
│ ├── java/com/fissionlabs/evergreen/ auth, user, education, skill,
│ │ company, project, certification,
│ │ resume (docx generator), ai,
│ │ upload, common, config
│ └── resources/
│ ├── application.yml JWT, Anthropic, upload config
│ ├── db/migration/V1__…sql Flyway schema
│ └── templates/ runtime assets
│ └── images/{logo,watermark}.png
├── frontend/ Vite + React + Tailwind
│ └── src/{auth,api,components,pages,lib}
├── templates/Fission_Labs_Resume_format.odt visual source of truth
├── docker-compose.yml
└── README.md
A standalone sample (no DB needed) can be produced via:
cd backend
mvn test -Dtest=ResumeDocxGeneratorTest
open target/sample-resume.docxOpen it side-by-side with templates/Fission_Labs_Resume_format.odt —
they should look like they came from the same template.
The full app produces the same output through POST /api/resume/generate
once a user has filled in their profile.
All authenticated endpoints require Authorization: Bearer <jwt>.
POST /api/auth/signup {name,email,password}
POST /api/auth/login {email,password}
GET /api/users/me current profile
PUT /api/users/me {name?,currentRole?,majorStack?,highlights?}
POST /api/users/me/photo multipart/form-data file=...
GET /api/users/me/educations
POST /api/users/me/educations
PUT /api/users/me/educations/{id}
DELETE /api/users/me/educations/{id}
GET /api/users/me/skills
POST /api/users/me/skills {name,category}
PUT /api/users/me/skills/{id}
DELETE /api/users/me/skills/{id}
GET /api/users/me/companies
POST /api/users/me/companies {name,startDate,endDate?,current}
PUT /api/users/me/companies/{id}
DELETE /api/users/me/companies/{id}
GET /api/users/me/companies/{companyId}/projects
POST /api/users/me/companies/{companyId}/projects {name,role?,description?,bullets,techUsed,startDate,endDate?}
PUT /api/users/me/companies/{companyId}/projects/{id}
DELETE /api/users/me/companies/{companyId}/projects/{id}
GET /api/users/me/certifications
POST /api/users/me/certifications {name,issuer?,month?,year?,credentialId?}
PUT /api/users/me/certifications/{id}
DELETE /api/users/me/certifications/{id}
POST /api/resume/preview {profileNumber?,selected*Ids?} -> ResumeData JSON
POST /api/resume/generate {profileNumber?,selected*Ids?} -> .docx download
POST /api/ai/polish-bullet {text} -> {polished}
POST /api/ai/bullets-from-text {description} -> {bullets}
POST /api/ai/categorize-skill {skillName} -> {category}
If you want hot reload during development:
# 1. Postgres only
docker compose up -d db
# 2. Backend on host (uses host:5433 to reach the docker DB)
cd backend
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5433/evergreen \
SPRING_DATASOURCE_USERNAME=evergreen \
SPRING_DATASOURCE_PASSWORD=evergreen \
JWT_SECRET=replace-with-a-long-random-string-of-at-least-32-chars \
UPLOAD_DIR=$(pwd)/uploads \
mvn spring-boot:run
# 3. Frontend on host
cd frontend
npm install
npm run dev # http://localhost:5173users— name, email, password_hash, role_title, major_stack, photo_url, highlights (text[])educations— degree, institution, year, scoreskills— name, category (10-value enum); unique per (user, name)companies— name, start_date, end_date?, is_current (partial unique index ensures ≤1 current company per user)projects— name, role, description, bullets (jsonb), tech_used (text[]), start_date, end_date?certifications— name, issuer, month, year, credential_id
current_role is a SQL reserved word in PostgreSQL, so the column is
named role_title; the JSON field stays currentRole.