Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.3.17] - 2026-08-04

Minimum supported Python is now **3.11**. No library API changes.

### Added

- Source distribution is now published, so `pip install tenseal` works on
platforms without a prebuilt wheel by building from source ([#522])
- Wheels for Python 3.13 and 3.14 ([#519])

### Changed

- Build and test on Python 3.11–3.14 across Linux, macOS (arm64) and Windows via
cibuildwheel ([#519])
- Packaging modernized to PEP 517/621, with metadata in `pyproject.toml`; dev
dependencies moved to a PEP 735 group ([#521])
- Linting moved from flake8 and black to ruff, run through pre-commit ([#521])
- Microsoft SEAL updated to v4.3.3 ([#519])
- Releases are published in a single upload rather than once per matrix job, so a
partial failure can no longer leave a half-published release ([#522])

### Fixed

- `numpy` is now declared as a runtime dependency. It was previously undeclared,
so a bare `pip install tenseal` could produce an unimportable package ([#521])
- Benchmarks workflow, which had been failing since February 2025 ([#520])

### Removed

- Support for Python 3.9 and 3.10

## [0.3.16] - 2025-02-28

### Added

- Support for Python 3.13, with wheels built by cibuildwheel ([#509])

### Removed

- Support for Python 3.8

## Earlier releases

Release notes for v0.3.15 and earlier are on the
[GitHub releases page](https://github.com/OpenMined/TenSEAL/releases).

[Unreleased]: https://github.com/OpenMined/TenSEAL/compare/v0.3.17...HEAD
[0.3.17]: https://github.com/OpenMined/TenSEAL/compare/v0.3.16...v0.3.17
[0.3.16]: https://github.com/OpenMined/TenSEAL/compare/v0.3.15...v0.3.16
[#509]: https://github.com/OpenMined/TenSEAL/pull/509
[#519]: https://github.com/OpenMined/TenSEAL/pull/519
[#520]: https://github.com/OpenMined/TenSEAL/pull/520
[#521]: https://github.com/OpenMined/TenSEAL/pull/521
[#522]: https://github.com/OpenMined/TenSEAL/pull/522
113 changes: 62 additions & 51 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The following is a set of guidelines for contributing to [TenSEAL](https://githu
* [Pull Requests](#pull-requests)

[Your Local Development Environment](#your-local-development-environment)
* [Fetching Third Party Libraries](#fetching-third-party-libraries)
* [Third Party Libraries](#third-party-libraries)
* [Building TenSEAL](#building-tenseal)
* [Testing Your Changes](#testing-your-changes)

Expand All @@ -31,23 +31,35 @@ The following is a set of guidelines for contributing to [TenSEAL](https://githu
[Additional Notes](#additional-notes)
* [Issue and Pull Request Labels](#issue-and-pull-request-labels)

## Other documents

## I have a question!
- [SECURITY.md](SECURITY.md) — what to report here versus to Microsoft SEAL, and how
- [RELEASING.md](RELEASING.md) — how a release is rehearsed and published
- [CHANGELOG.md](CHANGELOG.md) — notable changes per release

If you have a question regarding release dates, how to use the library, and things like that, we suggest using the [OpenMined Slack](https://slack.openmined.org), you should get faster results in the slack channels than in a Github issue.
## I have a question

Make sure to ask your questions in the **#lib_tenseal** channel.
For how-to questions, the **#lib_tenseal** channel on the
[OpenMined Slack](https://slack.openmined.org) is where TenSEAL users gather, and
[past issues](https://github.com/OpenMined/TenSEAL/issues?q=is%3Aissue) are often the
fastest answer. Nobody is on duty in either place, so treat a reply as a favour
rather than an expectation.

## What should I know before I get started?

### TenSEAL Library

TenSEAL is a C++ library, all the core functionalities should be implemented in C++. Bindings to other languages such as Python should only have language specific functionalities. This make sure that the same features are available through all languages.

### Roadmap
### Project Status

The roadmap of the project which can be found [here](https://github.com/OpenMined/Roadmap/blob/master/crypto_team/projects/TenSEAL.md) is mainly drawn by the homomorphic encryption group in the [crypto team](https://github.com/OpenMined/Roadmap/tree/master/crypto_team) at OpenMined.
TenSEAL is not actively maintained. No team is working on it, and there is no
committed support: issues and pull requests may sit unanswered, and a release
happens only when a contributor drives one.

Contributions are still welcome, and the open
[GitHub issues](https://github.com/OpenMined/TenSEAL/issues) are the closest
thing to a list of outstanding work.

## How Can I Contribute?

Expand Down Expand Up @@ -112,104 +124,103 @@ We also suggest starting with the [tutorials](https://github.com/OpenMined/TenSE

TenSEAL can be developed locally. Check [this section](#your-local-development-environment) for instructions on how to setup your local development environment.


### Pull Requests

The process described here has several goals:

- Maintain TenSEAL's quality
- Fix problems that are important to users
- Engage the community in working toward the best possible TenSEAL
- Enable a sustainable system for TenSEAL's maintainers to review contributions

Please follow these steps to have your contribution considered by the maintainers:
There is no review rota, so a pull request may sit for a long time. What you can do
is make it easy to merge whenever someone does look:

1. Follow all instructions in the pull request template
1. Keep it to a single concern, with a title and description that explain the why
2. Follow the [styleguides](#styleguides)
3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing <details><summary>What if the status checks are failing?</summary>If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.</details>

While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted.
3. Make sure the [status checks](https://help.github.com/articles/about-status-checks/) pass

If a check fails for a reason unrelated to your change, say so in a comment and
explain why. CI has known flaky spots, and the next person to read the PR will need
that context.

## Your Local Development Environment

Before being able to make any code contribution, you need to be able to setup your local environment to make changes, build the new library, as well as running the tests to make sure previous functionalities are still working, and to tests new ones.

> **Note:** You can always use our [Docker images](https://hub.docker.com/r/openmined/tenseal) for a ready to use environment. We provide images for different Python versions on every release or code change in the master branch, so it's also easy to try older non-released versions.
TenSEAL requires **Python 3.11 or newer**, a C++17 toolchain, and CMake. See [Build from Source](README.md#build-from-source) in the README for the per-platform requirements and known toolchain limitations.

> **TODO:** the [Docker images](https://hub.docker.com/r/openmined/tenseal) are unmaintained — the newest was published in 2021 — so they are not a usable development environment today. Refreshing or retiring them is tracked as future work.

### Fetching Third Party Libraries

After cloning TenSEAL into your machine, or cloning one of your forks, you must make sure to fetch thrid party libraries that TenSEAL depends on, you can do that by running:

```bash
$ git submodule init
$ git submodule update
```
### Third Party Libraries

There is nothing to fetch by hand. All third-party dependencies — Microsoft SEAL, Protocol Buffers, xtensor, pybind11 — are downloaded and built by CMake during the build. The repository has no git submodules.

### Building TenSEAL

You can build and install the library (with Python bindings) locally by running:
Development dependencies are declared as a [PEP 735](https://peps.python.org/pep-0735/) group in `pyproject.toml` and pinned in `uv.lock`.

Using [uv](https://docs.astral.sh/uv/), which installs the locked set:

```bash
$ pip install --group dev
$ pip install .
uv sync --group dev
```

Development dependencies are declared as a [PEP 735](https://peps.python.org/pep-0735/) group in `pyproject.toml` and pinned in `uv.lock`. If you use [uv](https://docs.astral.sh/uv/), `uv sync --group dev` installs the locked set instead.
Or with pip, which resolves the group fresh rather than from the lock file:

This will trigger the build of the C++ library as well as the Python bindings. Please refer to [this section](https://github.com/OpenMined/TenSEAL#build-from-source) for more information about the dependencies required to build TenSEAL.
```bash
pip install --group dev
pip install .
```

> **Note:** You can skip the building step if you are testing the library using Bazel, as it make sure the library is built before running the tests.
Either path triggers the build of the C++ library as well as the Python bindings.


### Testing Your Changes

#### C++

If you use Bazel, you can do that pretty easily by running:
Build and run the C++ test suite with CMake:

```bash
$ bazel test --test_output=all --spawn_strategy=standalone //tests/cpp/...
cmake . -D BUILD_TEST=TRUE
make -j && CTEST_OUTPUT_ON_FAILURE=1 make test
```

Otherwise, you can always build the tests using CMake for the C++ test, and run them as follows:
#### Python

The Python tests run with pytest. The full suite is slow, so CI skips the tests marked `slow`:

```bash
$ cmake . -D BUILD_TEST=TRUE
$ make && make test
pytest -m "not slow" -v tests/python/tenseal
pytest -v tests/python/sealapi
```

#### Python
Drop `-m "not slow"` to run everything, including the long-running serialization tests.

The Python tests can be ran using pytest:
> **TODO:** the Bazel test targets (`bazel test //tests/cpp/...` and `//tests/python/...`) are currently broken and cannot be used. See the Bazel note in the [README](README.md#using-bazel).

```bash
$ pytest -v tests/
```

You can also start the tests with Bazel:
## Styleguides

Formatting and linting are enforced by [pre-commit](https://pre-commit.com/), which runs the same hooks locally that CI runs on your pull request. Install the git hook once, and your changes are checked on every commit:

```bash
$ bazel test --test_output=all --spawn_strategy=standalone //tests/python/...
pre-commit install
```

To check everything at any time — this is exactly what the `Linter` CI job does:

## Styleguides
```bash
pre-commit run --all-files
```

### C++ Styleguide

All C++ code must be formatted based on our [clang-format config file](https://github.com/OpenMined/TenSEAL/blob/master/.clang-format). You can easily setup that into your prefered IDE, so that formatting is applied as you code.
C++ code is formatted with [clang-format](https://clang.llvm.org/docs/ClangFormat.html) using the [.clang-format](.clang-format) config in the repository root.

### Python Styleguide

All Python code must be formatted using [black](https://github.com/psf/black). You can easily setup that into your prefered IDE, so that formatting is applied as you code.

Python code is linted and formatted with [ruff](https://docs.astral.sh/ruff/), configured under `[tool.ruff]` in [pyproject.toml](pyproject.toml). Ruff replaces the black and flake8 setup used previously; most editors have a ruff integration that formats as you type.

### Documentation Styleguide

TBD
- Keep the README accurate about what currently works. If a feature is broken or unmaintained, say so rather than leaving stale instructions in place.
- Python code is documented with docstrings on public classes and methods, using Google style (`Args:`, `Returns:`).
- Prefer relative links between files in the repository so they keep working on forks and branches.


## Additional Notes
Expand Down
83 changes: 83 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Releasing TenSEAL

Releases are built and published by the **Build and Publish** workflow
([`.github/workflows/publish_wheels.yml`](.github/workflows/publish_wheels.yml)).
It produces 12 wheels — Python 3.11–3.14 for Linux, macOS (arm64) and Windows —
plus a source distribution, and uploads them in a single step so a partial
failure cannot leave a half-published release on the index.

## 1. Bump the version

The version lives in one place, [`tenseal/version.py`](tenseal/version.py), and
is read from there by the build backend:

```python
__version__ = "0.3.18"
```

Commit that through a pull request as usual.

## 2. Rehearse on TestPyPI

Do this before every release. It exercises the whole pipeline — build, sdist,
rebuilding a wheel *from* the sdist, upload, and installing the result back on
all three platforms.

1. **Actions** → **Build and Publish** → **Run workflow**
2. Leave `Index to publish to` set to `testpypi`
3. Wait for all jobs to go green (roughly 30 minutes)

The rehearsal automatically stamps a unique version — `0.3.18.dev<run_number>` —
because PyPI and TestPyPI both refuse to overwrite an existing version. Without
that, only the first rehearsal of a given version would upload anything.

Check the run for:

- **Build a wheel from the sdist** — proves `MANIFEST.in` is complete. If the
sdist is missing `CMakeLists.txt`, `cmake/` or the `.proto` files it unpacks
fine and then fails to build, and `twine check` will not catch it.
- **Install from TestPyPI** — proves a real `pip install` selects the right wheel
per platform and that the package imports.

## 3. Tag and publish the release

Tags are lightweight and named `vMAJOR.MINOR.PATCH`:

```bash
git tag v0.3.18 <commit>
git push upstream v0.3.18
```

Then create the GitHub Release for that tag. **Publishing the release is what
triggers the real upload** — pushing the tag alone does nothing.

```bash
gh release create v0.3.18 --title "v0.3.18" --notes "..."
```

The workflow runs again on the `release: published` event, this time targeting
PyPI. If the `pypi` environment has a required reviewer, the publish job waits
for approval; the build jobs run first regardless.

## 4. Verify

```bash
pip index versions tenseal
```

Or check [the project page](https://pypi.org/project/tenseal/) for 12 wheels and
one `.tar.gz`. Update [`CHANGELOG.md`](CHANGELOG.md) if it was not part of the
version bump.

## Notes

- **A failed publish does not require a rebuild.** The wheels and sdist are kept
as workflow artifacts, so the publish job can be re-run on its own.
- **`--skip-existing` is used**, so re-running a publish will not error on files
that are already uploaded — but it also means a silently skipped upload looks
like success. Always verify the file count on the index.
- **TestPyPI is not durable.** It is pruned periodically; never treat it as a
record of what was released.
- **Publishing to PyPI uses the `PYPI_USERNAME` / `PYPI_PASSWORD` secrets**, and
the TestPyPI rehearsal uses `TEST_PYPI_API_TOKEN`. These are separate accounts
and separate credentials.
48 changes: 48 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Security Policy

## Supported versions

[![Supported version](https://img.shields.io/pypi/v/tenseal?label=supported%20version)](https://pypi.org/project/tenseal/)

Only the latest release receives fixes. Older versions are not patched — if you
hit a problem, please upgrade first and check whether it still reproduces.

## Scope

TenSEAL is a Python binding over [Microsoft SEAL](https://github.com/microsoft/SEAL),
plus a tensor layer built on top of it. That split decides where an issue belongs.

Issues in the underlying homomorphic encryption schemes, their security
guarantees, or SEAL's own implementation belong to SEAL — see its
[security policy](https://github.com/microsoft/SEAL/blob/main/SECURITY.md).

Everything in this repository belongs here:

- Memory safety in the C++ layer (`tenseal/cpp/`) or the pybind11 bindings
- Deserialization of untrusted contexts, keys or encrypted tensors
- Incorrect handling or propagation of encryption parameters in the TenSEAL layer
- Leakage of secret key material through the Python API or serialization
- Supply chain issues in the build or release workflow

## Reporting a vulnerability

Open a [GitHub issue](https://github.com/OpenMined/TenSEAL/issues/new/choose).

Please include a minimal reproduction, the TenSEAL version, and how you installed it.

## A note on the threat model

Homomorphic encryption protects data in use, but it does not by itself make an
application secure. TenSEAL does not protect against:

- **Parameter misconfiguration.** The security level of a context follows from the
parameters you choose. See the
[SEAL documentation](https://github.com/microsoft/SEAL#getting-started) for what
they mean.
- **Sharing a secret key.** A serialized context includes the secret key unless you
explicitly drop it before sharing.
- **Side channels.** Timing and memory access patterns are not hardened.

These are usually application design questions rather than vulnerabilities in
TenSEAL. If you believe the library makes an unsafe choice easy to reach by
accident, that is still worth reporting.
Loading