PTP² Observer
Join the ROKO time-quality mesh without authoring blocks.
Join ROKO PTP² as a non-authoring observer
An observer runs a fully synchronized ROKO node and advertises authenticated clock measurements without becoming a validator.
What PTP² is—and is not
ROKO PTP² is carried by the ROKO libp2p protocol `/roko/timesync/1`. It is not IEEE 1588 PTP and does not use UDP/319 or UDP/320.
An observer:
- has one dedicated host-scoped sr25519 key of type `ptp2`;
- is measured by peers;
- is excluded from validator convergence, consensus votes, temporal receipts,
and block authoring; and
- does not become an authority unless separately enrolled and promoted.
Joining PTP² does not discipline the host clock and does not make the host an NTP server. Chrony handles host discipline; optional LAN NTP is a separate configuration.
1. Build a healthy non-authoring node
Follow Run a node. Do not use `--validator`. Wait for:
- healthy Chrony tracking;
- at least one P2P peer;
- full synchronization;
- advancing finalized heads; and
- a controlled restart with the same libp2p peer ID.
Use multiple independent time sources. `time.roko.network` can be one source, not the only source.
2. Insert the observer key locally
Stop the node. Run the key insertion command as the node service user and omit `--suri`; the CLI then prompts without putting the secret URI in the process list or shell history:
sudo systemctl stop roko-node
sudo -u roko /usr/local/bin/roko-node key insert \
--base-path /var/lib/roko \
--chain /etc/roko/roko-testnet-v2.json \
--key-type ptp2 \
--scheme sr25519
Generate the secret in a trusted offline workflow, enter it only at the prompt, and protect its recovery copy. Do not paste it into an issue, environment variable, service file, or command line.
The observer startup contract is exactly one `ptp2` key. Startup fails if it finds zero or more than one. Key replacement is therefore a stop, back up, replace, verify, and restart operation—not an unbounded additive insert.
3. Enable observer advertisement
Add these flags to the existing node service:
--timesync-time-source auto
--timesync-advertise-observer
If Chrony uses a non-default command socket, add:
--timesync-chrony-socket <CHRONY_SOCKET>
Grant the service only the minimum group/socket permission required to read Chrony health. Do not make the socket public.
Keep the systemd clock gate:
After=network-online.target chrony.service
ExecStartPre=/usr/bin/chronyc waitsync 60 0.01
4. Verify
After starting:
chronyc tracking
chronyc sources -v
systemctl status roko-node
journalctl -u roko-node --since today
Query safe local RPC:
curl --fail --silent http://127.0.0.1:9944 \
-H 'content-type: application/json' \
--data '[
{"jsonrpc":"2.0","id":1,"method":"system_health","params":[]},
{"jsonrpc":"2.0","id":2,"method":"system_nodeRoles","params":[]},
{"jsonrpc":"2.0","id":3,"method":"temporal_getMeshState","params":[]},
{"jsonrpc":"2.0","id":4,"method":"temporal_getConsensusTime","params":[]}
]'
Confirm the node role remains `Full`, not `Authority`. Mesh peer count and quality can change as the network measures peers; observer presence must not change the active authority count.
Reboot and verify Chrony converges before the node, both identities persist, P2P resynchronizes, and the node remains non-authoring.
Optional: serve your private network
Continue with Private NTP server. This creates a separate path:
independent NTP/NTS sources -> Chrony -> home LAN clients (NTP)
|
`-> ROKO PTP² (libp2p observer)
References
- ROKO observer implementation: `node/cli/src/cli.rs` and
`node/cli/src/service.rs`