Running the CalcpadCE Server¶
The desktop app and the VS Code extension start the CalcpadCE calculation server for you, so most people will never run it by hand. However, people who are familiar with coding can also run it directly — for example, to point several tools at one shared instance, or to script conversions and calls against its API. This page covers running the server and the API it exposes.
Localhost only. This build runs the server bound to your own machine (
localhost,127.0.0.1, or::1) only. If you point it at any other address, it refuses to start. There is no multi-user hosting, user-based authentication, or shared file storage in this build.
Running¶
cd Calcpad.Web/backend
dotnet run
With no port set, the server takes a free port from the operating system rather than a fixed one, so two instances never collide.
The port it picked is on the Now listening on: line in the startup output, and is also written to .calcpad-server.port next to the binary (bin/Debug/net10.0/ for a dotnet run launch) for as long as the server is up.
To pin the port instead, set CALCPAD_PORT:
CALCPAD_PORT=9420 dotnet run
That serves http://127.0.0.1:9420.
CALCPAD_HOST changes the host part and defaults to 127.0.0.1; passing a full bind URL with --urls works too.
Either way the address has to stay on a loopback interface, or the server refuses to start.
Launching from a script¶
The server watches its stdin and exits when it reaches EOF, so that it doesn't outlive the process that spawned it.
That means a launch with redirected or piped stdin (dotnet run | tee, most CI steps) shuts down immediately.
Set CALCPAD_DETACHED=1 to opt out and keep the server running independently of its parent — this is what the VS Code extension does so one server can be shared across windows.
Authentication¶
When CALCPAD_API_TOKEN is set, every /api request must present that value in an X-Calcpad-Token header, or it is rejected with 401.
The desktop app and the VS Code extension always set it, so a server they launched will not answer an unauthenticated request.
A bare dotnet run leaves the variable unset, and the API is then open to anything on the machine.
Two restrictions apply either way:
- Requests whose
Hostheader is not a loopback name are rejected with421, which blocks DNS-rebinding attacks from a browser. - Browser requests are only accepted from loopback origins and the desktop app's own
tauri://origin. Native callers send noOriginheader and are unaffected.
API endpoints¶
All paths are relative to /api/calcpad.
| Endpoint | Method | Purpose |
|---|---|---|
/convert |
POST | Convert a document to an HTML report (with theme + settings) |
/docx |
POST | Generate a Word .docx document |
/pdf |
POST | Generate a PDF from rendered HTML |
/pdf/health |
GET | PDF service health check |
/pdf/browser |
GET | Which browser PDF export would use, and whether one is available |
/pdf/browser/install |
POST | Download the bundled headless Chromium |
/sample |
GET | Fetch a sample document |
/highlight |
POST | Tokenize a full document for syntax highlighting |
/highlight-line |
POST | Tokenize a single line (incremental) |
/lint |
POST | Run the linter and return diagnostics |
/definitions |
POST | List macros, functions, variables, and units |
/symbol-at-position |
POST | The symbol under a cursor position and all its occurrences |
/prettify |
POST | Pretty-print CalcpadCE source |
/snippets |
GET | Snippets, optionally filtered by category |
/cpdz/decode |
POST | Decode a compiled .cpdz worksheet to its source |
/cpdz/encode |
POST | Encode source as a .cpdz worksheet |
/portable/bundle |
POST | Rewrite a worksheet into self-contained form, ready to compile |
/portable/package |
POST | Pack a worksheet and the files it references into a ZIP |
/debug-crash |
GET | Crash-path testing. Only served in the Development environment |
Pass ?unwrap=true to /convert for HTML of the raw, fully expanded source, with its line links rewritten for error navigation.
The full request/response schema lives at Calcpad.Web/backend/API_SCHEMA.md; the common shapes are summarized below.
Common request fields¶
/convert and /docx take a document request:
content— the contents of the CalcpadCE filesettings— math / plot / unit configurationtheme—"light"or"dark"sourceFilePath— the document's file path, used to resolve relative#includeand#readpaths against the file's folderforPrint— whentrue,#preregions are hidden and#postregions shown (used by PDF export)enableUi— whentrue,#UIlines render as interactive controls and#postis hiddenuiOverrides— values entered into#UIcontrols, keyed by the control identity the preview reports indata-ui-varincludeLineAnchors— emits the per-line anchors and error boxes the preview navigates by. Defaults to the opposite offorPrinthideErrorLines— drops the "on line [N]" reference from error messages. Defaults toenableUiwrite— whether this request may run#write/#append.falseby default, so a preview refresh doesn't rewrite output on every keystroke
The analysis endpoints (/highlight, /lint, /definitions, /symbol-at-position) take content and sourceFilePath only.
Response shapes¶
Convert¶
text/html — the rendered document, not JSON.
Calculation errors come back alongside it in an X-Calcpad-Errors response header, URL-encoded JSON of:
Array<{ sourceLine, outputLine, message, source: "Macro" | "Expression" }>
Definitions¶
Four parallel arrays, plus the resolved projectPath and libraryPath when the document's #include chain declares them:
macros[]— name, parameters, isMultiline, content, lineNumber, source, sourceFile, description, paramTypes, paramDescriptionsfunctions[]— name, parameters, expression, returnType, returnTypeId, hasCommandBlock, commandBlockType, commandBlockStatements, lineNumber, source, sourceFile, description, paramTypes, paramDescriptionsvariables[]— name, expression, type, typeId, lineNumber, source, sourceFile, descriptioncustomUnits[]— name, expression, lineNumber, source, sourceFile, description
source is "local" or "include"; sourceFile names the file for the latter.
typeId values: 0 Unknown, 1 Value, 2 Vector, 3 Matrix, 4 CustomUnit, 5 Function, 6 InlineMacro, 7 MultilineMacro, 8 Various.
Symbol-at-position¶
Takes line and column alongside the content, and resolves the user-defined symbol under that cursor position.
One round-trip serves go-to-definition, find-all-references, and rename:
{
symbolName: string,
kind: "variable" | "function" | "macro",
locations: Array<{ line, column, length, source, sourceFile?, isAssignment }>
}
isAssignment: true marks a definition or reassignment.
The response is null when no symbol sits under the position.
Highlight¶
{ tokens: Array<{ line, column, length, type, typeId, text? }> }
The text field is omitted by default; pass includeText: true to include it.
Lint¶
{
errorCount: number,
warningCount: number,
diagnostics: Array<{
line: number, column: number, endColumn: number,
code: string, // "CPD-XXXX"
message: string,
severity: "error" | "warning" | "information",
severityId: 0 | 1 | 2,
source: "Calcpad Linter"
}>
}
See Linter and Diagnostics for what each code means.
Snippets¶
{
count: number,
snippets: Array<{
insert: string, // § marks cursor placement
description: string,
documentation?: string,
example?: string,
label?: string,
category: string, // e.g. "Functions/Trigonometric"
quickType?: string, // e.g. "a" for ~a → α
keywordType?: string, // "Function", "Keyword", "Command", "Constant", "Unit", ...
returnType?: string,
returnTypeDescription?: string,
isElementWise: boolean,
acceptsAnyCount: boolean,
parameters?: Array<{ name, description?, type?, typeDescription?, isOptional, isVariadic }>
}>
}
Filter with a query string, e.g. ?category=Functions/Trigonometric.