Skip to content
Connect > Client Libraries

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 pyami package (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:

username|password|ISADMIN=true|AMIDB_PERMISSIONS=READ,WRITE,ALTER,EXECUTE|...

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 example ami.center.ssl.keystore.file, ami.db.jdbc.ssl.keystore.file, and ami.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 default keys/3forge.auth) lists the OpenSSH ssh-rsa public 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 a BAD_CERTIFICATE alert. The server also publishes its own public key (a .pub next 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:

node.jdbc_config.shards[0].ssl = ac.AmiSslOptions(trusted_server_pub_file="keys/3forge.pub")

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

1
2
3
openssl req -x509 -newkey rsa:2048 -nodes \
    -keyout client.key.pem -out client.crt.pem \
    -days 3650 -subj "/CN=my-ami-client"

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:

1
2
3
4
shard = node.jdbc_config.shards[0]
shard.ssl.client_cert_file = "client.crt.pem"
shard.ssl.client_key_file  = "client.key.pem"
# shard.ssl.client_key_password = "..."   # only if you encrypted the key (omit -nodes above)

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:

openssl x509 -in client.crt.pem -pubkey -noout > client.pub.pem
ssh-keygen -i -m PKCS8 -f client.pub.pem       # prints: ssh-rsa AAAA...

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_raw hot 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.