Query Archi models on demand.
A small TypeScript + Node.js service that drives Archi 5.6 in headless mode with the jArchi scripting and coArchi collaboration plugins, and returns the script output as JSON.
# Overview
One HTTP call, one isolated Archi run.
Isolated runs
Each request gets its own scratch directory and workspace, so concurrent runs never share state.
Bounded resources
A semaphore caps parallel JVMs and a hard timeout kills runaway scripts.
Git-backed repos
Models and scripts are cloned and synced as sub-folders of /models and /scripts, over HTTPS with an access token.
flowchart LR
client(["API client"]) -->|"POST /api/run"| server["archi-server"]
client -->|"repo CRUD + sync"| server
server -->|"spawn"| archi["Archi ACLI (jArchi + coArchi)"]
archi -->|"stdout + resultFile"| server
server -->|"JSON"| client
subgraph volumes ["Mounted volumes"]
direction TB
scripts["/scripts - script repos (git)"]
models["/models - model repos + archimate files"]
end
server -.->|"clone / fetch / pull"| scripts
server -.->|"clone / fetch / pull"| models
# Quickstart
Fetch the binaries, build once, run anywhere.
1. Fetch Archi and coArchi
# downloads into vendor/ (git-ignored)
scripts/fetch-archi.sh
jArchi's binary is not publicly downloadable. Drop the provided
*.archiplugin into vendor/plugins/,
set JARCHI_URL, or build it from source (see below).
2. Build and run with Docker Compose
docker compose up --build
3. Call the API
curl -s http://localhost:3000/api/run \ -H 'content-type: application/json' \ -d '{ "correlationId": "demo-1", "model": "my-model", "script": "archiServer.setResult({ elements: $(\".archimate-element\").size() });" }'
# API
Run a script, or manage the git repos behind it.
POST /api/run
Execute a jArchi script against a model and return its output.
Request
{
"correlationId": "string",
"script": "jArchi script source",
"model": "path inside the /models volume"
}
| Field | Type | Description |
|---|---|---|
correlationId | string | Echoed back in the response. |
script | string | jArchi source, run with --script.runScript. |
model | string | Resolved inside /models. A directory loads as a coArchi repository (--modelrepository.loadModel); a file loads with --loadModel. Path traversal is rejected. |
Response
{
"correlationId": "demo-1",
"stdout": "...",
"stderr": "...",
"exitCode": 0,
"result": {
"file": "/var/archi/runs/run-xxxx/result.json",
"encoding": "json",
"content": { "any": "parsed JSON, text, or base64" },
"size": 42,
"truncated": false
}
}
result is null when a script does not
advertise a result file. A run that exceeds the timeout returns 504
with the same shape.
GET /health
Returns { "status": "ok" }. Used by the container healthcheck.
GET /openapi.json
OpenAPI 3.0 document describing every endpoint. Point your own Swagger UI or client generator at it.
Export views to Markdown + Mermaid
POST /api/export/mermaid
Run a bundled jArchi script that turns one view, a model folder, or the whole model into Markdown with a Mermaid flowchart per view.
curl -s http://localhost:3000/api/export/mermaid \ -H 'content-type: application/json' \ -d '{ "correlationId": "export-1", "model": "my-model", "view": "Customer Journey", "direction": "LR" }'
| Field | Description |
|---|---|
view | View id (#id) or exact name. Mutually exclusive with folder. |
folder | Model folder name or Parent/Child path; exports every view under it. |
direction | LR (default) | RL | TB | BT. |
relationships | Include relationships as edges (default true). |
Omit view and folder to export all views. The response adds a top-level markdown field; 404 means the model is missing or no view matched.
Result convention
A script advertises a result file by printing a JSON object with a
resultFile key (aliases: result_file,
result). Relative paths resolve against the run directory.
Every script gets a small prelude:
archiServer = { correlationId: "demo-1", runDir: "/var/archi/runs/run-xxxx", resultFile: "/var/archi/runs/run-xxxx/result.json", // writes result.json and prints { "resultFile": ... } setResult: function (value) { /* ... */ } };
sequenceDiagram
autonumber
participant C as Client
participant S as archi-server
participant A as Archi ACLI
C->>S: POST /api/run
S->>S: validate + acquire semaphore
S->>S: write script.ajs (+ prelude)
S->>A: runScript on --loadModel / --modelrepository.loadModel
A-->>S: stdout, stderr, exit code
A->>S: resultFile printed to stdout
S->>S: read + decode result file
S-->>C: 200 {correlationId, stdout, stderr, exitCode, result}
# Repositories
Models and scripts are git-managed sub-folders of the mounted volumes. Add over HTTPS with an access token, sync, and remove by id.
flowchart LR
add["POST /api/models clone url"] --> ready["/models/{id} git repo"]
ready --> run["POST /api/run model = id"]
ready --> sync["POST /api/models/:id/sync fetch + ff-only"]
sync --> ready
ready --> del["DELETE /api/models/:id"]
Models
| Method | Path |
|---|---|
| GET | /api/models |
| POST | /api/models |
| POST | /api/models/:id/sync |
| DELETE | /api/models/:id |
Scripts
| Method | Path |
|---|---|
| GET | /api/scripts |
| POST | /api/scripts |
| POST | /api/scripts/:id/sync |
| DELETE | /api/scripts/:id |
Add a repository
curl -s http://localhost:3000/api/models \ -H 'content-type: application/json' \ -d '{ "url": "https://git.example.com/org/my-model.git", "id": "my-model", "branch": "main", "username": "oauth2", "token": "glpat-xxxxxxxx" }'
| Field | Required | Description |
|---|---|---|
url | yes | http(s):// git URL. A token may be embedded (https://oauth2:TOKEN@host/org/repo.git). SSH is not supported. |
id | no | Folder name. Derived from the URL's last segment. Must match [A-Za-z0-9][A-Za-z0-9._-]*. |
branch | no | Branch to check out; defaults to the remote HEAD. |
username | no | Defaults to oauth2 when only a token is given (works for GitHub and GitLab). |
token | no | Access token, sent as the password. |
Returns 201 with the repository object, or 409 if the id exists:
{
"id": "my-model",
"kind": "model",
"path": "/models/my-model",
"url": "https://git.example.com/org/my-model.git",
"branch": "main",
"commit": "7fd1a60b01f9",
"subject": "Merge pull request #6",
"date": "2012-03-06T15:06:50-08:00",
"dirty": false
}
Sync & remove
- POST /:id/sync:
git fetch --prunethen a fast-forward-only merge. Returns the object plusupdatedandpreviousCommit. Non-fast-forward local commits are refused. - DELETE /:id: deletes the folder and returns
204.
.git/config: the remote URL is stored sanitized and the token lives in <repo>/.git/archi-server-credentials.json (mode 0600) so sync stays non-interactive. git runs through an askpass helper, so the token never appears in process arguments.
| Status | When |
|---|---|
400 | Invalid body, non-http(s) URL, bad id or branch. |
404 | Unknown repository id. |
409 | Id exists, folder is not a git repo, or detached HEAD. |
502 | git failed (its stderr is returned in details). |
504 | git exceeded ARCHI_GIT_TIMEOUT_MS. |
# Configuration
Everything is environment driven.
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | HTTP port. |
HOST | 0.0.0.0 | Bind address. |
ARCHI_HOME | /opt/archi | Archi install root. |
ARCHI_BINARY | $ARCHI_HOME/Archi | Launcher path. |
ARCHI_SCRIPTS_DIR | /scripts | jArchi user scripts volume. |
ARCHI_MODELS_DIR | /models | Model volume. |
ARCHI_RUNS_DIR | /var/archi/runs | Per-run scratch directory. |
ARCHI_XMS | 256m | JVM initial heap (written into Archi.ini). |
ARCHI_XMX | 2g | JVM max heap (written into Archi.ini). |
ARCHI_TIMEOUT_MS | 120000 | Kill a run after this long. |
ARCHI_MAX_CONCURRENCY | 1 | Parallel Archi runs (each is a JVM + display). |
ARCHI_RESULT_MAX_BYTES | 26214400 | Max result file returned. |
ARCHI_KEEP_RUN_DIRS | false | Keep per-run directories for debugging. |
API_KEY | unset | When set, requests must send x-api-key. |
LOG_LEVEL | info | debug | info | warn | error. |
Volumes
/scripts → jArchi user scripts folder (git-managed script repos).
/models → coArchi repositories and/or .archimate files.
Heap handling
The entrypoint injects -Xms/-Xmx into Archi.ini at start, so no JVM flags leak onto stderr.
# Plugins
Why these exact versions, and how to build jArchi yourself.
| Component | Version | Requirement | Reason |
|---|---|---|---|
| Archi | 5.6.0 | JRE 21 | Requested base. |
| jArchi | 1.11.0 | Archi ≥ 5.5.0 | jArchi 1.12 requires Archi ≥ 5.8, so it cannot run on 5.6. 1.11.0 ships the same GraalVM 24.1.2 JS engine. |
| coArchi | 0.9.7 | Archi ≥ 4.9.0 | Latest 0.9.x, compatible with 5.6. |
Build jArchi from source
# requires git + maven; compiles tag v1.11.0 against the local Archi install
scripts/build-plugins.sh
- Publishes the Archi install as a p2 repository used as the Tycho target platform.
- Produces vendor/plugins/com.archimatetool.script*.jar.
# Notes & limitations
- Each run starts a fresh JVM. Keep ARCHI_MAX_CONCURRENCY low.
- The image runs as non-root user archi (uid 1001, override with --build-arg ARCHI_UID=$(id -u)). /models and /scripts must be writable by that uid so repositories can be cloned and synced.
- Repository auth is HTTPS + access token only (no SSH). Tokens live under <repo>/.git/ with mode 0600.
- CI typechecks and runs the unit tests on every push and PR; the image is published to GHCR only on v* tag commits.
- Directory models must already be coArchi/GRAFICO repositories (contain model/folder.xml); otherwise coArchi reports an error in stderr.
- Archi writes a harmless SLF4J(W): No SLF4J providers were found. line to stderr. Silence it with JAVA_TOOL_OPTIONS=-Dslf4j.internal.verbosity=ERROR if desired.