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
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 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.