Metadata-Version: 2.4
Name: truenas-unlock
Version: 1.6.0
Summary: Unlock encrypted ZFS datasets on TrueNAS via the API
Project-URL: Homepage, https://github.com/basnijholt/truenas-unlock
Project-URL: Repository, https://github.com/basnijholt/truenas-unlock
Project-URL: Issues, https://github.com/basnijholt/truenas-unlock/issues
Author-email: Bas Nijholt <bas@nijho.lt>
License: MIT
License-File: LICENSE
Keywords: encryption,nas,truenas,unlock,zfs
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Systems Administration
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.25
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.9
Description-Content-Type: text/markdown

# TrueNAS Unlock

[![PyPI](https://img.shields.io/pypi/v/truenas-unlock)](https://pypi.org/project/truenas-unlock/)
[![Python](https://img.shields.io/pypi/pyversions/truenas-unlock)](https://pypi.org/project/truenas-unlock/)
[![Tests](https://github.com/basnijholt/truenas-unlock/actions/workflows/pytest.yml/badge.svg)](https://github.com/basnijholt/truenas-unlock/actions/workflows/pytest.yml)
[![License](https://img.shields.io/github/license/basnijholt/truenas-unlock)](LICENSE)

Unlock encrypted ZFS datasets on TrueNAS via the API.

https://github.com/user-attachments/assets/172c8fd7-5b66-4c5b-bae0-32e378e9305a

## Why?

TrueNAS supports encrypted ZFS datasets, but:

1. **Storing keys on the NAS defeats the purpose**—if it's stolen, the thief has both the encrypted data and the keys
2. **Manual unlocking is tedious**—after every reboot, you need to manually decrypt each dataset through the UI

This tool solves both problems with a **"poor-man's second-factor"** setup:

1. Run `truenas-unlock` on a **separate device** (Raspberry Pi, home server, etc.)
2. Store encryption keys **only on that device**
3. Datasets auto-unlock when both devices are on the network
4. If the NAS is stolen, data remains encrypted and inaccessible

Think of it as a hardware security key for your storage—hidden somewhere in your house, it automatically unlocks your datasets whenever your TrueNAS boots. No manual intervention required.

## Table of Contents

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Install](#install)
- [Setup](#setup)
- [Usage](#usage)
- [CLI](#cli)
- [Running as a Service](#running-as-a-service)
- [Development](#development)
- [Credits](#credits)
- [License](#license)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Install

```bash
# With uv (recommended)
uv tool install truenas-unlock

# With pip
pip install truenas-unlock
```

## Setup

Create an API key at `http://truenas.local/ui/credentials/users/api-keys` (replace with your TrueNAS hostname).

Then create `~/.config/truenas-unlock/config.yaml`:

```yaml
host: 192.168.1.214:443
api_key: ~/.secrets/truenas-api-key  # file path or literal
skip_cert_verify: true

# secrets: auto  # auto (default) | files | inline

datasets:
  tank/syncthing: ~/.secrets/syncthing-key  # reads from file
  tank/photos: my-literal-passphrase        # used as-is (no such file)
```

The `secrets` mode controls how values are interpreted:
- **auto** (default): if file exists, read from it; otherwise use as literal
- **files**: always treat values as file paths
- **inline**: always treat values as literal secrets

## Usage

```bash
# Run once
truenas-unlock

# Run as daemon
# (Checks every 1s if TrueNAS is unreachable, otherwise every 30s)
truenas-unlock --daemon

# Custom interval (for the "relaxed" state)
truenas-unlock --daemon --interval 60

# Dry run
truenas-unlock --dry-run
```

## CLI

```bash
truenas-unlock --help
```

<!-- CODE:BASH:START -->
<!-- export NO_COLOR=1 -->
<!-- export TERM=dumb -->
<!-- export TERMINAL_WIDTH=90 -->
<!-- echo '```bash' -->
<!-- truenas-unlock --help -->
<!-- echo '```' -->
<!-- CODE:END -->

<!-- OUTPUT:START -->
<!-- ⚠️ This content is auto-generated by `markdown-code-runner`. -->
```bash

 Usage: truenas-unlock [OPTIONS] COMMAND [ARGS]...

 Unlock TrueNAS ZFS datasets

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --config    -c      PATH     Config file path                                │
│ --dry-run   -n               Show what would be done                         │
│ --daemon    -d               Run continuously                                │
│ --interval  -i      INTEGER  Seconds between checks (1s if unreachable)      │
│                              [default: 30]                                   │
│ --dataset   -D      TEXT     Filter by dataset path                          │
│ --version   -v               Show version and exit                           │
│ --help      -h               Show this message and exit.                     │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ lock      Lock configured datasets.                                          │
│ status    Show lock status of configured datasets.                           │
│ service   Manage system service                                              │
╰──────────────────────────────────────────────────────────────────────────────╯

```

<!-- OUTPUT:END -->



## Running as a Service

Requires [uv](https://docs.astral.sh/uv/) to be installed. Auto-detects Linux (systemd) or macOS (launchd):

```bash
# Install and start
truenas-unlock service install

# Check status
truenas-unlock service status

# View logs (follows by default)
truenas-unlock service logs

# Uninstall
truenas-unlock service uninstall
```

## Development

```bash
# Clone and install
git clone https://github.com/basnijholt/truenas-unlock
cd truenas-unlock
uv sync --dev

# Run tests
uv run pytest

# Run lints
uv run ruff check .
uv run mypy truenas_unlock.py
```

## Credits

Inspired by [ThorpeJosh/truenas-zfs-unlock](https://github.com/ThorpeJosh/truenas-zfs-unlock).

## License

MIT
