Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ crates/
| `KR0KI_SYSMLV2_BASE_URL` | unset | Live SysML-v2 client test target (`just sysml-api-up` serves one on `127.0.0.1:9000`; `just test-live-sysml`) |
| `OPENAI_API_URL` | (in `.env`) | The agent's / `/health`'s LLM. Boot default is Qwen3.8 NEO-CODER + mmproj (vision) on `127.0.0.1:8002/v1` via `deploy/systemd/b00t-hive-inference-heretic-neo-coder.service`; Qwen3.6 units are disabled |
| `KR0KI_IDENTITIES_FILE` | unset | Per-identity bearer tokens (stored as SHA-256) and capability grants (`render`, `model.read`, `model.commit`, `assurance.read`, `assurance.propose`, `assurance.verify`, `audit.read`, `ui.steer`, `admin`). When set it **replaces** the shared `KR0KI_AUTH_TOKEN`; every route is classified in `kr0ki-server/src/gateway.rs` and an unclassified route needs `admin`. A tool's visibility or annotations are never what enforces anything |
| `KR0KI_PUBLIC_UI` | unset (off) | `1`/`true`: serve the static `/playbook` UI **without a token** (a browser navigation cannot send an `Authorization` header, so with per-identity grants on the page could not otherwise load). GET only, exact static routes only (`gateway::PUBLIC_UI_PATTERNS`); every data route still needs a token. Enter the token under Playb00k Setup (kept in `sessionStorage` only). Other Playb00k tabs still call the server without a token, so with grants on they need an `anonymous`-style open server or will show auth errors; the **Assurance** tab sends the token |
| `KR0KI_AUDIT_FILE` | `./.kr0ki-audit/audit.jsonl` when identities are set | Hash-chained, append-only audit log (one record per invocation, permitted or denied). If a record cannot be written the request is refused (503) |
| `KR0KI_ASSURANCE_BASELINE` / `KR0KI_REPO_ROOT` / `KR0KI_EVIDENCE_DIR` / `KR0KI_ASSURANCE_PREFIX` | `docs/assurance/kr0ki.assurance.toml` / `.` / `./.kr0ki-evidence` / `kr0ki:assurance:` | Enable `/assurance/*` (the assurance thread) and where its baseline, repository and evidence live |
| `KR0KI_SYSMD_URL` | unset | SysMD constraint-solver sidecar (`containers/kr0ki-sysmd`, `127.0.0.1:8081`, `just sysmd-image` then `just sysmd-up`); enables `POST /sysmd/solve` (MCP `solve_constraints`) and the `sysmd` health check. See `docs/SYSMD-SIDECAR.md` |
| `KR0KI_SYSML_MCP_URL` | unset | SysML v2 MCP sidecar (`containers/kr0ki-sysml-mcp`, `127.0.0.1:8790`); enables `/sysml/{validate,parse,symbols,summary}` and the `sysml_mcp` health check |

---
Expand Down
1 change: 1 addition & 0 deletions containers/kr0ki-sysmd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sysmd.jar
12 changes: 12 additions & 0 deletions containers/kr0ki-sysmd/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# kr0ki sidecar: SysMD (tukcps/SysMD, interval constraint solver) running headless on :8081. Black box: kr0ki talks REST only.
# Build: `just sysmd-image` (builds the bootJar from the PromptExecution/SysMD fork, then this image). Needs Java 25.
FROM docker.io/library/eclipse-temurin:25-jre
RUN useradd --system --uid 10001 --home-dir /app --shell /usr/sbin/nologin sysmd && mkdir -p /app/SysMD /tmp/sysmd && chown sysmd /app /app/SysMD /tmp/sysmd
WORKDIR /app
COPY sysmd.jar /app/sysmd.jar
USER sysmd
EXPOSE 8081
# Run with: --init --memory 1g --read-only --tmpfs /tmp -v kr0ki-sysmd-data:/app/SysMD:U --cap-drop=ALL --security-opt no-new-privileges
# (SysMD keeps each project as files under /app/SysMD; kr0ki treats that as a throwaway store.)
ENV JAVA_TOOL_OPTIONS="-Xmx512m -Djava.io.tmpdir=/tmp/sysmd -Dlogging.file.name=/tmp/sysmd/sysmd.log -Djava.awt.headless=true"
CMD ["java", "-jar", "/app/sysmd.jar", "headless"]
1 change: 1 addition & 0 deletions crates/kr0ki-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub mod reqif_fetch;
pub mod reqif_import;
pub mod reqif_roundtrip;
pub mod sync_engine;
pub mod sysmd_client;
pub mod sysml_mcp;
pub mod ui_bus;
pub mod verification_runner;
Expand Down
28 changes: 27 additions & 1 deletion crates/kr0ki-core/src/mcp_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub enum McpTool {
ValidateSysml,
SysmlSymbols,
SysmlSummary,
SolveConstraints,
ListRequirements,
GetRequirement,
TraceRequirement,
Expand Down Expand Up @@ -70,6 +71,7 @@ impl McpTool {
Self::ValidateSysml,
Self::SysmlSymbols,
Self::SysmlSummary,
Self::SolveConstraints,
Self::ListRequirements,
Self::GetRequirement,
Self::TraceRequirement,
Expand Down Expand Up @@ -106,6 +108,7 @@ impl McpTool {
Self::ValidateSysml => "validate_sysml",
Self::SysmlSymbols => "sysml_symbols",
Self::SysmlSummary => "sysml_summary",
Self::SolveConstraints => "solve_constraints",
Self::ListRequirements => "list_requirements",
Self::GetRequirement => "get_requirement",
Self::TraceRequirement => "trace_requirement",
Expand Down Expand Up @@ -192,6 +195,13 @@ impl McpTool {
Self::SysmlSummary => {
"Summarise SysML v2 text: counts of elements by kind (part def, requirement, ...), via the SysML MCP sidecar."
}
Self::SolveConstraints => {
"Solve the numeric constraints of a model with the SysMD sidecar (an interval constraint solver). Send SysML v2 / KerML \
text with attributes that have units and ranges; get back each variable's range as a closed interval (already widened \
outward for SysMD's 5-digit rounding, so it contains the true range) with its unit, plus the solver's issues and a \
verdict: consistent, inconsistent (a constraint cannot hold: the values are not a solution) or error (the model did \
not parse). Read-only. A range that is unbounded or empty is reported as such, never as a number."
}
Self::ListRequirements => {
"List the assurance baseline's requirements, each with its status, owner, assurance state (unsatisfied / satisfied_untested / verified / failing / stale) and gap kinds. Filters narrow the result so only what is needed enters your context; call get_requirement for one in full."
}
Expand Down Expand Up @@ -232,6 +242,14 @@ impl McpTool {
"required": ["code"],
"properties": {"code": {"type": "string", "description": "SysML v2 textual notation (max 256 KiB)."}}
}),
Self::SolveConstraints => serde_json::json!({
"type": "object",
"required": ["code"],
"properties": {
"code": {"type": "string", "description": "SysML v2 / KerML / SysMD text (max 256 KiB)."},
"language": {"type": "string", "enum": ["sysml", "kerml", "sysmd"], "description": "Notation of `code`; default sysml."}
}
}),
Self::ListRequirements => serde_json::json!({
"type": "object",
"properties": {
Expand Down Expand Up @@ -433,6 +451,14 @@ impl McpTool {
path_template: "/sysml/summary",
args: &[ArgBinding { name: "code", placement: ArgPlacement::Body }],
},
Self::SolveConstraints => HttpBinding {
method: HttpMethod::Post,
path_template: "/sysmd/solve",
args: &[
ArgBinding { name: "code", placement: ArgPlacement::Body },
ArgBinding { name: "language", placement: ArgPlacement::Query },
],
},
Self::ListDiagramTypes => HttpBinding {
method: HttpMethod::Get,
path_template: "/api/catalog",
Expand Down Expand Up @@ -875,7 +901,7 @@ mod tests {
names.sort_unstable();
names.dedup();
assert_eq!(names.len(), before, "duplicate McpTool name in ALL");
assert_eq!(McpTool::ALL.len(), 32);
assert_eq!(McpTool::ALL.len(), 33);
}

#[test]
Expand Down
Loading
Loading