Kontor is a Progressive Web App that consolidates personal finance data - transactions, portfolios, and market data - and uses a GenAI layer to surface personalised, actionable insights. Stock and ETF data is sourced via Yahoo Finance; the AI component uses Retrieval-Augmented Generation (RAG) over the user's own financial data and curated financial news.
| Layer | Technology |
|---|---|
| Client | React 19, TypeScript 5.9, Vite 7, Tailwind CSS 4, shadcn/ui |
| Server | Java 25, Spring Boot 4, Gradle |
| AI Service | Python 3.14, FastAPI, uv |
| Linting/Formatting | Biome (client), Spotless + Checkstyle + Error Prone (server), Ruff (AI) |
The repository includes a root .env.example and a local .env with the default compose values. Update .env if you want to change the Postgres credentials, Keycloak database credentials, or shared local Keycloak dev-user password.
The AI service requires a Logos API key (LOGOS_API_KEY=lg-…) in .env. The Logos endpoint is only reachable from the TUM network or via eduVPN — the AI service will start without the key, but recommendation calls will fail.
The Compose Keycloak service is for local development only. It builds the custom Keycloak image from infra/keycloak/theme/ (Keycloak with the Kontor login theme baked in), runs start-dev, imports infra/keycloak/realms/kontor-realm.json, and stores Keycloak state in the keycloak_postgres_data Docker volume. The realm import is skipped once the realm already exists, so delete that volume if you need to re-apply the import from scratch. The first docker compose up --build builds the theme image (Node + Maven), which takes a few minutes; subsequent runs use the cached layer.
Local Keycloak users live in infra/keycloak/realms/kontor-users-0.json. Add another object to the users array to create more local accounts with their own realmRoles and attributes. The sample users share the KEYCLOAK_DEV_USERS_PASSWORD environment placeholder so adding users does not require more Compose variables.
docker compose up --build| Service | URL |
|---|---|
| Client | http://localhost:5173 |
| Server | http://localhost:8080 |
| AI Service | http://localhost:8000 |
cd client
npm install
npm run dev| Task | Command |
|---|---|
| Type check | npm run typecheck |
| Lint | npm run lint |
| Lint (autofix) | npm run lint:fix |
| Format (autofix) | npm run format |
| Build | npm run build |
cd core
./gradlew build| Task | Command |
|---|---|
| Test | ./gradlew test |
| Compile check | ./gradlew compileJava |
| Format check | ./gradlew spotlessCheck |
| Format fix | ./gradlew spotlessApply |
| Lint | ./gradlew checkstyleMain checkstyleTest |
Requires uv.
cd ai
uv sync
uv run uvicorn advisor.main:app --reload| Task | Command |
|---|---|
| Install deps | uv sync |
| Dev server | uv run uvicorn advisor.main:app --reload |
| Test | uv run pytest |
| Type check | uv run ty check |
| Format check | uv run ruff format --check . |
| Format fix | uv run ruff format . |
| Lint | uv run ruff check . |
| Lint (autofix) | uv run ruff check --fix . |
The client's TypeScript types and Zod schemas are generated from each backend's OpenAPI spec — never hand-written. After changing a route or its request/response models, regenerate the spec and the client, then commit the result:
cd core && ./gradlew generateOpenApiDocs # core spec → core/docs/openapi.yml
cd ai && uv run export-openapi # AI spec → ai/docs/openapi.json
cd client && npm run generate:api # both specs → client/src/network/generated*CI's openapi-sync workflow fails if the committed files are out of sync.
The Kontor stack ships as a single Helm chart in
deploy/helm/kontor/ that bundles the client,
core, Postgres (with pgvector), and an optional Keycloak. The chart README
documents environment overlays (values-prod.yaml,
values-pr.template.yaml), required secrets, and the install / upgrade flow.
Copy deploy/helm/kontor/secrets.example.yaml to secrets.yaml and fill in all REPLACE_ME values, including ai.logosApiKey (the lg-… key from your tutor — requires TUM network / eduVPN):
helm upgrade --install kontor ./deploy/helm/kontor \
-n team-3m \
-f deploy/helm/kontor/values-prod.yaml \
-f deploy/helm/kontor/secrets.yamlKeycloak ships as a custom image with the Kontor login theme baked in, rather
than stock Keycloak plus a sibling jar. The theme source is vendored under
infra/keycloak/theme/ — a minimal
Keycloakify project whose Docker build assembles the
theme jar and copies it into /opt/keycloak/providers/. CI builds and pushes it
to ghcr.io/aet-devops26/team-3m/kontor-keycloak; the realm selects it via
loginTheme: kontor. See the vendored README for how to edit the theme.
This project is configured for AI coding agents (Claude Code, Codex). Rules, skills, and MCP servers ensure agents follow consistent standards across the codebase.
CLAUDE.md is the top-level instruction file for Claude Code. It is automatically loaded at the start of every agent session and provides the agent with project context, commands, and coding rules. AGENTS.md serves the same purpose for Codex. For more information, see the Claude Code docs on CLAUDE.md.
Coding rules live in .claude/rules/ and are automatically loaded based on the files being edited. They enforce consistent style, testing, security, and architectural patterns. For more information, see the Claude Code Docs.
.claude/rules/
├── common/ # Apply to all code
│ ├── coding-style.md # Immutability, KISS/DRY/YAGNI, naming, file organization
│ ├── testing.md # TDD workflow, AAA pattern, 80%+ coverage
│ ├── patterns.md # Repository pattern, API envelope, service classes
│ └── code-review.md # Review checklist, security triggers, severity levels
├── java/ # Apply to *.java, pom.xml, build.gradle*
│ ├── coding-style.md # Records, sealed classes, Optional, modern Java (16+)
│ ├── patterns.md # Constructor injection, Spring conventions, Flyway, jOOQ
│ └── testing.md # JUnit Jupiter 5, AssertJ, Mockito, Testcontainers
└── typescript/ # Apply to *.ts, *.tsx, *.js, *.jsx
├── coding-style.md # Biome, kebab-case files, React patterns, Zod, shadcn/ui
├── patterns.md # Custom hooks, data fetching, repository pattern
└── testing.md # Playwright for E2E testing
- Common rules apply to all code. Language-specific rules extend them — they don't replace them.
- When common and language-specific rules conflict, the language-specific rule takes precedence.
MCP (Model Context Protocol) servers provide tool integrations for AI coding agents. They are configured in two places to support both Claude Code and Codex:
| File | Agent |
|---|---|
.mcp.json |
Claude Code (Anthropic) |
.codex/config.toml |
Codex (OpenAI) |
Both files define the same servers — keep them in sync when adding or removing MCP servers.
Skills extend AI coding agents with reusable instructions. They live in .claude/skills/ (Claude Code) and .agents/skills/ (Codex) and are tracked via skills-lock.json.
# Add a skill
npx skills add shadcn/ui
# List installed skills
npx skills list
# Update skills
npx skills update
# Remove a skill
npx skills remove <skill-name>Only skills prefixed with local_ (e.g. .agents/skills/local_conventional-commit/) are tracked in git. Externally synced skills are ignored via .gitignore.
Local skills are project-specific skills that live in the repository. To create one:
-
Create a directory in
.agents/skills/with alocal_prefix (e.g..agents/skills/local_my-skill/). -
Add a
SKILL.mdfile with frontmatter and instructions:--- name: my-skill description: Short description of when and how the skill should be triggered. --- # My Skill Instructions for the agent...
-
Run
./install-skills.shto symlink the skill into.claude/skills/.
The local_ prefix ensures the skill is tracked in git and not overwritten by external skill updates.
To install skills from the lock file and symlink all agent skills (including local ones) to Claude Code:
./install-skills.sh