@tikoci/centrs 0.1.2 → 0.1.4

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 (134) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/GLOSSARY.txt +284 -0
  3. package/LICENSE +21 -0
  4. package/README.md +55 -11
  5. package/SECURITY.md +3 -3
  6. package/commands/AGENTS.md +7 -1
  7. package/commands/api/README.md +46 -27
  8. package/commands/api/examples.md +124 -6
  9. package/commands/btest/README.md +21 -32
  10. package/commands/check/README.md +376 -53
  11. package/commands/check/examples.md +390 -0
  12. package/commands/devices/README.md +177 -37
  13. package/commands/devices/examples.md +148 -0
  14. package/commands/discover/README.md +5 -11
  15. package/commands/execute/README.md +73 -26
  16. package/commands/execute/examples.md +131 -9
  17. package/commands/explain/README.md +1716 -0
  18. package/commands/explain/examples.md +606 -0
  19. package/commands/retrieve/README.md +55 -71
  20. package/commands/retrieve/examples.md +56 -9
  21. package/commands/settings/README.md +286 -22
  22. package/commands/settings/examples.md +408 -0
  23. package/commands/terminal/README.md +6 -0
  24. package/commands/transfer/README.md +57 -19
  25. package/commands/transfer/examples.md +95 -0
  26. package/docs/CLI.md +353 -0
  27. package/docs/CONSTITUTION.md +102 -26
  28. package/docs/MATRIX.md +106 -173
  29. package/docs/errors/README.md +5 -1
  30. package/docs/errors/cdb/empty-selection.md +19 -0
  31. package/docs/errors/input/incomplete-gps.md +9 -0
  32. package/docs/errors/input/invalid-altitude.md +9 -0
  33. package/docs/errors/input/invalid-bbox.md +9 -0
  34. package/docs/errors/input/invalid-coordinate.md +9 -0
  35. package/docs/errors/input/invalid-radius.md +9 -0
  36. package/docs/errors/internal/settings-failed.md +9 -0
  37. package/docs/errors/quickchr/machine-not-found.md +22 -0
  38. package/docs/errors/quickchr/machine-stopped.md +20 -0
  39. package/docs/errors/quickchr/package-unavailable.md +27 -0
  40. package/docs/errors/quickchr/unsupported-via.md +35 -0
  41. package/docs/errors/quickchr/unsupported.md +35 -0
  42. package/docs/errors/routeros/version-unsupported.md +26 -0
  43. package/docs/errors/settings/consequential-value.md +26 -0
  44. package/docs/errors/settings/invalid-boolean.md +21 -3
  45. package/docs/errors/settings/invalid-format.md +18 -3
  46. package/docs/errors/settings/invalid-integer.md +18 -3
  47. package/docs/errors/settings/invalid-timeout.md +20 -3
  48. package/docs/errors/settings/invalid-via.md +22 -3
  49. package/docs/errors/settings/reserved-key.md +29 -0
  50. package/docs/errors/settings/skip-env-file-active.md +25 -0
  51. package/docs/errors/settings/unknown-key.md +25 -0
  52. package/docs/errors/transport/provider-trust.md +29 -0
  53. package/docs/errors/usage/fanout-not-supported.md +16 -0
  54. package/docs/errors/usage/stdin-ignored.md +28 -0
  55. package/docs/errors/validation/unknown-attribute.md +37 -4
  56. package/package.json +49 -14
  57. package/src/api-fanout.ts +469 -0
  58. package/src/api.ts +171 -56
  59. package/src/cli/api.ts +156 -13
  60. package/src/cli/btest.ts +38 -4
  61. package/src/cli/common.ts +11 -0
  62. package/src/cli/devices.ts +235 -6
  63. package/src/cli/discover.ts +3 -1
  64. package/src/cli/execute.ts +129 -9
  65. package/src/cli/explain.ts +503 -0
  66. package/src/cli/mcp.ts +8 -1
  67. package/src/cli/retrieve.ts +167 -90
  68. package/src/cli/selection.ts +260 -0
  69. package/src/cli/settings.ts +286 -0
  70. package/src/cli/terminal.ts +22 -7
  71. package/src/cli/transfer.ts +192 -51
  72. package/src/cli.ts +22 -2
  73. package/src/core/envelope.ts +5 -2
  74. package/src/core/error-catalog.ts +95 -0
  75. package/src/core/fanout.ts +143 -2
  76. package/src/core/preflight.ts +306 -0
  77. package/src/core/routeros-string.ts +24 -0
  78. package/src/devices.ts +212 -20
  79. package/src/discover.ts +7 -2
  80. package/src/errors.ts +1 -0
  81. package/src/execute-fanout.ts +453 -0
  82. package/src/execute.ts +216 -68
  83. package/src/explain/arg-tokens.ts +114 -0
  84. package/src/explain/args.ts +1403 -0
  85. package/src/explain/blocks.ts +132 -0
  86. package/src/explain/brace-slots.ts +143 -0
  87. package/src/explain/brace-tokens.ts +38 -0
  88. package/src/explain/catalog.ts +1345 -0
  89. package/src/explain/coordinates.ts +329 -0
  90. package/src/explain/defects.ts +217 -0
  91. package/src/explain/is-known-menu.ts +17 -0
  92. package/src/explain/menus.ts +664 -0
  93. package/src/explain/operator-tokens.ts +310 -0
  94. package/src/explain/operators.ts +458 -0
  95. package/src/explain/path-tokens.ts +150 -0
  96. package/src/explain/pathresolve.ts +1324 -0
  97. package/src/explain/quoted-string.ts +241 -0
  98. package/src/explain/scope-brace.ts +308 -0
  99. package/src/explain/segment.ts +757 -0
  100. package/src/explain/string-tokens.ts +67 -0
  101. package/src/explain/symbol-values.ts +520 -0
  102. package/src/explain/symbols.ts +1597 -0
  103. package/src/explain/token-ranges.ts +72 -0
  104. package/src/explain/transport.ts +497 -0
  105. package/src/explain/value-tokens.ts +80 -0
  106. package/src/explain/values.ts +357 -0
  107. package/src/explain/verbs.ts +64 -0
  108. package/src/explain/verbsplit.ts +813 -0
  109. package/src/explain/write.ts +815 -0
  110. package/src/explain.ts +2152 -0
  111. package/src/index.ts +307 -1
  112. package/src/mcp/config.ts +12 -2
  113. package/src/mcp/tools.ts +137 -2
  114. package/src/protocols/adapter.ts +38 -18
  115. package/src/protocols/index.ts +11 -5
  116. package/src/protocols/mac-telnet-console.ts +61 -17
  117. package/src/resolver/cdb.ts +10 -2
  118. package/src/resolver/comment-kv.ts +58 -4
  119. package/src/resolver/config-file.ts +136 -0
  120. package/src/resolver/facts.ts +78 -0
  121. package/src/resolver/geo.ts +453 -0
  122. package/src/resolver/index.ts +65 -0
  123. package/src/resolver/mac.ts +12 -3
  124. package/src/resolver/quickchr-provider.ts +597 -0
  125. package/src/resolver/selection.ts +391 -0
  126. package/src/resolver/service-endpoint.ts +155 -0
  127. package/src/resolver/settings.ts +54 -5
  128. package/src/resolver/target.ts +21 -3
  129. package/src/retrieve-fanout.ts +239 -133
  130. package/src/retrieve.ts +120 -25
  131. package/src/settings.ts +1078 -0
  132. package/src/terminal.ts +11 -1
  133. package/src/transfer-fanout.ts +544 -0
  134. package/src/transfer.ts +133 -40
@@ -6,17 +6,18 @@ CDB / env / flags, validates the request through `/console/inspect`, runs the
6
6
  single REST or native-API operation, and returns the result in the standard
7
7
  envelope.
8
8
 
9
- Status: `CHR-passed` over `rest-api` and `native-api` (single-target),
10
- including open-ended `--stream` follow (native-api only), per `docs/MATRIX.md`;
11
- multi-target fan-out is a later phase. This file describes intent and flags; the
12
- matrix holds the cell states. Load-bearing rules — envelope, errors, settings
13
- precedence, identity, validation, protocol selection — live in
9
+ Status: `CHR-passed` over `rest-api` and `native-api`, including multi-target
10
+ fan-out and open-ended `--stream` follow (native-api only), per
11
+ `docs/MATRIX.md`. This file describes intent and flags; the matrix holds the
12
+ cell states. Load-bearing rules — envelope, errors, settings precedence,
13
+ identity, validation, protocol selection — live in
14
14
  [`docs/CONSTITUTION.md`](../../docs/CONSTITUTION.md); the `(constitution: …)`
15
15
  notes below point there rather than restating them.
16
16
 
17
17
  `api` absorbs the former `stream` command: open-ended follow is `api <router>
18
18
  <endpoint> --stream` (or the `/listen` endpoint form). `--listen` is an accepted
19
- alias of `--stream`.
19
+ alias of `--stream`. Streaming is single-session — it cannot combine with
20
+ multi-target fan-out (see below).
20
21
 
21
22
  ## Where `api` sits — the verb trichotomy
22
23
 
@@ -56,27 +57,45 @@ when a bare-collection `POST` carries create-looking fields.
56
57
 
57
58
  ## Flags
58
59
 
59
- | Flag | Behavior |
60
- | ---- | -------- |
61
- | `-X` / `--method <verb>` | HTTP method, default `GET`, case-insensitive. |
62
- | `-f <key=value>` | String field, repeatable; assembled into the JSON body. Values pass through verbatim (no type-guessing). |
63
- | `-d` / `--data <json>` | Raw JSON request body. Collides with `-f` → `usage/conflicting-flags`. |
64
- | `--input <file\|->` | Read the raw JSON body from a file or stdin (`-`). |
65
- | `--query` / `--filter <expr>` | RouterOS-side row filter, AND-combined, repeatable. `name=value` (eq), `name!=value` (ne), `name>value`/`name<value` (cmp), `name` (has-property). Maps to REST `.query` words / native `?` words. |
66
- | `--raw-query <word>` | Verbatim RouterOS query word (repeatable) for OR / absence / stack expressions — e.g. `--raw-query type=ether --raw-query type=vlan --raw-query '#\|'` (OR). Emitted as-is; the caller owns the stack. |
67
- | `--attribute` / `--proplist <a,b>` | Property projection → `.proplist`. |
68
- | `--raw` | Strip the envelope; emit bare RouterOS JSON. Implies `--validate=false`; does **not** imply `--yes`. |
69
- | `--yes` | Confirm a mutating (non-read) request in non-interactive runs. |
70
- | `--stream` (alias `--listen`) | Native-api-only open-ended follow → NDJSON stream of envelopes, ending with a summary envelope. Inferred from a `/listen` endpoint. `--via rest-api --stream` errors `transport/capability-unsupported` (REST's 60 s cap cannot follow). |
71
- | `--duration <dur>` / `--count <n>` | Bound a `--stream`: stop after a wall-clock window / after N frames. |
72
- | `--validate[=false]` | Default `true`. Gate is `/console/inspect` (see Validation). |
73
- | `--via <protocol>` | `rest-api` (default) or `native-api`. No silent downgrade. |
74
- | `--format <json\|yaml\|text>` | Output format. Defaults to `json` for `api` (machine-first); `CENTRS_FORMAT` overrides. Under `--stream`, `json`/`yaml` emit one compact envelope per line (NDJSON); `text` emits a concise row per frame. |
75
- | target/auth | `--host`, `--port`, `--username`/`--user`/`-u`, `--password`, `--insecure`, `--timeout`, `--cdb-file`, `--cdb-password`, `--resolve <none\|arp>` — same single-target resolver as `retrieve`/`execute`. |
76
-
77
- Multi-target fan-out (`--group` / `--where` / `--all` / `--default` /
78
- `--concurrency`, reusing `src/core/fanout.ts`) lands in a later phase. When it
79
- does, `--stream` is single-session: N>1 targets → `usage/fanout-not-supported`.
60
+ Implemented flags are generated from the CLI metadata into
61
+ [`docs/CLI.md` → api](../../docs/CLI.md#api); this file does not duplicate
62
+ that table, and `api` has no designed-but-unimplemented flags. Behavior notes
63
+ the generated reference cannot carry:
64
+
65
+ - `-f` values pass through verbatim (no type-guessing); `-d`/`--input`
66
+ collide with `-f` → `usage/conflicting-flags`.
67
+ - `--query`/`--filter` map to REST `.query` words / native `?` words.
68
+ - `--raw-query` is emitted as-is; the caller owns the stack — e.g.
69
+ `--raw-query type=ether --raw-query type=vlan --raw-query '#|'` (OR).
70
+ - `--via rest-api --stream` errors `transport/capability-unsupported`
71
+ (REST's 60 s cap cannot follow).
72
+ - Under `--stream`, `--format json`/`yaml` emit one compact envelope per
73
+ line (NDJSON); `text` emits a concise row per frame.
74
+ - The target/auth flags (`--host`, `--port`, `--username`, `--password`,
75
+ `--insecure`, `--timeout`, `--cdb-file`, `--cdb-password`, `--resolve`)
76
+ use the same single-target resolver as `retrieve`/`execute`; `--quickchr`
77
+ resolves host/port/auth from the live `@tikoci/quickchr` descriptor
78
+ instead (constitution: resolution providers).
79
+
80
+ ## Fan-out (multi-target)
81
+
82
+ Selecting more than one router — any selector flag (`--group` / `--where` /
83
+ `--near` / `--bbox` / `--all` / `--default`) or more than one positional target — switches `api` into
84
+ **fan-out mode** (`src/api-fanout.ts`, on the shared `src/core/fanout.ts` engine
85
+ and the `src/resolver/selection.ts` grammar). A plain single-positional call
86
+ stays the single-target envelope. Output is the locked `FanoutData` envelope
87
+ (`data = { summary, targets[] }`; outer `ok` = orchestration success; per-target
88
+ failures are inner `ok:false`), and the process exit code is granular: `0`
89
+ all-ok, `2` partial, `1` orchestration error or every target failed. See
90
+ `docs/CONSTITUTION.md` (Target selection) for the shared grammar and `--where`
91
+ vs `--query` distinction.
92
+
93
+ - **Writes fan out under `--yes`**, confirmed once up front (not per target).
94
+ Without `--yes`, the error names the blast radius (how many routers) and that
95
+ `--yes` is required.
96
+ - **`--listen`/`--stream` is single-session** → `usage/fanout-not-supported` in
97
+ fan-out mode. **`--raw` strips the envelope** → `usage/conflicting-flags` in
98
+ fan-out mode (per-target envelopes can't be bare).
80
99
 
81
100
  ## Validation
82
101
 
@@ -1,11 +1,13 @@
1
1
  # api — examples
2
2
 
3
- Each numbered example is an executable spec, asserted by CHR integration tests
4
- (booted by `@tikoci/quickchr`): `test/integration/api.test.ts` (rest-api
5
- examples), `test/integration/api-native.test.ts` (native-api examples, N…), and
6
- `test/integration/api-listen.test.ts` (streaming examples, L…), with example N ↔
7
- assertion N. If a line here is not exercised by a test, the test file is wrong; if
8
- a line passes only with `--validate=false`, the implementation is wrong (see
3
+ Each numbered example is an executable spec asserted by CHR integration tests
4
+ (booted by `@tikoci/quickchr`), example N ↔ assertion N:
5
+ `test/integration/api.test.ts` (rest-api examples),
6
+ `test/integration/api-native.test.ts` (native-api examples, N…),
7
+ `test/integration/api-listen.test.ts` (streaming examples, L…), and
8
+ `test/integration/api-fanout.test.ts` (fan-out examples, F…). If a line here is not
9
+ exercised by a test, the test file is wrong; if a line passes only with
10
+ `--validate=false`, the implementation is wrong (see
9
11
  [`docs/CONSTITUTION.md`](../../docs/CONSTITUTION.md)).
10
12
 
11
13
  `$R` is `<host>:<rest-port>` resolved by quickchr. `$A` is `<host>` and
@@ -323,3 +325,119 @@ centrs api $A ip/address --stream --duration 2s --via native-api --port $API_POR
323
325
 
324
326
  With no change during the window, the stream ends after ~2 s with a summary
325
327
  envelope whose `data.stopReason=duration-elapsed`. Exit code 0.
328
+
329
+ ## fanout (multi-target, F…)
330
+
331
+ These run against a CDB fixture with two records sharing group `$G`: record 0 is
332
+ the live CHR (comment `board=chr`), record 1 is an unreachable host (comment
333
+ `board=dead`). Fan-out output is the locked `FanoutData` envelope
334
+ (`data = { summary, targets[] }`; outer `ok` = orchestration success; per-target
335
+ failures are inner `ok:false`), with the granular exit code (0 all-ok / 2 partial /
336
+ 1 all-failed or orchestration error). See `test/integration/api-fanout.test.ts`.
337
+ The `--raw`/`--listen` + fan-out guards are network-free and validated in
338
+ `test/integration/cli-smoke.test.ts`.
339
+
340
+ ### F1. `--group` fans a GET out; a dead target is an inner failure
341
+
342
+ ```bash
343
+ centrs api --group $G ip/address --cdb-file $CDB --json
344
+ ```
345
+
346
+ `ok: true`, `data.summary = { total: 2, ok: 1, failed: 1 }`, `data.targets[]` in
347
+ record-index order (`[0, 1]`): target 0 is an inner success (`meta.via=rest-api`),
348
+ target 1 is inner `ok:false` with `error.code=transport/connection-refused`.
349
+ `meta.operation.kind=fanout`. Exit code 2.
350
+
351
+ ### F2. `--where` selects only the matching device
352
+
353
+ ```bash
354
+ centrs api --where board=chr ip/address --cdb-file $CDB --json
355
+ ```
356
+
357
+ The device-class selector matches only record 0, so `data.summary = { total: 1,
358
+ ok: 1, failed: 0 }`, one inner success. Exit code 0.
359
+
360
+ ### F3. An empty selection is `ok:true` with summary 0/0/0
361
+
362
+ ```bash
363
+ centrs api --group no-such-group ip/address --cdb-file $CDB --json
364
+ ```
365
+
366
+ `ok: true`, `data.summary = { total: 0, ok: 0, failed: 0 }`, `data.targets = []`,
367
+ and a `cdb/empty-group` warning. Exit code 0.
368
+
369
+ ### F4. A mutating fan-out without `--yes` is rejected, naming the blast radius
370
+
371
+ ```bash
372
+ centrs api -X PUT ip/address -f address=10.99.0.1/32 -f interface=ether1 --group $G --cdb-file $CDB --json
373
+ ```
374
+
375
+ Non-interactive and unconfirmed: outer `ok:false`,
376
+ `error.code=usage/confirmation-required`, and the message names the router count
377
+ (`2 router(s)`) and that `--yes` is required. No write is attempted. Exit code 1.
378
+
379
+ ### F5. A mutating fan-out with `--yes` writes across the selected devices
380
+
381
+ ```bash
382
+ centrs api -X PUT ip/address -f address=10.99.0.7/32 -f interface=ether1 --where board=chr --yes --cdb-file $CDB --json
383
+ ```
384
+
385
+ Confirmed once up front; the add runs on record 0 only (selected by `--where`).
386
+ `data.summary = { total: 1, ok: 1, failed: 0 }`, the inner success carries the
387
+ created `.id`. Exit code 0.
388
+
389
+ The fixture for F6–F9 adds record 2 — the reserved `__default__` credential record.
390
+
391
+ ### F6. `--all` fans across every record except `__default__`
392
+
393
+ ```bash
394
+ centrs api --all ip/address --cdb-file $CDB --json
395
+ ```
396
+
397
+ `data.summary = { total: 2, ok: 1, failed: 1 }`, `data.targets[]` record indices
398
+ `[0, 1]` (record 2 `__default__` is excluded). Exit code 2.
399
+
400
+ ### F7. A positional + `--group` union de-dupes by record index
401
+
402
+ ```bash
403
+ centrs api $R0 --group $G ip/address --cdb-file $CDB --json
404
+ ```
405
+
406
+ `$R0` is record 0's target, also a member of `$G`; the union is `{0, 1}` — record
407
+ indices `[0, 1]`, `total = 2` (not 3). Exit code 2.
408
+
409
+ ### F8. `--concurrency` bounds the worker pool
410
+
411
+ ```bash
412
+ centrs api --group $G --concurrency 1 ip/address --cdb-file $CDB --json
413
+ ```
414
+
415
+ Runs one target at a time; `meta.operation.concurrency = 1`, still
416
+ `data.summary = { total: 2, ok: 1, failed: 1 }`. (`--concurrency 2abc` / `1.5` are
417
+ rejected at parse time with `usage/invalid-concurrency`.) Exit code 2.
418
+
419
+ ### F9. `--default` selects `__default__`, which fails the connectable guard
420
+
421
+ ```bash
422
+ centrs api --default ip/address --cdb-file $CDB --json
423
+ ```
424
+
425
+ The reserved record is a credential fallback, not a connectable router, so its one
426
+ target fails deterministically: inner `ok:false` with
427
+ `error.code=target/unresolved` (never a `transport/dns` attempt on `"__default__"`).
428
+ `data.summary = { total: 1, ok: 0, failed: 1 }`. Exit code 1 (every target failed).
429
+
430
+ ## quickchr targets (#134)
431
+
432
+ `$NAME` is the machine name of a running quickchr-managed CHR. Host/port/auth
433
+ come from the live descriptor (see `commands/retrieve/examples.md` Q-series).
434
+ Covered by `test/integration/quickchr-target.test.ts`.
435
+
436
+ ### Q1. GET against a quickchr target
437
+
438
+ ```bash
439
+ centrs api --quickchr $NAME system/resource --json
440
+ ```
441
+
442
+ Envelope: `ok: true`, `data` is the resource object,
443
+ `meta.target.source.kind=provider`.
@@ -80,38 +80,27 @@ centrs btest server [--authenticate[=false]] [--user U] [--password P] \
80
80
 
81
81
  ## Flags
82
82
 
83
- ### `btest client <router>`
84
-
85
- | Flag | RouterOS analog / default | Notes |
86
- | ---- | ------------------------- | ----- |
87
- | `--protocol <udp\|tcp>` | `protocol`, default `udp` | UDP measures raw throughput + loss; TCP measures goodput. |
88
- | `--direction <receive\|transmit\|both>` | `direction`, default `receive` | `receive` = download (server→client), `transmit` = upload. |
89
- | `--duration <dur>` | `duration` | Bounds the run; reuses `parseDuration`. Omit for open-ended (Ctrl-C). |
90
- | `--interval <dur>` | `interval`, default `1s` | Report cadence (`20ms`..`5s`). One report frame per interval. |
91
- | `--connection-count <n>` | `connection-count` (1..255) | **TCP only.** Parallel TCP data connections. centrs opens the negotiated extra connections (join format grounded against RouterOS 7.23.1) so throughput aggregates across streams. **Authenticated (EC-SRP5) sessions stay single-stream** — the post-auth token is not captured — and emit a `routeros/btest-connection-count-single-stream` warning. |
92
- | `--local-udp-tx-size <n>` | `local-udp-tx-size` (28..64000) | **UDP only.** Client→server packet size. |
93
- | `--remote-udp-tx-size <n>` | `remote-udp-tx-size` (28..64000) | **UDP only.** Server→client packet size. |
94
- | `--local-tx-speed <bps>` | `local-tx-speed` | Cap on client→server rate (bits/sec). |
95
- | `--remote-tx-speed <bps>` | `remote-tx-speed` | Cap on server→client rate (bits/sec). |
96
- | `--random-data` | `random-data`, default off | Incompressible payload (defeats link compression; CPU-heavier). |
97
- | `--user` / `--password` | `user` / `password` | Fall back to CDB / `CENTRS_*`. Required when the server has `authenticate=yes`. |
98
- | `--port <n>` | — (default `2000`) | Control port override. |
99
- | `--format <text\|csv\|json\|yaml>` | — (default `text`) | `text` = live human reports; `csv` = streaming CSV records; `json`/`yaml` = final summary envelope. |
100
- | `--csv` | — | Shortcut for `--format csv`. |
101
-
102
- ### `btest server`
103
-
104
- | Flag | RouterOS analog / default | Notes |
105
- | ---- | ------------------------- | ----- |
106
- | `--authenticate[=false]` | `authenticate`, **default `true`** | Require EC-SRP5 auth; `--authenticate=false` accepts anonymous clients. |
107
- | `--user` / `--password` | — | The accepted credential when `authenticate=true` (v1: one user). Falls back to `CENTRS_*`. |
108
- | `--bind <addr>` | **centrs-only** | Listen address; **default `127.0.0.1`**. Use `0.0.0.0` to expose on the LAN. |
109
- | `--port <n>` | — (default `2000`) | Control-connection port. |
110
- | `--allocate-udp-ports-from <n>` | `allocate-udp-ports-from` | Base of the UDP data-port range; default `2001` (just above the TCP/2000 control port). |
111
- | `--max-sessions <n>` | `max-sessions` (1..1000), default `100` | Concurrent test cap; further clients get `routeros/btest-too-many-sessions`. |
112
- | `--duration <dur>` | — | Optional auto-stop; otherwise runs until Ctrl-C. |
113
- | `--format <text\|csv\|json\|yaml>` | — (default `text`) | `text` = human session log; `csv` = streaming CSV records; `json`/`yaml` = final summary envelope. |
114
- | `--csv` | — | Shortcut for `--format csv`. |
83
+ Implemented flags are generated from the CLI metadata into
84
+ [`docs/CLI.md` → btest](../../docs/CLI.md#btest) — one table for both
85
+ subcommands, with `(client)`/`(server)` markers scoping each flag; "did you
86
+ mean?" hints are scoped to the active mode. This file does not duplicate that
87
+ table. Behavior notes the generated reference cannot carry:
88
+
89
+ - Flag names mirror the RouterOS analogs verbatim (`protocol`, `direction`,
90
+ `local-udp-tx-size`, `connection-count`, `authenticate`,
91
+ `allocate-udp-ports-from`, …); centrs-only additions are `--bind`, `--port`,
92
+ the shared `--format`/`--csv`, and the CDB credential flags.
93
+ - `--direction receive` = download (server→client), `transmit` = upload.
94
+ - `--connection-count` is TCP-only; **authenticated (EC-SRP5) sessions stay
95
+ single-stream** — the post-auth token is not captured — and emit a
96
+ `routeros/btest-connection-count-single-stream` warning (see Open
97
+ questions).
98
+ - `--user`/`--password` fall back to CDB / `CENTRS_*`; required when the
99
+ server has `authenticate=yes`. On the server they are the accepted
100
+ credential (v1: one user).
101
+ - Option ranges (`28..64000` UDP sizes, `1..255` connections, `20ms..5s`
102
+ interval, `1..1000` sessions) are validated before any socket opens (see
103
+ Errors).
115
104
 
116
105
  ## How it works
117
106