Skip to main content

FHEnom AI Python Client Library

Official Python SDK for FHEnom for AI™ - Confidential AI with fully encrypted models and data.

Python 3.8+ License: MIT

Node compatibility — this release targets FHEnomAI-tee >= 1.9.0 and FHEnomAI-server >= 1.5.0. Minimums, not pins: a newer node is expected to work. fhenomai --version prints them, fhenomai health live checks the node against them, and CHANGELOG.md records the floor for every release (machine-readable in fhenomai.compat).

Against an older node a missing route answers 404 — but a request field the node does not declare is dropped silently, so an option you passed simply does not take effect. That is what the check exists for.

🚀 Quick Start

Installation

pip install fhenomai

Or install from source:

CLI Configuration

First, configure the CLI with your TEE server details:

# Initialize configuration (interactive)
fhenomai config init \
  --admin-host YOUR_TEE_IP \
  --admin-port 9099 \
  --user-host YOUR_TEE_IP \
  --user-port 9999 \
  --sftp-host YOUR_TEE_IP \
  --sftp-username admin \
  --sftp-password YOUR_PASSWORD

# If the TEE host was installed with an SFTP public key, that account is key-only —
# point the client at the matching PRIVATE key instead of (or as well as) a password:
#   --sftp-key-path ~/.ssh/fhenom_sftp

# Verify configuration
fhenomai config show

# Test connectivity
fhenomai test connection

Basic CLI Usage

# List models
fhenomai model list --show-status

# Upload model via SFTP (upload/ prefix added automatically)
fhenomai sftp upload ./my-model my-model --recursive

# Encrypt model (paths normalized automatically)
fhenomai model encrypt my-model my-model-encrypted \
  --encrypted-model-id my-model-encrypted \
  --wait --show-progress

# Download encrypted model (download/ prefix added automatically)
fhenomai sftp download my-model-encrypted ./encrypted/my-model --recursive

# Start serving
fhenomai serve start my-model-encrypted \
  --server-url http://YOUR_VLLM_SERVER_IP:8000 \
  --display-model-name my-model

# Stop serving
fhenomai serve stop my-model-encrypted

# --- Sovereign split inference ---
# Split the model: keep the first/last layers in the TEE, run the middle
# layers on your own server.
fhenomai model split my-model-encrypted --first-layers 1 --last-layers 1

# Start the split model: the TEE connects out to your sovereign server
# (no --server-url for sovereign mode).
fhenomai serve start my-model-encrypted \
  --display-model-name my-model-sovereign \
  --sovereign-server-host 172.19.0.1 \
  --sovereign-server-port 8000

Administration

# What is this token, and what may it do? (master or scoped; needs TEE v1.8.3+)
fhenomai whoami

# Signed inference usage — the figures a deployment is billed on.
# Counters are hourly; prefer hour-aligned bounds. Needs TEE v1.8.1+.
fhenomai admin usage --from 2026-08-01 --to 2026-09-01

# Export the exact signed bytes plus the signature, for verification
fhenomai admin usage --from 2026-08-01 --to 2026-09-01 --export august.json

# Scoped admin tokens (RBAC): mint a least-privilege token for an automation.
# Roles: operator, encryptor, readonly, syncer — see docs/ADMIN_TOKENS.md.
fhenomai admin token create --label adk-agent --role operator --ttl 7d
fhenomai admin token list
fhenomai admin token revoke <token_id>

# Licence state, including the model and parameter budgets
fhenomai admin license

See docs/SIGNED_USAGE.md for the hourly-bucket rule and how to verify an exported signature, and docs/ADMIN_TOKENS.md for the two credential tiers, roles, and which status code means what.

Basic Python SDK Usage

from fhenomai import FHEnomClient, FHEnomConfig

# Load configuration from file
config = FHEnomConfig.from_file()  # Reads from ~/.fhenomai/config.yaml

# Initialize client
client = FHEnomClient(config)

# List available models
models = client.admin.list_models()
print(f"Available models: {models}")

# Encrypt a model (paths auto-prefixed with /models/upload/ and /models/download/)
job_id = client.admin.encrypt_model(
    model_name_or_path="llama-3-8b",  # Becomes /models/upload/llama-3-8b
    out_encrypted_model_path="llama-3-8b-encrypted",  # Becomes /models/download/llama-3-8b-encrypted
    encrypted_model_id="llama-3-8b-encrypted"
)

# Wait for completion
result = client.admin.wait_for_job(job_id, timeout=3600)

# Start serving
client.admin.start_serving(
    encrypted_model_id="llama-3-8b-encrypted",
    server_url="http://YOUR_VLLM_SERVER_IP:8000",  # vLLM server IP/hostname
    display_model_name="llama-3-8b-instruct"  # Optional: for vLLM --served-model-name
)

Sovereign Split Inference

In sovereign mode the security-critical first/last layers stay inside the TEE while the structurally-inert middle layers run on your own server. Split the model once, then start it pointing the TEE at your sovereign server.

# Split: keep 1 leading + 1 trailing layer in the TEE, middle layers go sovereign
job_id = client.admin.split_model(
    encrypted_model_id="llama-3-8b-encrypted",
    first_layers=1,
    last_layers=1,
)
client.admin.wait_for_job(job_id, timeout=3600)

# Start sovereign serving — the TEE connects OUT to your sovereign server,
# so no server_url is passed.
client.admin.start_serving(
    encrypted_model_id="llama-3-8b-encrypted",
    display_model_name="llama-3-8b-sovereign",
    sovereign_server_host="172.19.0.1",  # reachable from inside the TEE
    sovereign_server_port=8000,
)

Thick-client serving (encrypted prompts and responses)

By default an encrypted model serves plaintext prompts and returns plaintext completions: FHEnom's encryption protects the model's weights, not the text. Thick-client mode is an opt-in posture where the node also encrypts the traffic, each client under its own AES-256 key. Plaintext to such a model is refused with HTTP 400.

# 1. Serve the model in thick-client mode
fhenomai serve start my-model --server-url http://gpu-host:8000 --thick-client
#    (or, driving vLLM too: fhenomai model serve my-model --thick-client)

# 2. Mint a key for each client. The key is shown ONCE — --save writes it 0600.
fhenomai thick-client add my-model --label my-agent --save agent.json

# 3. Talk to it. The prompt is encrypted here, the answer decrypted here.
fhenomai test chat "What is the capital of France?" --thick-key-file agent.json

fhenomai thick-client list my-model            # metadata only, never the key
fhenomai thick-client remove my-model <id>     # immediate, irreversible
from fhenomai import FHEnomClient, FHEnomConfig, ThickClientSession

client = FHEnomClient(FHEnomConfig.from_file())
client.serve_model("my-model", server_url="http://gpu-host:8000",
                   display_model_name="my-model", thick_client=True)

key = client.add_thick_client("my-model", label="my-agent")
key.save("~/.fhenomai/thick/my-agent.json")     # shown once — store it now

session = ThickClientSession(base_url=client.config.user_url,
                             model="my-model", key=key)
print(session.chat("What is the capital of France?"))

Three things that bite, all of them by design:

  • The key is returned once. The node keeps it (TPM-sealed where available) to encrypt and decrypt, but has no endpoint that gives it back — list returns metadata only. Lose it and the only recovery is to mint a new client.
  • A thick-client model with no keys refuses every request, with a 400 that reads like a malformed prompt. Provision at least one before pointing clients at it; serve start --thick-client says so if you have not.
  • Every text field must be encrypted, including a system prompt. The SDK handles this for you, and for multimodal messages encrypts only the text parts — the node cannot decrypt media, so media travels as plaintext.

A deployment proxy can mint keys for agents without full admin power, using a scoped token that grants /admin/thick_clients/* and nothing else:

fhenomai admin token create --label deploy-proxy --role thick_client_provisioner

Deep dive: docs/THICK_CLIENT.md — the threat model (what the mode does not protect), key lifecycle and revocation, how to confirm the node really applied the flag, and the blob wire format for writing a client that is not this SDK.

Endpoint attestation (no admin token needed)

Before a client trusts this endpoint with a prompt — or with a thick-client key — it can require the endpoint to prove it is a genuine enclave:

fhenomai attest          # exits 0 only when it verifies, so it can gate a deploy
from fhenomai.inference_attestation import verify_endpoint

result = verify_endpoint("tee-host", 9999)
if not result.verified:
    raise SystemExit(f"refusing to send a prompt: {result.error}")

This is a stronger claim than admin attestation, and it needs no token. admin attestation binds a report to a nonce, which proves some enclave answered you — a relay can satisfy it with another node's genuine report. Here the node binds the report to its own TLS serving key, so a verified result means the enclave and the endpoint you connected to are the same machine.

Details: docs/ENDPOINT_ATTESTATION.md.

📚 Features

Core Capabilities

  • CLI Tool: Full-featured command-line interface for all operations
  • Python SDK: Programmatic access via FHEnomClient and AdminAPI
  • Model Encryption: Encrypt models on TEE server with progress tracking
  • Dataset Encryption: Encrypt datasets using encrypted models
  • SFTP Integration: Upload/download with automatic path normalization
  • Job Monitoring: Real-time progress updates and status checking
  • Serving Control: Start/stop model serving with vLLM integration
  • Thick-Client Serving: encrypted prompts and encrypted responses, per client — see Thick-client serving

CLI Commands

  • config: init, show, get, validate
  • model: list, info, encrypt, encrypt-dataset, split, upload, download, delete, delete-encrypted, chat-template, serve, stop
  • serve: start, stop, list
  • thick-client: add, list, remove — per-client AES keys for thick-client serving (needs TEE v1.8.4+)
  • sftp: upload, download, list, clear
  • job: list, status, wait, cancel
  • health: check, live, ready, status, admin, sftp, server
  • test: connection, admin, sftp, chat
  • admin: usage, token, license, logs, reload, ssl, sync, attestation, verify-attestation, ita, deprovision
  • whoami (top level): describe the configured admin token and its access

model serve / model stop drive a deployment end to end — starting vLLM for the model and registering it with the TEE — whether vLLM runs on a separate GPU host (dual-machine) or on the TEE machine itself (joined install). serve start / serve stop act on the TEE only, for when you start vLLM yourself. admin usage needs TEE v1.8.1 or newer; admin token and whoami need TEE v1.8.3 or newer; thick-client and --thick-client need TEE v1.8.4 or newer.

Advanced Features

  • Progress Bars: Rich terminal UI with real-time progress
  • Auto Path Normalization: Automatic upload/ and download/ prefix handling
  • Duplicate Detection: Warns about existing model names
  • Directory Management: Bulk operations on TEE directories
  • Health Monitoring: Test connectivity to all services
  • Context Manager: Automatic resource cleanup
  • TEE Attestation: Generate and verify TEE attestation reports with built-in verification
  • Endpoint Attestation: fhenomai attest — unauthenticated, channel-bound proof that the inference endpoint itself is a genuine enclave, for clients that hold no admin token
  • Honest Failure Reporting: Operations that fail raise rather than reporting success, errors name the actual cause (permission denied, unprovisioned node, rate limit), and CLI commands exit non-zero whenever the work did not happen — so fhenomai ... && next-step is safe to script

TEE Attestation Support (v1.0.7)

!!! info "New in v1.0.7" Enhanced attestation with automatic file management, format inference, and built-in verification. Report formatting is now integrated into fhenomai for stability.

FHEnom AI includes integrated TEE attestation with AMD SEV-SNP and Intel TDX support. These commands are for the node's operator and require an admin token; a client checking the endpoint it is about to use wants fhenomai attest instead.

# Install fhenomai (includes dk-tee-attestation for verification)
pip install fhenomai

# Generate attestation report (creates 3 files)
fhenomai admin attestation --output report.html
# Creates: report.html, report.bin, report.nonce

# Verify attestation (nonce auto-loads from report.nonce)
fhenomai admin verify-attestation --report report.bin

# Generate detailed PDF with hex dump
fhenomai admin attestation --format detailed --output analysis.pdf

# Verify with detailed output
fhenomai admin verify-attestation --report report.bin --format detailed

What's New in v1.0.7:

  • Triple file output: All attestation commands create .html/.pdf/.txt + .bin + .nonce
  • Format inference: File extension determines output type (.html, .pdf, .txt)
  • Changed --format behavior: Now controls display style (standard/detailed) not output type
  • Auto-load nonce: Verification automatically loads .nonce file if not provided
  • Built-in verification: New verify-attestation command with color-coded output
  • Parsed reports: CPU info, TCB details, and signatures cleanly displayed
  • Integrated formatter: Report formatting moved from dk-tee-attestation to fhenomai for API stability

Python SDK usage:

from fhenomai import FHEnomClient, AttestationReportFormatter

client = FHEnomClient.from_config()

# Generate attestation (nonce auto-generated)
report = client.admin.attestation()

# Save report
with open("report.bin", "wb") as f:
    f.write(report)

# Verify attestation
result = client.admin.verify_attestation(
    report=report,
    engine_type="amd_sev_snp"
)

if result['verified']:
    print(f"✓ Verified - Platform: {result['platform']}")
    print(f"  CPU: {result['cpu_info']}")

# Use the formatter directly for custom output
formatter = AttestationReportFormatter()
html_report = formatter.format_html(report)
with open("custom_report.html", "w") as f:
    f.write(html_report)

Verification Features:

  • ✅ ECDSA P-384 signature validation
  • ✅ Nonce binding verification
  • ✅ TCB (Trusted Computing Base) parsing
  • ✅ CPU identification
  • ✅ Color-coded hex dumps
  • ✅ HTML/PDF report generation
  • ✅ Platform detection (AMD SEV-SNP, Intel TDX)

📖 Documentation

Admin API Operations

# Model discovery
models = client.admin.list_models()
online_models = client.admin.list_online_models()
model_info = client.admin.get_model_info(model_id)

# Model encryption (paths auto-normalized)
job_id = client.admin.encrypt_model(
    model_name_or_path="model-name",  # Auto-prefixed with /models/upload/
    out_encrypted_model_path="model-name-encrypted",  # Auto-prefixed with /models/download/
    symbolic_name="model-name-encrypted",  # What the model is called on the node
    encryption_impl="decoder-only-llm",
    preprocessing_impl="default",
    server_ip="fhenom_ai_server",
    server_port=9100
)

# Dataset encryption (paths auto-normalized)
dataset_job = client.admin.encrypt_dataset(
    encrypted_model_id="my-encrypted-model",
    dataset_name_or_path="my-dataset",  # Auto-prefixed with /models/upload/
    out_encrypted_dataset_path="my-dataset-encrypted",  # Auto-prefixed with /models/download/
    dataset_encryption_impl="numeric",
    text_fields=["text"],
    server_ip="fhenom_ai_server",
    server_port=9100
)

Implementation-selector parameters

The encryption methods accept several string parameters that select an implementation strategy. The canonical list lives in fhenomai/impl_choices.py and is enforced at runtime by the SDK and CLI — passing an unrecognized value raises ValueError immediately.

Parameter Default Allowed values
encryption_impl (model) decoder-only-llm decoder-only-llm, moe-llm, gpt-oss-llm, gemma3-llm, gemma3-multimodal, qwen-vl-multimodal, llama-vision-multimodal, nomic-bert-text-embedding, llama-bidirectional-embedding
preprocessing_impl (model) default default, lora, dequantize, bert-model, final-linear, gemma3, gemma3-multimodal, llama-vision-multimodal
inference_mode (model) double_tokenizer double_tokenizer, embedding_only_double_tokenizer
driver_mode (model) — (inert) safetensor
dtype (model) — (inert) bfloat16, float16, float32
dataset_encryption_impl (dataset) numeric numeric

dtype, driver_mode and encrypted_model_id are inert, as is api_key on serve start. The node's request models do not declare them, and pydantic drops an undeclared field — so these were sent, discarded, and reported as success. They are still accepted so existing scripts keep running, but they are no longer sent and they now warn. Encryption runs at the node's own dtype and driver, the node generates the encrypted model's ID (--encrypted-model-id only stands in for --symbolic-name when that is omitted), and the TEE cannot present a bearer token to your upstream server.

These parameters are not independent. Each model architecture has exactly one correct (encryption_impl, preprocessing_impl) pairing, and the inference_mode follows from the encryption impl. Nothing on the wire rejects a wrong combination: the node accepts the request and the encryption job fails hours later, or produces a model that cannot be served. fhenomai model encrypt warns and names the right value; the tables are also available directly:

from fhenomai import ENCRYPTION_IMPL, ALL_CHOICES, impls_for_architecture, check_combination

print(ENCRYPTION_IMPL.values_tuple)        # ('decoder-only-llm', 'moe-llm', ...)
for choice in ALL_CHOICES:
    print(choice.name, choice.values_tuple)

# Look the pairing up by the "architectures" field of the model's config.json
impls_for_architecture("Gemma3ForCausalLM")     # ('gemma3-llm', 'gemma3')
impls_for_architecture("GptOssForCausalLM")     # ('gpt-oss-llm', 'dequantize')

check_combination("gemma3-llm", "default", "double_tokenizer")
# ["encryption_impl='gemma3-llm' is normally paired with preprocessing_impl='gemma3', ..."]

The three multimodal impls (gemma3-multimodal, qwen-vl-multimodal, llama-vision-multimodal) encrypt the model end to end — language model, vision tower and projector — but only the text tokenizer is shuffled, so media travels as plaintext at inference. gpt-oss-llm must be paired with the dequantize preprocessing: GPT-OSS ships MXFP4-quantized and is converted to bfloat16 before encryption.

encrypt_model() accepts an optional related_encrypted_model_id argument. When set, the TEE retrieves the tokenizer / embedding keying material from that previously-encrypted model and reuses it for the new one. Typical use: encrypting a LoRA adapter or embedding head on top of an already-encrypted base model, or batching a family of fine-tuned variants that must share a vocabulary.

# SDK
client.admin.encrypt_model(
    model_name_or_path="my-lora-adapter",
    out_encrypted_model_path="my-lora-encrypted",
    encryption_impl="llama-bidirectional-embedding",
    preprocessing_impl="final-linear",
    inference_mode="embedding_only_double_tokenizer",
    related_encrypted_model_id="9a2504c671064c5087120ed7c1ae3cb0",
)
# CLI
fhenomai model encrypt my-lora-adapter my-lora-encrypted \
    --encryption-impl llama-bidirectional-embedding \
    --preprocessing-impl final-linear \
    --inference-mode embedding_only_double_tokenizer \
    --related-encrypted-model-id 9a2504c671064c5087120ed7c1ae3cb0

Accuracy-preserving encryption

encrypt_model() accepts an optional optimize_accuracy flag (default False; --optimize-accuracy on the CLI). When enabled, the server first profiles the plaintext model for its few outlier ("massive-activation") hidden dimensions and the TEE keeps those out of the model-key rotation, so a later quantization of the encrypted model stays accurate. It costs one extra pass over the plaintext model at encryption time and changes nothing at inference; if profiling fails, encryption continues with a dense rotation rather than aborting. Needs TEE v1.8.5+ with server v1.4.4+.

Channel security (encrypted token channel)

encrypt_model() accepts an optional secure_channel flag (default False). When enabled, the TEE provisions a TPM-protected pre-shared key at encryption time and uses an AES-GCM-encrypted token channel for inference and dataset encryption, so token IDs never travel in cleartext. The choice is baked into the model at encryption time. Note that return_token_ids is not supported at inference time when channel security is enabled.

# SDK
client.admin.encrypt_model(
    model_name_or_path="llama-3-8b",
    out_encrypted_model_path="llama-3-8b-secure",
    secure_channel=True,
)
# CLI
fhenomai model encrypt llama-3-8b llama-3-8b-secure --secure-channel
# Serving control
client.admin.start_serving(
    encrypted_model_id=model_id,
    server_url="http://YOUR_VLLM_SERVER_IP:8000",  # vLLM server IP/hostname
    api_key=None,  # Optional
    display_model_name="my-model"  # Optional: custom name for vLLM
)
client.admin.stop_serving(model_id)

# Job management
status = client.admin.get_job_status(job_id)
result = client.admin.wait_for_job(
    job_id, 
    poll_interval=5, 
    timeout=3600,
    callback=lambda s: print(f"Progress: {s.get('progress', 0)*100:.1f}%")
)

Provisioning & License Management

A node that has not been provisioned yet serves only the provisioning endpoint. Any other call against it raises ProvisioningModeError (a subclass of APIError), and the CLI reports that the node is awaiting provisioning — rather than a bare 404 that looks like a missing endpoint or a licensing problem.

CLI: Standard Provisioning

Provision (or re-provision) a TEE node with client configuration and license:

# Initial provisioning with client parameters
fhenomai provision \
  --client-id "my-client" \
  --num-encryptable 100 \
  --admin-token "my-admin-token" \
  --rotation-token "my-rotation-token"

# With SSL client certificates and optional out-of-band license
fhenomai provision \
  --client-id "my-client" \
  --num-encryptable 100 \
  --admin-token "my-admin-token" \
  --client-ssl-cert-hex <hex-encoded-cert> \
  --client-ssl-key-hex <hex-encoded-key> \
  --root-cert-hex <hex-encoded-root-ca> \
  --instance-cert-hex <hex-encoded-instance-cert> \
  --instance-key-hex <hex-encoded-instance-key>

CLI: License Reuse (After Deprovision)

Fast re-provisioning using the cached license from p2p-auth:

# 1. Deprovision (factory-reset, clears provisioned state)
fhenomai admin deprovision --force --yes-i-understand

# 2. Re-provision using cached internal license (no parameters needed)
fhenomai provision --reuse-license

This workflow reuses the license certificate stored in p2p-auth (TEE), avoiding License Manager contact. Works only if the license is not expired.

SDK: Standard Provisioning

=== "Signature" python def provision( client_id: str, num_encryptable: int, admin_token: Optional[str] = None, rotation_token: Optional[str] = None, client_ssl_cert_hex: Optional[str] = None, client_ssl_key_hex: Optional[str] = None, root_cert_hex: Optional[str] = None, instance_cert_hex: Optional[str] = None, instance_key_hex: Optional[str] = None, ) -> Dict[str, Any]

=== "Example" ```python from fhenomai import FHEnomClient, FHEnomConfig

config = FHEnomConfig.from_file()
client = FHEnomClient(config)

# Provision with client config (contacts License Manager)
result = client.provisioning.provision(
    client_id="my-client",
    num_encryptable=100,
    admin_token="my-token",
    rotation_token="my-rotation-token"
)
print(f"Status: {result['status']}")  # "Provisioned"
```

Parameters:

Parameter Type Required Description
client_id str Yes Unique client identifier
num_encryptable int Yes Number of models allowed to encrypt
admin_token str No Admin authentication token
rotation_token str No Token for credential rotation
client_ssl_cert_hex str No Client SSL cert (hex); requires client_ssl_key_hex
client_ssl_key_hex str No Client SSL key (hex); requires client_ssl_cert_hex
root_cert_hex str No Root CA cert (hex); requires both instance certs
instance_cert_hex str No Instance cert (hex); requires all out-of-band params
instance_key_hex str No Instance key (hex); requires all out-of-band params

Returns: Dict[str, Any] - Provisioning response with status field

Exceptions:

Exception Condition
ValueError SSL or out-of-band license parameters incomplete
ConnectionError Cannot connect to TEE server
HTTPError Server returns an error

Note: Without a valid License Manager certificate, only limited endpoints available on the node.


SDK: Deprovision and License Reuse

from fhenomai import FHEnomClient, FHEnomConfig

config = FHEnomConfig.from_file()
client = FHEnomClient(config)

# Check current license status
license_info = client.admin.get_license_info()
print(f"Status: {license_info['status']}")
print(f"Client: {license_info['client_id']}")
print(f"Models: {license_info['locally_encrypted_models']} / {license_info['available_to_encrypt']}")

# Deprovision the node (destructive factory-reset)
result = client.admin.deprovision()
print(f"Deprovisioned: {result['response']}")

# Wait for node to restart after deprovision
import time
time.sleep(5)

# Re-provision using the cached license from p2p-auth
# (requires node to be in PROVISIONING state after deprovision)
result = client.provisioning.reuse_license()
print(f"Re-provisioned: {result['status']}")

# Verify license was restored (if node is ready)
try:
    license_info = client.admin.get_license_info()
    print(f"Status: {license_info['status']}")
except Exception:
    print("Node still restarting after license reuse")

!!! note "Security" - Private keys remain protected in TPM throughout the process - The license certificate is reused internally from p2p-auth (not portable) - License Manager is not contacted during reuse - Provisioning is always attestation-based

SFTP Operations

# Get SFTP manager
sftp = client.get_sftp_manager()

# Upload model (upload/ prefix added automatically)
sftp.upload_directory(
    local_path="./llama-3-8b",
    remote_path="llama-3-8b"  # Becomes upload/llama-3-8b
)

# Download encrypted model (download/ prefix added automatically)
sftp.download_directory(
    remote_path="llama-3-8b-encrypted",  # Becomes download/llama-3-8b-encrypted
    local_path="./encrypted/llama-3-8b"
)

# List files in upload directory
files = sftp.list_upload_directory()
for file in files:
    print(f"{file.name}: {file.size_mb:.2f} MB")

# Clear download directory
sftp.clear_download_directory()

# Get directory size
size_gb = sftp.get_directory_size("upload")
print(f"Upload directory: {size_gb:.2f} GB")

# Check if file exists (via Admin API's SFTP manager)
exists = client.admin.sftp.file_exists("upload/my-model/config.json")

Directory transfers attempt every file and then raise SFTPError if any of them failed, naming the files and the reason — so a transfer that returns has moved everything, and a partial transfer is never reported as complete:

from fhenomai.exceptions import SFTPError

try:
    count = sftp.download_directory(
        remote_path="llama-3-8b-encrypted",
        local_path="./encrypted/llama-3-8b",
    )
    print(f"Downloaded {count} files")
except SFTPError as e:
    # Nothing to clean up: a failed download removes its own partial file.
    print(f"Download did not complete: {e}")

Permission problems on the node are reported as permission problems rather than looking like a missing file, including for exists().

Health & Testing

# Test connectivity (via CLI)
# fhenomai health check
# fhenomai test connection

# In Python - test admin API
try:
    models = client.admin.list_models()
    print(f"✓ Admin API connected ({len(models)} models)")
except Exception as e:
    print(f"✗ Admin API failed: {e}")

# Test SFTP connection
try:
    sftp = client.get_sftp_manager()
    files = sftp.list_upload_directory()
    print(f"✓ SFTP connected ({len(files)} files in upload/)")
except Exception as e:
    print(f"✗ SFTP failed: {e}")

User Inference (via OpenAI SDK)

For inference, use the standard OpenAI Python SDK:

from openai import OpenAI

# Connect to FHEnom User API (port 9999)
client = OpenAI(
    base_url="http://your-tee-ip:9999/v1",
    api_key="not-needed"  # TEE doesn't require API key
)

# Standard OpenAI-compatible inference
response = client.chat.completions.create(
    model="your-model-name",
    messages=[
        {"role": "user", "content": "Explain quantum computing"}
    ],
    max_tokens=200
)

print(response.choices[0].message.content)

🛠️ Advanced Usage

Context Manager Usage

from fhenomai import FHEnomClient, FHEnomConfig

# Load config
config = FHEnomConfig.from_file()

# Context manager handles connection lifecycle
with FHEnomClient(config) as client:
    # SFTP connection auto-managed
    sftp = client.get_sftp_manager()
    
    # Upload model (upload/ prefix added automatically)
    sftp.upload_directory("./model", "model")
    
    # Encrypt (paths auto-normalized)
    job_id = client.admin.encrypt_model(
        model_name_or_path="model",
        out_encrypted_model_path="model-enc",
        encrypted_model_id="model-enc"
    )
    
    # Wait for completion
    result = client.admin.wait_for_job(job_id)
    
    if result.get('status') == 'done':
        # Download encrypted model (download/ prefix added automatically)
        sftp.download_directory(
            "model-enc",
            "./encrypted/model"
        )
# Connection automatically closed

Job Monitoring with Callbacks

import time

# Encrypt with progress callback (paths auto-normalized)
job_id = client.admin.encrypt_model(
    model_name_or_path="large-model",
    out_encrypted_model_path="large-model-enc",
    encrypted_model_id="large-model-enc"
)

# Define callback for progress updates
def progress_callback(status):
    progress = status.get('progress', 0) * 100
    message = status.get('message', 'Processing')
    print(f"\r{message}: {progress:.1f}%", end='', flush=True)

# Wait with callback
result = client.admin.wait_for_job(
    job_id,
    timeout=3600,
    poll_interval=5,
    callback=progress_callback
)

print(f"\nCompleted: {result.get('status')}")

## 📋 Configuration

### Configuration File

Create `~/.fhenomai/config.yaml`:

```yaml
# Admin API Configuration
admin:
  host: "your-tee-ip"
  port: 9099
  url: "http://your-tee-ip:9099"  # Alternative to host+port

# User API Configuration (for inference)
user:
  host: "your-tee-ip"
  port: 9999
  url: "http://your-tee-ip:9999/v1"  # Alternative to host+port

# SFTP Configuration
sftp:
  host: "your-tee-ip"
  port: 22
  username: "admin"
  password: "your-password"  # Or use key_path
  # key_path: "~/.ssh/id_rsa"  # Alternative to password
  # base_path: directory containing upload/ and download/. Leave unset (default)
  # to resolve paths relative to the SFTP login dir — correct for chrooted or
  # home-rooted SFTP accounts. Set to an absolute path only for no-chroot
  # installs where the login does NOT land in the admin directory:
  # base_path: "/var/lib/fhenomai/FHEnomAI-server/admin"

# Optional settings
timeout: 30
max_retries: 3
verify_ssl: true
auth_token: "default-auth-token-2026"  # X-Auth-Token header

Environment Variables

export FHENOM_ADMIN_HOST="your-tee-ip"
export FHENOM_ADMIN_PORT="9099"
export FHENOM_SFTP_HOST="your-tee-ip"
export FHENOM_SFTP_USERNAME="admin"
export FHENOM_SFTP_PASSWORD="your-password"

Then use without parameters:

from fhenomai import FHEnomClient, FHEnomConfig

# Load from environment
config = FHEnomConfig.from_env()
client = FHEnomClient(config)

# Or load from file
config = FHEnomConfig.from_file()  # Reads ~/.fhenomai/config.yaml
client = FHEnomClient(config)

🔧 API Reference

FHEnomClient

Main client class for FHEnom AI operations.

Key Methods:

  • admin - Access AdminAPI instance for model/serving operations
  • get_sftp_manager() - Get SFTPManager for file operations
  • Context manager support with __enter__ and __exit__

AdminAPI

Admin operations (accessible via client.admin):

Model Operations:

  • list_models() - List all encrypted models
  • list_online_models() - List currently served models
  • get_model_info(model_id) - Get model details
  • encrypt_model(...) - Encrypt a plaintext model
  • encrypt_dataset(...) - Encrypt a dataset
  • split_model(encrypted_model_id, first_layers, last_layers) - Split a model for sovereign inference

Serving Operations:

  • start_serving(encrypted_model_id, server_url=..., ...) - Start external serving
  • start_serving(encrypted_model_id, sovereign_server_host=..., sovereign_server_port=...) - Start sovereign split inference
  • stop_serving(encrypted_model_id) - Stop serving

Job Operations:

  • get_job_status(job_id) - Check job status
  • wait_for_job(job_id, timeout, callback) - Wait for completion

SFTP Operations (via admin.sftp):

  • Access to SFTPManager for TEE directory operations

SFTPManager

High-level SFTP operations (accessible via client.get_sftp_manager() or client.admin.sftp):

Directory Operations:

  • upload_directory(local_path, remote_path) - Upload directory
  • download_directory(remote_path, local_path) - Download directory
  • list_upload_directory() - List files in upload/
  • list_download_directory() - List files in download/
  • clear_upload_directory() - Clear upload directory
  • clear_download_directory() - Clear download directory

File Operations:

  • upload_file(local_file, remote_file) - Upload single file
  • download_file(remote_file, local_file) - Download single file
  • file_exists(remote_path) - Check if file exists
  • get_directory_size(directory) - Get size in GB

🤝 Contributing

Contributions are welcome! Please contact DataKrypto for contribution guidelines.

📄 License

This project is licensed under the MIT License - see LICENSE file.

📞 Contact

DataKrypto

United States
533 Airport Blvd. Ste 400
Burlingame, CA 94010
+1 (650) 373-2083

Italy
Via Marche, 54
00187 Rome - Italy
+39 (06) 88923849


© 2026 DataKrypto. All rights reserved.

Release files for fhenomai 1.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fhenomai 1.4.0
File Size Uploaded
fhenomai-1.4.0.tar.gz 278.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fhenomai 1.4.0
File Interpreter ABI Platform
fhenomai-1.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 506.0 kB

Release files / fhenomai-1.4.0.tar.gz

Download URL fhenomai-1.4.0.tar.gz
Size 278.3 kB
Tags Source
SHA-256 checksum
How to use checksums
85bea8e8658d1da0de9b86021f11e25af6911a78c7f94ce297d6146716660caf
BLAKE2b-256 checksum
How to use checksums
124dba61d320e9ca62946039c2a14857e24a1e010bd5d32d80af7072084769e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release files / fhenomai-1.4.0-py3-none-any.whl

Download URL fhenomai-1.4.0-py3-none-any.whl
Size 227.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
786671c8f5cc8c625a9b0164be5c86b06c7e5d6efc7b6169cd834bd7812f6f43
BLAKE2b-256 checksum
How to use checksums
fff353b36cf3128226cde54f70bf61cb10deb0ae6831d0692dbcf7950e36cc23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release history Release notifications | RSS feed

This release

1.4.0 This release

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.3

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.24

2 release files

1.0.23

2 release files

1.0.22

2 release files

1.0.21

2 release files

1.0.19

2 release files

1.0.18

2 release files

1.0.17

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.13

2 release files

1.0.12

2 release files

1.0.11

2 release files

1.0.10

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page