Data API Configuration¶
Both Data APIs are configured with process-level properties. Put your changes in local.properties (they override the shipped defaults.properties, which you should not edit), then restart the process. These are process/system properties: set them once for the process, not inside a component directory.
Make sure local.properties is actually loaded
local.properties takes effect only if it is included by the property chain (typically root.properties has #INCLUDE local.properties). On some deployments that line is commented out. If your overrides seem ignored, confirm local.properties is included/active for your deployment, then restart.
Enable the REST query endpoint¶
The REST server itself rides the Web component's HTTP/HTTPS port by default (ami.rest.uses.web.port=web), so the informational endpoints already answer. But the SQL endpoint is switched off by the shipped default:
*,!QUERY,!ADMIN loads every REST endpoint except QUERY (SQL) and ADMIN (console). To enable SQL over REST, override it in local.properties so QUERY loads. For example:
Exposing SQL over REST is a security decision
/3forge_rest/query runs arbitrary Center SQL as the authenticated user. Enable it deliberately, keep it behind authentication and TLS, and restrict access with an IP allow-list where appropriate. The recommended production posture for exposing SQL vs. steering integrations to a narrower custom endpoint is still being confirmed with the engineering team.
REST properties¶
| Property | Default | Meaning |
|---|---|---|
ami.rest.uses.web.port |
web |
Name of the Web component whose port the REST server rides. false = use a standalone port instead. |
ami.rest.http.port / .https.port |
unset | Standalone HTTP / HTTPS port (used only when not riding the Web port). |
ami.rest.http.port.bindaddr / .whitelist |
unset | Bind interface and IP allow-list for a standalone port. |
ami.rest.https.keystore.file / .password |
unset | TLS keystore for a standalone HTTPS port. |
ami.rest.http.connections.max |
8 |
Handler thread-pool size for a standalone REST port, and so the cap on concurrent REST requests there. Used only when not riding the Web port; on the default Web port it has no effect. |
ami.rest.plugin.classes |
*,!QUERY,!ADMIN |
Which endpoints load. * = all; !NAME excludes one. |
ami.rest.auth.plugin.class |
${ami.auth.plugin.class} |
Authenticator used for HTTP Basic. |
ami.rest.auth.plugin.cache.duration |
10 seconds |
How long an authentication result is cached. |
ami.rest.show.errors |
true |
Include error detail in responses. |
ami.rest.show.endpoints |
true |
List available endpoints at /3forge_rest. |
Enable the WebSocket streaming server¶
The WebSocket server is opt-in: it opens a port only when you set one. Add to local.properties:
Restart the process. With at least one Center running, the log confirms it started:
components=[...] lists the Center (and Gateway) names you can reach: the URL path selects one, e.g. ws://host:3410/center. If instead you see WebSocket server disabled (no ami.websocket.port configured), the port is not set.
WebSocket authentication¶
The shipped default is:
DISABLED accepts every connection without a credential check, as anonymous when it sends no credentials. Any other value names an authentication plugin, the same plugin type ami.auth.plugin.class takes, which checks the username and password from HTTP Basic credentials on the handshake. The handshake carries nothing else the plugin can check: no session cookie, token, or client certificate. Browsers cannot set an Authorization header on the handshake, so a browser client puts the credentials in the URL. The browser first connects without them, and sends them only when the server answers 401 with a WWW-Authenticate: Basic challenge. With DISABLED there is no challenge, so a browser never sends URL credentials. There are 2 workable postures; which one fits depends on what's actually protecting the connection, not on a single blanket recommendation:
DISABLED, behind a trusted network or reverse proxy. Fits when the WebSocket port is never reachable from outside a network you already control: a proxy, a VPN boundary, or a deployment where only trusted services can reachami.websocket.portat all. The proxy (or network boundary) is the actual control;DISABLEDjust means AmiCore itself adds nothing on top of it. Wrong fit the moment the port is reachable from an untrusted network, since then nothing authenticates the connection at all.- Basic credentials in the URL,
wss://user:pass@host:3410/center, with an authentication plugin configured. Fits when you need per-user identity rather than a shared network boundary. The plugin can beACCESS_FILEor the plugin your Web logins use, such as LDAP, so the WebSocket checks the same users. Costs: credentials appear in the URL, which browsers, proxies, and server logs can all persist: usewss://(never plainws://) so the URL is at least encrypted in transit, and treat the credential as sensitive as any other password.
Neither is "the" production recommendation; pick by what's actually true of the deployment: is the port reachable only from a trusted boundary, or does the client need per-user identity. To require a client certificate as well, turn on mutual TLS (see Secure wss://); the authentication plugin still sees only the Basic credentials.
Secure wss://¶
Set a keystore to serve wss:// with a server certificate; browsers then authenticate with Basic credentials and no client certificate:
Startup then logs ... on port 3410 (secure), components=[...]. Browsers must trust the server certificate (import it, or front the port with a reverse proxy holding a CA-signed cert). Client-certificate mutual TLS is an additional opt-in (ami.websocket.ssl.authorized.clients.file) and is rarely appropriate for browsers.
WebSocket properties¶
| Property | Default | Meaning |
|---|---|---|
ami.websocket.port |
(unset → disabled) | TCP listen port. Setting this starts the server. |
ami.websocket.port.bindaddr |
all interfaces | Interface to bind (e.g. 127.0.0.1 for loopback only). |
ami.websocket.port.whitelist |
none | Per-connection client allow-list (text: / file: / plugin:). |
ami.websocket.auth.plugin.class |
DISABLED |
Authenticator for the handshake. DISABLED = accept without a credential check. |
ami.websocket.ssl.keystore.file / .password |
unset → ws:// |
Keystore → server-side wss://. |
ami.websocket.ssl.authorized.clients.file |
unset | Setting it turns on client-certificate mutual TLS. |
ami.websocket.connections.max |
64 |
Max concurrent connections. |
ami.websocket.conflate.interval.ms |
100 |
How often coalesced row changes are flushed to clients. |
ami.websocket.ping.interval.ms |
30000 |
Idle window before a keepalive PING; 0 disables liveness checks. |
ami.websocket.idle.probes |
2 |
Unanswered PING windows tolerated before a silent client is dropped. |
ami.websocket.max.message.bytes |
1048576 (1 MB) |
Max size of an inbound message split across several frames. A single frame is capped at 1 MB regardless of this setting. Either limit, when exceeded, closes the connection with status 1009 and reason message too large. |