diff options
| author | Luke Hoersten <[email protected]> | 2026-07-28 20:13:03 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-07-28 20:13:03 -0500 |
| commit | 3a535a57c1b5fa48d5495c31d308b42943f92583 (patch) | |
| tree | f752c07530fcb10b75de41428b82df34d43e5a87 | |
| parent | cf5eabb9647e6d53ec29a3f7de599a38dbfd5490 (diff) | |
Rename project to mattertimectl; default fabric label to Matter Time Controllerv0.1.0
- Rename crate, binary, and repo from mattertimesync to mattertimectl
- Change default fabricLabel to "Matter Time Controller"
- Point README at src.nth.io as the main repo, GitHub as the mirror
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | README.md | 45 | ||||
| -rw-r--r-- | examples/config.example.json | 4 | ||||
| -rw-r--r-- | examples/mattertimectl.service (renamed from examples/mattertimesync.service) | 8 | ||||
| -rw-r--r-- | examples/mattertimectl.timer (renamed from examples/mattertimesync.timer) | 0 | ||||
| -rw-r--r-- | src/config.rs | 14 | ||||
| -rw-r--r-- | src/controller/mod.rs | 4 | ||||
| -rw-r--r-- | src/main.rs | 4 |
9 files changed, 43 insertions, 40 deletions
@@ -946,7 +946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] -name = "mattertimesync" +name = "mattertimectl" version = "0.1.0" dependencies = [ "anyhow", @@ -1,5 +1,5 @@ [package] -name = "mattertimesync" +name = "mattertimectl" version = "0.1.0" edition = "2024" description = "CLI Matter controller that synchronizes the clocks of Matter devices via the standard Time Synchronization cluster" @@ -1,4 +1,7 @@ -# mattertimesync +# mattertimectl + +> Main repository: [src.nth.io/luke/mattertimectl](https://src.nth.io/luke/mattertimectl). The +> [GitHub mirror](https://github.com/lukehoersten/mattertimectl) is a backup. A small CLI Matter controller that sets the clocks of Matter devices via the standard Time Synchronization cluster. Rust implementation, built on [rs-matter](https://crates.io/crates/rs-matter) @@ -66,21 +69,21 @@ The same principles as the TypeScript implementation this replaces, plus the Rus Local, read-only (no device contact): ```bash -mattertimesync status [--node <id>] [--json] # config, identity, and per-device sync state +mattertimectl status [--node <id>] [--json] # config, identity, and per-device sync state ``` Fabric interrogation (connects to the device, reads only): ```bash -mattertimesync inspect [--node <id>] [--json] +mattertimectl inspect [--node <id>] [--json] ``` Fabric-writing: ```bash -mattertimesync commission <pairing-code> # writes the device's fabric table + local storage -mattertimesync sync [--node <id>] [--time <hh:mm>] [--json] # set clock, time zone, DST offsets -mattertimesync decommission [--node <id>] # device(s) drop this controller's fabric +mattertimectl commission <pairing-code> # writes the device's fabric table + local storage +mattertimectl sync [--node <id>] [--time <hh:mm>] [--json] # set clock, time zone, DST offsets +mattertimectl decommission [--node <id>] # device(s) drop this controller's fabric ``` No command writes local state alone; the registry and sync results change only as part of a @@ -93,12 +96,12 @@ Exit codes: 0 success, 1 runtime failure (timer retries), 2 usage/config error. ## Configuration -`/etc/mattertimesync/config.json` by default; override with `--config <path>`. Same file format as +`/etc/mattertimectl/config.json` by default; override with `--config <path>`. Same file format as the TypeScript implementation (see `examples/config.example.json`): ```json { - "storagePath": "/var/lib/mattertimesync", + "storagePath": "/var/lib/mattertimectl", "timezone": "America/Chicago", "logLevel": "info", "fabricLabel": "My Home" @@ -110,7 +113,7 @@ the TypeScript implementation (see `examples/config.example.json`): | `storagePath` | (required) | Directory for persistent Matter fabric state. Contains secrets. | | `timezone` | `America/Chicago` | IANA time-zone name. Never a fixed UTC offset; DST is derived. | | `logLevel` | `info` | `debug`, `info`, `warn`, or `error`. | -| `fabricLabel` | `Matter Time Sync` | Label other ecosystems show for this controller (max 32 chars). | +| `fabricLabel` | `Matter Time Controller` | Label other ecosystems show for this controller (max 32 chars). | Unknown fields are rejected loudly. Matter requires fabric labels to be unique per device and Apple Home labels its own entry with the home's name, so use a variant like "Lakeside Time Sync" rather @@ -119,7 +122,7 @@ than the home name itself; `sync` pushes label changes to each device. ## Build and deploy ```bash -cargo build --release # target/release/mattertimesync, one self-contained binary +cargo build --release # target/release/mattertimectl, one self-contained binary cargo test ``` @@ -129,30 +132,30 @@ The binary must match the target's architecture; the simplest path is building o ```bash # on the Pi, one-time toolchain install (user-local, ~/.rustup and ~/.cargo) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal -git clone <repository> && cd mattertimesync +git clone <repository> && cd mattertimectl ~/.cargo/bin/cargo build --release ``` The target machine needs nothing but the binary (and a tzdb, present on any Linux). On the target: ```bash -sudo install -D -m 0755 mattertimesync /opt/mattertimesync/mattertimesync -sudo useradd --system --home /var/lib/mattertimesync --shell /usr/sbin/nologin mattertimesync -sudo mkdir -p /etc/mattertimesync /var/lib/mattertimesync -sudo chown mattertimesync:mattertimesync /var/lib/mattertimesync -sudo chmod 700 /var/lib/mattertimesync -sudo cp examples/mattertimesync.service examples/mattertimesync.timer /etc/systemd/system/ +sudo install -D -m 0755 mattertimectl /opt/mattertimectl/mattertimectl +sudo useradd --system --home /var/lib/mattertimectl --shell /usr/sbin/nologin mattertimectl +sudo mkdir -p /etc/mattertimectl /var/lib/mattertimectl +sudo chown mattertimectl:mattertimectl /var/lib/mattertimectl +sudo chmod 700 /var/lib/mattertimectl +sudo cp examples/mattertimectl.service examples/mattertimectl.timer /etc/systemd/system/ sudo systemctl daemon-reload ``` Commission (as the service user, within the pairing window opened in the primary ecosystem): ```bash -sudo -u mattertimesync /opt/mattertimesync/mattertimesync \ - --config /etc/mattertimesync/config.json commission 12345678901 +sudo -u mattertimectl /opt/mattertimectl/mattertimectl \ + --config /etc/mattertimectl/config.json commission 12345678901 ``` -Then `sudo systemctl enable --now mattertimesync.timer` (2 minutes after boot, hourly thereafter). +Then `sudo systemctl enable --now mattertimectl.timer` (2 minutes after boot, hourly thereafter). ## Security notes @@ -179,7 +182,7 @@ key, not a storage path. Migration is one re-pairing: additional admin (Matter allows several; the fabric table has at least 5 slots). Both implementations can sync side by side while validating. 2. Once satisfied, `decommission` with the **old** implementation so the device drops its fabric, - point `mattertimesync.service` at this binary, and delete the old storage directory. + point `mattertimectl.service` at this binary, and delete the old storage directory. ## Troubleshooting diff --git a/examples/config.example.json b/examples/config.example.json index 831630c..5409179 100644 --- a/examples/config.example.json +++ b/examples/config.example.json @@ -1,6 +1,6 @@ { - "storagePath": "/var/lib/mattertimesync", + "storagePath": "/var/lib/mattertimectl", "timezone": "America/Chicago", "logLevel": "info", - "fabricLabel": "Rust Matter Time Sync" + "fabricLabel": "Rust Matter Time Controller" } diff --git a/examples/mattertimesync.service b/examples/mattertimectl.service index 8fc48c1..43a662b 100644 --- a/examples/mattertimesync.service +++ b/examples/mattertimectl.service @@ -5,11 +5,11 @@ After=network-online.target time-sync.target [Service] Type=oneshot -User=mattertimesync -Group=mattertimesync -ExecStart=/opt/mattertimesync/mattertimesync --config /etc/mattertimesync/config.json sync +User=mattertimectl +Group=mattertimectl +ExecStart=/opt/mattertimectl/mattertimectl --config /etc/mattertimectl/config.json sync NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true -ReadWritePaths=/var/lib/mattertimesync +ReadWritePaths=/var/lib/mattertimectl diff --git a/examples/mattertimesync.timer b/examples/mattertimectl.timer index 4d7b64a..4d7b64a 100644 --- a/examples/mattertimesync.timer +++ b/examples/mattertimectl.timer diff --git a/src/config.rs b/src/config.rs index 3e53975..8288d4e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,5 @@ //! Validated JSON configuration, file-compatible with the TypeScript -//! implementation (`/etc/mattertimesync/config.json`, camelCase keys). +//! implementation (`/etc/mattertimectl/config.json`, camelCase keys). //! //! Unknown fields are rejected loudly via serde's `deny_unknown_fields`; //! device membership deliberately lives in controller storage, not here. @@ -10,7 +10,7 @@ use std::path::PathBuf; use jiff::tz::TimeZone; use serde::Deserialize; -pub const DEFAULT_CONFIG_PATH: &str = "/etc/mattertimesync/config.json"; +pub const DEFAULT_CONFIG_PATH: &str = "/etc/mattertimectl/config.json"; /// Matter FabricDescriptorStruct label limit. const FABRIC_LABEL_MAX_LENGTH: usize = 32; @@ -94,7 +94,7 @@ fn default_timezone() -> String { } fn default_fabric_label() -> String { - "Matter Time Sync".into() + "Matter Time Controller".into() } impl Config { @@ -168,18 +168,18 @@ impl Config { mod tests { use super::*; - const MINIMAL: &str = r#"{ "storagePath": "/var/lib/mattertimesync" }"#; + const MINIMAL: &str = r#"{ "storagePath": "/var/lib/mattertimectl" }"#; #[test] fn minimal_config_applies_defaults() { let config = Config::parse(MINIMAL).unwrap(); assert_eq!( config.storage_path, - PathBuf::from("/var/lib/mattertimesync") + PathBuf::from("/var/lib/mattertimectl") ); assert_eq!(config.timezone, "America/Chicago"); assert_eq!(config.log_level, LogLevel::Info); - assert_eq!(config.fabric_label, "Matter Time Sync"); + assert_eq!(config.fabric_label, "Matter Time Controller"); } #[test] @@ -220,7 +220,7 @@ mod tests { let config = Config::parse(r#"{ "storagePath": "/var/lib/$USER/mts" }"#).unwrap(); assert!(config.path_warnings()[0].contains("unexpanded shell variable")); - let config = Config::parse(r#"{ "storagePath": "/var/lib/mattertimesync" }"#).unwrap(); + let config = Config::parse(r#"{ "storagePath": "/var/lib/mattertimectl" }"#).unwrap(); assert!(config.path_warnings().is_empty()); } diff --git a/src/controller/mod.rs b/src/controller/mod.rs index 739615c..c712c68 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -558,7 +558,7 @@ fn lock_storage(storage: &Path) -> anyhow::Result<std::fs::File> { let rc = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }; if rc != 0 { bail!( - "another mattertimesync instance is already running against {}", + "another mattertimectl instance is already running against {}", storage.display() ); } @@ -590,7 +590,7 @@ fn prepare_storage_dir(path: &Path) -> anyhow::Result<()> { .context("chmod storage directory")?; } else { log::warn!( - "storagePath {} already exists and does not look like a mattertimesync storage \ + "storagePath {} already exists and does not look like a mattertimectl storage \ directory; leaving its permissions unchanged (it should be mode 0700, owned by the \ service user)", path.display() diff --git a/src/main.rs b/src/main.rs index f372f8e..d245578 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -//! mattertimesync: a one-shot CLI Matter controller that sets the clocks of +//! mattertimectl: a one-shot CLI Matter controller that sets the clocks of //! Matter devices via the standard Time Synchronization cluster. //! //! # CLI invariants @@ -54,7 +54,7 @@ use crate::time::MatterMicros; #[derive(Parser)] #[command( - name = "mattertimesync", + name = "mattertimectl", version, about, disable_help_subcommand = true |
