A tiny Clojure CLI client for Roughtime servers. It crafts a request, verifies the response, and prints the verified time and skew.
While this may be used as a CLI client, its primary purpose is to demonstrate how to make Roughtime requests in client applications.
Requires the Clojure CLI (clj / clojure).
# Query a named server from the built-in ecosystem:
clj -M:run :server-name SturdyStatistics
# Verbose (pretty-print request/response frames):
clj -M:run :server-name int08h :print-request :print-response
# Chain a request through known servers
clj -M:run :chain
Example output:
{:skew 0,
:online-key-expires-in "∞",
:maxt 18446744073709551615,
:midp 1761408075,
:local-time 1761408075}
You can also pass explicit server parameters (no ecosystem lookup):
clj -M:run :address "roughtime.int08h.com:2002" :public-key "AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE=" :version-no "0x8000000c" :protocol "udp"
These options are parsed/validated via babashka.cli. Defaults:
{:protocol "udp"
:msg-size 1024
:print-request false
:print-response false}
| Option | Alias | Type | Default | Description |
|---|---|---|---|---|
:server-name | :s | string | — | Name from the built-in ecosystem (see below). |
:address | :a | string | — | host:port (IPv4/IPv6 or DNS), e.g. roughtime.int08h.com:2002. |
:protocol | :p | string | udp | Transport protocol. |
:public-key | :k | base64 | — | Server’s Ed25519 public key (base64). |
:version-no | :V | string | — | Roughtime version (integer or hex), e.g. "0x8000000c". |
:msg-size | :n | long | 1024 | Pad messages to this size (bytes). |
:print-request | — | boolean | false | Pretty print the serialized request map/frame. |
:print-response | — | boolean | false | Pretty print the server response (CERT, SREP, PATH, etc.). |
:server-name is provided, the client loads connection parameters from the ecosystem (below) and applies CLI overrides (e.g., :protocol, :msg-size).:address, :public-key, and :version-no.A small curated set of known servers:
{
"Cloudflare"
{:name "Cloudflare-Roughtime-2"
:version "IETF-Roughtime"
:version-no "0b000080"
:msg-size 1012
:public-key "0GD7c3yP8xEc4Zl2zeuN2SlLvDVVocjsPSL8/Rl/7zg="
:addresses [{:protocol "udp" :address "roughtime.cloudflare.com:2003"}]}
"int08h"
{:name "int08h-Roughtime"
:version "IETF-Roughtime"
:version-no "0c000080"
:msg-size 1024
:public-key "AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE="
:addresses [{:protocol "udp" :address "roughtime.int08h.com:2002"}]}
"roughtime.se"
{:name "roughtime.se"
:version "IETF-Roughtime"
:version-no "0c000080"
:msg-size 1024
:public-key "S3AzfZJ5CjSdkJ21ZJGbxqdYP/SoE8fXKY0+aicsehI="
:addresses [{:protocol "udp" :address "roughtime.se:2002"}]}
"TXRyan"
{
:name "time.txryan.com"
:version "Google-Roughtime"
:version-no 0x00
:supported-versions nil
:msg-size 1024
:public-key-type "ed25519"
:public-key "iBVjxg/1j7y1+kQUTBYdTabxCppesU/07D4PMDJk2WA="
:addresses [ {:protocol "udp" :address "time.txryan.com:2002"}]}
"SturdyStatistics"
{
:name "Sturdy-Statistics"
:version "IETF-Roughtime"
:version-no 0x8000000c
:supported-versions nil
:msg-size 1024
:public-key-type "ed25519"
:public-key "NqIjwLopQn6yQChtE21Mb97dAbAPe5UOuTa0tOakgD8="
:addresses [{:protocol "udp" :address "roughtime.sturdystatistics.com:2002"}]}
}
You can also chain requests. The command
clj -M:run :chain
Will chain a request through all servers in the ecosystem and report a bad actor if one is found.
Example output:
Running chain script; ignoring all other options.
All servers are consistent.
| :name | :public-key | :lower-limit | :upper-limit | :expires-in |
|------------------------+----------------------------------------------+--------------+--------------+-------------|
| int08h-Roughtime | AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE= | -5 | 5 | ∞ |
| Cloudflare-Roughtime-2 | 0GD7c3yP8xEc4Zl2zeuN2SlLvDVVocjsPSL8/Rl/7zg= | -1 | 1 | 13h 12m |
| Sturdy-Statistics | NqIjwLopQn6yQChtE21Mb97dAbAPe5UOuTa0tOakgD8= | -10 | 10 | 1d 11h |
| roughtime.se | S3AzfZJ5CjSdkJ21ZJGbxqdYP/SoE8fXKY0+aicsehI= | -1 | 1 | 14d 2h |
| time.txryan.com | iBVjxg/1j7y1+kQUTBYdTabxCppesU/07D4PMDJk2WA= | -1 | 1 | 2y 5mo |
Query a named server:
clj -M:run :server-name SturdyStatistics
{:skew 0,
:online-key-expires-in "∞",
:maxt 18446744073709551615,
:midp 1768457585,
:local-time 1768457585}
Verbose request/response:
clj -M:run :server-name int08h :print-request :print-response
==== REQUEST ====
{"VER" "0x8000000c",
"SRV"
["4a4344b8 ed36a263 cae105a2 92fe2661"
"d964691d be07548f 43b5b523 2671f3ab"],
"NONC"
["5e74bbfb 6902f802 9c464d0c e0a94bf5"
"a4f2f803 fff56505 13d319c8 824dcfff"],
"TYPE" 0,
"ZZZZ" "0{912}"}
==== RESPONSE ====
{"SIG"
["2359bfd7 f3c4fa29 6847c016 40938256"
"344c5c91 cdede673 372fc79f 4332a482"
"7f2ffc4d 478c2295 413d57d5 505170ff"
"aa11f3e2 c609453d be14192c 2edb060e"],
"NONC"
["5e74bbfb 6902f802 9c464d0c e0a94bf5"
"a4f2f803 fff56505 13d319c8 824dcfff"],
"TYPE" 1,
"PATH" [],
"SREP"
{"VER" "0x8000000c",
"RADI" 10,
"MIDP" 1768457531,
"VERS"
("0"
"0x80000001"
"0x80000002"
"0x80000003"
"0x80000004"
"0x80000006"
"0x80000008"
"0x80000009"
"0x8000000a"
"0x8000000b"
"0x8000000c"),
"ROOT"
["f5681bf4 c46ee065 3f21988c 23ed8708"
"9461c0c8 b2267f4e f2720269 0105d74f"]},
"CERT"
{"SIG"
["c8735ec3 502f2b25 c4855de8 91e8ce24"
"cf6b09c9 c954a958 ef7915ad f47c1097"
"7f74063c e4b5c528 e3075c6f 0ca93ef3"
"074892f6 4cb2608a 10a6ea36 0132c406"],
"DELE"
{"PUBK"
["c51da875 891887e1 9557784c d8e3b163"
"990dd688 851633f6 dcced7de fcd9fcc2"],
"MINT" 1768450687,
"MAXT" 1768623487}},
"INDX" 0}
==== RESULT ====
{:skew 0,
:online-key-expires-in "1d 22h",
:maxt 1768623487,
:midp 1768457531,
:local-time 1768457531}
Direct parameters (no ecosystem):
# Loopback example (adjust to your server):
clj -M:run :address "127.0.0.1:2002" :protocol "udp" :public-key "dWPfGcRTe9nbILRO3cyJsWFHUnSF2AEKQ6g2530UNHA=" :version-no "0x8000000c"
# Public server (int08h):
clj -M:run :version-no "0x8000000c" :public-key "AW5uAoTSTDfG5NfY1bTh08GUnOqlRb+HVhbJ3ODJvsE=" :address "roughtime.int08h.com:2002"
Apache License 2.0
Copyright © Sturdy Statistics
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |