REST API server

The optional snt server command exposes the command-oriented C++ API over HTTP. It uses the same snt::api commands as the command-line tool; PUEL evaluation and DIPL parsing therefore retain the same semantics.

Getting started

Enable the server explicitly when configuring SNT:

cmake -G Ninja -B build -DENABLE_SNT_SERVER=ON
cmake --build build --target snt
./build/bin/snt server --port 8081

The server listens on 127.0.0.1:8080 by default. Pass --port PORT to choose another port, for example when the default is already occupied. --address ADDRESS changes the listening address; it is intended for container deployments, where 0.0.0.0 accepts connections from outside the container. --help shows the available program options. It returns JSON and has no authentication or authorization layer. Run it only for trusted local users unless a reverse proxy or equivalent deployment boundary supplies those controls.

OpenAPI document

The server exposes an OpenAPI 3.1 document at /openapi.json. Load http://127.0.0.1:8080/openapi.json in Swagger UI, Swagger Editor, or any other OpenAPI-compatible client to browse and invoke the API.

Quantities and units

All PUQ endpoints use GET and return {"result":"..."} on success. Optional query parameters are named after the corresponding API command arguments: input_system, output_system, output_units, and output_quantity.

Unit-system names select the interpretation of unit symbols; they are not prefixes within a PUEL unit name. For example, use mi together with input_system=US, not US_mi.

$ curl --get http://127.0.0.1:8080/snt/puq/eval \
    --data-urlencode 'expression=23*cm + 3*m'
{"result":"3.23*m"}

$ curl --get http://127.0.0.1:8080/snt/puq/convert \
    --data-urlencode 'expression=3.048*m' \
    --data-urlencode 'output_system=US' \
    --data-urlencode 'output_units=ft'
{"result":"10*ft"}

$ curl --get http://127.0.0.1:8080/snt/puq/convert \
    --data-urlencode 'expression=1*mi' \
    --data-urlencode 'input_system=US' \
    --data-urlencode 'output_system=SI' \
    --data-urlencode 'output_units=km'
{"result":"1.60934*km"}

/snt/puq/info requires expression and optionally accepts input_system. /snt/puq/list accepts optional list and system parameters. The list names are prefix, base, deriv, log, temp, const, quant, and sys.

DIPL parameters

Send a DIPL document in the request body to POST /snt/dip/parse. This can be inline text or a client-side file uploaded with curl --data-binary; the server receives its contents and adds them as DIPL text. By default, the response prints all resolved nodes. request selects a path, tags is a comma-separated tag list, and value=true requests one unitless scalar; type may then constrain it to bool, integer, float, or string.

$ curl --request POST http://127.0.0.1:8080/snt/dip/parse \
    --data-binary $'length float = 2.5 m\nwidth float = 40 cm\narea float = ({?length} * {?width}) m2'
{"result":"length = 2.5 m\nwidth = 40 cm\narea = 1 m2\n"}

$ curl --request POST http://127.0.0.1:8080/snt/dip/parse \
    --data-binary @model.dipl
{"result":"..."}

$ curl --request POST 'http://127.0.0.1:8080/snt/dip/parse?request=answer&value=true&type=integer' \
    --data-binary @model.dipl
{"result":"42\n"}

Project bundles

For an ordered multi-input environment, submit a standard DIPfile project and its referenced files as multipart form uploads. The project part is the DIPfile; every file part is written under a request-scoped temporary directory using its supplied relative filename. The server then invokes the ordinary add_project API. Thus the manifest retains its native units[], sources[], schemas[], overrides[], and ordered code[] semantics; no second REST-specific project format exists.

For example, if DIPfile refers to parameters/base.dip:

$ curl --request POST http://127.0.0.1:8080/snt/dip/parse \
    --form 'project=@DIPfile' \
    --form 'file=@parameters/base.dip;filename=parameters/base.dip'
{"result":"answer = 42\n..."}

Upload source, schema, and override files in exactly the same way, using the relative paths declared by the project’s sources[].filepath, schemas[].file, and overrides[].file values. Absolute paths, . and .. path components, duplicate paths, extra form fields, and unknown part names are rejected. The temporary bundle directory is removed after the request, whether parsing succeeds or fails. The endpoint therefore never resolves a path supplied by a caller outside its isolated request bundle.

Registering schemas

POST /snt/dip/parse accepts a multipart request with exactly one code part and named schema:<name> parts. Each schema part contains a schema body without a $schema wrapper. Both inline fields and uploaded files are accepted; the part name supplies the schema name, independently of the filename.

curl http://127.0.0.1:8080/snt/dip/parse \
    --form 'schema:settings=@settings.dipl' \
    --form-string 'code=physics : settings'

Schemas are registered before the code is parsed. Duplicate or empty schema names, multiple code parts, and mixing these parts with a project bundle are rejected. Existing query options and output=diph5 also work with this input. Raw DIPL request bodies and project bundles remain supported.

Overriding values

Multipart DIPL requests accept override fields or file parts alongside the required code part and optional schema:<name> parts. Each override part contains an unwrapped body of path = value modifications. Repeated parts are allowed, but repeated target paths are errors.

curl --form 'code=@parameters.dip' \
    --form-string 'override=simulation.steps = 1024' \
    'http://localhost:8080/snt/dip/parse'

Project bundles also accept override fields or files alongside their project and file parts. The uploaded model files need no edits. Plain DIPL request bodies accept top-level $override regions.

Published environments

For deployed models, start the server with a named project or DIPH5 input:

snt server --project model=/srv/model/DIPfile
snt server --diph5 model=/srv/model/environment.diph5

--project NAME=PATH parses the ordinary DIPfile project once at startup; --diph5 NAME=PATH loads a previously evaluated environment once. Either option may be repeated for multiple names. The inputs are then read-only and are never selected by HTTP-supplied file paths. Restart the server to load a changed mounted file.

GET /snt/dip/environments lists published names. Retrieve all or part of one environment with GET /snt/dip/environment using name and the same request, tags, value, and type query parameters used by the parse endpoint:

$ curl http://127.0.0.1:8080/snt/dip/environments
{"environments":["model"]}

$ curl --get http://127.0.0.1:8080/snt/dip/environment \
    --data-urlencode 'name=model' \
    --data-urlencode 'request=answer' \
    --data-urlencode 'value=true' \
    --data-urlencode 'type=integer'
{"result":"42\\n"}

DIPH5 response

Add output=diph5 to receive the complete evaluated environment as an application/x-hdf5 attachment. The server writes it only in the request-scoped temporary directory, returns its bytes, and then removes the directory:

$ curl --request POST 'http://127.0.0.1:8080/snt/dip/parse?output=diph5' \
    --data-binary @model.dipl \
    --output environment.diph5

output=diph5 cannot be combined with value=true. It always contains the complete environment; request and tag filters apply only to textual output and therefore do not filter the DIPH5 result.

The REST endpoint does not expose DIPH5 loading, direct server-side save paths, static parameter code generation, or direct server-side file paths.

Generating reports

POST /snt/dip/report accepts the same DIPL body or multipart project bundle as /snt/dip/parse and returns a TeX attachment by default. format also accepts pdf, md, rst, html, typ, txt, and json. Brief++ renders the text formats without external tools. PDF requires pdflatex on the server host. JSON is a briefpp/1 document tree. Cover metadata may be supplied through title, author, date, version, and input_label query parameters. The report includes all evaluated parameters, irrespective of parse result filters. Uploaded project files are kept in a request-scoped temporary directory.

$ curl --request POST http://127.0.0.1:8080/snt/dip/report \
    --data-binary @model.dipl --output report.tex

$ curl --request POST \
    'http://127.0.0.1:8080/snt/dip/report?format=pdf&title=Example&author=Research%20Team' \
    --data-binary @model.dipl --output report.pdf

$ curl --request POST 'http://127.0.0.1:8080/snt/dip/report?format=html' \
    --data-binary @model.dipl --output report.html

The default title is DIP parameter report; an empty author appears as Not specified. Date and version default to the local generation date and SNT build version. See the CreateReport example for the report layout. This endpoint accepts DIPL and project bundles rather than saved DIPH5 files.

Errors

Invalid parameters, expressions, or DIPL input return HTTP 400 with a JSON error string. For example:

{"error":"Missing required query parameter: expression"}