Running the web API
valem-web is the runnable Spring Boot deployable; valem-api is the headless library it wraps. It
exposes the model runtime over REST + WebSocket, serves the management UI, and — optionally — an MCP
endpoint and LLM-powered spec generation.
- Run with Docker
- Build and run from source
- What it exposes
- Example: create and mutate
- Durable storage
- Enable LLM spec generation
- Before you expose it
- The management UI
- Next
Run with Docker
The quickest way to a running server on a machine without Java or Maven. The image builds Valem’s two JSON dependencies (not on Maven Central yet) and the server itself, so the first build takes a few minutes; after that it starts in seconds.
git clone https://github.com/vlad-public-code/org.json-kula.valem.git
cd org.json-kula.valem
docker build -t valem .
docker run --rm -p 8080:8080 valem # → http://localhost:8080
Anything after the image name is passed straight to Spring Boot, so configuration works exactly as it does outside a container:
docker run --rm -p 8080:8080 -v valem-data:/data valem \
--valem.persistence-dir=/data --valem.api.key="$KEY"
docker build --build-arg SKIP_FRONTEND=true -t valem-api-only . # REST-only, no Node build
The image serves the management UI at / unless you build with SKIP_FRONTEND=true. It runs as a
non-root user and stores nothing outside the container unless you mount a volume.
Build and run from source
From the repository root:
# Build the engine, then start the server on port 8080
mvn install -pl valem-core,valem-service -q
mvn spring-boot:run -pl valem-web
The API is now at http://localhost:8080, with the management SPA served at /. Storage is
in-memory by default. Add -Dskip.frontend=true for a REST-only, backend-fast build (it skips the
Node build of valem-ui).
For a deployable artifact:
mvn package -pl valem-web
java -jar valem-web/target/valem-web-1.0.0-SNAPSHOT.jar
What it exposes
| Surface | Path | Notes |
|---|---|---|
| REST | /models/**, /blobs/** |
Full list in the API reference |
| WebSocket | /models/{id}/subscribe |
A ChangeEvent after every committed mutation |
| MCP over HTTP | /mcp |
Same tool surface as the stdio server — see Running the MCP server |
| Management UI | / |
The valem-ui SPA, built and served by valem-web |
| LLM generation | /models/generate, /models/generate/preview |
503 unless a provider is configured |
REST at a glance
| Method | Path | Description |
|---|---|---|
GET |
/models |
List all registered model IDs |
POST |
/models |
Create a model from a spec |
GET |
/models/{id} |
Model info (version, counts) |
POST |
/models/{id}/mutations |
Apply field mutations |
POST |
/models/{id}/mutations/patch |
Apply an RFC 6902 JSON Patch |
GET |
/models/{id}/state |
Full merged state (base + derived); ?at= for point-in-time |
GET |
/models/{id}/schema/{path} |
Effective JSON Schema for a field |
GET |
/models/{id}/explain/{path} |
Constraint / derivation evaluation traces |
GET |
/models/{id}/audit |
Durable, tamper-evident audit trail |
POST |
/models/{id}/snapshot · /restore |
Capture / restore a state snapshot |
POST |
/models/{id}/spec/evolve |
Incremental spec evolution |
DELETE |
/models/{id} |
Remove a model |
POST |
/blobs · GET /blobs/{id} |
Content-addressed binary storage |
WS |
/models/{id}/subscribe |
Push a ChangeEvent after each mutation |
POST |
/models/generate/preview · /generate |
Build a prompt / generate a validated spec |
Request/response shapes, the WebSocket protocol, and the console command reference are in the API reference — the single source of truth.
Example: create and mutate
# Create a model with a derived total and a rollback constraint
curl -s -X POST http://localhost:8080/models \
-H 'Content-Type: application/json' \
-d '{
"id": "order", "version": "1.0.0", "schema": {},
"derivations": [{ "path": "$.order.total", "expr": "order.subtotal + order.tax" }],
"constraints": [{ "id": "max-order", "expr": "order.total <= 5000",
"message": "Order exceeds the cap", "policy": "rollback" }]
}'
# Mutate base fields — total derives automatically
curl -s -X POST http://localhost:8080/models/order/mutations \
-H 'Content-Type: application/json' \
-d '{ "$.order.subtotal": 200, "$.order.tax": 20 }'
# Read merged state
curl -s http://localhost:8080/models/order/state
Durable storage
State is in-memory by default and lost on restart. Persistence is per concern — spec, state, and blob each select a backend independently:
valem:
persistence-dir: /var/valem/data # filesystem spec + state
blob-store: filesystem
blob-store-path: /var/valem/blobs
memory and filesystem work out of the box. PostgreSQL, MongoDB, Redis, and S3 ship as
à-la-carte adapter jars discovered at runtime — bundle one into the web jar with a convenience
profile, then select it:
mvn -Pweb-postgres -pl valem-web package # also: -Pweb-mongo | -Pweb-redis | -Pweb-s3
java -jar valem-web/target/valem-web-1.0.0-SNAPSHOT.jar \
--valem.storage.type=postgres --spring.datasource.url=jdbc:postgresql://localhost:5432/valem
Selecting a backend whose adapter jar is absent aborts startup naming the jar to add — there is no silent fallback. Run modes, compaction, and the audit trail are covered in Persistence & operations; every property is in Configuration.
Enable LLM spec generation
Configure a provider before starting to unlock the /models/generate endpoints. Without one, the
server still runs and those endpoints return 503.
# Anthropic (default provider)
export VALEM_LLM_API_KEY=sk-ant-...
mvn spring-boot:run -pl valem-web
# OpenAI
VALEM_LLM_PROVIDER=openai VALEM_LLM_MODEL=gpt-4o \
VALEM_LLM_API_KEY=$OPENAI_API_KEY mvn spring-boot:run -pl valem-web
# Ollama (local, no key)
VALEM_LLM_PROVIDER=ollama VALEM_LLM_MODEL=llama3 mvn spring-boot:run -pl valem-web
The key is always read from valem.llm.api-key (env VALEM_LLM_API_KEY) — there is no
provider-specific fallback such as an implicit ANTHROPIC_API_KEY read. Providers supported:
anthropic, openai, ollama, openrouter, groq, mistral, gemini, cerebras.
Workflow and the repair loop: Generating specs with an LLM.
Before you expose it
The API is open by default — a development posture, not a production one. At minimum, set
valem.api.key, decide on valem.websocket.allowed-origins and valem.mcp.allowed-origins, and
review the egress guard. See Security model.
The management UI
A React SPA (valem-ui) is built and served at / by valem-web. For live development, run it
against the backend separately:
cd valem-ui
npm install
npm run dev # http://localhost:5173, proxies /models + /blobs to :8080
Next
- Configuration — every
valem.*property. - Persistence & operations — run modes, compaction, blobs, audit.
- Security model — auth, egress, limits.
- Client SDKs — typed Java and TypeScript clients.