---
title: "silentpayments.net · Timecode"
description: "A hardware wallet has no clock, so it cannot tell a valid DNSSEC proof from an expired one. This page shows the current time as a QR code the device can scan. Unsigned on purpose: you are the clock."
url: "https://silentpayments.net/timecode"
language: "en"
---

> A hardware wallet has no clock, so it cannot tell a valid DNSSEC proof from an expired one. This page shows the current time as a QR code the device can scan. Unsigned on purpose: you are the clock.

_This page has three detail levels in HTML: Easy, Advanced and Cypherpunk._
_This twin carries the Advanced and Cypherpunk text, which is the complete version._

For airgapped signers

# Hold this up to the device

A hardware wallet has no clock, so it cannot tell a valid proof from one that expired last month. This code carries the time across.

BIP-353 names are proved with DNSSEC, and an RFC 9102 proof is only valid inside a signature window measured in days. Checking that window needs a date. A signer has no battery-backed clock and no network, so the date has to arrive the same way everything else does: through the camera.

§ 01 · The code

—

Reading the clock…

`sptime:…`

Refresh

This page draws the code in your browser, so it needs JavaScript. The same number is at [/api/time](https://silentpayments.net/api/time).

It refreshes on its own every 20 seconds. Anything within a few hours is close enough for the check the device does, so there is no rush.

§ 02 · The one rule

## Use a different device

Show this page on your **phone**, not on the computer that built the transaction.

A compromised coordinator can supply a forged DNSSEC proof together with a date chosen to fall inside that forgery's validity window. Both halves then agree, and the device has no way to disbelieve either. Sourcing the timecode from a device the coordinator does not control removes the second half.

§ 03 · What this is not

## It is not signed, and it should not be

We could sign this number with our key. We deliberately do not, and the reason is worth reading before anyone asks us to.

#### A signature proves where, not when

It would prove the number came from us. It would say nothing about whether the number is _now_, and now is the only thing that matters here.

#### An old signed code replays perfectly

Hand the device a genuine, correctly signed timecode from three weeks ago and it verifies. Catching that would need a clock, and the missing clock is the whole problem. So the signature adds cost and ceremony while defending against nothing the attacker was going to do.

#### It would put us inside your signing path

This site argues you should not have to trust its operator. Making a signer depend on our key to approve a payment would contradict that on the one screen where it matters most.

**You are the clock.** You read the time on the screen, you know whether it is roughly now, and only then do you scan. The device is trusting your eyes, not our server.

§ 04 · If you skip it

## No timecode means not verified

Without a date the proof cannot be checked, so the device knows nothing about the name. It has to say so as loudly as it would say verified. A quiet fallback reads as approval to everyone who is not hunting for a missing badge.

| What happened | What the device knows | What it must show |
|---|---|---|
| Timecode scanned, proof valid | The name resolves to this address | Verified, and the time it checked against |
| Timecode scanned, proof expired | The proof is bad | **Not verified**, and why |
| No timecode | Nothing about the name | **Not verified**, no date available |

The middle row and the bottom row are different failures, and a person can act on the difference, so a device should not collapse them into one message.

§ 05 · For implementers

## The format

One line of ASCII. Copy it; we would rather every signer read the same thing than own this.

`sptime:<unix-seconds>`

Decimal seconds since the Unix epoch, UTC, no padding and no separators. `sptime:1756857600` is 17 bytes, which fits a version 1 code at error correction level L, so it scans at once on a 240×240 panel with a fixed-focus camera.

#### Parsing

Match `^sptime:([0-9]{1,10})$` on the decoded bytes and reject anything else, including leading zeroes and a trailing newline. Reject below `1700000000` and above `4102444800`: no firmware that can read this format predates the first, and the second is the year 2100.

#### Do not persist it

A signer that remembers a scanned time across a reboot is claiming a clock it does not have.

#### No version field

The prefix is the version. A future incompatible format is `sptime2:`, which an old device rejects outright rather than misreading. It fails closed and costs no bytes.

The same number as JSON: [/api/time](https://silentpayments.net/api/time).
