REST API
LoomCache ships two HTTP surfaces:
- Spring Boot starter — bundled controllers under
/api/*. - Operational helpers — direct-node health checks and host-mounted management handlers.
The legacy in-node /api/v1 data REST ingress has been removed. Use LoomClient/the binary protocol for production AP cache access.
Spring Boot controllers provide the bundled HTTP data/admin surfaces. In production, node-local AP data-structure REST
writes, direct CP REST writes, direct data-structure reads, and operator admin mutations are disabled by default.
Raft-backed REST mutations such as transactions and dynamic config stay available behind their role gates. CP access in
production uses the binary protocol; direct CP HTTP lock/atomic endpoints are non-production only, and
other CP extension requests are not production-supported.
Role hierarchy
Section titled “Role hierarchy”All Spring Boot REST endpoints use Spring Security with three roles. The table describes role authorization before
production safety filters run; loomcache.profile=production still blocks direct data-structure reads/writes,
direct data-structure management reads, and operator admin mutations unless the operation is on an explicitly
release-validated Raft-backed path.
| Role | Access |
|---|---|
ADMIN | Full access to all endpoints including cluster management, config CRUD, and management views. |
USER | Read and write access to data-structure endpoints (maps, queues, sets, lists, etc.). |
READONLY | Read-only access to data-structure inspection and management-read endpoints. |
mTLS-authenticated callers derive their principal from the TLS client certificate common name (CN). The CN is resolved
through loomcache.auth.cert-permissions: ADMIN grants ADMIN, USER, and READONLY; READ_WRITE grants USER
and READONLY; READ_ONLY grants READONLY. When JWT is enabled, roles are embedded in the Bearer token claims. HTTP
Basic is disabled in the bundled server filter chain.
Spring Boot starter (/api)
Section titled “Spring Boot starter (/api)”Bundled Spring Boot endpoint groups:
| Endpoint group | Base path | Verbs |
|---|---|---|
| Maps | /api/map | GET /{key}, PUT /{key}, DELETE /{key}, GET /, GET /size, DELETE / |
| Queues | /api/queue | POST /, POST /poll, GET /peek, GET /size, POST /drain |
| Sets | /api/set | POST /, GET /members/{element}, DELETE /members/{element}, GET /, GET /size, DELETE / |
| Topics | /api/topic | POST /, GET /stats |
| Queries | /api/query | POST / (requires embedded-server beans plus loomcache.client.enabled=true smart-routing client bean and query allow-listing) |
| Cluster operations | /api/cluster | GET /status, POST /state, POST /version, POST /rebalance, GET /members, GET /health (public), GET /health/details (ADMIN), POST /raft/step-down, POST /cp-subsystem/reset, GET /cp-subsystem/groups?offset&limit, GET /cp-subsystem/sessions?offset&limit&detail (break-glass diagnostics), POST /cp-subsystem/sessions/{sessionId}/force-close (destructive break-glass mutation; admin mutations are production-fail-closed unless release-validated on a Raft-backed path) |
| Locks | /api/lock | POST /acquire, POST /release, GET / (non-production controller; not registered under loomcache.profile=production, and still fail-closed with default write-safety settings) |
| Atomic values | /api/atomic | GET /long, PUT /long, POST /long/add, POST /long/increment, POST /long/decrement, POST /long/cas, GET /reference, PUT /reference, DELETE /reference, POST /reference/cas (non-production controller; not registered under loomcache.profile=production, and still fail-closed with default write-safety settings) |
| Multimaps | /api/multimap | POST /{key}, GET /{key}, DELETE /{key}/values, DELETE /{key}, GET /, GET /keys, GET /size |
| Lists | /api/list | POST /, POST /{index}, GET /{index}, PUT /{index}, DELETE /{index}, GET /, GET /size, DELETE / |
| Ring buffers | /api/ringbuffer | POST /, GET /{sequence}, GET /, GET /status, DELETE / |
| Reliable topics | /api/topic-reliable | POST /, GET /, GET /stats |
| Counters | /api/counter | GET /, GET /details, POST /increment, POST /decrement |
| Transactions | /api/transactions | POST /, POST /{id}/operations, POST /{id}/commit, POST /{id}/rollback |
| Dynamic config | /api/config | GET /, GET /{key}, PUT /{key}, DELETE /{key} (ADMIN only) |
| Data-structure inspection | /api/v1/management/data-structures | GET /?type&page&size, GET /{type}/{name}?page&size (ADMIN or READONLY; non-production diagnostics only, production rejects direct data-structure management reads) |
The server validates all inputs before controller execution; invalid payloads return 400 Bad Request with a JSON error body.
Health endpoints
Section titled “Health endpoints”GET /api/cluster/health is Spring Security permit-all and intended as a liveness probe for load balancers and
orchestrators. It carries no @RolesAllowed annotation. The Spring Boot actuator health endpoints, including
GET /actuator/health and GET /actuator/health/**, use the same permit-all role policy. In production these
endpoints still sit behind the configured TLS/mTLS transport boundary; permit-all means no Spring role or Bearer-token
check is required after the transport checks succeed. The cluster-health response body is {"status":"UP"} with HTTP
200 when the cluster is ready, or {"status":"DOWN"} with HTTP 503 otherwise.
GET /api/cluster/health/details requires the ADMIN role. In addition to the status field it includes a "leader" field ("self" when the responding node is leader, the leader node ID otherwise, or "none" when no leader is known). The 503 body adds "reason":"Unavailable".
Counter ?delta query parameter
Section titled “Counter ?delta query parameter”POST /api/counter/increment and POST /api/counter/decrement accept an optional ?delta=<long> query parameter (default 1). The value must be between 1 and 1,000,000,000; values outside that range (including zero or negative) are rejected with 400. There is no request body for these endpoints.
List Content-Type: text/plain
Section titled “List Content-Type: text/plain”POST /api/list (append) and PUT /api/list/{index} (set/replace) require Content-Type: text/plain. The element value is the raw text body. Other list endpoints use the default application/json.
Raft-replicated dynamic config
Section titled “Raft-replicated dynamic config”/api/config exposes Raft-replicated cluster-wide configuration as a key/value store. All operations require the ADMIN role. Mutations are committed through the Raft log and are visible on all nodes once committed.
| Method | Path | Description |
|---|---|---|
GET | /api/config | List all config entries. Supports ?offset=<int>&limit=<int> (max 1,000 per page). |
GET | /api/config/{key} | Fetch a single config entry by key. 404 if not found. |
PUT | /api/config/{key} | Upsert a config entry. Body: {"value":"<string>"}. Key ≤ 256 chars; value ≤ 8,192 chars. |
DELETE | /api/config/{key} | Remove a config entry. 404 if key absent. |
Data-structure inspection
Section titled “Data-structure inspection”/api/v1/management/data-structures provides read-only introspection over registered data structures for
non-production diagnostics. Requires ADMIN or READONLY; production profiles reject these direct data-structure
management reads.
| Method | Path | Description |
|---|---|---|
GET | /api/v1/management/data-structures | List all registered data structures. Optional ?type=<type> filter. Paged via ?page=<int>&size=<int>. |
GET | /api/v1/management/data-structures/{type}/{name} | Inspect a specific data structure; returns paged entries, items, or elements depending on type. |
Supported type values: map, queue, topic, set, multimap, ringbuffer, list, priorityqueue, reliabletopic, pncounter, gset, orset, lwwregister, idgenerator, cqc, executor. Hyphenated aliases (e.g., multi-map, ring-buffer) are also accepted.
Idempotency-Key header
Section titled “Idempotency-Key header”Mutating endpoints in the following endpoint groups accept an optional Idempotency-Key request header:
Queues, topics, multimaps, lists, ring buffers, reliable topics, direct atomic endpoints (non-production only), counters, and transactions.
The header value is an arbitrary client-chosen string (max 512 characters). The server retains completed responses for replay for up to 10 minutes and up to 10,000 retained responses. It also caps concurrent keyed requests at 10,000 in-flight entries. Concurrent requests with the same key block for up to 30 seconds waiting for the in-flight result. Response replay survives restarts only when the public loomcache.rest.idempotency.persistence-file setting is configured as a JVM system property or Spring application property, or when server persistence/WAL is enabled with a non-blank loomcache.server.persistence.wal-directory; otherwise the REST idempotency store is in-memory only. Restart replay also requires the completed response to fit the retained response and persisted-field caps; over-cap responses can replay from memory until restart but are not written to the durable idempotency file. When durable REST idempotency persistence is configured and the response snapshot cannot be persisted after a completed action, the request fails closed instead of silently claiming durable replay; in-memory dedupe remains available until restart. The legacy camelCase spelling remains accepted as a fallback for existing deployments, but the public kebab-case setting is always preferred when both are present. Resolution order is public JVM property, public Spring Environment property, legacy fallback, then the WAL-derived path.
OpenAPI
Section titled “OpenAPI”LoomCache’s grouped OpenAPI document is opt-in for dependency consumers. Add
org.springdoc:springdoc-openapi-starter-webmvc-api, then set loomcache.openapi.enabled=true and
springdoc.api-docs.enabled=true to publish the /api/** controller group and /token at
GET /v3/api-docs/loomcache-api. Host applications that include SpringDoc may still expose their own default
/v3/api-docs endpoint; set springdoc.api-docs.enabled=false when no docs endpoint should exist. The LoomCache group is
ADMIN-gated by default, matching the documented HTTP surfaces. Development deployments can explicitly opt into public
OpenAPI exposure for client generation or gateway validation, but production rejects that unauthenticated-docs opt-in. Direct
LoomApplication launches keep Swagger UI disabled through the executable default springdoc.swagger-ui.enabled=false;
dependency consumers should set that key explicitly, or omit the optional UI dependency, unless UI tooling is exposed
only behind an approved internal gateway.
Ops CLI
Section titled “Ops CLI”The loom-cli module packages com.loomcache.cli.LoomCli with a manifest main class. It calls the same HTTP
surfaces described above.
The CLI rejects credentials passed directly on the command line (--basic / --bearer) because argv leaks to
ps(1). Pass the name of an environment variable with --bearer-env for the Spring REST server. --basic-env
exists for generic HTTPS targets and legacy tooling, but the bundled Spring Security configuration disables HTTP Basic;
production operators should use mTLS and, when JWT is enabled, Bearer tokens. The following example builds a
protected TLS argfile and runs CLI commands with a Bearer token taken from the environment:
export LOOM_URL=https://node-1:8080export LOOM_ADMIN_TOKEN="${LOOM_ADMIN_TOKEN:?set to a JWT Bearer token}"export LOOMCACHE_VERSION="<loomcache-version>"loom_tls_args="$(mktemp)"chmod 600 "$loom_tls_args"trap 'rm -f "$loom_tls_args"' EXIT{ printf '%s\n' '-Djavax.net.ssl.keyStore=/etc/loomcache/client.p12' printf '%s\n' "-Djavax.net.ssl.keyStorePassword=${LOOM_CLIENT_KEYSTORE_PASSWORD:?set}" printf '%s\n' '-Djavax.net.ssl.trustStore=/etc/loomcache/truststore.p12' printf '%s\n' "-Djavax.net.ssl.trustStorePassword=${LOOM_TRUSTSTORE_PASSWORD:?set}"} > "$loom_tls_args"
java @"$loom_tls_args" \ -jar "loom-cli/target/loom-cli-${LOOMCACHE_VERSION}.jar" --bearer-env LOOM_ADMIN_TOKEN cluster statejava @"$loom_tls_args" \ -jar "loom-cli/target/loom-cli-${LOOMCACHE_VERSION}.jar" --bearer-env LOOM_ADMIN_TOKEN cp-admin list-groupsjava @"$loom_tls_args" \ -jar "loom-cli/target/loom-cli-${LOOMCACHE_VERSION}.jar" --bearer-env LOOM_ADMIN_TOKEN \ cp-admin list-sessions --detail --offset 0 --limit 100The CLI uses the JDK HttpClient; for production mTLS, configure client-key and trust material with standard
javax.net.ssl.* JVM properties or an equivalent JVM-wide SSL setup before calling HTTPS operator endpoints.
cp-admin list-sessions is a break-glass diagnostic for CP session reconciliation. cp-admin force-close-session --session-id <session-id> --force is a destructive break-glass mutation that terminates the target CP session and
releases session-bound locks and semaphore permits. It does not mutate atomic values and is not the normal production
workflow for CP primitives. Application code should use the typed Java client CP APIs.
Non-production diagnostics can also inspect direct host-mounted management endpoints:
java -jar "loom-cli/target/loom-cli-${LOOMCACHE_VERSION}.jar" \ data-structures list --type priority-queue --page 0 --size 100java -jar "loom-cli/target/loom-cli-${LOOMCACHE_VERSION}.jar" \ data-structures inspect --type map --name default --page 0 --size 50cluster state and CP list commands call ADMIN-gated /api/cluster Spring endpoints and forward the documented
pagination query parameters. cp-admin list-sessions --detail also forwards ?detail=true for owner, held-lock, and
held-permit diagnostics. force-leader-step-down and
cp-admin force-close-session --session-id <session-id> --force are cluster-admin mutations; production profiles keep
them fail-closed because
the REST write-safety guard blocks operator admin mutations and rejects enabling
loomcache.rest.write-safety.operator-admin-mutations-enabled under production. The data-structures commands use the host-mounted management JSON
endpoints (/api/v1/management/data-structures) exposed by the hosting application; production profiles also block
these direct data-structure reads and reject the loomcache.rest.write-safety.direct-reads-enabled opt-in. The production REST write guard applies to
node-local AP data-structure paths such as maps, queues, sets, lists, ringbuffers, counters, topics, and multimaps;
Raft-backed transaction and dynamic-config endpoints remain available behind their role gates. Direct CP REST paths
(/api/atomic/** and /api/lock/**) are non-production only; production CP access uses the binary protocol.
Local break-glass WAL diagnostics strictly validate the input WAL and write JSONL metadata or a migration-safe diagnostic
copy for offline analysis.
Authentication
Section titled “Authentication”The Spring Boot starter uses Spring Security for /api/** and /token. HTTP Basic is disabled in the bundled server filter chain. mTLS-authenticated callers are identified by certificate CN and authorized through loomcache.auth.cert-permissions; mTLS-authenticated callers with the token-issuer authority and either the ADMIN or USER role can request a short-lived JWT from POST /token when loomcache.security.jwt.enabled=true, loomcache.security.jwt.signing-secret is a high-entropy, non-placeholder UTF-8 secret of at least 32 bytes (for example, generated with openssl rand -base64 32), and production JWT deployments keep loomcache.security.jwt.local-revocation-enabled=false; use short token TTLs, signing-key rotation, or configured deny-list entries for emergency revocation. The default token TTL is 15 minutes. Bearer-token validation for protected Spring REST resources runs when JWT support is enabled; production deployments must enable certificate binding, while explicit verification secrets are only for validating tokens signed by rotated or retired keys and must meet the same strength requirement. Bearer tokens cannot mint fresh tokens from /token.
Spring Security permit-all health endpoints are GET /api/cluster/health, GET /actuator/health, and
GET /actuator/health/**. In production they still sit behind the configured TLS/mTLS transport boundary;
“permit-all” means no Spring role or Bearer-token check is required after the client certificate / transport checks
succeed.
The optional /actuator/info and OpenAPI document endpoints remain ADMIN-gated unless their explicit exposure opt-in
properties are enabled; production rejects those opt-ins.
Errors
Section titled “Errors”Error response bodies are not a single universal shape. Two patterns are in use:
Unhandled exception responses (for errors caught across all controllers):
{ "error": "<human-readable message>", "code": "<MACHINE_TOKEN>", "timestamp": "<ISO-8601>", "correlationId": "<request-correlation-id>"}Endpoint-specific inline responses (returned directly by selected endpoint groups, e.g., list-conflict and config-validation responses):
{ "error": "<human-readable message>", "code": "<MACHINE_TOKEN>", "detail": "<per-call context>", "timestamp": "<ISO-8601>"}List-conflict 409 responses additionally include "index" (for LIST_INDEX_CHANGED) or "offset", "limit", and "size" (for LIST_PAGE_CHANGED). There is no message field in any response body.
| HTTP | Meaning | Example trigger |
|---|---|---|
| 400 | Bad Request — validation failure | Missing required field, value out of range, blank idempotency key |
| 403 | Forbidden — role check failed | USER calling an ADMIN-only endpoint; query on a map not in the allow-list; TX commit from non-owner |
| 404 | Not Found — resource absent | Key not present, unknown data-structure name |
| 405 | Method Not Allowed | Wrong HTTP verb |
| 409 | Conflict — concurrent modification | LIST_INDEX_CHANGED / LIST_PAGE_CHANGED (concurrent list mutation during request) |
| 500 | Internal Server Error | Unexpected server-side exception |
| 503 | Service Unavailable — Raft not ready | No Raft leader elected; node is rebalancing and cannot serve mutations |
LoomCache is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Hazelcast, Inc. or by any other company whose products are named in this documentation. “Hazelcast” is a trademark of Hazelcast, Inc.; references to it are nominative and describe only migration and comparison. All other product and company names are trademarks of their respective owners and are used for identification purposes only.