HomeboxSearchProvider used to own the RestClient call and item mapping directly; pulling it into a shared HomeboxItemClient lets the upcoming sync pipeline page through the same Homebox items without duplicating the fetch/mapping logic. ConnectionIdentifiable lets both SearchRequest and SyncRequest be resolved to a connection by the same client.
Project Intent
Vaessl is a technical playground and proof-of-concept designed to showcase a complete, professional-grade software lifecycle. The primary goal is not just to build a functional utility, but to demonstrate a multi-experience approach to full-stack engineering, DevOps, and AI integration.
This project serves as a transparent portfolio of my take on:
- Infrastructure: Moving from local-machine to browser-based development environment hosted on my Proxmox server.
- Modern AI Orchestration: Building a system that actually uses AI to do heavy lifting, like identifying objects and understanding natural language using Spring AI and LiteLLM.
- Architectural Foresight: Designing with abstraction layers early to ensure the system can scale from a single-target tool to a multi-app bridge without significant refactoring.
What is Vaessl?
Vaessl acts as a translator between user inputs via text or image and digital management systems. It performs image and semantic search analysis before serving results from applications (e.g., Homebox, WikiJS) via REST API.
Core Functionality
- Connection Management: Users connect to supported services (e.g. Homebox) via a credential login flow. Tokens are stored in a JDBC-backed HTTP session, enabling secure multi-service connections per browser session.
- AI-Powered Analysis: The system utilises LiteLLM as a unified gateway to process inputs via LLM of choice (pipeline in progress).
- Semantic Discovery: By utilising Spring AI and pgvector, Vaessl will store description embeddings to enable intent-based search (e.g., finding a "Wrench" by searching for "tool to fix a leaky pipe") (planned).
Technical Stack
| Layer | Technology |
|---|---|
| Backend | Spring Boot 4.1.0, Java 25 (LTS), Spring AI |
| Frontend | React 19, Vite 8, SCSS |
| Database | PostgreSQL + pgvector |
| AI Gateway | LiteLLM (Unified API proxy) |
| DevOps | Docker, Portainer, Hoppscotch (API testing), Gitea Actions, Jenkins |
Infrastructure & Methodology
1. Centralized Development (Code-Server)
To maintain a zero-footprint local setup, I develop within a custom code-server Docker instance.
- Custom Image: The environment is baked with a specific toolchain (OpenJDK 25, Node.js 24) to ensure absolute environment parity.
- Remote Access: Secured via Pangolin tunnel, allowing for secure, remote development without exposing ports to the public internet.
2. Abstraction & Scalability
A core requirement was to prevent vendor or application lock-in:
- Provider Pattern: Both connection and search logic are fully abstracted behind
ConnectionProviderandSearchProviderinterfaces. Adding a new service (e.g. WikiJS) means implementing those interfaces — no changes to core dispatch logic required. - AI Agnostic: LiteLLM abstracts the AI provider, allowing the backend to switch between cloud APIs and local inference engines (Ollama) without code changes.
3. Testing Strategy
- Database Parity: Mirrored PostgreSQL containers are used for development and testing (
vaessl-dbvsvaessl-test-db). This ensures JPA operations are tested against the real PostgreSQL engine andpgvectorextension, not mocks. - WireMock: External HTTP APIs (Homebox, WikiJS) are stubbed in integration tests using WireMock, isolating tests from live service availability.
- Frontend: A Vitest-based stack provides a fast feedback loop for UI components.
Architecture Overview
Backend (backend/src/main/java/com/vaessl/app/)
Package-by-feature layout. Server context path is /api.
| Package | Purpose |
|---|---|
shared/ |
Cross-cutting types: ServiceType enum, ServiceProvider base interface, Endpoint enum, SessionKeys utility |
config/ |
CORS (env-driven allowed origins) and JDBC-backed Spring Session |
connection/ |
Login, session management, credential persistence (Single Table Inheritance) |
search/ |
Paged search against connected services, guarded by active session |
exception/ |
GlobalExceptionHandler via @ControllerAdvice; domain exceptions mapped to typed HTTP responses |
REST Endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/api/login |
Authenticates a service and stores its connection ID in the session |
GET |
/api/connections/status |
Lists all connected services for the current session |
DELETE |
/api/connections/{serviceType} |
Removes a service from the session; invalidates the session if none remain |
POST |
/api/search |
Paged search against a connected service; returns 401 if no active session |
Frontend (frontend/src/)
React 19 + TypeScript + SCSS, Vite 8 build. Package-by-feature under components/.
| Module | Description |
|---|---|
api/ |
Typed apiFetch wrapper + per-feature API call modules (connections.ts, searches.ts) |
types/ |
Shared TS types aligned with backend records (ServiceType, LoginRequest, SearchResponse, etc.) |
components/connections/ |
Dashboard, ConnectModal, ServiceCard — full connection management UI |
components/search/ |
SearchModal — search dialog with paged result rendering |
components/ui/ |
Shared primitives (ActionButton, Modal) |
Roadmap
Phase 1: Foundation
- Deployment of core infrastructure (PostgreSQL, LiteLLM, Hoppscotch via Docker).
- Custom Code-Server image build and deployment.
- Spring Boot skeleton with Java 25 and Gradle Kotlin DSL.
Phase 2: Connection & Basic Search (Current)
- Provider-pattern abstraction for connections and search.
- Homebox authentication — login, session-tracked token, expiry checking.
- Paged search against Homebox entities via REST.
- React connection management dashboard (connect, disconnect, status).
- React search modal with paged results.
- Integration test suite (WireMock + mirrored PostgreSQL container).
- Image/text input → LLM inference → staging table workflow.
- Data refinement UI for reviewing and approving LLM-processed results.
Phase 3: Semantic Search & Expansion
- Spring AI vector embeddings with pgvector for intent-based search.
- Additional API targets (WikiJS).
- Public-facing demo.
Setup & Deployment
- Clone the repository.
- Copy
.env.localintobackend/with:
DB_URL=jdbc:postgresql://192.168.1.{subnet}:{port}/vaessl
DB_TEST_URL=jdbc:postgresql://192.168.1.{subnet}:{port}/vaessl_test
DB_USERNAME={username}
DB_PASSWORD={password}
PG_DRIVER_CLASS_NAME=org.postgresql.Driver
OPENAI_KEY={api-key}
OPENAI_BASE_URL=https://{litellm-host}/v1
FRONTEND_LOCAL_URL=http://localhost:5173
FRONTEND_PUBLIC_URL=https://{your-public-domain}
- Start the pgvector-enabled PostgreSQL instances (production + test) — use the official
pgvector/pgvectorDocker image or install the extension into your existing PostgreSQL instance. - Install frontend dependencies and build the backend:
# Frontend
cd frontend && npm install
# Backend
cd backend && ./gradlew build
- Run the backend and frontend dev servers:
# Backend
cd backend && ./gradlew bootRun
# Frontend
cd frontend && npm run dev
