TenSEAL: homomorphic encryption on tensors, with SEAL underneath
A library for doing homomorphic encryption operations on tensors
At a glance
- What is it?
- TenSEAL wraps Microsoft SEAL in a Python tensor layer, so you can add, multiply and matmul encrypted vectors. Here is what it does, how to install it, and where the noise budget runs out.
- Who is it for?
- Adopt TenSEAL when the computation is a short arithmetic circuit over vectors and you can pick the encryption parameters deliberately: CKKS for real numbers, BFV for integers. Do not adopt it if you need arbitrary Python over encrypted data, if you expect the Docker Hub images to work, or if you are on macOS with Xcode 16 or newer and do not want to use a wheel.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 18 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What TenSEAL solves, and for whom
Homomorphic encryption lets a computation run on ciphertext and produce a result that decrypts to the same answer as running it on plaintext. The hard part is not the idea, it is the arithmetic. Microsoft SEAL exposes a C++ API where you manage polynomial moduli, Galois keys, relinearisation and a noise budget that shrinks with every operation. TenSEAL keeps SEAL's semantics and puts a tensor interface on top, so the unit you manipulate is a vector or an N-dimensional tensor rather than a plaintext polynomial.
The audience is narrow and specific. If you are prototyping a privacy-preserving inference path, or you need to compute a dot product or a matrix multiplication without seeing the inputs, TenSEAL gives you that in a few lines. The README states the library implements most operations in C++ and exposes them through Python, so the tensor layer is not a Python reimplementation of the crypto. If you only need to encrypt a blob and store it, you do not need this: ordinary authenticated encryption is faster and simpler. TenSEAL exists for the case where the party doing the computation should not learn the data.
The mechanism: SEAL contexts, a tensor layer, and a shrinking noise budget
Everything starts from a context. You choose a scheme, a polynomial modulus degree, coefficient modulus bit sizes, and for CKKS a global scale. The README is explicit that these parameters carry exactly their SEAL meaning, and that they determine both the security level and how many operations you can chain before the noise budget is exhausted. That sentence is the whole design contract: TenSEAL does not abstract the parameters away, it hands them to you with SEAL's documentation as the reference.
Two schemes are exposed. BFV encrypts vectors of integers. CKKS encrypts vectors of real numbers, which is why the example decrypts to approximately [4, 4, 4, 4, 4] rather than exactly. On top of that, the library supports element-wise addition, subtraction and multiplication between encrypted and encrypted or encrypted and plain vectors, plus dot product and vector-matrix multiplication. The tensor types CKKSTensor and BFVTensor add reshape, broadcast and transpose for N-dimensional data. Galois keys, generated separately, are what make the rotations inside those operations possible.
Serialisation covers contexts, keys and encrypted tensors. That matters more than it sounds: in a real deployment the key holder and the compute party are different processes, so the context has to travel. The README also notes that the complete SEAL API is reachable under tenseal.sealapi, which is the escape hatch when the tensor layer does not expose something you need.
Install TenSEAL with pip, uv or conda
The supported path is pip, and the package requires Python 3.11 or newer with NumPy as its only declared runtime dependency. Prebuilt wheels are published for Linux glibc and musl on x86-64, Linux glibc on aarch64, macOS Apple Silicon, and Windows x64. pip selects the right one automatically.
pip install tensealAfter that, confirm the import works and check the version. The README gives exactly this check.
import tenseal as ts
print(ts.__version__)If your system Python is older than 3.11, or you would rather not touch it, the README points at uv, which can fetch a Python for you and keep TenSEAL in its own environment.
uv venv --python 3.13
source .venv/bin/activate
uv pip install tensealFor a throwaway session, uv can fetch Python 3.13 and TenSEAL and discard the environment afterwards. Replace 3.13 with any version from 3.11 to 3.14.
uv run --python 3.13 --with tenseal pythonTenSEAL is not published on conda-forge. The README's instruction is to create the conda environment and then install with pip inside it.
conda create -n tenseal python=3.13
conda activate tenseal
pip install tensealA first real use is the CKKS example from the README. The context sets the polynomial modulus degree to 8192, coefficient modulus bit sizes to [60, 40, 40, 60], and the global scale to 2**40. Galois keys are generated because the later operations need rotations.
import tenseal as ts
context = ts.context(
ts.SCHEME_TYPE.CKKS,
poly_modulus_degree=8192,
coeff_mod_bit_sizes=[60, 40, 40, 60]
)
context.generate_galois_keys()
context.global_scale = 2**40
v1 = [0, 1, 2, 3, 4]
v2 = [4, 3, 2, 1, 0]
enc_v1 = ts.ckks_vector(context, v1)
enc_v2 = ts.ckks_vector(context, v2)
result = enc_v1 + enc_v2
print(result.decrypt())The decrypted sum is approximately [4, 4, 4, 4, 4]. The approximation sign is not decoration: CKKS is a floating-point scheme, so the low bits are noise, not rounding error you can eliminate.
Where TenSEAL stops being the right tool
The noise budget is the first wall. Every multiplication consumes budget, and once it is exhausted the decrypted result is garbage rather than an error. Nothing in the README suggests TenSEAL tracks or reports the remaining budget for you, and the parameters are yours to choose, so a circuit that is one multiplication too deep fails silently at the end. Compare the cost of a plain vector addition with an encrypted one and the gap is orders of magnitude, not percent.
The second wall is expressiveness. There is no branching on encrypted values and no comparison that yields a usable boolean. You can multiply by a mask, you cannot write an if. Any algorithm you port has to be rewritten as a fixed arithmetic circuit, and that rewrite is usually the bulk of the work.
The third is tooling. The README marks the Docker Hub images as unmaintained, with the newest published in 2021 for v0.3.4, and states that the docker-images directory targets Python 3.6 to 3.9, all end-of-life and below the supported minimum. It says plainly not to rely on them. The Bazel build is described as currently broken. On macOS, AppleClang 17 and newer, meaning Xcode 16 or later, currently fails to compile the vendored xtensor, so a source build there needs Xcode 15.x or a published wheel. And with CMake 4.0 or newer, the build fails with a compatibility error until you set CMAKE_POLICY_VERSION_MINIMUM=3.5 in the environment. None of these are fatal, but each one is a detour you should know about before you start.
TenSEAL against Pyfhel
Pyfhel is the obvious alternative and people search for it alongside TenSEAL. Both are Python bindings over a C++ homomorphic encryption library, but the layer they choose to expose differs. Pyfhel binds to SEAL and to other backends and presents an API shaped like the underlying scheme: ciphertext objects, plaintext objects, explicit encode and decode steps, and the operations that go with them. TenSEAL picks one backend, SEAL, and invests in a tensor abstraction instead: encrypted vectors and N-dimensional tensors with reshape, broadcast and transpose, plus dot product and vector-matrix multiplication.
That difference decides the choice. If your computation is a neural network layer or a linear algebra expression, TenSEAL's tensor types save you from writing the rotation and accumulation logic yourself. If you need access to a second backend, or you want to work at the level of the scheme rather than the tensor, Pyfhel's shape is closer to what you are doing. TenSEAL does expose the full SEAL API under tenseal.sealapi, so the low level is not out of reach, but it is a fallback rather than the main interface.
Maintenance, licence and upgrade cost
The last push to the default branch was on 2026-09-08, and the most recent release is v0.3.17 from 2026-08-04. The release history is uneven rather than steady: v0.3.15 is dated 2024-09-30, and v0.3.16 and v0.3.17 both landed on consecutive days in August 2026. That pattern suggests bursts of work rather than a continuous cadence, which is worth knowing if you plan to depend on upstream fixes arriving on a schedule.
Upgrades are not free. TenSEAL is a binding over SEAL plus a tensor layer, so the parameters in your context are SEAL parameters. A change to the vendored SEAL version can change what a given poly_modulus_degree and coeff_mod_bit_sizes combination means for security and for usable depth. Your serialised contexts, keys and encrypted tensors are also tied to the format, so an upgrade is a chance to re-check that stored artefacts still load. The project publishes a source distribution as well as wheels, so platforms without a wheel compile from source, which pulls in a C++17 toolchain and CMake 3.14 or newer.
The licence is Apache-2.0, declared in pyproject.toml with license-files pointing at LICENSE. Apache-2.0 is permissive and includes a patent grant, which is the usual reason projects pick it over MIT for cryptography. That is a description of the terms, not legal advice; if you are shipping in a regulated context, read the LICENSE file and the SEAL project's own terms, since TenSEAL builds on it.
Editorial conclusion
Adopt TenSEAL when the computation is a short arithmetic circuit over vectors and you can pick the encryption parameters deliberately: CKKS for real numbers, BFV for integers. Do not adopt it if you need arbitrary Python over encrypted data, if you expect the Docker Hub images to work, or if you are on macOS with Xcode 16 or newer and do not want to use a wheel. Before writing code, verify three things: that a wheel exists for your platform, that your Python is 3.11 or newer, and that your chosen poly_modulus_degree and coeff_mod_bit_sizes leave enough noise budget for the depth you plan to evaluate.
Frequently asked questions
How do I install TenSEAL?
Install it with pip install tenseal, which requires Python 3.11 or newer and NumPy. Prebuilt wheels are published for Linux glibc and musl on x86-64, Linux glibc on aarch64, macOS Apple Silicon, and Windows x64, and pip selects the right one automatically.
What is TenSEAL used for?
It performs homomorphic encryption operations on tensors, so arithmetic can run on encrypted vectors and matrices. The README lists element-wise addition, subtraction and multiplication, dot product, vector-matrix multiplication, and N-dimensional tensors with reshape, broadcast and transpose.
Which encryption schemes does TenSEAL support?
Two: BFV for vectors of integers and CKKS for vectors of real numbers. The scheme is chosen when you build the context with ts.context, and for CKKS you also set context.global_scale.
Can I install TenSEAL with conda?
Not from conda-forge, because the README states TenSEAL is not published there. The documented approach is to create a conda environment and then run pip install tenseal inside it.
Can I use the official TenSEAL Docker images?
The README marks them as unmaintained and says not to rely on them. The newest image on Docker Hub was published in 2021 for v0.3.4, and the docker-images directory targets Python 3.6 to 3.9, which are end-of-life and below the supported minimum.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openmined-tenseal)