Client Library Configuration¶
How a client connects: ports, authentication, TLS, timeouts and limits, failover, and sharding. The exact constructor calls are on the Python client and C++ client reference pages; this page explains the settings behind them.
What you install, what an administrator configures¶
Two sides have to be in place before a client can connect:
- On your machine (the client): Python 3.14 and the
pyamipackage (see Python client → Installing), or the C++ client library. That is the whole client-side install. - On the server: the server must expose the listeners you connect to, and you need the credentials, and, if the server is secured, a client certificate. A server administrator controls this through the server's property files. You do not edit these to connect: you ask your administrator for the ports, credentials, and (if mutual TLS is on) a client certificate. The rest of this section explains what those settings are, so the pieces make sense.
Server property files¶
Server settings live in two files. defaults.properties ships with 3forge Enterprise and holds the defaults, do
not edit it. Deployment-specific changes go in local.properties, which overrides the defaults. The
settings below are the ones that determine whether, and how, a client can connect.
The listener ports¶
The client protocols are separate listeners, each with a port property (shown with its default):
| Property | Protocol | Default |
|---|---|---|
ami.center.port |
Center (subscribe) | 3270 |
ami.db.jdbc.port |
JDBC (SQL) | 3280 |
ami.port |
Relay (publish) | 3289 |
These listeners are on by default; a client connects to whichever protocols it uses. (These are the ports the server listens on, on the client you still pass the port explicitly, as the next Ports section explains.)
Authentication, who the client logs in as¶
ami.auth.plugin.class selects the authenticator. A common choice is ACCESS_FILE, which reads a
users file named by users.access.file (for example data/access.txt). Each line defines one user:
The client logs in with one of those username / password pairs (the JDBC and Center configs take
them). Other authenticators, LDAP, OAuth, SAML, Okta, replace ACCESS_FILE and are configured
separately; from the client's side you still supply a username and password.
The client has no single-sign-on flow of its own, no Kerberos/SPNEGO and no token exchange. Whichever authenticator the server runs, the client always logs in with a username and password (and, on a secured listener, a client certificate for mutual TLS, see below). If your organization needs SSO, it is arranged on the server, not in the client configuration.
TLS and mutual TLS, set by the presence of two files¶
A listener's security is turned on by setting a keystore, not a boolean:
- A listener speaks TLS when it has a keystore:
ami.<listener>.ssl.keystore.file(+ami.<listener>.ssl.keystore.password), for exampleami.center.ssl.keystore.file,ami.db.jdbc.ssl.keystore.file, andami.port.keystore.file(the Relay listener). - A listener with a keystore also requires a client certificate (mutual TLS). There is no
property to turn this off. The file named by
ami.<listener>.ssl.authorized.clients.file(by defaultkeys/3forge.auth) lists the OpenSSHssh-rsapublic keys allowed to connect, one per line. A client whose certificate's public key is not in that file is rejected at the handshake with aBAD_CERTIFICATEalert. The server also publishes its own public key (a.pubnext to its keystore) for the client to pin.
So every connection to a listener that has a keystore needs a client certificate registered on the server. That is the case the Connecting to a secured server section below walks through. To stand up your own evaluation server, or for the full property reference, see the Center documentation.
Ports¶
Each protocol connects to its own acceptor port on the server.
| Protocol | Common port | Is it a default? |
|---|---|---|
| JDBC (SQL) | 3280 | Yes: the Python DB-API and the ODBC driver fall back to 3280 if you omit it. |
| Center (subscribe) | 3270 | No: convention only. Set it explicitly. |
| Relay (publish) | 3289 | No: convention only. Set it explicitly. |
Only JDBC 3280 is a real default, set the Center and Relay ports explicitly
The native client's Center and Relay configurations require the port as an argument; there is no
built-in default for them. 3270 and 3289 are the conventional values shown in the library's own
guide, not defaults the code supplies. Always pass the actual port your deployment uses. Confirm the
ports on your server before relying on any of these numbers.
Authentication¶
- JDBC and Center (when the server puts a gateway in front of subscriptions) take a username and password on their configuration.
- Relay identifies the publisher with a login id.
- A plain Center with no gateway can authenticate by client certificate alone (see TLS below).
TLS and mutual TLS¶
TLS is configured per shard through an SslOptions (C++) / AmiSslOptions (Python) with four fields:
| Field | Purpose |
|---|---|
trusted_server_pub_file |
Pins the server's public key. Give it the server's OpenSSH ssh-rsa .pub. |
client_cert_file |
The client's certificate, for mutual TLS. |
client_key_file |
The client's private key. |
client_key_password |
Password for the key, if the key is encrypted. |
A secure server requires mutual TLS
When a listener has a keystore, the server requires a client certificate: you must set both client_cert_file and client_key_file, or the connection
is refused. Server-key pinning ignores certificate chain and expiry and pins the key itself, so the
pinned .pub must match the server's current key. The full key-exchange procedure is in the C++
library's user guide.
On the server side, the relevant properties are ami.<listener>.ssl.keystore.file,
ami.<listener>.ssl.keystore.password, and ami.<listener>.ssl.authorized.clients.file, where <listener> is the listener name (for example
ami.center.ssl.*, ami.db.jdbc.ssl.*).
For a first connection against an evaluation server you can use a non-TLS listener; add pinning and mutual TLS once the plaintext path works.
Connecting to a secured server (mutual TLS)¶
A secured listener, one that has a keystore, requires a client certificate. A plaintext or
server-only-TLS connection is rejected at the handshake, you will see a
BAD_CERTIFICATE alert. Getting connected takes three things: pin the server, present a client
certificate, and have that certificate registered on the server. (The pure-Python pyami.pyjdbc layer
does no TLS at all, so it cannot connect to a secured port, use the native or C++ client.)
1. Pin the server's public key¶
The server publishes an OpenSSH ssh-rsa public key next to its keystore (for example keys/3forge.pub).
Ask your administrator for that .pub (the public key only), copy it to your client machine, and point
each config's shard at it:
The handshake checks that the server's certificate carries this key. The certificate chain and expiry are not checked, only the key is pinned.
2. Generate a client certificate and key¶
Java: a keystore, generated for you¶
The Java client takes a Java keystore rather than the PEM pair above, and does not need the manual
steps in this section. Building its TLS context creates the keystore if it is missing and writes a
sibling .pub beside it, already in the ssh-rsa form step 3 asks for. See
Java client → Connect.
Point the client at both files, alongside the server pin:
C++ is identical, set shard.ssl.client_cert_file / client_key_file; see
C++ client → TLS.
Set this on every config your program uses. jdbc_config, center_config, and relay_config
each have their own shard(s) with an independent .ssl, a subscriber sets it on center_config, a
publisher on relay_config, exactly as shown here for jdbc_config. (Verified against a live secured
instance: the same per-shard setup works for the Center and Relay connections, not just JDBC.)
3. Register your certificate on the server¶
The server only accepts certificates it already knows. Convert your certificate's public key to the
ssh-rsa form and give the resulting line to your administrator:
A Java client needs none of this: the sibling .pub written alongside its keystore is already in
this form. See Java client → Connect.
The administrator appends that ssh-rsa AAAA... line to the file named by the listener's
ami.<listener>.ssl.authorized.clients.file.
The server reads the authorized-clients file only at startup
A listener loads the file once, when it builds its TLS context, and never re-reads it. Both directions need a restart:
- A key added to the file is rejected until the server restarts. The failure is identical to having no entry at all, so it looks like the key was added wrongly.
- A key removed from the file keeps working until the server restarts. Editing the file does not revoke access on a running server. Plan a restart as part of revoking a client.
Until all three are in place, the connection fails at the TLS handshake, see Troubleshooting → TLS.
Timeouts and limits¶
| Setting | Default | Applies to |
|---|---|---|
| JDBC query timeout | 30 000 ms | The native client's SQL calls. |
| JDBC row limit | 10 000 rows | The native client's SQL calls, results are capped at this many rows. |
| DB-API / ODBC query timeout | 60 000 ms | The Python DB-API driver and the ODBC driver. |
The row limit matters: a SELECT that would return more than 10 000 rows is truncated. Raise the limit
on the configuration, or narrow the query.
There is no fixed limit on the number of concurrent subscriptions a client can hold: the client library does not cap it, and the practical ceiling is memory and the server's own configuration (see the Center documentation). The one throughput constraint the client does impose is on SQL, one JDBC request at a time per cluster, not on subscriptions.
I/O threads and message size¶
Two environment variables tune the native client:
AMI_IO_THREADS: the size of the client's I/O thread pool. Defaults to the number of hardware cores, capped at 16.AMI_MAX_FRAME_BYTES: the largest single wire frame the client will accept. Defaults to 512 MB.
Wire format and throughput¶
- No wire compression. The client does not compress data on any of the three protocols; size your network for the uncompressed volume.
- No published throughput figure. The libraries are built for high volume, binary bulk insert, a
publish outbox, and a
serialize_object+publish_rawhot path, but there is no guaranteed or benchmarked sustained messages-per-second number in the documentation. Measure against your own workload and hardware.
Sharding and failover¶
A cluster can span several servers per protocol by giving a configuration more than one shard. This enables read fan-out and write broadcast across servers, and a distribution function can spread published objects across shards by round-robin or by key.
Failover is built in and needs no retry code in your program:
- Each shard can carry failover endpoints, tried with exponential backoff.
- On reconnect, a Center subscription auto-resubscribes and re-snapshots.
- A Relay publisher replays its outbox, so objects queued during the outage are not lost.
- A JDBC request retries once.
Verified live (build 32437)
The ports, mutual-TLS setup (including the client-certificate flow), and ACCESS_FILE auth on this
page were confirmed against a live instance.