archi-server
Headless Archi · REST

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.

Archi 5.6.0 jArchi 1.11.0 coArchi 0.9.7

# Overview

One HTTP call, one isolated Archi run.

POST /api/run -> queue (semaphore) -> Archi ACLI + jArchi + coArchi -> stdout + result file -> JSON response

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"
}
FieldTypeDescription
correlationIdstringEchoed back in the response.
scriptstringjArchi source, run with --script.runScript.
modelstringResolved 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"
  }'
FieldDescription
viewView id (#id) or exact name. Mutually exclusive with folder.
folderModel folder name or Parent/Child path; exports every view under it.
directionLR (default) | RL | TB | BT.
relationshipsInclude 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

MethodPath
GET/api/models
POST/api/models
POST/api/models/:id/sync
DELETE/api/models/:id

Scripts

MethodPath
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"
  }'
FieldRequiredDescription
urlyeshttp(s):// git URL. A token may be embedded (https://oauth2:TOKEN@host/org/repo.git). SSH is not supported.
idnoFolder name. Derived from the URL's last segment. Must match [A-Za-z0-9][A-Za-z0-9._-]*.
branchnoBranch to check out; defaults to the remote HEAD.
usernamenoDefaults to oauth2 when only a token is given (works for GitHub and GitLab).
tokennoAccess 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

Tokens are never written to .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.
StatusWhen
400Invalid body, non-http(s) URL, bad id or branch.
404Unknown repository id.
409Id exists, folder is not a git repo, or detached HEAD.
502git failed (its stderr is returned in details).
504git exceeded ARCHI_GIT_TIMEOUT_MS.

# Configuration

Everything is environment driven.

VariableDefaultDescription
PORT3000HTTP port.
HOST0.0.0.0Bind address.
ARCHI_HOME/opt/archiArchi install root.
ARCHI_BINARY$ARCHI_HOME/ArchiLauncher path.
ARCHI_SCRIPTS_DIR/scriptsjArchi user scripts volume.
ARCHI_MODELS_DIR/modelsModel volume.
ARCHI_RUNS_DIR/var/archi/runsPer-run scratch directory.
ARCHI_XMS256mJVM initial heap (written into Archi.ini).
ARCHI_XMX2gJVM max heap (written into Archi.ini).
ARCHI_TIMEOUT_MS120000Kill a run after this long.
ARCHI_MAX_CONCURRENCY1Parallel Archi runs (each is a JVM + display).
ARCHI_RESULT_MAX_BYTES26214400Max result file returned.
ARCHI_KEEP_RUN_DIRSfalseKeep per-run directories for debugging.
API_KEYunsetWhen set, requests must send x-api-key.
LOG_LEVELinfodebug | 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.

ComponentVersionRequirementReason
Archi5.6.0JRE 21Requested base.
jArchi1.11.0Archi ≥ 5.5.0jArchi 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.
coArchi0.9.7Archi ≥ 4.9.0Latest 0.9.x, compatible with 5.6.
If jArchi 1.12 is mandatory, move the base image to Archi 5.8+ (coArchi 0.9.7 still fits).

Build jArchi from source

# requires git + maven; compiles tag v1.11.0 against the local Archi install
scripts/build-plugins.sh

# Notes & limitations