silentpayments.net
Detail
plain words, no jargon the standards, named every number, and how to check it

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:…

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.

If one machine hands the signer both the payment and the time, and that machine has been tampered with, it can pick a time that makes a fake name look real. A second screen breaks that. 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 happenedWhat the device knowsWhat 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.