KeYmaera X API Server for Agent Harnesses
WARNING: the README is slop. Instructions about how to use the infra are probably correct but statements about the tool itself seem meh.
A Docker container providing a REST API for AI agents to interact with KeYmaera X, a theorem prover for hybrid systems. Agents can submit models and proof scripts, then receive structured feedback on proof results.
Limitations
- ~2 minute startup overhead per request (KeYmaera X JVM + lemma derivation)
- Z3 only - no Mathematica support (Z3 is less powerful for some arithmetic)
- Some derived lemmas fail with Z3 (23 of them), but basic proofs still work
- In-memory job storage - jobs are lost on container restart
- Single worker - requests are processed serially
What is This For?
This API enables AI agent harnesses to:
- Verify hybrid system models - Submit
.kyxfiles containing differential dynamic logic (dL) specifications - Execute proof scripts - Run Bellerophon tactic scripts to prove safety properties
- Get structured feedback - Receive agent-friendly JSON responses with success/failure status, hints for fixing issues, and detailed proof metrics
Quick Start
# Build the Docker image
docker build -t keymaerax-api .
# Run the container
docker run -p 8080:8080 keymaerax-api
# Test it works
curl http://localhost:8080/health
The API will be available at http://localhost:8080.
Helper Scripts
./build.sh # Build the Docker image
./run.sh # Build (if needed) and run the container
Docker Compose
# Older Docker versions
docker-compose up --build
# Docker 20.10+
docker compose up --build
API Reference
POST /prove/sync - Submit Proof (Synchronous)
Recommended for most use cases. Submit a .kyx file and wait for the result.
Request:
curl -X POST http://localhost:8080/prove/sync \
-H "Content-Type: application/json" \
-d '{
"kyx": "ArchiveEntry \"Test\"\nProgramVariables Real x; End.\nProblem x>=0 -> [x:=x+1;] x>=1 End.\nTactic \"Proof\" implyR(1); assignb(1); QE End.\nEnd.",
"timeout": 60
}'
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
kyx |
string | Yes | The .kyx file content |
timeout |
int | No | Proof timeout in seconds (default: 120, max: 600) |
Response (success):
{
"job_id": "abc-123",
"success": true,
"status": "completed",
"message": "All proof entries completed successfully.",
"entries_summary": [
{"name": "Test", "proved": true, "steps": 38}
]
}
Response (unfinished proof):
{
"job_id": "abc-123",
"success": false,
"status": "completed",
"message": "1 of 1 entries did not complete.",
"action_required": "Review unfinished entries and strengthen tactics.",
"unfinished_entries": [
{
"name": "Test",
"result": "unfinished",
"hint": "The tactic did not close all proof branches. Consider: (1) strengthening loop invariants, (2) adding differential cuts for ODEs, (3) using 'ODE(1)' for automatic ODE reasoning."
}
]
}
Response (parse error):
{
"job_id": "abc-123",
"success": false,
"status": "completed",
"message": "The .kyx file contains syntax errors.",
"action_required": "Fix the parse error and resubmit.",
"parse_error": {
"line": 8,
"column": 22,
"type": "term",
"found": ";] x >= 1",
"expected": "number | dot | function | variable | termList | \"-\""
},
"fix_hint": "Ensure assignments end with semicolons: 'x := e;'"
}
POST /prove - Submit Proof (Asynchronous)
Submit a proof and poll for results. Useful for long-running proofs.
Request:
curl -X POST http://localhost:8080/prove \
-H "Content-Type: application/json" \
-d '{"kyx": "...", "timeout": 120}'
Response:
{
"job_id": "abc-123",
"status": "pending",
"message": "Proof submitted. Poll /status/{job_id} for results."
}
GET /status/{job_id} - Check Proof Status
Request:
# Agent-friendly summary (default)
curl http://localhost:8080/status/abc-123
# Full details including raw KeYmaera X output
curl http://localhost:8080/status/abc-123?format=full
Response (running):
{
"job_id": "abc-123",
"status": "running",
"message": "Proof is running (elapsed: 45.2s)",
"success": false
}
Response (completed):
Same format as /prove/sync response.
POST /parse - Validate Syntax
Check .kyx syntax without running proofs. Note: This also has ~2 minute startup overhead.
Request:
curl -X POST http://localhost:8080/parse \
-H "Content-Type: application/json" \
-d '{"kyx": "..."}'
Response:
{
"valid": true,
"message": "File parsed successfully"
}
GET /jobs - List Recent Jobs
curl http://localhost:8080/jobs?limit=10
Response:
{
"jobs": [
{
"job_id": "abc-123",
"status": "completed",
"created_at": 1234567890.123,
"overall_result": "proved"
}
]
}
GET /health - Health Check
curl http://localhost:8080/health
Response:
{
"status": "healthy",
"service": "keymaerax-api"
}
GET / - API Documentation
Returns a JSON summary of available endpoints.
.kyx File Format
KeYmaera X uses .kyx archive files containing models and proofs in differential dynamic logic (dL):
ArchiveEntry "Entry Name"
ProgramVariables
Real x; /* Position */
Real v; /* Velocity */
End.
Definitions
Real g; /* Gravity constant */
End.
Problem
x >= 0 & v >= 0 -> [x := x + v;] x >= 0 /* Safety property */
End.
Tactic "Proof Strategy"
implyR(1); assignb(1); QE /* Bellerophon tactics */
End.
End.
Key Components
| Block | Purpose |
|---|---|
ProgramVariables |
Declare state variables (all Real) |
Definitions |
Constants, functions, predicates, hybrid programs |
Problem |
The dL formula to prove |
Tactic |
Bellerophon proof script |
Common Tactic Patterns
/* Propositional */
implyR(1) /* Decompose implication on right */
andR(1) /* Split conjunction on right */
id /* Close by matching antecedent/succedent */
/* Hybrid programs */
assignb(1) /* Handle [x:=e] assignment */
composeb(1) /* Split [a;b] sequential composition */
loop("inv", 1) /* Apply loop invariant */
/* ODEs */
ODE(1) /* Automatic ODE reasoning */
dI(1) /* Differential invariant */
dC("fact", 1) /* Differential cut */
/* Arithmetic */
QE /* Quantifier elimination (sends to Z3) */
Agent Integration
Recommended Workflow
- Submit proof with
/prove/sync(simplest approach) - Check
successfield -truemeans all entries proved - On failure, read
action_requiredandhintfields for guidance - Iterate on the proof script based on feedback
Python Client Example
import requests
class KeYmaeraXClient:
def __init__(self, base_url="http://localhost:8080"):
self.base_url = base_url
def prove(self, kyx_content: str, timeout: int = 120) -> dict:
"""Submit a proof and wait for result."""
response = requests.post(
f"{self.base_url}/prove/sync",
json={"kyx": kyx_content, "timeout": timeout},
timeout=timeout + 200 # Account for startup overhead
)
return response.json()
def is_healthy(self) -> bool:
"""Check if the API is available."""
try:
r = requests.get(f"{self.base_url}/health", timeout=5)
return r.status_code == 200
except:
return False
# Usage
client = KeYmaeraXClient()
result = client.prove('''
ArchiveEntry "Simple"
ProgramVariables Real x; End.
Problem x >= 0 -> [x := x + 1;] x >= 1 End.
Tactic "Proof" implyR(1); assignb(1); QE End.
End.
''')
if result["success"]:
print("Proof completed!")
for entry in result.get("entries_summary", []):
print(f" {entry['name']}: {entry['steps']} steps")
else:
print(f"Proof failed: {result['message']}")
if "action_required" in result:
print(f"Action: {result['action_required']}")
if "unfinished_entries" in result:
for entry in result["unfinished_entries"]:
print(f" {entry['name']}: {entry['hint']}")
if "parse_error" in result:
pe = result["parse_error"]
print(f" Parse error at line {pe.get('line')}, col {pe.get('column')}")
Handling the Startup Overhead
Since each request takes ~2 minutes, consider:
- Batch multiple entries in a single
.kyxfile - Set appropriate timeouts in your HTTP client (at least 3 minutes)
- Use async endpoint for very long proofs and poll periodically
Configuration
Environment variables for the container:
| Variable | Default | Description |
|---|---|---|
DEFAULT_TIMEOUT |
120 | Default proof timeout in seconds |
MAX_TIMEOUT |
600 | Maximum allowed timeout |
STARTUP_OVERHEAD |
180 | Buffer added for JVM startup |
KEYMAERAX_JAR |
/app/keymaerax.jar | Path to KeYmaera X JAR |
WORKDIR |
/app/workdir | Directory for temp files |
Example with custom config:
docker run -p 8080:8080 \
-e DEFAULT_TIMEOUT=300 \
-e MAX_TIMEOUT=900 \
keymaerax-api
Troubleshooting
Proof Times Out
The timeout parameter controls only the proof time, not the total request time. Total time = startup (~2 min) + proof time.
Solutions:
- Increase timeout:
{"timeout": 300} - Simplify proof with intermediate lemmas using
cut("fact") - Use
hideL(-n)to remove unnecessary hypotheses beforeQE - Replace
masterautomation with manual proof steps
QE Fails or is Slow
Z3 is less powerful than Mathematica for real arithmetic. For complex quantifier elimination:
- Break into smaller steps:
cut("x > 0"); <(QE, ...) - Remove unused hypotheses:
hideL(-1); hideL(-2); QE - Add explicit bounds as assumptions in your model
Parse Errors
The response includes line/column information. Common issues:
| Error | Fix |
|---|---|
| Missing semicolon | Assignments need ;: x := e; |
| Brace mismatch | Check {...}* for loops |
| Wrong arrow | Use -> not => for implication |
| Undefined variable | Declare in ProgramVariables block |
Container Issues
# Check container logs
docker logs keymaerax-api
# Verify Java and Z3 work
docker exec keymaerax-api java -version
docker exec keymaerax-api z3 --version
# Test KeYmaera X directly
docker exec keymaerax-api java -jar /app/keymaerax.jar -help
Architecture
┌─────────────────────────────────────────────────────────┐
│ Docker Container │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Flask API (server.py) │ │
│ │ - /prove/sync, /prove, /status, /parse, etc. │ │
│ └──────────────────────┬──────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ KeYmaera X (keymaerax.jar) │ │
│ │ - JDK 17 runtime │ │
│ │ - Spawned as subprocess per request │ │
│ └──────────────────────┬──────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Z3 Solver │ │
│ │ - System package (ARM/x86 compatible) │ │
│ │ - Used for quantifier elimination (QE) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file keymaerax-0.3.0.tar.gz.
File metadata
- Download URL: keymaerax-0.3.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48c694867e6eb2b77accde6c32a085442a44b49e641766a8a9d6049b8944e563
|
|
| MD5 |
829663c27d3bb6afff39ce3fc73a8aa9
|
|
| BLAKE2b-256 |
40c77ba9b88a382253c3f14ba46161b169a181121187d2ec805668391d0ab818
|
File details
Details for the file keymaerax-0.3.0-py3-none-any.whl.
File metadata
- Download URL: keymaerax-0.3.0-py3-none-any.whl
- Upload date:
- Size: 21.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e382d1472da37f05fc8319648fa45c5ea52fe78028710e869e74bfbf04bf136
|
|
| MD5 |
7ed17c872722c23a35cddb369d442dcf
|
|
| BLAKE2b-256 |
e34d9c3deb1c9c97cd04a090b6eba835f3cf8e91c62629ef0c44513700368118
|