Running APL as an Isolated Compute Worker: Architecture and Safety Checks
Run rank-polymorphic APL in a stateless worker: validate array shape before evaluation, sandbox file and network access, enforce resource limits, and emit JSON-serializable output only.
30 Oct 2025, 05:34 UTC

Problem: a team wants to offload array-heavy computations to APL inside a service, but APL source is executable and dialect defaults vary. A worker that accepts untrusted expressions, shares a persistent workspace, or ignores shape mismatches can execute arbitrary code, leak state, or return confusing rank errors instead of actionable failures.
Takeaway: use one stateless APL worker per request, validate rank, type, and shape before evaluation, sandbox file-system and network access, enforce CPU, memory, and time limits, and return only JSON-serializable arrays. Verify the behavior in the installed dialect before production use; primitives, system functions, index origin, and print precision are dialect-specific.
Requirements
- Evaluate rank-polymorphic array expressions: functions apply to arrays of any rank, with scalar extension where shapes conform.
- Accept a read-only input schema only; no user-supplied function definitions and no workspace mutation.
- Return output that serializes to JSON: numbers, strings, booleans, and nested arrays. No functions, namespaces, file handles, or workspace objects.
- Enforce resource limits and surface errors as structured responses, not interpreter crashes.
- Make results reproducible by fixing index origin and print precision for the dialect.
"Rank-polymorphic" means an APL function can operate on a scalar, vector, matrix, or higher-dimensional array without being rewritten for each shape. "Conformable" means array shapes match or one operand is a scalar, so scalar extension applies. "Index origin" is whether the first element of an array is at index 0 or 1; it is a dialect setting, not a universal constant.
Smallest suitable design
Start with a stateless worker that loads a fresh workspace for each request, evaluates one fixed expression or a small allowed subset, then exits. The worker should not persist variables between requests.
# Illustrative configuration only; confirm names and defaults for your dialect.
APL_DIALECT=dyalog
APL_INDEX_ORIGIN=0
APL_PRINT_PRECISION=17
MAX_CPU_SECONDS=5
MAX_MEMORY_BYTES=268435456
ALLOWED_PRIMITIVES="+ - × ÷ ⌈ ⌊ * ⍟ = ≠ < ≤ > ≥ ∊ ⍳ ⍴ ⌽ ⍉
Run the interpreter in a staging container as a non-root user with no write access to the host file system and no outbound network. The command is a placeholder; replace [apl-interpreter] with the installed dialect binary.
[apl-interpreter] --workspace [read-only-workspace] --input [request-json]
Expected checks: the interpreter starts, reports the configured index origin and print precision, evaluates the expression, and exits with a nonzero status on rank or length errors. Risk: if the dialect does not honor these flags, the worker may run with different defaults and produce shape-dependent results that pass local tests but fail in production.
Trust and data boundaries
Treat APL source as executable code. Untrusted APL input is an untrusted code-execution vector, not a data format. The trust boundary is between the request handler and the APL interpreter; everything inside the interpreter is considered compromised if the input is untrusted.
| Boundary element | Control |
|---|---|
| APL source text | Validate rank, type, and shape before evaluation; reject user-defined functions and workspace assignment primitives. |
| File-system access | Sandbox or disable file read/write; allow only the read-only input payload. |
| Network access | Disable outbound connections at the container or OS level. |
| Workspace state | Use a fresh workspace per request; never reuse a persistent workspace across users. |
| Output | Serialize only JSON-safe arrays; drop or reject non-serializable APL objects. |
Operational checks
- Launch the interpreter and display index origin and print precision for the dialect; record these values in worker logs.
- Compare worker output against an independent scalar reference implementation for edge shapes: empty array, scalar, vector, and matrix.
- Log workspace size and elapsed time for every run; alert when limits are breached or a run aborts.
- Alert on rank or length errors; treat them as input-shape failures, not interpreter bugs, until proven otherwise.
Diagnostic decision: if a worker returns a rank error, first check whether the input arrays are conformable and whether the index origin matches the caller's assumption. Do not change the expression until both are confirmed.
Failure modes
| Failure mode | Condition | Check |
|---|---|---|
| Index-origin-dependent indexing | Caller assumes origin 1; worker runs origin 0 | Compare results against scalar reference with both origins. |
| Floating-point rounding in reductions | Summing large arrays or comparing equality after division | Use an agreed tolerance, not exact equality. |
| Workspace-full or time-limit abort | Array exceeds memory or expression runs too long | Alert on limit breaches; return a structured payload-too-large or timeout error. |
| Side effects from system functions | Expression calls file or environment primitives | Disable or sandbox system functions; audit allowed primitives. |
| Non-conformable array error | Shapes do not match and neither is scalar | Validate shape before evaluation; return a 400-style invalid-shape response. |
| State leaking across sessions | Persistent workspace reused between requests | Force a fresh workspace per request and confirm workspace size resets. |
Conditions that would change the design
- Concurrent users require isolation: move from one worker process per request to one sandboxed process per user or per request, with no shared workspace.
- Data exceeds workspace memory: replace in-memory APL arrays with chunked processing or an out-of-core array store; APL workspace limits are a hard boundary.
- Untrusted input is required: do not evaluate arbitrary APL. Either restrict to a validated expression subset or move the computation out of APL.
- Interpreter startup and latency cannot meet the service-level objective: pre-warm a pool of stateless workers or select a different array runtime with lower cold-start cost.
Limitations and verification
This note is planning context, not a tested production design. APL dialects differ: Dyalog APL, GNU APL, and APL2 have different system functions, defaults, and file or network primitives. Before deployment, verify primitives, resource-limit behavior, index origin, and print precision in the installed dialect.
Practical check: launch the interpreter, print index origin and print precision, evaluate one conformable expression and one non-conformable expression, and record workspace size before and after. Confirm the non-conformable expression raises a rank or length error rather than silently broadcasting. Then run the same computation in APL and an independent scalar reference for boundary shapes and compare within an agreed tolerance.