@tikoci/centrs 0.1.9-next.24 → 0.1.9

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 (71) hide show
  1. package/CHANGELOG.md +121 -6
  2. package/GLOSSARY.txt +9 -1
  3. package/README.md +43 -29
  4. package/commands/api/AGENTS.md +3 -2
  5. package/commands/api/README.md +8 -1
  6. package/commands/btest/README.md +9 -0
  7. package/commands/btest/examples.md +7 -0
  8. package/commands/devices/README.md +160 -30
  9. package/commands/devices/examples.md +168 -2
  10. package/commands/execute/README.md +13 -2
  11. package/commands/explain/README.md +223 -3
  12. package/commands/explain/examples.md +8 -1
  13. package/commands/retrieve/README.md +24 -3
  14. package/commands/retrieve/examples.md +84 -0
  15. package/commands/settings/README.md +6 -0
  16. package/commands/settings/examples.md +17 -0
  17. package/commands/transfer/README.md +25 -0
  18. package/commands/transfer/examples.md +46 -0
  19. package/docs/CLI.md +53 -11
  20. package/docs/MATRIX.md +5 -3
  21. package/docs/errors/cdb/encrypt-failed.md +10 -0
  22. package/docs/errors/cdb/password-required.md +5 -4
  23. package/docs/errors/cdb/plaintext-backups.md +11 -0
  24. package/docs/errors/input/empty-stdin.md +27 -0
  25. package/docs/errors/input/invalid-routeros-path.md +25 -3
  26. package/docs/errors/input/invalid-stdin.md +28 -0
  27. package/docs/errors/usage/stdin-is-tty.md +21 -0
  28. package/package.json +11 -7
  29. package/src/api-fanout.ts +2 -0
  30. package/src/api.ts +12 -0
  31. package/src/btest.ts +7 -0
  32. package/src/cli/api.ts +19 -2
  33. package/src/cli/btest.ts +9 -0
  34. package/src/cli/common.ts +1 -0
  35. package/src/cli/devices.ts +248 -15
  36. package/src/cli/execute.ts +12 -3
  37. package/src/cli/retrieve.ts +9 -0
  38. package/src/cli/settings.ts +28 -6
  39. package/src/cli/terminal.ts +9 -0
  40. package/src/cli/transfer.ts +10 -1
  41. package/src/core/error-catalog.ts +28 -2
  42. package/src/core/menu-path.ts +89 -0
  43. package/src/devices.ts +378 -33
  44. package/src/execute-fanout.ts +2 -0
  45. package/src/execute.ts +7 -0
  46. package/src/explain/blocks.ts +2 -2
  47. package/src/explain/catalog.ts +22 -12
  48. package/src/explain/pasted-prompt.ts +198 -0
  49. package/src/explain/pathresolve.ts +2 -2
  50. package/src/explain/segment.ts +2 -2
  51. package/src/explain/symbols.ts +2 -2
  52. package/src/explain/verbsplit.ts +2 -2
  53. package/src/explain/write.ts +2 -2
  54. package/src/explain.ts +2 -0
  55. package/src/index.ts +5 -0
  56. package/src/mcp/safety.ts +7 -6
  57. package/src/mcp/tools.ts +30 -68
  58. package/src/protocols/adapter.ts +2 -0
  59. package/src/protocols/btest-session.ts +10 -30
  60. package/src/protocols/ec-srp5.ts +19 -10
  61. package/src/protocols/mac-telnet-console.ts +2 -2
  62. package/src/resolver/cdb.ts +66 -9
  63. package/src/resolver/index.ts +1 -0
  64. package/src/resolver/quickchr-provider.ts +5 -0
  65. package/src/resolver/selection.ts +15 -4
  66. package/src/retrieve-fanout.ts +2 -0
  67. package/src/retrieve.ts +11 -0
  68. package/src/settings.ts +24 -8
  69. package/src/terminal.ts +7 -0
  70. package/src/transfer-fanout.ts +2 -0
  71. package/src/transfer.ts +79 -14
package/CHANGELOG.md CHANGED
@@ -10,13 +10,36 @@ Each stable version has one section. Work in progress goes under
10
10
  `## X.Y.Z — next`, which is what `@next` installs (`X.Y.Z-next.N`); the stable
11
11
  release dates that heading. A `-next.N` build has no section of its own.
12
12
 
13
- ## 0.1.9 — next
13
+ ## 0.1.9 — 2026-11-03
14
14
 
15
- In progress on npm tag `next`. The `0.1.9-next.0` cut (2026-10-13) carried
16
- part of this section.
15
+ The `0.1.9-next.0` preview cut (2026-10-13) carried part of this section.
17
16
 
18
17
  ### Added
19
18
 
19
+ - `explain` publishes a syntactic statement tree (#497, #522): statement
20
+ `id`/`parent`, substitution `owner`, and block ownership/index links let
21
+ consumers locate nested source without inferring ancestry from spans.
22
+ Stored script and callback bodies remain opaque; ownership does not claim
23
+ execution or lexical inheritance.
24
+ - `devices add`/`set --password-stdin` reads the record password from stdin,
25
+ so automation can write a credential without it showing in argv or `ps`
26
+ (#582). One trailing `\n` or `\r\n` is stripped. Empty input is
27
+ `input/empty-stdin`, non-UTF-8 bytes or a leading byte-order mark are
28
+ `input/invalid-stdin`, a terminal on stdin is `usage/stdin-is-tty`, and
29
+ combining it with `--password` is `usage/conflicting-flags`. Other `devices`
30
+ subcommands refuse the flag.
31
+ - `explain` offers a non-applied repair tip for pasted console prompts
32
+ (#370, #567, #578), including `[user@host] /menu>` and HotLock `>>` forms.
33
+ The candidate preserves command separators and names the menu context;
34
+ multiline repairs render as fenced code. The original analysis stays intact.
35
+ - `--match user=<name>|target=<addr>|<record-type>` on `retrieve`, `execute`,
36
+ `api`, `transfer`, `terminal` and `btest client` picks one of several CDB
37
+ records saved for the same `<router>` (#583), as `devices show --match`
38
+ does. One address saved under two users was `identity/ambiguous` on every
39
+ command, and the error named a `--match` they rejected. The flag is
40
+ single-target: with a fan-out selection or `--quickchr` it is
41
+ `usage/conflicting-flags`, and with no CDB record in play it is
42
+ `identity/no-match` rather than ignored.
20
43
  - **Live `explain` over rest-api + native-api is `CHR-passed`** (#236).
21
44
  Examples 7–16, 19, 24, 48 and 52–53 pass on both transports on stable
22
45
  7.24.5 and long-term 7.23.8. `runtimeAcceptance` stays `not-proven`.
@@ -92,9 +115,33 @@ part of this section.
92
115
  client, its controller.
93
116
  - `execute --cmr` accepts a command that opens with a relative menu change
94
117
  (`/system; identity/print`); its `:parse` return was read as incomplete.
118
+ - CDB encryption lifecycle (#558). `devices init --encrypt` creates an
119
+ encrypted empty CDB; `devices encrypt` converts an existing open CDB in place
120
+ with a fresh salt, keeping its records, unknown fields, and file mode. Both
121
+ need a non-empty `--cdb-password` / `CENTRS_CDB_PASSWORD`; a password alone
122
+ still never converts. Conversion keeps every backup and writes a plaintext
123
+ snapshot unless `--no-backup` is passed. It warns with `cdb/plaintext-backups`.
124
+ An already-encrypted file is verified and left unchanged. Library:
125
+ `initCdb({ encrypt })`, `encryptCdb({ skipBackup })`. `init` results now
126
+ include `encrypted`. `init` and `encrypt` refuse flags they would otherwise
127
+ ignore (`--user`, `--group`, `--via`, …) with `input/invalid-command`.
95
128
 
96
129
  ### Changed
97
130
 
131
+ - Bun 1.4.4 is the minimum supported version (`engines.bun: >=1.4.4 <2`, was
132
+ `>=1.3.11 <2`). The CI/release/Pages `packageManager` pin, Docker image and
133
+ `@types/bun` now use 1.4.4 together (0.1.8 shipped on 1.3.14; #438).
134
+ - **Breaking:** `devices show`, `add` and `set` no longer print the record password
135
+ (#551). Text and JSON carry `passwordSet: boolean` instead, and
136
+ `--show-password` on those three subcommands opts back in; other subcommands
137
+ refuse the flag. The `--explain` record's password field reads `<redacted>`
138
+ unless the flag is given, as does an unknown-tcode tail that may hold the
139
+ password. **Library consumers:** exported `showDevice`,
140
+ `addDevice` and `setDevice` now redact too. `DevicesShowItem` has no
141
+ `password` field; pass `showPassword: true` to get
142
+ `DevicesShowItemWithPassword`. MCP output is unchanged (it already redacted)
143
+ and now uses the same core redaction. Dialing with CDB credentials is
144
+ unaffected: the resolver and MCP allowlist read the raw record.
98
145
  - **Breaking:** permission and edge-auth failures get their own error codes
99
146
  (#541). Three codes change for `--json` and library readers:
100
147
  - A REST policy refusal (HTTP 500 `not enough permissions (9)` or `std
@@ -181,9 +228,7 @@ part of this section.
181
228
  `/cmr/upgrade/trigger`, `/cmr/upgrade/job/run-next`) are pinned as writes in
182
229
  every spelling, over `execute`, `api` and MCP `mcp=ro`. On 7.26beta1 a
183
230
  controller login with only `read` + `api` can run them (#456).
184
- - CI, release, Pages and the Docker image run Bun 1.4.2 (was 1.3.14), the
185
- version local development already used; `@types/bun` moves with it (#438).
186
- `engines.bun` stays `>=1.3.11 <2`. The `typescript` peer stays `^6.0.3`:
231
+ - The `typescript` peer stays `^6.0.3`:
187
232
  the source passes `tsc --noEmit` under TypeScript 7.0.2, but typedoc 0.28 (the
188
233
  API reference build) supports TypeScript up to 6.0.x only.
189
234
  - QA records a CHR leg in `qa-history` only when its test step passed or
@@ -201,6 +246,60 @@ part of this section.
201
246
 
202
247
  ### Fixed
203
248
 
249
+ - Help now lists SSH for `execute --via`, the offline analyzer before device
250
+ validation for `execute` and `api /execute`, and `devices --match` on
251
+ `show`/`set`/`remove`. `devices --group` names `add`/`set`, and
252
+ `devices init --help` / `encrypt --help` show only the flags they accept.
253
+ The generated CLI reference includes both file-command help sections.
254
+ - Offline `explain` withdraws unsupported readings of unlisted bare paths
255
+ and space-for-`=` arguments (#211, #350), distinguishes command operands
256
+ from menu blocks (#496), and correctly reads `where` arrays, column-0
257
+ array comments and nested relative submenu blocks (#502, #503, #351).
258
+ - Offline `explain` rejects CHR-grounded group, operator, escape, variable,
259
+ name and glued-token faults (#505, #511, #506, #518), including unquoted
260
+ `?`, backticks, second argument `=` and non-ASCII bytes (#358, #371).
261
+ Quoted values and accepted controls retain their supported readings.
262
+ A stray top-level `}` or `]` warns `discarded-close` and marks the
263
+ unread tail; the execution gate refuses it because RouterOS would discard
264
+ part of the submitted script (#504).
265
+ - `explain` block spans use analyzed UTF-8 byte offsets, matching statements
266
+ and tokens even after Unicode text (#523, #524).
267
+ - `settings get <attr>` works in text mode (#587). It crashed reading
268
+ `typed.source` because the text renderer treated `get`'s single entry as
269
+ `print`'s key-to-entry map; `--json` was unaffected. It now prints one
270
+ `key: value [source: …]` line in `print`'s format, under the canonical attr
271
+ name, and the JSON envelope names that key in `meta.operation.attr`. A fault
272
+ after the arguments parse is now `internal/settings-failed` (exit 1), not
273
+ `input/invalid-command` with `--help` advice.
274
+ - **`retrieve` and `api` can no longer send a non-read** (#586). A `#` or
275
+ `?` in the path cut the appended `/print` off the REST URL, so
276
+ `retrieve '/system/reboot#' --no-validate --attribute uptime` posted
277
+ `/rest/system/reboot` and rebooted CHR 7.24.5 without `--yes`; `api` with a
278
+ projection did the same. Both now require every path segment to be a
279
+ RouterOS menu name (letters, digits, hyphens; case folds) on every
280
+ transport, even with `--validate=false`, failing
281
+ `input/invalid-routeros-path` before resolution. The REST adapter also
282
+ refuses any request path containing `?`, `#`, `\`, control characters or a
283
+ dot segment (including `%2e%2e`), for every command. Reported by
284
+ routeros-muse.
285
+ - `devices set <target> key=` removes the comment token instead of storing an
286
+ empty value, as documented (#590). Removing an absent token is a no-op;
287
+ `--password ""` still clears the password, and bare `password=`/`user=`
288
+ stay `cdb/reserved-key`.
289
+ - `transfer` honors a CDB record's `via=` preference (#591). A record with
290
+ `via=ssh` drove `execute` over SSH but transfer ignored it and needed
291
+ `--via sftp`. Transfer now maps the record's transport to a method
292
+ (`ssh`→`sftp`, `rest-api`→`rest`, `native-api`→`native`) after `--via` and
293
+ `CENTRS_TRANSFER_VIA`, and accepts `--via ssh` as an alias of `sftp`. A
294
+ record transport that cannot carry files fails `settings/invalid-via`
295
+ naming the record; a record-pinned REST upload over 60 KB names the record
296
+ in its `transport/unsupported-operation` remediation.
297
+ - The `explain` catalog generator skips the CLI Reference's new
298
+ `compare-versions` widget page (2026-10-30). The page carries no command
299
+ entry, and it aborted the catalog drift report. The refreshed catalog adds
300
+ 10 published paths (`/interface/wifi/easymesh/*`,
301
+ `/app/settings/remove-certificate`, three `/system/resource/hardware`
302
+ commands), and the `/cmr/client` gates drop `!powerpc`.
204
303
  - `retrieve --sample`, `--follow` and `--wait` no longer report a validation
205
304
  verdict the gate never gave (#537). A cancellation before the gate ran now
206
305
  reports the device stage `not reached` (`interrupted` for `--sample` and
@@ -277,6 +376,12 @@ part of this section.
277
376
  `:for from=1 counter=i to=2` binds `i`, so later `$` references link;
278
377
  previously the argument name was declared and a reordered `counter=` left
279
378
  `$i` unbound.
379
+ - EC-SRP5 hashes shared `z` and the REDP1 loop seed minimal-length, as
380
+ RouterOS does (#565). centrs padded both to 32 bytes, following `mtwei.c`.
381
+ As a result, about 1 in 256 MAC-Telnet logins failed on a short `z`. On
382
+ both MAC-Telnet and btest, every login failed for an account whose salt hit
383
+ the REDP1 edge, about 1 in 317 password changes. btest's `z`-only workaround
384
+ (#516) is replaced by the shared fix.
280
385
  - mac-telnet with an explicit directed-broadcast `--host` (`192.168.74.255`)
281
386
  sends the owning interface's real MAC (#546, #547). On Linux, `connect()` to
282
387
  a broadcast reports `0.0.0.0`, so the session fell back to a synthetic
@@ -286,6 +391,16 @@ part of this section.
286
391
 
287
392
  ### Maintenance
288
393
 
394
+ - QA allows 50 minutes per CHR leg so the full integration suite can finish
395
+ (#611).
396
+ - The explain corpus uses schema 5 and one reader, with pinned channel
397
+ sessions, a regenerated highlight slice and a wiresage conformance gate
398
+ (#498, #514, #515). Bounded-execution tests use deterministic structural
399
+ work guards instead of depth-cap timing (#521).
400
+ - The explain contract records proposed topic checks and related live reads
401
+ (#493), the CHR-grounded context submission matrix (#574), and import
402
+ verbose dry-run behavior (#520). These are design/grounding evidence;
403
+ `--context`, topic checks and contextual reads are not shipped CLI options.
289
404
  - Releases are one dispatch from `main` (#484, #485). `@next` publishes
290
405
  `X.Y.Z-next.<run_number>` with no commit, PR or tag, and reuses main's QA
291
406
  run on the commit instead of sweeping every channel again. `@latest` creates
package/GLOSSARY.txt CHANGED
@@ -94,6 +94,7 @@ cwd
94
94
  datagram
95
95
  davidanson
96
96
  DDNS
97
+ dsv # `:serialize to=dsv` delimiter-separated-values format (RouterOS 7.x); the #374 comma-value control pins offline acceptance of a `delimiter=,` value
97
98
  dialable # an endpoint a client can actually connect to (advertised AND reachable)
98
99
  defconf
99
100
  demux
@@ -116,6 +117,7 @@ fat-chr
116
117
  figcaption
117
118
  FlaUI
118
119
  frontends
120
+ frontmatter # YAML metadata block at the top of a Markdown file (e.g. instruction applyTo headers)
119
121
  haakonnessjoen
120
122
  Hammerspoon
121
123
  HMAC
@@ -373,6 +375,8 @@ underflows
373
375
  Werror
374
376
 
375
377
  # MTWEI / EC-SRP vocabulary (src/protocols/mtwei.ts, mac-telnet auth)
378
+ # libucrypto: RouterOS's shared cryptography library; EC-SRP5 encoding source.
379
+ libucrypto
376
380
  # docrypto: mtwei.c's client EC-SRP proof function (mtwei_docrypto), ported to TS.
377
381
  docrypto
378
382
  # redp: REDP1 (IEEE P1363.2) random-point derivation used in the MTWEI tangle.
@@ -721,8 +725,11 @@ remoteap # caps-man definition module for `/caps-man/remote-cap`
721
725
  seccfg # caps-man definition module for `/caps-man/security`
722
726
  ifaces # CLI-Reference definition module for `/tool/graphing/interface`
723
727
  datapath # `/caps-man/datapath` — the real CLI menu behind `dpathcfg`
724
- easymesh # `/interface/wifi/easymesh` — the doc spelling of `/interface/wifi/wps-push-button`
728
+ easymesh # `/interface/wifi/easymesh` — once a doc spelling of `/interface/wifi/wps-push-button`; published as its own ungated menu since 2026-10-30, in no CHR tree through 7.26beta3
725
729
  wps # Wi-Fi Protected Setup — `/interface/wifi/wps-push-button`
730
+ bss # Wi-Fi basic service set — `/interface/wifi/easymesh/bss`
731
+ dpp # Wi-Fi Device Provisioning Protocol — `/interface/wifi/easymesh/dpp-trigger`
732
+ overcurrent # `/system/resource/hardware/reset-overcurrent`, gated by syscap `usb-hub`
726
733
  qos # switch-chip quality of service — `/interface/ethernet/switch/qos`, a real menu segment R1 must never drop
727
734
  qca # Qualcomm Atheros — the `wireless-qca` package gate
728
735
  multiswitch # syscap gate: more than one switch chip
@@ -807,6 +814,7 @@ zzznotamenu # a menu name no RouterOS build has — `/ip { zzznotamenu { print }
807
814
  # #361 absent-menu controls. Deliberate NON-words: each names a path no RouterOS
808
815
  # build has, or quotes a published gate verbatim, so the spelling is load-bearing.
809
816
  nosuch # a top-level menu no RouterOS build has — `/nosuch` is `bad command name nosuch` on CHR 7.23.7, the `validation/unknown-path` control (#361)
817
+ nosuchcmd # a root command no RouterOS build has — `:local varID [nosuchcmd where name=x]` is `bad command name nosuchcmd (line 1 column 15)` on CHR 7.25 and 7.23.8, the #374 unknown-command control
810
818
  nosuchmenu # a submenu no RouterOS build has — `/ip/nosuchmenu/print` is `syntax error (line 1 column 15)` on CHR 7.23.7, pointing at the `/` after it (#361)
811
819
  nosuchverb # a command no RouterOS build has under a real menu — `/ip/address/nosuchverb` is `bad command name nosuchverb` on CHR 7.23.7 (#361)
812
820
  qqq # the second segment of the parse-oracle row `/zzz qqq print`, captured on CHR 7.23.3 (GH#230)
package/README.md CHANGED
@@ -4,21 +4,21 @@
4
4
  CLI (with an MCP server; TUI and HTTP proxy frontends planned) for talking to
5
5
  MikroTik RouterOS devices through a regularized, validated interface.
6
6
 
7
- Preview builds are published on npm. To try the current offline analyzer without
8
- installing centrs or connecting to a router:
7
+ Stable releases are published on npm under `latest`. To try the offline
8
+ analyzer without installing centrs or connecting to a router:
9
9
 
10
10
  ```bash
11
- bunx @tikoci/centrs@next explain '/ip/address print' --json
11
+ bunx @tikoci/centrs@latest explain '/ip/address print' --json
12
12
  ```
13
13
 
14
- Use `bunx @tikoci/centrs@next --help` to inspect the full preview CLI, and
15
- `bunx @tikoci/centrs@next --version` (`-V`; `--json` for an envelope) to print
14
+ Use `bunx @tikoci/centrs@latest --help` to inspect the release CLI, and
15
+ `bunx @tikoci/centrs@latest --version` (`-V`; `--json` for an envelope) to print
16
16
  the exact version for scripts and field reports. The dist-tag follows standard
17
- SemVer semantics: a plain version (`0.1.8`) publishes to `latest` and carries
18
- `next` forward with it, while a `next` build (`0.1.9-next.23`) publishes to
17
+ SemVer semantics: a plain version (`0.1.9`) publishes to `latest` and carries
18
+ `next` forward with it, while a `next` build (`0.1.10-next.23`) publishes to
19
19
  `next` only and never moves `latest`. A `next` build is described by the
20
- `## 0.1.9 — next` section of [`CHANGELOG.md`](CHANGELOG.md). So `@next` is the
21
- leading edge and can equal `@latest` between cuts —
20
+ next version's `## X.Y.Z — next` section of [`CHANGELOG.md`](CHANGELOG.md).
21
+ So `@next` is the leading edge and can equal `@latest` between cuts —
22
22
  `npm view @tikoci/centrs dist-tags` is the authority.
23
23
 
24
24
  The full flag reference for every command is generated from the CLI metadata
@@ -40,7 +40,8 @@ product; without them this would just be a worse `curl`.
40
40
  > Check current dist-tags with
41
41
  > `npm view @tikoci/centrs dist-tags --json`. The library, device registry, and
42
42
  > first MCP server phases are CHR-verified; the CLI is wired (`coded`);
43
- > encrypted-CDB writes are still blocked, and the TUI/proxy frontends are planned.
43
+ > encrypted-CDB reads and writes are supported, and the TUI/proxy frontends
44
+ > are planned.
44
45
  > `docs/MATRIX.md` is the single source of truth for what works today — treat
45
46
  > anything not green there as not-yet-shipped.
46
47
 
@@ -49,9 +50,10 @@ product; without them this would just be a worse `curl`.
49
50
  - **Frontends:** TypeScript API, CLI, MCP server (Phase 1 + first Phase 2 CDB
50
51
  mutation `CHR-passed` — see `commands/mcp/`), TUI and HTTP proxy/daemon
51
52
  (future).
52
- - **Protocols:** REST API (preferred), native API, and SNMP for reads;
53
- SSH / MAC-Telnet / RoMON / WinBox Terminal for interactive and L2 execute;
54
- MNDP for discovery. centrs picks the protocol for you (and lets you pin one
53
+ - **Protocols:** REST API and native API for reads and writes; SSH / MAC-Telnet
54
+ for execution and interactive terminals; SFTP for file transfer; MNDP for
55
+ discovery; bandwidth test for peer measurement. SNMP, RoMON and WinBox
56
+ Terminal are planned. centrs picks the protocol for you (and lets you pin one
55
57
  with `via=` or `--via`).
56
58
  - **Commands:** every router-facing command resolves a *target* — a router you
57
59
  name on the CLI — to a real address, credentials, and a protocol, then runs.
@@ -60,12 +62,14 @@ product; without them this would just be a worse `curl`.
60
62
 
61
63
  | Command | Purpose |
62
64
  | ---------- | ------- |
63
- | `retrieve` | Read RouterOS state over REST/native API and SNMP OID/MIB values. `--follow` keeps a menu's rows current over native API (snapshot, then a line per change; NDJSON); `--sample <interval>` re-reads it on a timer for counters; `--query` filters on the router with the `print where` grammar; `--wait` retries until a read succeeds (or an optional `--until` condition holds), up to a deadline. |
64
- | `execute` | Run RouterOS CLI-shaped read/write commands (add/set/remove) over native API/REST/L2 surfaces. |
65
+ | `retrieve` | Read RouterOS state over REST/native API. `--follow` keeps a menu's rows current over native API (snapshot, then a line per change; NDJSON); `--sample <interval>` re-reads it on a timer for counters; `--query` filters on the router with the `print where` grammar; `--wait` retries until a read succeeds (or an optional `--until` condition holds), up to a deadline. SNMP OID/MIB reads are planned. |
66
+ | `execute` | Run RouterOS CLI-shaped read/write commands (add/set/remove) over native API, REST, SSH or MAC-Telnet. |
65
67
  | `api` | Structured RouterOS API passthrough (gh-api style): one command per operation, structured in/out, can write, over REST or native API. Follow changes with `api … <menu>/listen`; `--stream` streams any command's replies (native API; NDJSON). |
66
68
  | `terminal` | Open an interactive console, primarily SSH or MAC-Telnet. |
69
+ | `transfer` | Upload, download and manage device files over REST/native API or SFTP; `upload` and `download` are shortcuts. |
70
+ | `btest` | Run a MikroTik bandwidth-test client or server over TCP/UDP. |
67
71
  | `check` | Probe reachability and management protocol availability. **Designed, not implemented** — see `docs/MATRIX.md`. |
68
- | `explain` | Analyze a RouterOS command before running it: canonical form, syntax diagnostics, and what is valid at a path. Offline analysis runs today; the live-device probes are designed. |
72
+ | `explain` | Analyze RouterOS source offline, or add read-only device evidence over REST/native API, including path schemas, completion candidates and value types. Runtime acceptance remains unproven. |
69
73
  | `devices` | View and maintain the CDB-backed device registry (the only writer). |
70
74
  | `discover` | Discover MNDP neighbors and optionally save them into the CDB. |
71
75
  | `settings` | Manage centrs's own settings (`centrs.env`, `__default__`) and run first-time setup. |
@@ -92,7 +96,9 @@ stores:
92
96
  comment carries `identity=edge1`. Every other comment token stays inert prose.
93
97
  See `commands/devices/README.md` (Identity model) for the full resolution rules.
94
98
  - **`user` / `password`** — the credentials centrs connects with. They stay in
95
- the CDB and are redacted from MCP results/resources.
99
+ the CDB. `devices show` / `add` / `set` withhold the password and report
100
+ `passwordSet` (`--show-password` reveals it); MCP results/resources always
101
+ redact it.
96
102
  - **`group`, `profile`, `session`, port** — used as-is.
97
103
 
98
104
  So `centrs retrieve edge1 /ip/address` works with no flags: `edge1` is looked
@@ -168,18 +174,24 @@ so you can always see *why* a given port or transport was chosen.
168
174
 
169
175
  `devices` is the only command that writes the CDB, and it does so safely
170
176
  (timestamped backup, atomic rename, unknown WinBox fields preserved verbatim).
171
- Encrypted-CDB **reads** work with `--cdb-password` / `CENTRS_CDB_PASSWORD`;
172
- encrypted **writes** are currently blocked pending a verified WinBox round-trip.
177
+ Encrypted-CDB reads and writes work with `--cdb-password` /
178
+ `CENTRS_CDB_PASSWORD`; writes decrypt, mutate and re-encrypt with a fresh salt.
179
+ `devices init --encrypt` creates an encrypted registry, and `devices encrypt`
180
+ explicitly converts an existing open CDB. Conversion retains existing backups
181
+ and warns that plaintext copies remain. The manual WinBox round-trip is still
182
+ pending; see the per-command contract for the current evidence and limits.
173
183
  The full contract — identity resolution, ambiguity handling, write strategy,
174
184
  and the comment grammar — lives in `commands/devices/README.md` and
175
185
  `docs/CONSTITUTION.md` (Identity and CDB).
176
186
 
177
187
  ## The result envelope, validation, and errors
178
188
 
179
- - **Validation is on by default** (`validate=true`). Reads are checked through
180
- `/console/inspect`; CLI-shaped `execute` commands through `[:parse]`. Set
181
- `validate=false` (per device or per call) only as a deliberate escape hatch.
182
- - **Every call returns one envelope:** `{ ok, data?, warnings?, error?, meta }`,
189
+ - **Validation is on by default** (`validate=true`). CLI-shaped `execute` and
190
+ `api /execute` scripts pass the offline analyzer before device validation.
191
+ Reads are checked through `/console/inspect`; CLI-shaped commands through
192
+ `[:parse]`. Set `validate=false` (per device or per call) only as a deliberate
193
+ escape hatch.
194
+ - **Every call returns one envelope:** `{ ok, data?, warnings, tips, error?, meta }`,
183
195
  with provenance and source-of-truth reporting in `meta`.
184
196
  - **Errors are typed values**, not thrown strings, with slash-namespaced codes
185
197
  (`routeros/…`, `transport/…`, `validation/…`, `cdb/…`) and a `details_url`
@@ -213,8 +225,8 @@ GitHub issues it links are the roadmap.
213
225
 
214
226
  Requirements:
215
227
 
216
- - Bun 1.3.11 or newer (CI runs the `packageManager` version in `package.json`;
217
- Bun 2.x not yet validated).
228
+ - Bun 1.4.4 or newer within 1.x (`engines.bun`: `>=1.4.4 <2`; CI runs the
229
+ `packageManager` version in `package.json`; Bun 2.x not yet validated).
218
230
  - Git.
219
231
  - `uv`, only for `bun run lint:actions` (zizmor workflow audit).
220
232
  - For integration tests: QEMU plus `@tikoci/quickchr` (handles CHR image
@@ -250,7 +262,9 @@ bun run explain:value-census # value-surface census (#225)
250
262
 
251
263
  ## Done definition (short version)
252
264
 
253
- A feature is done when every line in `commands/<name>/examples.md` is green on
254
- real CHR via `bun run test:integration`. Code existing is not done. Unit tests
255
- passing is not done. Full rule:
256
- `.github/instructions/done-definition.instructions.md`.
265
+ Live capabilities require every applicable example in `commands/<name>/examples.md`
266
+ green on real CHR via `bun run test:integration`. Offline analysis requires its
267
+ command README's full baseline acceptance criteria, including committed
268
+ CHR-grounded semantic evidence. The fixture-backed `devices` and `settings`
269
+ integration suites retain their command-specific contracts. Full rule:
270
+ [`docs/CONSTITUTION.md` → Done definition](docs/CONSTITUTION.md#done-definition).
@@ -87,8 +87,9 @@ stack-language access without centrs modelling the whole query grammar.
87
87
 
88
88
  ## CONFIRMED ON CHR (Phase 0 grounding spikes — CHR 7.23.1, 2026-06-29)
89
89
 
90
- Probes: scratchpad `api-grounding-spike.ts` (listen/CRUD/execute) and
91
- `api-grounding-spike2.ts` (duration/monitor + query forms + inspect path). Raw
90
+ Probes (phase-0 lab history, not runnable methods): `api-grounding-spike.ts`
91
+ (listen/CRUD/execute) and `api-grounding-spike2.ts` (duration/monitor + query
92
+ forms + inspect path). Raw
92
93
  native codec for the listen probe; `fetch` for REST; `connectNativeApi().talk()`
93
94
  for native.
94
95
 
@@ -51,7 +51,14 @@ centrs api <router> <endpoint> [flags]
51
51
  segment (`ip/address/*1`) addresses one row for GET/PATCH/DELETE; PUT and
52
52
  POST reject it with `input/invalid-path`. A trailing `/listen` segment is
53
53
  the menu's `listen` command, which implies `--stream` + `--via native-api`
54
- and requires GET (`ip/address/*1/listen` follows one row).
54
+ and requires GET (`ip/address/*1/listen` follows one row). After that
55
+ normalization every menu segment must be a RouterOS menu name (letters,
56
+ digits, hyphens; case folds), or the request fails with
57
+ `input/invalid-routeros-path` before any transport is chosen, whatever
58
+ `--validate` says. A `#` or `?` once cut `/print` off a projected GET's
59
+ REST URL and posted the bare command (`/system/reboot#` rebooted CHR 7.24.5,
60
+ #586); the grammar and retrieve's REST guard are described in
61
+ [retrieve → Validation](../retrieve/README.md#validation).
55
62
 
56
63
  The HTTP method (`-X`, default `GET`) is honored **literally** against RouterOS's
57
64
  REST mapping — `GET`→print/get, **`PUT`→add**, `PATCH`→set, `DELETE`→remove,
@@ -139,6 +139,15 @@ is net-new — `mtwei.ts` only implements the client. A bad proof (or a client t
139
139
  will not offer EC-SRP5 against an `authenticate=yes` server) maps to
140
140
  `transport/auth-failed`. Legacy pre-6.43 double-MD5 is out of scope for v1.
141
141
 
142
+ The shared core hashes minimal-length big-endian integers in the REDP1 loop
143
+ and for shared-point `z`; public keys and the REDP1 input keep their fixed
144
+ 32-byte X encoding (#565). The distinction is grounded in RouterOS
145
+ `libucrypto.so` 6.49.23/7.25 and `tikoci/m2ir`'s live WinBox discriminators.
146
+ `test/fixtures/ec-srp5.json` pins calculated regression transcripts;
147
+ `test/integration/ec-srp5.test.ts` forces the encoding edges against a real
148
+ CHR btest client, including a short `z` and a leading-zero REDP1 input
149
+ (confirmed on CHR 7.23.8 and 7.25).
150
+
142
151
  ## Output
143
152
 
144
153
  btest runs over time, so it emits **live records** during the run plus a final
@@ -99,6 +99,13 @@ and non-zero throughput. This is the example that exercises the net-new
99
99
  server-side EC-SRP5 verifier against real RouterOS (TCP keeps the gated path off
100
100
  the SLIRP UDP-reverse-NAT question).
101
101
 
102
+ `test/integration/ec-srp5.test.ts` also exercises this exchange with chosen
103
+ test salts and ephemeral scalars, one edge per pass: a short REDP1 loop hash
104
+ input, a REDP1 input whose leading zero must stay, and that input again with a
105
+ forced short shared `z`. RouterOS's client proof must match the minimal-length
106
+ derivation. This discriminator stops at the
107
+ proof; the normal btest suites cover completed sessions and throughput (#565).
108
+
102
109
  ### 4. Wrong credentials are rejected
103
110
 
104
111
  > *Designed, not yet gated* — the auth-reject path is asserted by the loopback