Project Overview

Development Setup

Prerequisites

Requirement Notes

JDK 21

The Gradle toolchain targets Java 21. Use the wrapper (./gradlew); no separate Gradle install is needed.

Docker with Compose

Runs the local MongoDB and Elasticsearch.

Node.js 20 or newer, npm

For the Studio. frontend/mise.toml pins Node.js 24 for mise users.

Keycloak client credentials

The Studio signs in through the shared Keycloak (realm platform, client platform). The client secret and the NextAuth secret are not in the repository; ask DevOps for them.

Provider credentials

Only for the features you work on: Azure OpenAI (AI Assistant), OpenAI (knowledge embeddings, dictation), Azure AI Document Intelligence (file knowledge), Stripe, Mailgun.

ffmpeg (optional)

Re-encodes uploaded interactive-content videos as in a deployment (brew install ffmpeg). Without ffmpeg and ffprobe on the backend’s PATH, uploaded videos are stored and served as uploaded.

The Gradle module of the backend is :promethist-platform-backend — the root project name is prefixed to the directory name, so :backend:... does not exist.

Local Infrastructure

docker compose up -d

This starts the two stores the backend needs, with their data under the git-ignored data/ directory:

Service Local address Notes

MongoDB 7

localhost:9417

Database promethist-platform, the default of spring.mongodb.database.

Elasticsearch 8.18

localhost:9481

Single node with security disabled.

Keycloak is not part of the compose file: the backend validates tokens against the shared Keycloak.

Running the Backend

The committed backend/src/main/resources/application.properties targets the cloud services, so the backend is run with a local Spring profile whose file you create yourself: backend/src/main/resources/application-local.properties. It is ignored by git (the *local.* rule in .gitignore) and must never be committed — it holds your credentials.

./gradlew :promethist-platform-backend:bootRun --args='--spring.profiles.active=local'

The backend listens on 9310 (HTTP) and 9311 (Socket.IO). A working local profile sets at least:

Property Local value

spring.elasticsearch.uris

http://localhost:9481

promethist.storage.type, promethist.storage.ref

File and a local directory, for example data/storage. The committed default is Azure blob storage, which fails at startup without real credentials.

promethist.mcp.issuer, promethist.mcp.jwks-uri, promethist.mcp.audience, promethist.mcp.resource-metadata-url, promethist.mcp.web-url, promethist.mcp.engine-url

The local Studio and backend: http://localhost:3000, http://localhost:3000/oauth/jwks, http://localhost:9310/api/v1/mcp, http://localhost:3000/.well-known/oauth-protected-resource, http://localhost:3000 and an engine, for example the preview one. The backend refuses to start when any of the six is blank (see Hosted MCP Server).

promethist.ai.azure-open-ai.api-key, promethist.ai.azure-open-ai.endpoint, promethist.ai.open-ai.api-key

Your provider keys, if you work on the AI Assistant or knowledge.

The full list of properties is in Configuration.

To point the backend’s calls to the engine and the bridge at a deployed environment instead of a local engine, pass that environment’s domain and namespace:

./gradlew :promethist-platform-backend:bootRun \
  --args='--spring.profiles.active=local --app.domain=eu.promethist.ai --app.namespace=preview'
A fresh local database has no realtime configurations ("voices"), and an agent cannot be completed without one — the AI Assistant’s agent generation fails outright. Staff accounts can create one in the Studio under Admin → Voices.

Running the Studio

cd frontend
npm install
npm run dev

The Studio runs on http://localhost:3000. Keep that port: the Keycloak client accepts only http://localhost:3000 as a local redirect URI, so a Studio on another port cannot complete a sign-in. Sign-in offers Google, Microsoft and work email (company SSO); all go through Keycloak (Studio Sign-In).

Configuration comes from frontend/.env.local (git-ignored). frontend/.env.example is a template for running only the Studio locally against the preview environment’s backend; copy it and add the two secrets. To run against a local backend, point the Studio at it:

AUTH_URL=http://localhost:3000
NEXT_PUBLIC_URL=http://localhost:3000
NEXT_PUBLIC_OVERRIDE_PLATFORM_URL=http://localhost:9310
NEXT_OVERRIDE_PLATFORM_URL=http://localhost:9310
NEXT_PUBLIC_OVERRIDE_PLATFORM_SOCKET_URL=http://127.0.0.1:9311
MCP_OAUTH_ISSUER=http://localhost:3000
MCP_RESOURCE=http://localhost:9310/api/v1/mcp
MCP_OAUTH_PRIVATE_JWK=<generated, see below>

The Socket.IO URL deliberately uses 127.0.0.1: cookies are scoped to the host, and the browser would otherwise send the Studio’s large session cookies to localhost:9311, where they exceed the Socket.IO server’s 8 KB header limit and the assistant connection keeps failing with websocket error.

MCP_OAUTH_PRIVATE_JWK is only needed to sign tokens for the hosted MCP server. Generate a key of your own with the jose package the Studio already depends on, run from frontend/:

node -e "import('jose').then(async (j) => { const { privateKey } = await j.generateKeyPair('RS256', { extractable: true }); const jwk = await j.exportJWK(privateKey); jwk.kid = 'local-' + Date.now(); console.log(JSON.stringify(jwk)) })"

Every environment needs its own key; never reuse a deployed one locally.

All environment variables are listed in Configuration.

Local Ports

Port Service

3000

Studio (Next.js dev server)

9310

Backend HTTP: REST API, MCP endpoint, Stripe webhook, Actuator

9311

Backend Socket.IO: AI Assistant

9417

MongoDB (compose)

9481

Elasticsearch (compose)

Tests

# the whole backend suite
./gradlew :promethist-platform-backend:test

# one class
./gradlew :promethist-platform-backend:test --tests 'ai.promethist.platform.mcp.McpAgentToolsTest'

The test task skips classes named *IntegrationTest; those run with integrationTests. Results are in backend/build/test-results/test/.

Things to know about the suite:

  • PlatformApplicationTests.contextLoads starts the full application and needs the infrastructure and credentials of a real environment; it fails on a bare machine. LLMServiceTest and AzureParserTest call the real providers and run only when the local profile carries Azure credentials.

  • The contract tests of the MCP server — McpToolRegistryTest (tool names, confirmation gates, argument naming, description length budget) and McpGuideServiceTest (guide size budget) — run in the default suite. They are described in Hosted MCP Server.

  • Writing tests has a few rules of its own — the test properties file, the Mockito helpers for Kotlin, where test configurations may live — collected in Code Conventions.

The pipeline does not run the backend tests (gradle:build builds the distribution only), so run the suite before you push, together with the formatting, lint and type checks of Code Conventions.

Feature Flags for Local Work

Variable Effect

NEXT_PUBLIC_ENABLE_BILLING

In development, billing requests are skipped unless this is true (a local backend usually has no Stripe setup).

NEXT_PUBLIC_FORCE_SUBSCRIPTION_CHECKS

In development, the subscription checks that gate parts of the Studio are skipped unless this is true.

NEXT_PUBLIC_MARKER_IO_ENABLED

Injects the Marker.io feedback widget in production builds when true.