@domsync/win32-arm64 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +116 -58
  2. package/domsync.exe +0 -0
  3. package/package.json +20 -1
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # domsync
2
2
 
3
- `domsync` keeps a single domain list in sync between your laptop and an OpenClash rule-provider file on a router. It reads the local list and the provider file over SSH, merges them (the local entries win, router-only domains go into an `other` section), validates everything, then atomically rewrites each file that changed and optionally restarts OpenClash.
3
+ `domsync` keeps local sectioned domain lists in sync with OpenClash/Mihomo rule-provider files on your servers. One config lists any number of servers, and each server lists any number of **pairs**: one local domain list and one provider file. For every pair `domsync` reads the local list and the provider file over SSH, merges them (the local entries win, provider-only domains go into an `other` section), validates everything, then atomically rewrites each file that changed and optionally runs per-server post-write commands.
4
4
 
5
- It never silently rewrites anything it cannot parse: an ambiguous local file or a malformed provider line stops the run before a single byte is written.
5
+ Pairs are independent: a dead server or a broken pair is reported and the rest still run. It never silently rewrites anything it cannot parse: an ambiguous local file or a malformed provider line stops that pair before a single byte is written.
6
6
 
7
- **Not supported in v1:** homeproxy, subscription URLs or any remote source other than the current provider file, SSH password authentication, `ProxyJump`, a fallback to the system `ssh`, IDN domains (use punycode), and cryptographic verification of updates inside the CLI. There is no daemon, watcher or scheduler; run `domsync` from cron or by hand.
7
+ **Not supported in v1:** homeproxy, subscription URLs or any remote source other than the current provider file, SSH password authentication, `ProxyJump`, a fallback to the system `ssh`, IDN domains (use punycode), and cryptographic verification of updates inside the CLI. One local list cannot be shared by two pairs (cross-sync), servers and pairs cannot be selected on the command line, and execution is sequential. There is no daemon, watcher or scheduler; run `domsync` from cron or by hand.
8
8
 
9
9
  ## Install
10
10
 
@@ -22,6 +22,14 @@ npm (macOS, Linux and Windows):
22
22
  npm i -g @domsync/cli
23
23
  ```
24
24
 
25
+ Go (any platform with a Go toolchain, built from source):
26
+
27
+ ```sh
28
+ go install github.com/neiromaster/domsync/cmd/domsync@latest
29
+ ```
30
+
31
+ `go install` places the binary in `$GOBIN` (or `$HOME/go/bin`). The toolchain version is pinned in `go.mod`.
32
+
25
33
  Install script (macOS and Linux):
26
34
 
27
35
  ```sh
@@ -34,46 +42,48 @@ The script downloads the release archive and `checksums.txt`, verifies the SHA-2
34
42
 
35
43
  ```sh
36
44
  cp domsync.example.yaml domsync.yaml
37
- cat > domains.txt <<'EOF'
45
+ mkdir -p domains
46
+ cat > domains/media.txt <<'EOF'
38
47
  [media]
39
48
  +.example.com
40
49
  +.example.net
41
-
42
- [misc]
50
+ EOF
51
+ cat > domains/ai.txt <<'EOF'
52
+ [ai]
43
53
  +.example.org
44
54
  EOF
45
55
  ```
46
56
 
47
- Edit `domsync.yaml` for your router (`target.host`, `target.user`, `target.ssh_key`, `target.path`), make sure the router's host key is already in `~/.ssh/known_hosts`, then run:
57
+ Edit `domsync.yaml`: in the `router` server set `host`, `user`, `ssh_key` and the `target` of each file. The example also lists a second server, `vps-1`, to show the shape; delete that entry or fix its settings before running. Set `enabled: false` instead to park it. Make sure each server's host key is already in `known_hosts`, then run:
48
58
 
49
59
  ```sh
50
60
  domsync
51
61
  ```
52
62
 
53
- The config is looked up in this order: `--config <path>`, the `DOMSYNC_CONFIG` environment variable, then `./domsync.yaml` in the current directory. Relative paths inside the config (`source.domains`, `target.ssh_key`, `target.known_hosts`, `logging.file`) are resolved against the config file's directory; `~` and `~/...` are expanded to your home directory.
63
+ The config is looked up in this order: `--config <path>`, the `DOMSYNC_CONFIG` environment variable, then `./domsync.yaml` in the current directory. Relative paths inside the config (`servers[].ssh_key`, `servers[].known_hosts`, `servers[].files[].source`, `logging.file`) are resolved against the config file's directory; `~` and `~/...` are expanded to your home directory.
54
64
 
55
- A first run needs the router's host key in `known_hosts`. If it is missing, the error includes the SHA-256 fingerprint and the exact `ssh-keyscan -H ... >> ~/.ssh/known_hosts` command to add it.
65
+ A first run needs each server's host key in `known_hosts`. If it is missing, the error includes the SHA-256 fingerprint and the exact `ssh-keyscan -H ... >> ~/.ssh/known_hosts` command to add it.
56
66
 
57
67
  ## OpenClash provider
58
68
 
59
- Add this provider to the OpenClash configuration:
69
+ Add one provider per synced file to the OpenClash configuration:
60
70
 
61
71
  ```yaml
62
72
  rule-providers:
63
- domsync:
73
+ media:
64
74
  type: file
65
75
  behavior: domain
66
76
  format: text
67
- path: ./rule_provider/domsync.txt
77
+ path: ./rule_provider/media.txt
68
78
  ```
69
79
 
70
80
  `format: text` is **mandatory**. Without it Mihomo parses the file as YAML, the provider ends up empty, and OpenClash gives no error.
71
81
 
72
- `target.path` in `domsync.yaml` is the absolute path of the same file; OpenClash keeps provider files under `/etc/openclash/rule_provider/`.
82
+ Each pair's `target` in `domsync.yaml` is the absolute path of the same file; OpenClash keeps provider files under `/etc/openclash/rule_provider/`.
73
83
 
74
84
  `/etc/init.d/openclash reload` does **not** re-read provider files. Use `restart`, which is why the example config's post-write command is `/etc/init.d/openclash restart`. Any other command that genuinely reloads providers works too.
75
85
 
76
- In the provider file, `# [section]` lines are comments for humans only: Mihomo ignores lines starting with `#` or `//`. domsync writes section headers only in that commented form, because a bare `[media]` line would be accepted by Mihomo as a literal domain and silently inflate the rule count. Comments are never preserved from the router: the file is regenerated from the local state.
86
+ In the provider file, `# [section]` lines are comments for humans only: Mihomo ignores lines starting with `#` or `//`. domsync writes section headers only in that commented form, because a bare `[media]` line would be accepted by Mihomo as a literal domain and silently inflate the rule count. Comments are never preserved from the server: the file is regenerated from the local state.
77
87
 
78
88
  ## Local file format
79
89
 
@@ -92,9 +102,9 @@ In the provider file, `# [section]` lines are comments for humans only: Mihomo i
92
102
 
93
103
  ### Sections
94
104
 
95
- A section header is `[name]` on its own line, optionally with surrounding whitespace. Names must be non-empty, at most 64 characters, free of control characters, and unique. Section order follows first appearance in the local file, but the `target.other_section` section (default `other`) is always rendered last. Empty sections are dropped, together with their comments.
105
+ A section header is `[name]` on its own line, optionally with surrounding whitespace. Names must be non-empty, at most 64 characters, free of control characters, and unique. Section order follows first appearance in the local file, but the pair's `other_section` section (default `other`) is always rendered last. Empty sections are dropped, together with their comments.
96
106
 
97
- A file with no section headers is a flat list and stays flat; in that case no `other` section is ever created, even if router-only domains are adopted.
107
+ A file with no section headers is a flat list and stays flat; in that case no `other` section is ever created, even if provider-only domains are adopted.
98
108
 
99
109
  ### Patterns
100
110
 
@@ -120,10 +130,10 @@ Rules enforced by the parser:
120
130
 
121
131
  A leading `-` is the only structural marker: `-` followed by any valid pattern is a deletion directive. A `+` or `*` inside the pattern is Mihomo syntax, not a sign.
122
132
 
123
- - `-pattern` with no positive entry for the same pattern **deletes** it from the router and from the local snapshot.
124
- - `-pattern` plus a positive entry for the same pattern in a **different** section **moves** it to that section. On the router only the section comment changes.
133
+ - `-pattern` with no positive entry for the same pattern **deletes** it from the provider file and from the local snapshot.
134
+ - `-pattern` plus a positive entry for the same pattern in a **different** section **moves** it to that section. On the server only the section comment changes.
125
135
  - `-pattern` and a positive entry in the **same** section is redundant: the positive entry wins and a warning is printed. With `--strict` it is an error.
126
- - A `-pattern` that exists neither locally nor on the router produces a warning; `--strict` turns it into an error.
136
+ - A `-pattern` that exists neither locally nor in the provider file produces a warning; `--strict` turns it into an error.
127
137
 
128
138
  To delete a domain that still has a positive line, remove that positive line and add `-pattern`. While the positive line remains, domsync treats the pair as a move, not a deletion.
129
139
 
@@ -131,7 +141,7 @@ After a successful sync the `-` lines disappear from the local file: the domain
131
141
 
132
142
  ### Comments
133
143
 
134
- - `#` at the start of a line is a comment. Comments are local-only: they are kept in the local snapshot and are never written to the router.
144
+ - `#` at the start of a line is a comment. Comments are local-only: they are kept in the local snapshot and are never written to the provider file.
135
145
  - Comments attach to the current section and are rendered immediately after that section's header, in their original order. Comments before the first section stay at the top of the file.
136
146
  - Inline comments are invalid. The line `+.example.com # note` is a validation error; put the note on its own line.
137
147
  - `//` is not a local comment. A local line starting with `//` is a pattern and is rejected.
@@ -139,64 +149,103 @@ After a successful sync the `-` lines disappear from the local file: the domain
139
149
 
140
150
  ### Output rules
141
151
 
142
- Within each section, patterns are sorted; section order is otherwise preserved; the `other_section` is last. The local file is rewritten as a snapshot of the merged state, comments included. When the local file has sections, the router file contains `# [section]` headers followed by the same sorted patterns; a flat local file produces a flat router file. Router-only domains are added to `other_section`; if the local file already has that section it is reused, so a second one is never created.
152
+ Within each section, patterns are sorted; section order is otherwise preserved; the `other_section` is last. The local file is rewritten as a snapshot of the merged state, comments included. When the local file has sections, the provider file contains `# [section]` headers followed by the same sorted patterns; a flat local file produces a flat provider file. Provider-only domains are added to `other_section`; if the local file already has that section it is reused, so a second one is never created.
143
153
 
144
- A missing local file is treated as an empty list, so the first run bootstraps the local snapshot from the router: every router domain is adopted and written locally. An empty local file with an empty router is a no-op. A plan whose final list would be empty while the router is not is an error: domsync refuses to wipe the router.
154
+ A missing local file is treated as an empty list, so the first run bootstraps the local snapshot from the provider file: every provider-only domain is adopted and written locally. An empty local file with an empty provider file is a no-op. A plan whose final list would be empty while the provider file is not is an error: domsync refuses to wipe the provider file.
145
155
 
146
- ### Router file validation
156
+ Concurrent runs are refused. A run holds an exclusive lock on `<config-path>.domsync.lock` next to the config file from the moment the configuration is loaded until every write and post-write command has finished, so a cron job and a manual run cannot interleave their reads and writes. One lock covers the whole config, so two different configs can run at the same time. The lock file stays on disk after the run; the lock itself is released by the operating system when the process exits, including after a crash.
147
157
 
148
- When reading the provider file from the router, domsync ignores blank lines and lines starting with `#` or `//`. Every other line must be a valid pattern; otherwise the run fails with the file, line number and offending text, and nothing is written. Duplicate patterns are collapsed. A bare `[media]` header on the router is therefore an error, not a domain.
158
+ ### Provider file validation
159
+
160
+ When reading the provider file from a server, domsync ignores blank lines and lines starting with `#` or `//`. Every other line must be a valid pattern; otherwise that pair fails with the file, line number and offending text, and nothing is written for it. Duplicate patterns are collapsed. A bare `[media]` header on the server is therefore an error, not a domain.
149
161
 
150
162
  ## Configuration reference
151
163
 
152
164
  YAML parsing is strict: unknown fields, multiple YAML documents and an empty file are errors.
153
165
 
166
+ Top-level keys:
167
+
154
168
  | Key | Type | Default | Meaning |
155
169
  |---|---|---|---|
156
- | `version` | integer | required | Must be `1`. |
157
- | `source.domains` | path | `domains.txt` | Local domain list, resolved relative to the config file. |
158
- | `target.host` | string | required | Router host name or address. |
159
- | `target.port` | integer | `22` | SSH port (1-65535). |
160
- | `target.user` | string | required | SSH user. |
161
- | `target.ssh_key` | path | empty | Private key (ed25519, ecdsa or rsa). If empty, `SSH_AUTH_SOCK` must provide an agent. Both are used when available. |
162
- | `target.known_hosts` | path | `~/.ssh/known_hosts` | Must already exist and be a regular file. |
163
- | `target.path` | string | required | Absolute path of the provider file on the router; a trailing `/` is rejected. |
164
- | `target.other_section` | string | `other` | Section that receives router-only domains. |
165
- | `target.mode` | string | `0644` | Octal permissions for the written provider file, at most `0777`. Quote it in YAML. |
166
- | `target.backup` | boolean | `true` | Copy the previous provider file to the backup path before replacing it. |
167
- | `target.backup_suffix` | string | `.domsync.bak` | Suffix appended to `target.path` for the backup file. |
168
- | `target.post_write.run` | string | `on_change` | When to run `target.post_write.commands`: `on_change`, `always` or `never`. |
169
- | `target.post_write.timeout` | duration string | `120s` | Timeout for each command. See the note below. |
170
- | `target.post_write.commands` | list of strings | empty | Commands run sequentially over the existing SSH connection. |
170
+ | `version` | integer | required | Must be `2`; a `version: 1` config is rejected. |
171
+ | `servers` | list | required | One or more servers; at least one entry. |
171
172
  | `update.check` | boolean | `true` | Check GitHub for a newer release at launch. `--no-update-check` overrides it. |
172
- | `logging.file` | path | empty (disabled) | When set, one summary line is appended on a completed run (and when a post-write command fails). |
173
+ | `logging.file` | path | empty (disabled) | When set, one summary line is appended on every non-dry-run run that reaches the end, including runs with failed pairs or servers. |
174
+
175
+ Server keys:
176
+
177
+ | Key | Type | Default | Meaning |
178
+ |---|---|---|---|
179
+ | `name` | string | required | Unique across servers, `[a-z0-9][a-z0-9_-]*`, at most 32 characters. Used in reports and logs. |
180
+ | `enabled` | boolean | `true` | `false` skips the server entirely, `validate` included. See below. |
181
+ | `host` | string | required | Server host name or address. |
182
+ | `port` | integer | `22` | SSH port (1-65535). |
183
+ | `user` | string | required | SSH user. |
184
+ | `ssh_key` | path | empty | Private key (ed25519, ecdsa or rsa). If empty, `SSH_AUTH_SOCK` must provide an agent. Both are used when available. |
185
+ | `known_hosts` | path | `~/.ssh/known_hosts` | Must already exist and be a regular file. |
186
+ | `post_write.run` | string | `on_change` | When to run `post_write.commands`: `on_change`, `always` or `never`. |
187
+ | `post_write.timeout` | duration string | `120s` | Timeout for each command. See the note below. |
188
+ | `post_write.commands` | list of strings | empty | Commands run sequentially over the existing SSH connection, after the server's pairs. |
189
+ | `files` | list | required | One or more pairs; at least one entry, even when the server is disabled. |
190
+
191
+ File (pair) keys:
192
+
193
+ | Key | Type | Default | Meaning |
194
+ |---|---|---|---|
195
+ | `source` | path | required in version 2 | Local domain list, resolved relative to the config file. A regular file or absent; absent means an empty list. Must be unique across the whole config. |
196
+ | `target` | string | required | Absolute path of the provider file on that server; a trailing `/` is rejected. Must be unique within the server; the same target on two servers is fine. |
197
+ | `other_section` | string | `other` | Section that receives provider-only domains. |
198
+ | `mode` | string | `0644` | Octal permissions for the written provider file, between `0001` and `0777`. Quote it in YAML. |
199
+ | `backup` | boolean | `true` | Copy the previous provider file to the backup path before replacing it. |
200
+ | `backup_suffix` | string | `.domsync.bak` | Suffix appended to `target` for the backup file. |
173
201
 
174
- `target.post_write.run` values:
202
+ Two pairs may not share a `source` (resolved absolute path): both would rewrite it, so the config is rejected naming both positions. Two pairs on the same server may not share a `target`; the same path on two different servers is fine. Relative paths resolve against the config file's directory and `~` expands to your home, as described in Quick start.
175
203
 
176
- - `on_change` - run the commands only when the router provider file changed.
177
- - `always` - run them after every successful sync, including a run with no changes at all.
204
+ `post_write.run` values:
205
+
206
+ - `on_change` - run the commands when at least one pair on this server changed something, on the server or locally.
207
+ - `always` - run them after every non-dry-run sync of the server, including a run with no changes and a run in which a pair failed.
178
208
  - `never` - never run them.
179
209
 
180
- `--dry-run` never runs commands, and no commands run when an earlier step failed. A failing command stops the sequence and exits with code 5.
210
+ `--dry-run` never runs commands, and a server whose connection failed runs none. A command failure is recorded and does not undo any write; the sequence stops at the first failing command.
211
+
212
+ `enabled: false` parks a server: `sync` and `validate` skip it entirely, and the report marks it `skipped`. Every shape check still applies to a disabled server — required fields, name syntax and uniqueness, `port`, `mode`, `other_section`, `post_write.run`, absolute `target`, unique `source` across the whole config, and unique `target` within the server — so a typo in a parked server is a config error, not a surprise on the day it is switched on. Only filesystem and environment checks are skipped: `ssh_key` exists, `known_hosts` exists, `SSH_AUTH_SOCK` when `ssh_key` is empty, and `source` being a regular file or absent. That makes `enabled: false` a legitimate way to park a server whose key is not on this machine yet.
181
213
 
182
214
  **The command timeout cannot be disabled.** `timeout` applies to each command separately. A value of `0s` is treated as "unset" and replaced by the built-in default of `120s`; negative values are rejected. Choose a large finite value if a command legitimately runs long.
183
215
 
184
216
  **Host keys are always verified.** This is true for key-file authentication and for ssh-agent authentication alike. There is no trust-on-first-use mode.
185
217
 
186
- Config validation runs before SSH is dialled: a missing key file, a missing `known_hosts`, or an empty `target.ssh_key` with `SSH_AUTH_SOCK` unset fails with exit code 1 before any connection is attempted.
218
+ Config validation runs before SSH is dialled: a missing key file, a missing `known_hosts`, an empty `ssh_key` with `SSH_AUTH_SOCK` unset, or a `source` that is not a regular file fails with exit code 1 before any connection is attempted. Disabled servers are exempt from these filesystem checks.
219
+
220
+ ## Validation
221
+
222
+ `domsync validate` loads the configuration, walks every enabled server's pairs and parses each local list without opening an SSH connection. It covers everything `domsync` checks before dialling — the config schema, path resolution, cross-pair uniqueness, the existence of each server's `ssh_key` and `known_hosts`, and every local list's syntax — and nothing that needs a server. Disabled servers are skipped and reported as `skipped`.
223
+
224
+ Validation continues past a broken pair: it is reported and the remaining pairs are still checked.
225
+
226
+ ```sh
227
+ domsync validate
228
+ domsync --json validate
229
+ ```
230
+
231
+ Exit codes follow the table below: `1` for a configuration or read error and `2` for an invalid local list. When several pairs fail, the exit code is the code of the first failure in config order (server order, then file order).
232
+
233
+ With `--json` the output is one object with `ok` and `servers`; each server carries `name`, `ok`, `skipped` and `files`, and each file carries `source`, `ok`, `total`, `sections` and, on failure, an `error` object with the exit code and message. A config-level error produces `ok: false` with an `error` object and exits 1 before any pair is examined. Without `--json` the human output prints the resolved config path, then a block per server (`name`, then each local file with its counts) and the usual summary line (`domsync: N доменов, M секций`); a failed file prints `ошибка: ...`.
187
234
 
188
235
  ## Exit codes
189
236
 
190
237
  | Code | Meaning |
191
238
  |---|---|
192
239
  | `0` | Success, including "no changes". Also `--help` and `--version`. |
193
- | `1` | Usage or configuration error, local file read/write error, or log write error. |
194
- | `2` | Validation error in the local list or the router provider file, including `--strict` warnings, and an empty final list. |
240
+ | `1` | Usage or configuration error, local file read/write error, log write error, or another run holding the config lock. |
241
+ | `2` | Validation error in a local list or a provider file, including `--strict` warnings, and an empty final list. |
195
242
  | `3` | SSH connection, authentication, host-key or remote read/write/verify error. |
196
243
  | `4` | Update error, including the Homebrew/npm refusal. |
197
244
  | `5` | A post-write command failed (non-zero exit or transport error). |
198
245
 
199
- `--strict` only promotes local warnings (deletion target not found, redundant `+`/`-` in one section) to errors. Problems in the router file are always errors, and a failed launch-time update check never changes the sync exit code.
246
+ When several things fail, the exit code is the code of the first failure in execution order: server order, then file order. A server that cannot be dialled counts at its own position, before its pairs, so a connection error beats a later pair failure. A failure in one pair or one server never stops the rest.
247
+
248
+ `--strict` only promotes local warnings (deletion target not found, redundant `+`/`-` in one section) to errors, and fails only that pair. Problems in a provider file are always errors, and a failed launch-time update check never changes the sync exit code.
200
249
 
201
250
  ## Updates
202
251
 
@@ -228,30 +277,35 @@ So update those installs with `brew upgrade --cask domsync` or `npm i -g @domsyn
228
277
 
229
278
  ## Output
230
279
 
231
- The default human report is Russian and looks like this for a run that changed both files:
280
+ The default human report is Russian and looks like this for a run that changed one pair:
232
281
 
233
282
  ```text
234
- domsync: 14 доменов, 3 секции
235
- роутер: +1 −1, записан, бэкап /etc/openclash/rule_provider/domsync.txt.domsync.bak
236
- локально: +1 −1, перемещён 1
237
- команда: /etc/init.d/openclash restart — ok, 2.1s
283
+ router (192.168.1.1)
284
+ domains/media.txt → /etc/openclash/rule_provider/media.txt
285
+ роутер: +2 −1, записан, бэкап /etc/openclash/rule_provider/media.txt.domsync.bak
286
+ локально: +1, перемещён 1
287
+ domains/ai.txt → /etc/openclash/rule_provider/ai.txt: без изменений
288
+ команда: /etc/init.d/openclash restart — ok, 0.8s
289
+ vps-1: пропущен (disabled)
290
+ domsync: 42 домена, 7 секций
238
291
  ```
239
292
 
240
- - The header reports the total number of domains and non-empty sections.
293
+ - Each server block starts with its name and host, followed by one line per pair. A skipped server is a single `name: пропущен (disabled)` line; a server that failed to connect prints its name and the error, with the number of pairs that were not attempted.
294
+ - A pair with no changes on either side stays on one line with `без изменений`; a failed pair prints `ошибка: <message>` on its own line.
241
295
  - `роутер` and `локально` show added and removed counts with the U+2212 minus sign. The router line adds `записан` only when the file was actually written, and the backup path when a backup was made; the local line adds `перемещён N` for moved patterns.
242
- - `без изменений` means that file was already identical.
296
+ - The final line reports the total number of domains and non-empty sections across every pair that ran. A successful run without changes says `domsync: изменений нет (N доменов, M секций)`.
243
297
  - With `--dry-run`, each changed file is marked `будет записан (dry-run)`.
244
298
  - Command results are `ok, <duration>` or `ошибка <exit code>`; a transport failure prints `ошибка: <message>`.
245
299
  - Warnings print as `предупреждение: <message>`.
246
300
 
247
- `--dry-run` still connects to the router and reads the current provider file; it skips both writes and all commands. It is not an offline mode.
301
+ `--dry-run` still connects to every enabled server and reads the current provider files; it skips both writes and all commands. It is not an offline mode.
248
302
 
249
- - `--json` writes a single JSON object to stdout; errors after argument parsing are JSON as well. The English field names are `ok`, `changed`, `dry_run`, `total`, `sections`, `router`, `local`, `warnings`, `commands`, `update`, `error`.
303
+ - `--json` writes a single JSON object to stdout; errors after argument parsing are JSON as well. Top-level fields are `ok`, `changed`, `dry_run`, `total`, `sections`, `servers`, `warnings`, `update` and `error`; each server carries `name`, `host`, `ok`, `changed`, `skipped`, `skipped_pairs`, `files`, `commands` and `error`; each file carries `source`, `target`, `ok`, `changed`, `total`, `sections`, `router`, `local`, `warnings` and `error`.
250
304
  - `--quiet` prints only errors; `--verbose` adds update-check failures to the warnings; `--strict` treats local warnings as errors.
251
305
 
252
306
  ## Security
253
307
 
254
- - **Strict host key checking.** Every connection verifies the router key against `known_hosts` using `golang.org/x/crypto/ssh/knownhosts`, whether authentication uses a key file or the ssh-agent. An unknown key aborts with its SHA-256 fingerprint and a `ssh-keyscan` hint; a changed key aborts naming both the presented and the expected fingerprints. TOFU does not exist in v1. The host key type is negotiated from the algorithms recorded for the host in `known_hosts`, so a host with only one recorded key type is not rejected merely because the server also offers another.
308
+ - **Strict host key checking.** Every connection verifies the server key against `known_hosts` using `golang.org/x/crypto/ssh/knownhosts`, whether authentication uses a key file or the ssh-agent. An unknown key aborts with its SHA-256 fingerprint and a `ssh-keyscan` hint; a changed key aborts naming both the presented and the expected fingerprints. TOFU does not exist in v1. The host key type is negotiated from the algorithms recorded for the host in `known_hosts`, so a host with only one recorded key type is not rejected merely because the server also offers another.
255
309
  - **Updates in v1 rely on SHA-256 only.** The binary checks the downloaded archive against `checksums.txt` from the release and extracts only the expected executable, rejecting path traversal. GoReleaser also publishes a cosign keyless signature bundle (`checksums.txt.sigstore.json`) and GitHub build attestations; these are for manual verification, not checked by the CLI. To verify a release by hand:
256
310
 
257
311
  ```sh
@@ -296,6 +350,10 @@ Tagging `v*` runs `.github/workflows/release.yml`. Maintainers need:
296
350
 
297
351
  GoReleaser builds the archives, `checksums.txt`, the cosign bundle and SBOMs, publishes the GitHub release and the Homebrew cask; the release workflow adds build provenance attestations and then publishes the npm packages.
298
352
 
353
+ ## Contributing
354
+
355
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the development setup, commit conventions and the pull request workflow. Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md).
356
+
299
357
  ## License
300
358
 
301
359
  MIT
package/domsync.exe CHANGED
Binary file
package/package.json CHANGED
@@ -1 +1,20 @@
1
- {"name":"@domsync/win32-arm64","version":"1.0.1","os":"win32","cpu":"arm64","files":["domsync","domsync.exe"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/neiromaster/domsync.git"},"publishConfig":{"provenance":true,"access":"public"}}
1
+ {
2
+ "name": "@domsync/win32-arm64",
3
+ "version": "1.2.0",
4
+ "packageManager": "npm@11.5.1+sha512.232e6f5d9e799bcb486920b3e9ba907fdf96e576cf7e8c9446c8162e33a416096a1d37a9e910d9a918f6b1f606791c99bc6bb61ee2569b496ec74af13d0dbd95",
5
+ "os": "win32",
6
+ "cpu": "arm64",
7
+ "files": [
8
+ "domsync",
9
+ "domsync.exe"
10
+ ],
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/neiromaster/domsync.git"
15
+ },
16
+ "publishConfig": {
17
+ "provenance": true,
18
+ "access": "public"
19
+ }
20
+ }