# tlock makes a message decryptable only in the future

> tlock is a Go library and a `tle` command line tool that encrypt data so that it can only be decrypted after a chosen delay, by leaning on a drand threshold network for the key material. It is small, Apache licensed and well specified, but its security is the security of the drand network it points at, which is a decision you have to make deliberately.

**drand/tlock** — Timelock Encryption made practical. The Go `tlock` library and the `tle` cmd line tool home to encrypt towards the future.

- Repository: https://github.com/drand/tlock
- Stars: 689 · Forks: 36
- Language: Go
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/drand-tlock

## Encryption whose key schedule is a public randomness beacon

The premise is a scheme rather than a library of conveniences. tlock gives you time based encryption and decryption capabilities by relying on a drand threshold network, as described in the tlock paper the project links to from the IACR eprint server. The idea is that a drand network publishes periodic, unpredictable random values that nobody can predict in advance but everybody can verify after the fact, and tlock uses that published randomness to derive a key that becomes available only once the network has produced enough of it. So a ciphertext encrypted with a duration of ten days cannot be decrypted for ten days, because the key material needed to open it does not exist publicly until the network has advanced that far. This is different from encrypting with a key you hand to a trusted party, and different from a timed release where a service holds the key and releases it on schedule. Here the timing is enforced by the mathematics of the key schedule and by the network's liveness, not by a custodian. The audience is therefore anyone building a system where a secret must be provably inaccessible before a deadline, and the scheme is described in enough detail in the paper that the design can be reviewed independently of the Go code.

## The unchained drand network and the one chain hash that supports it

The system's security rests on a specific network configuration, and the README is precise about which one. The timelock encryption system relies on an unchained drand network, a multi-frequency drand arrangement described in a drand blog post. Working mainnet endpoints are listed as api.drand.sh in the US, api2.drand.sh in the EU, api3.drand.sh in Asia, and a load balanced drand.cloudflare.com address across regions. On mainnet, the README names the only chainhash supporting timelock encryption, with a 3 second frequency and signatures on the G1 group, and gives the value 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971, which the CLI section identifies as the quicknet chain. The README describes this as a production ready network with high availability guarantees, considered fully secure by the drand team and run by the same League of Entropy that has been running drand in production since 2019. The testnet situation is different, with two networks supporting timelock, one with 3 second frequency and G1 signatures and one with G2 signatures, and the README warns that these rely on the League of Entropy testnet, which should not be considered secure. It also notes you can spin up your own drand network, and that the security guarantees then boil down to the trust you have in your network. That sentence is the honest summary of the whole design and worth reading before anything else.

## Installing the tle tool, and the pre-1.0 compatibility trap

The tool is pure Go and works without CGO, which the README says explicitly with CGO_ENABLED=0, so a static binary is the normal outcome. Installation with Go is one command.

```bash
go install github.com/drand/tlock/cmd/tle@latest
```

Or from a local clone.

```bash
git clone https://github.com/drand/tlock
go build cmd/tle/tle.go
```

Then there is the compatibility note, which is the kind of thing that bites people with archived ciphertexts. If you need to decrypt old ciphertexts produced before v1.0.0 against the testnet, you need the old binary, installed at the old version tag.

```bash
go install github.com/drand/tlock/cmd/tle@v0.1.0
```

The reason is stated plainly: the way a hash is mapped to a field element changed in v1.0.0 and this is not retro-compatible with the old ciphertext format. You can still produce ciphertexts against the testnet with the new binary, but it will use the new hash to field mechanism. So a system that archived timelocked material has to keep the old binary around, and the format change is a breaking change without a major version bump in the ciphertext itself, which is the sort of detail that belongs in a retention policy rather than in a readme footnote. The repository layout supports the picture of a small focused project: tlock.go, tlock_age.go and their test files at the root, a cmd/ directory for the tool, networks/ for network definitions, testdata/ for fixtures, a .goreleaser.yml for releases, a SECURITY.md, and both LICENSE-APACHE and LICENSE-MIT at the top level, which is the standard dual licence arrangement of the drand organisation.

## Encrypting with a duration or with an explicit round

The CLI surface is small and the usage block lists the flags. The tool encrypts by default, with --encrypt as the explicit form, and --decrypt switches to the other direction. The output goes to a file with -o, or to stdout, and if the output file exists it will be overwritten. The network is selected with -n and the chain with -c, and the chain can be either a beacon ID name or a beacon hash, with the README advising the beacon hash in order to ensure public key integrity. A message can be locked to a duration with -D, or to a specific round with -r, and the two cannot be combined. The --force flag forces encryption against past rounds, and --armor encrypts to a PEM encoded format. A duration is a number followed by one of the units ns, us or µs, ms, s, m, h, d, M or y, so a month and a minute are both expressible. The README's own example encrypts to a file and decrypts it after the specified duration, and the same section shows the armored form of the same operation.

```bash
$ tle -a -D 20s -o=encrypted_data.PEM data.txt
```

```bash
$ tle -d encrypted_data
```

The full example against the testnet with a five second duration shows how the network and the chain hash are given together, and the round variant shows the same thing against the EU relay with the quicknet chainhash and round 123456, encrypting data that can be decrypted only when that round becomes available in the network. A duration is a convenience over a round, since the tool resolves the duration into the round that will exist at that future time.

## Decryption is easier than encryption, and it auto-detects now

The asymmetry is deliberate. For decryption it is only necessary to specify the network if you are not using the default one, and since v1.3.0 some auto-detection of chainhash and network is done upon decryption. The simplest decrypt is therefore the shortest command in the tool.

```bash
$ tle -d encrypted_data
```

When you do need to point at a specific network, the old testnet unchained network, the command is longer and the file is written to disk, with the note that it will overwrite the decrypted_data file if it already exists. One small convenience is documented: if decoding an armored source you do not need to specify the armor flag again. The auto-detection is a real usability gain for a scheme where the user has to know the chain hash at encryption time, because it means the decryption side can usually be a single command with no configuration, which matters when the person decrypting is not the person who encrypted. What the tool does not do is verify anything beyond the chain it is pointed at, and the README's advice to use the beacon hash rather than the name is the only guidance offered on public key integrity, so an operator who has to make a strong guarantee should treat the chain hash as a configuration value worth pinning and auditing.

## The library is a thin layer over age and the drand client

Underneath the tool, the Go module shows the shape of the implementation. The module is github.com/drand/tlock, requiring Go 1.23.0 with a go1.23.2 toolchain directive, and the direct dependencies are filippo.io/age at 1.2.1, the drand module itself at v2.1.2, the drand go-clients package, the drand kyber and kyber-bls12381 packages, stretchr/testify for the tests, and yaml.v3. The age library is the important one to understand, because it explains the file format. age is a file encryption tool, and tlock_age.go in the repository name suggests the age compatible layer, while the armored PEM output option and the ability to apply another layer of encryption both point at the same design, namely that a timelocked value is wrapped inside an age file so that the result is an ordinary age encrypted file that only opens when the timelocked key becomes derivable. That design has a practical consequence. A recipient needs the age identity, and the file format is interoperable with the wider age ecosystem, so tlock output is not a sealed proprietary container but a standard age file with an unusual key derivation. The README lists a section on applying another layer of encryption, and the kyber and kyber-bls12381 dependencies are the pairing based cryptography behind the drand signatures, so the scheme is built on the BLS12-381 curve rather than on RSA or a hash of a block hash. There is also a typescript implementation named tlock-js and a third party Rust implementation named tlock-rs, and the presence of both means the ciphertext format is meant to be portable across languages rather than locked to Go.

## The trust model is the design, and it is not a small thing

Two limits deserve to be stated without hedging. The first is that this is a time lock, not a safe. The property tlock gives is that a ciphertext cannot be decrypted before a deadline, and it says nothing about what happens after the deadline, since the key material is public from then on and the ciphertext can be decrypted by anyone who can reach the network. If the requirement is that a message stays confidential for a period and then becomes readable, tlock fits. If the requirement is that a message stays confidential forever, or that only one named party can read it, a different construction is needed. The second is the dependency on a live, honest network. Decryption requires the relevant drand rounds to be published, so a network that stops advancing, is captured, or is rolled back, breaks the guarantee, and the README's own words are that you can run your own network but the security guarantees boil down to the trust you have in it. The mainnet endpoints are geographically distributed and run by the League of Entropy, and the CLI defaults to api.drand.sh, but the tool also accepts a private endpoint, which is the mechanism by which a deployment trades the public network's availability for a smaller trust base. There is also a version consideration. The tagged releases on record are v1.2.0 from 2024-08-21, v1.1.1 from 2024-01-19 and v1.1.0 from 2024-01-18, while the last push was on 2026-09-23, and the README mentions behaviour introduced in v1.3.0, which is later than any of the tagged releases on record. That mismatch matters for a tool whose output format has already broken compatibility once, since the version you install determines the ciphertext format you produce.

## Conclusion

tlock suits a team that needs a message to stay sealed until a known future moment, for example a disclosure that must not be readable before an embargo expires, and that can accept drand as the source of the time and randomness the scheme depends on. It is the wrong tool if you need the property to hold without trusting any external network, because the whole guarantee reduces to the trust you have in the drand network, and the README says so plainly about running your own. It is also the wrong tool for a confidentiality requirement that lasts years, since the relevant rounds must still be published by the network. Before you rely on it, pick the chain hash deliberately rather than accepting the default, since the README advises using the beacon hash to ensure public key integrity, and decide whether the mainnet endpoints or the testnet are appropriate, because the testnet is explicitly described as not secure.

## FAQ

### What is tlock and what problem does it solve?

tlock gives you time based encryption and decryption by relying on a drand threshold network, so a message encrypted with a chosen duration cannot be decrypted until that time has passed. It ships as a Go library plus the tle command line tool, and the scheme is described in a paper on eprint.

### How do I install the tle command line tool?

Run go install github.com/drand/tlock/cmd/tle@latest, or clone the repository and run go build cmd/tle/tle.go. The tool is pure Go and works without CGO with CGO_ENABLED=0.

### Which drand network should I use with tlock?

On mainnet the README names the quicknet chainhash 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971 as the only one supporting timelock encryption, with endpoints in the US, EU and Asia plus a Cloudflare load balanced address. The testnet networks rely on the League of Entropy testnet, which the README says should not be considered secure.

## Sources

- [drand/tlock on GitHub](https://github.com/drand/tlock)
- [Issues](https://github.com/drand/tlock/issues)
- [License: Apache-2.0](https://github.com/drand/tlock/blob/main/LICENSE)
- [README](https://github.com/drand/tlock/blob/main/README.md)
- [Releases](https://github.com/drand/tlock/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/drand-tlock
