Connecting
TrueNASClient(target, credentials=None, ...) opens (lazily) a websocket to the
middleware. The transport is chosen from target:
target |
Transport |
|---|---|
"nas.example.com" |
wss://nas.example.com/api/current (TLS) |
"wss://nas/api/current" |
explicit TLS websocket |
"ws+unix:///var/run/middleware/middlewared.sock" |
local unix socket |
"/var/run/middleware/middlewared.sock" |
the same socket, as a path |
None / omitted |
the local unix socket |
from pytruenas import TrueNASClient
client = TrueNASClient("nas.example.com", credentials="1-<64-char-api-key>", verify=False)
Credentials
The creds argument accepts:
- an API key string
"<id>-<64 chars>", "user:password"(optionally"user:password\n<otp>"),- a token string,
- a
(user, password)tuple, None— local socket auth (the root-owned socket authenticates by ownership, so no credentials are needed when running on the NAS).
Credentials.from_env() reads the PYTRUENAS_CREDENTIALS environment
variable.
Credentials may also travel in the target itself
(wss://root:secret@nas). An OTP follows the password after a newline — but a
URI must percent-encode it as %0A:
TrueNASClient("wss://root:secret%0Aotp:123456@nas")
A newline is used as the separator because a password can never contain one (pressing Enter submits the value rather than typing into it), so it splits the field without stealing a character or needing an escaping scheme.
Login timing
By default the client logs in lazily on first use (autologin=True). Pass
autologin=False to construct without connecting — useful in tests, or when you
only want client.path(...) / client.run(...) and no API call.
Construction never touches the network, so a bad hostname raises on first
use, not at construction. A target that names neither scheme nor API path
resolves to TLS and the current API: nas.example.com means
wss://nas.example.com/api/current. Plaintext is never chosen for you — ask
for it with ws:// (or http://, the same transport) — and a different API
version comes from version= or a path in the target.
The SSH leg
client.run(...) and the SFTP half of client.path(...) can use a separate SSH
connection, configured by the shell= argument and stored as
client.config.ssh:
client = TrueNASClient("nas", credentials=api_key, shell="ssh://root@nas")
Omit it and the client falls back to the web shell for a remote target — see
Running commands. client.install_sshcreds() provisions a
keypair, installs it on root's authorized_keys, and wires it in for you.
Host keys
The SSH leg — both commands and SFTP — verifies the server's host key against
~/.ssh/known_hosts. For a NAS that is not listed there, either add it
(ssh-keyscan nas >> ~/.ssh/known_hosts) or name the file of trusted keys with
known_hosts=:
TrueNASClient("nas", credentials=api_key, shell="ssh://root@nas", known_hosts="/srv/known_hosts")
For a lab box with a self-signed certificate and an unrecorded host key,
verify=False turns off both checks at once (TLS certificate verification
everywhere, and SSH host-key verification). It is the only way to skip the
host-key check; known_hosts=None is refused (ValueError).
from hostctl import SSHConfig
TrueNASClient("nas", credentials=api_key, shell="ssh://root@nas", verify=False) # check nothing
TrueNASClient("nas", credentials=api_key, verify=False, # host keys only
shell="ssh://root@nas", known_hosts="/srv/known_hosts")
TrueNASClient("nas", credentials=api_key, # TLS only
ssh=SSHConfig("nas", username="root", verify=False))
Keys named with known_hosts= are checked even under verify=False: the
stricter setting wins.
Certificates are checked the way the operating system checks them — through
its own verifier on Python 3.10+ (truststore: Windows CryptoAPI, the macOS
keychain, the system CA paths elsewhere), against OpenSSL's default store on
3.9 — the same for the API websocket, uploads/downloads and the web shell. To
trust a private CA without adding it to the OS, give verify= its bundle, or
set SSL_CERT_FILE (also honored: REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE,
WEBSOCKET_CLIENT_CA_BUNDLE, first one set wins). A bundle replaces the OS
trust rather than adding to it.
TrueNASClient("nas", credentials=api_key, verify="/etc/pki/lab-ca.pem")
verify= also takes a ready ssl.SSLContext, used as it is on every TLS
connection — the way to present a client certificate to a NAS behind a
reverse proxy that requires one (mTLS). The
sslcontext package builds one from
files, in-memory PEM, or the OS keystores and smart cards:
import ssl
import sslcontext
ctx = ssl.create_default_context(cafile="/etc/pki/lab-ca.pem")
ctx.load_cert_chain("client.pem", "client.key")
TrueNASClient("nas", credentials=api_key, verify=ctx)
# A certificate from the Windows store, the macOS Keychain or a smart card,
# chosen as the browser would:
TrueNASClient("nas", credentials=api_key, verify=sslcontext.system_context())
known_hosts= applies to the SSH leg built from shell= and to the one
install_sshcreds() wires in; an ssh=SSHConfig(...) you build yourself keeps
its own known_hosts, and verify=False reaches it on a copy. When the key cannot be verified (or SSH is unreachable),
commands and file operations fall back to the websocket transports, and the
client logs a warning the first time run() does so.
The SSH leg reads your ~/.ssh/config, as ssh does: a host alias,
HostName, Port, User, IdentityFile, ProxyJump or ProxyCommand there
applies to commands and file operations alike. A user or port written in
shell= wins over the file; with no user in either, pytruenas logs in as
root, TrueNAS's administrative account, on port 22.
TrueNASClient("nas", credentials=api_key, shell="nas") # ~/.ssh/config's User, else root
TrueNASClient("nas", credentials=api_key, shell="ssh://admin@nas") # admin, whatever the file says
For a connection that depends on no personal file, build the leg yourself with
ssh_config=None, or name another file with a pathlib.Path (a plain string
is configuration text, not a file name):
from pathlib import Path
from hostctl import SSHConfig
TrueNASClient("nas", credentials=api_key, ssh=SSHConfig("nas", username="root", ssh_config=None))
TrueNASClient("nas", credentials=api_key, ssh=SSHConfig("nas", ssh_config=Path("lab/ssh_config")))
Note
The shell= argument was also readable as client.shell before the move
to hostctl. .shell now means the
bound shell object (client.shell.run(...)), as it does throughout
hostctl, so the connection target lives on the configuration instead.
local = TrueNASClient(autologin=False) # no connection yet
local.login() # connect + authenticate explicitly
See the Client API for the full constructor.