@tikoci/centrs 0.1.9-next.24 → 0.1.9-next.27
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.
- package/CHANGELOG.md +57 -0
- package/GLOSSARY.txt +9 -1
- package/README.md +28 -16
- package/commands/api/AGENTS.md +3 -2
- package/commands/api/README.md +8 -1
- package/commands/btest/README.md +9 -0
- package/commands/btest/examples.md +7 -0
- package/commands/devices/README.md +72 -3
- package/commands/devices/examples.md +56 -1
- package/commands/execute/README.md +11 -0
- package/commands/explain/README.md +223 -3
- package/commands/explain/examples.md +8 -1
- package/commands/retrieve/README.md +22 -1
- package/commands/retrieve/examples.md +84 -0
- package/commands/transfer/README.md +25 -0
- package/commands/transfer/examples.md +46 -0
- package/docs/CLI.md +11 -3
- package/docs/MATRIX.md +5 -3
- package/docs/errors/cdb/encrypt-failed.md +10 -0
- package/docs/errors/cdb/password-required.md +5 -4
- package/docs/errors/cdb/plaintext-backups.md +11 -0
- package/docs/errors/input/invalid-routeros-path.md +25 -3
- package/package.json +8 -4
- package/src/api-fanout.ts +2 -0
- package/src/api.ts +12 -0
- package/src/btest.ts +7 -0
- package/src/cli/api.ts +18 -1
- package/src/cli/btest.ts +9 -0
- package/src/cli/devices.ts +76 -8
- package/src/cli/execute.ts +9 -0
- package/src/cli/retrieve.ts +9 -0
- package/src/cli/terminal.ts +9 -0
- package/src/cli/transfer.ts +10 -1
- package/src/core/error-catalog.ts +13 -2
- package/src/core/menu-path.ts +89 -0
- package/src/devices.ts +154 -9
- package/src/execute-fanout.ts +2 -0
- package/src/execute.ts +7 -0
- package/src/explain/blocks.ts +2 -2
- package/src/explain/catalog.ts +22 -12
- package/src/explain/pasted-prompt.ts +198 -0
- package/src/explain/pathresolve.ts +2 -2
- package/src/explain/segment.ts +2 -2
- package/src/explain/symbols.ts +2 -2
- package/src/explain/verbsplit.ts +2 -2
- package/src/explain/write.ts +2 -2
- package/src/explain.ts +2 -0
- package/src/index.ts +4 -0
- package/src/protocols/adapter.ts +2 -0
- package/src/protocols/btest-session.ts +10 -30
- package/src/protocols/ec-srp5.ts +19 -10
- package/src/protocols/mac-telnet-console.ts +2 -2
- package/src/resolver/cdb.ts +58 -2
- package/src/resolver/index.ts +1 -0
- package/src/resolver/quickchr-provider.ts +5 -0
- package/src/resolver/selection.ts +11 -0
- package/src/retrieve-fanout.ts +2 -0
- package/src/retrieve.ts +11 -0
- package/src/settings.ts +1 -0
- package/src/terminal.ts +7 -0
- package/src/transfer-fanout.ts +2 -0
- package/src/transfer.ts +79 -14
|
@@ -257,6 +257,22 @@ today's path/verb/args split and script-vs-structured gate:
|
|
|
257
257
|
`/interface/wifi/dashboard` and `/system/dashboard` are `inspect` only: the
|
|
258
258
|
trees carry them, the manual publishes no menu entry for them.
|
|
259
259
|
|
|
260
|
+
**Source refresh (accepted 2026-10-30).** The CLI Reference added
|
|
261
|
+
`compare-versions`, a version-comparison widget page with no `**Type:**`
|
|
262
|
+
entry. `llms.txt` lists it as a page and the sitemap serves it as a category
|
|
263
|
+
URL, so it aborted the parser and would have aborted the orphan-dir check.
|
|
264
|
+
It joins `index` as a hand-audited non-command slug, excluded from both
|
|
265
|
+
inventories. The CLI Reference moves from 1,099 pages / 1,106 entries to
|
|
266
|
+
1,109 / 1,116; the inspect pins and the 636-menu union are unchanged. The
|
|
267
|
+
catalog grows from 1,166 to 1,176 paths: 1,005 `both`, 67 `inspect`, 104
|
|
268
|
+
`published`. It adds 10 paths and removes none, with no kind change and no
|
|
269
|
+
provenance loss. The additions are `/app/settings/remove-certificate`, three
|
|
270
|
+
`/system/resource/hardware` commands (`power-cycle`, `power-reset`,
|
|
271
|
+
`reset-overcurrent`), and `/interface/wifi/easymesh` with five children. The
|
|
272
|
+
three `/cmr/client` gate strings drop `!powerpc`. easymesh is published with
|
|
273
|
+
no gate and is in no CHR tree through 7.26beta3, so the unexplained residue
|
|
274
|
+
grows from `/root/terminal` alone to seven paths.
|
|
275
|
+
|
|
260
276
|
There are real unknowns in how far offline parsing can go (expression
|
|
261
277
|
grammar, scope fidelity vs `:parse`, `[]`-nesting corner cases). Grounding
|
|
262
278
|
those unknowns was **phase 0** (#185); its findings are folded in throughout
|
|
@@ -3007,6 +3023,202 @@ a process boundary. A B5 rename is therefore a visible break here rather than a
|
|
|
3007
3023
|
silent one — which is the point of settling the vocabulary before an external
|
|
3008
3024
|
consumer commits to it.
|
|
3009
3025
|
|
|
3026
|
+
## Topic checks and related live reads (#493, design — no implementation)
|
|
3027
|
+
|
|
3028
|
+
This section is the bounded design #493 asks for. It proposes shapes, ownership,
|
|
3029
|
+
and acceptance gates; **no topic check, special-command rule, or contextual
|
|
3030
|
+
read ships with it**. Each check needs its own accepted/rejected CHR controls
|
|
3031
|
+
and committed evidence before an implementation slice lands, and every new
|
|
3032
|
+
RouterOS semantic claim is grounded then — not here. The three examples below
|
|
3033
|
+
are **SYNTHETIC shape illustrations, not device claims**: they show the envelope
|
|
3034
|
+
a check would produce, with invented content, so reviewers can judge the
|
|
3035
|
+
contract without mistaking illustration for evidence.
|
|
3036
|
+
|
|
3037
|
+
**Ownership: one rule table, three consumers.** Fact and check definitions live
|
|
3038
|
+
in one module beside the analyzer (sibling of `src/explain/write.ts`, which is
|
|
3039
|
+
the precedent: an explain-only inference over the shipped structure). The
|
|
3040
|
+
CLI renders it, the library returns it, and the MCP adapter reshapes the same
|
|
3041
|
+
object — no copies of rule logic per surface. Checks evaluate against the
|
|
3042
|
+
**statement tree** (`structure.statements[]` ids plus `parent`/`owner` links),
|
|
3043
|
+
never against byte containment: a finding anchors to a statement id and its
|
|
3044
|
+
span, so a reordered document moves the finding with its statement.
|
|
3045
|
+
|
|
3046
|
+
**Findings reuse the shipped envelope — no new finding object.** A check emits
|
|
3047
|
+
what explain already emits: an `ExplainDiagnostic` (`code`, `severity`,
|
|
3048
|
+
`message`, `span`, `ev`) for the finding, the `ExplainEvidence` entry it cites
|
|
3049
|
+
(`id`, `source`, `probe`, `basis`, `outcome`, `statementIds`) for how it was
|
|
3050
|
+
decided and which statement it anchors to, and a `tip` (`code`, `message`,
|
|
3051
|
+
`fix`) for the next step. Codes follow `explain/<source>/<slug>`, with the
|
|
3052
|
+
check family as the slug prefix (`topic-…`, `special-command-…`). Topic
|
|
3053
|
+
findings are `info`: like the pasted-prompt tip, advice never moves
|
|
3054
|
+
`data.verdict`, which stays the validity answer. A check that should raise the
|
|
3055
|
+
verdict is a separate, explicitly decided change, not a default. Certainty is
|
|
3056
|
+
the cited evidence's `basis` — `heuristic` for a ratified rule over the
|
|
3057
|
+
offline structure (the `containsWrite` precedent), `derived` once it rests on
|
|
3058
|
+
live responses — so how sure a finding is reads from the same field as every
|
|
3059
|
+
other explain fact.
|
|
3060
|
+
|
|
3061
|
+
**What is decided where.** Source-only: structural facts the canonicalizer
|
|
3062
|
+
already proves (unresolved statements, write-shape, symbol bindings). Live
|
|
3063
|
+
schema: what a menu accepts (argument names, enums) — only `--schema` /
|
|
3064
|
+
`--complete` against a named router, never a bundled snapshot (decision 3).
|
|
3065
|
+
Device observations: current state (`print` rows) — only through an explicitly
|
|
3066
|
+
requested contextual read (below). Partial script input is never treated as
|
|
3067
|
+
final device state, and a finding never grants execution permission: advice
|
|
3068
|
+
and the execution gate are separate answers, and a lost read response is
|
|
3069
|
+
reported as a failed read, never as a successful execution.
|
|
3070
|
+
|
|
3071
|
+
**Contextual reads are opt-in and bounded.** The default offline entry stays
|
|
3072
|
+
network-free; naming a router does not authorize reads. A contextual read
|
|
3073
|
+
fires only under an explicit flag (name TBD, e.g. `--with-print`), one bounded
|
|
3074
|
+
`print` per confidently resolved menu, projected and truncated, each reported
|
|
3075
|
+
independently with its read command, time, projection, and truncation — so an
|
|
3076
|
+
agent can distinguish current observed state from the proposed script. The
|
|
3077
|
+
target is the envelope's `meta.target`, not a per-read field. Deduplication is by (statement id, resolved path): two
|
|
3078
|
+
statements in one menu share one read, and a failed read fails alone. The
|
|
3079
|
+
read is a validated centrs `retrieve` on the resolved path — never the user's
|
|
3080
|
+
`[find]`, expression, or analyzed script executed to get context.
|
|
3081
|
+
|
|
3082
|
+
**Secrets follow the existing redaction contract.** Related reads ride the
|
|
3083
|
+
redaction paths from #359: secret-bearing menus and verbose file output are
|
|
3084
|
+
high-risk surfaces, so a contextual read defaults to a non-sensitive
|
|
3085
|
+
projection, names its projection explicitly, and never exports key material
|
|
3086
|
+
into advice artifacts or logs. A check whose finding would need a sensitive
|
|
3087
|
+
field states that need in its definition and is refused without an explicit
|
|
3088
|
+
sensitivity opt-in — no automatic sensitive exports.
|
|
3089
|
+
|
|
3090
|
+
**Shape 1 — a grounded topic finding (synthetic illustration).**
|
|
3091
|
+
Input `SYNTHETIC-1` (illustrative only):
|
|
3092
|
+
`/ip service set telnet disabled=no` — a statement the analyzer resolves with
|
|
3093
|
+
id `s0`, span 0–34. A management-plane topic check (definition owned beside
|
|
3094
|
+
the analyzer, evaluated against `s0`) adds one diagnostic, the evidence entry
|
|
3095
|
+
it cites, and a tip; the ids and the check's `probe` name are invented. A
|
|
3096
|
+
partial envelope — only the fields the finding adds or reads are shown:
|
|
3097
|
+
|
|
3098
|
+
```json
|
|
3099
|
+
{
|
|
3100
|
+
"data": {
|
|
3101
|
+
"verdict": "pass",
|
|
3102
|
+
"diagnostics": [
|
|
3103
|
+
{
|
|
3104
|
+
"code": "explain/canonicalizer/topic-plaintext-management-service",
|
|
3105
|
+
"severity": "info",
|
|
3106
|
+
"message": "This enables telnet, a plaintext management service.",
|
|
3107
|
+
"span": { "start": 0, "end": 34 },
|
|
3108
|
+
"ev": "e10"
|
|
3109
|
+
}
|
|
3110
|
+
],
|
|
3111
|
+
"evidence": [
|
|
3112
|
+
{
|
|
3113
|
+
"id": "e10",
|
|
3114
|
+
"source": "canonicalizer",
|
|
3115
|
+
"probe": "topicPlaintextManagementService",
|
|
3116
|
+
"basis": "heuristic",
|
|
3117
|
+
"outcome": "ok",
|
|
3118
|
+
"statementIds": ["s0"]
|
|
3119
|
+
}
|
|
3120
|
+
]
|
|
3121
|
+
},
|
|
3122
|
+
"tips": [
|
|
3123
|
+
{
|
|
3124
|
+
"code": "tip/explain-topic-plaintext-management-service",
|
|
3125
|
+
"message": "Telnet sends credentials in clear text.",
|
|
3126
|
+
"fix": "Disable the plaintext service or restrict it to a management address list."
|
|
3127
|
+
}
|
|
3128
|
+
]
|
|
3129
|
+
}
|
|
3130
|
+
```
|
|
3131
|
+
|
|
3132
|
+
The verdict stays `pass`; probe outcome and `runtimeAcceptance` are untouched —
|
|
3133
|
+
the finding does not claim the router accepted anything.
|
|
3134
|
+
|
|
3135
|
+
**Shape 2 — a special-command fact (synthetic illustration).** #363 records
|
|
3136
|
+
the real cases (deferred there, not duplicated here); the shape is Shape 1's,
|
|
3137
|
+
with the `special-command-` slug family and a consequence, not a
|
|
3138
|
+
configuration judgement. Illustrative only:
|
|
3139
|
+
|
|
3140
|
+
```json
|
|
3141
|
+
{
|
|
3142
|
+
"code": "explain/canonicalizer/special-command-connection-loss",
|
|
3143
|
+
"severity": "info",
|
|
3144
|
+
"message": "This command can drop the session that runs it.",
|
|
3145
|
+
"span": { "start": 64, "end": 79 },
|
|
3146
|
+
"ev": "e11"
|
|
3147
|
+
}
|
|
3148
|
+
```
|
|
3149
|
+
|
|
3150
|
+
`e11` cites `statementIds: ["s2"]`, and the paired tip's `fix` says to run it
|
|
3151
|
+
from a path that survives the change.
|
|
3152
|
+
|
|
3153
|
+
No isolated device-mode table ships first (the #363 deferral stands); a
|
|
3154
|
+
special-command fact names its consequence and its evidence, nothing more.
|
|
3155
|
+
|
|
3156
|
+
**Shape 3 — an explicitly requested contextual read (synthetic illustration).**
|
|
3157
|
+
Input (illustrative): `/ip address add address=198.51.100.1/32 interface=ether1`
|
|
3158
|
+
with an explicit `--with-print`-style opt-in. The read follows the live
|
|
3159
|
+
argument-name precedent (`data.live.arguments[]` cites a `live-inspect`
|
|
3160
|
+
evidence entry): one evidence entry per read, and the rows in `data.live`
|
|
3161
|
+
citing it, independent of the analysis verdict. A partial `data` object —
|
|
3162
|
+
`evidence` is `data.evidence`, `live` is `data.live`:
|
|
3163
|
+
|
|
3164
|
+
```json
|
|
3165
|
+
{
|
|
3166
|
+
"evidence": [
|
|
3167
|
+
{
|
|
3168
|
+
"id": "e12",
|
|
3169
|
+
"source": "live-read",
|
|
3170
|
+
"probe": "print",
|
|
3171
|
+
"basis": "direct",
|
|
3172
|
+
"outcome": "ok",
|
|
3173
|
+
"version": "SYNTHETIC-version",
|
|
3174
|
+
"observedAt": "SYNTHETIC-timestamp",
|
|
3175
|
+
"statementIds": ["s0"],
|
|
3176
|
+
"path": "/ip/address",
|
|
3177
|
+
"projection": ["address", "interface", "disabled"],
|
|
3178
|
+
"truncation": { "returned": 3, "total": 12 }
|
|
3179
|
+
}
|
|
3180
|
+
],
|
|
3181
|
+
"live": {
|
|
3182
|
+
"reads": [
|
|
3183
|
+
{
|
|
3184
|
+
"ev": "e12",
|
|
3185
|
+
"path": "/ip/address",
|
|
3186
|
+
"statementIds": ["s0"],
|
|
3187
|
+
"rows": [
|
|
3188
|
+
{ "address": "192.0.2.1/24", "interface": "ether1", "disabled": "false" },
|
|
3189
|
+
{ "address": "198.51.100.2/24", "interface": "ether2", "disabled": "false" },
|
|
3190
|
+
{ "address": "203.0.113.1/24", "interface": "bridge", "disabled": "true" }
|
|
3191
|
+
]
|
|
3192
|
+
}
|
|
3193
|
+
]
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
```
|
|
3197
|
+
|
|
3198
|
+
The target is the envelope's existing `meta.target`, not a per-read field.
|
|
3199
|
+
Three additions are proposed, not shipped: the `live-read` evidence source,
|
|
3200
|
+
the `projection` and `truncation` evidence fields, and `data.live.reads[]`.
|
|
3201
|
+
Originating statement and path, time, projection, and truncation are all
|
|
3202
|
+
present, so observed state cannot be mistaken for the proposed script. A
|
|
3203
|
+
failed read reports its `outcome` with `reason`/`errorCode` and fails alone.
|
|
3204
|
+
|
|
3205
|
+
**Acceptance controls and dependencies before implementation slices.**
|
|
3206
|
+
|
|
3207
|
+
- Each check ships with accepted/rejected CHR controls and committed evidence;
|
|
3208
|
+
synthetic shapes above are replaced by measured rows, never promoted as-is.
|
|
3209
|
+
- Controls assert statement-id anchoring (finding moves with its statement),
|
|
3210
|
+
verdict independence (a finding leaves `data.verdict` unchanged), the cited
|
|
3211
|
+
evidence's `basis`, and read independence (one failed read does not fail
|
|
3212
|
+
the analysis or sibling reads).
|
|
3213
|
+
- A check that needs a sensitive field is refused without the sensitivity
|
|
3214
|
+
opt-in; the redaction contract is asserted, not assumed.
|
|
3215
|
+
- Dependencies: published offline readings stay honest (#211, #350, #351,
|
|
3216
|
+
#358, #371); live path/argument/probe facts come from #236; live value
|
|
3217
|
+
types are distinct evidence (#225); the MCP result migration is separate
|
|
3218
|
+
(#223); text stdout/stderr routing is unchanged (#477); device-mode
|
|
3219
|
+
provisioning stays in #95. No new plugin DSL or framework is preselected —
|
|
3220
|
+
existing `warnings`/`tips` and explain diagnostics are reused where they fit.
|
|
3221
|
+
|
|
3010
3222
|
## Non-goals
|
|
3011
3223
|
|
|
3012
3224
|
- No LSP server in centrs — the LSP is a consumer the library must support,
|
|
@@ -3272,14 +3484,22 @@ a missing mandatory value, and a name collision. That was verified rather than
|
|
|
3272
3484
|
assumed — each blocker statement was replayed through `:put [:parse …]` and
|
|
3273
3485
|
`/console/inspect request=highlight` on both versions and **accepted every
|
|
3274
3486
|
time**, with controls (`no-such-attr=`, an unterminated string, a nonexistent
|
|
3275
|
-
menu) correctly rejected
|
|
3487
|
+
menu) correctly rejected by `:parse`. `highlight` is the weaker of the two: it
|
|
3488
|
+
marks only the nonexistent menu with an `error` class, reading the bad
|
|
3489
|
+
attribute as `obj-inactive` and the unterminated string as ordinary tokens, so
|
|
3490
|
+
the controls' rejection rests on `:parse`. So these results carry **no** implication about
|
|
3276
3491
|
whether `explain` can parse the forms, and they do not license claims about the
|
|
3277
3492
|
export styles in general: the captures come from one synthetic configuration on
|
|
3278
3493
|
CHR, which has a single NIC and no switch or wireless hardware, so its defconf
|
|
3279
3494
|
and its available menus differ from any real device's. Treat the provenance
|
|
3280
3495
|
label as **L1-emitted, L2-refuted for these captures on CHR**. Harness and
|
|
3281
|
-
per-document results:
|
|
3282
|
-
|
|
3496
|
+
per-document results: the q203 import-verify lab (history — see #203; its
|
|
3497
|
+
scripts and corpora were never promoted, so the counts above rest on those
|
|
3498
|
+
lab captures). Validity check: `bun run explain:probe:import-blocker-validity`
|
|
3499
|
+
(promoted from the q203 lab for #272; replays the four blockers plus the
|
|
3500
|
+
three rejecting controls through `:parse` and `highlight` on a disposable
|
|
3501
|
+
CHR per version; re-run on 7.23.2 and 7.24rc2 for #568, 6/6 and 7/7 rows
|
|
3502
|
+
agree).
|
|
3283
3503
|
|
|
3284
3504
|
**Non-blocking / deferred (not ratification-gating):** Q5 expression depth, Q7
|
|
3285
3505
|
tokenizer corners, Q9 potential-command taxonomy, Q12 span-vocabulary draft —
|
|
@@ -888,7 +888,14 @@ centrs explain '[admin@MikroTik] > /ip address print' --json
|
|
|
888
888
|
`data.verdict` is `fail` with `explain/canonicalizer/invalid-at-sign` at the
|
|
889
889
|
`@`. The device answers `syntax error (line 1 column 7)`, the same byte. This
|
|
890
890
|
is a pasted console prompt. `:put a@b` and `comment=a@b` pass, because a `@` is
|
|
891
|
-
a value byte in an argument.
|
|
891
|
+
a value byte in an argument. The envelope also carries
|
|
892
|
+
`tip/explain-pasted-prompt` offering the stripped `/ip address print` as
|
|
893
|
+
repair text (never applied). A `/menu>` prompt such as
|
|
894
|
+
`/interface/lte> monitor lte1` keeps a `warn` abstention with the same tip
|
|
895
|
+
shape, offering `monitor lte1` typed at `/interface/lte`. The manual's
|
|
896
|
+
in-menu prompt `[admin@MikroTik] /ip/address> add …` fails the same way and
|
|
897
|
+
offers `add …` typed at `/ip/address`; a HotLock `>>` is stripped with the
|
|
898
|
+
prompt.
|
|
892
899
|
|
|
893
900
|
### 41. A named comparison brace inside a `[find where …]` is rejected
|
|
894
901
|
|
|
@@ -96,6 +96,23 @@ error code, so its reason is `not reached: interrupted` (#537). With
|
|
|
96
96
|
If `--all-attributes` is combined with `--attribute(s)`, fail with
|
|
97
97
|
`usage/conflicting-flags` *before* hitting the network.
|
|
98
98
|
|
|
99
|
+
A read only ever dispatches a `print`/`get`, on every transport and even with
|
|
100
|
+
`--validate=false`: that flag skips command validation, not the read/write
|
|
101
|
+
split (#586). Each path segment must be a RouterOS menu name — letters,
|
|
102
|
+
digits and hyphens (case folds), the grammar of every dir and command in the
|
|
103
|
+
7.9–7.26 tree — or the read fails with `input/invalid-routeros-path` before
|
|
104
|
+
resolution. Before this check, a `#` or `?` cut the appended `/print` off the
|
|
105
|
+
REST URL, so `retrieve '/system/reboot#' --no-validate --attribute uptime`
|
|
106
|
+
posted `/rest/system/reboot` and rebooted CHR 7.24.5. A well-formed command
|
|
107
|
+
path is safe: RouterOS answers `POST /rest/system/reboot/print` (and a `GET` of
|
|
108
|
+
a command) with "no such command", and native API answers
|
|
109
|
+
`/system/reboot/print` the same way (CHR 7.24.5). Under the REST adapter, a
|
|
110
|
+
second guard refuses any request path containing `?`, `#`, `\`, a control
|
|
111
|
+
character, or a `.`/`..` segment (including the percent-encoded `%2e%2e` and
|
|
112
|
+
`.%2E` forms, which the URL parser also collapses) for every command. RouterOS
|
|
113
|
+
does not decode percent-escapes in REST paths, so nothing else changes the
|
|
114
|
+
target.
|
|
115
|
+
|
|
99
116
|
SNMP validation is separate from RouterOS `/console/inspect`: OIDs and MIB
|
|
100
117
|
names must resolve through the MIB cache before any SNMP request is sent.
|
|
101
118
|
|
|
@@ -524,7 +541,11 @@ stays the single-target envelope; a selector flag or a second positional target
|
|
|
524
541
|
switches to fan-out. `--quickchr <name>` (the named-live-provider) behaves like a
|
|
525
542
|
lone positional: one flag stays single-target, repeating it fans out, and mixing
|
|
526
543
|
it with positionals or CDB selectors is `usage/conflicting-flags` (constitution:
|
|
527
|
-
resolution providers).
|
|
544
|
+
resolution providers). When several CDB records share one `<router>` (the
|
|
545
|
+
same address saved under two users), `--match user=<name>` (or
|
|
546
|
+
`target=<addr>`, or a record type) picks one, as on `devices show`; it is
|
|
547
|
+
single-target only (see
|
|
548
|
+
[devices → Ambiguity](../devices/README.md#ambiguity), #583). The `--host` **flag** with no `<router>` positional
|
|
528
549
|
(`retrieve --host http://192.0.2.10:8080 /system/resource`) is a single target
|
|
529
550
|
too: no CDB lookup, so auth comes from flags, env or config. `CENTRS_HOST` never
|
|
530
551
|
stands in for `<router>`.
|
|
@@ -146,6 +146,43 @@ centrs retrieve $R /system/license --username $U --password $P --json
|
|
|
146
146
|
centrs retrieve $R /system/resource --attribute bogus --username $U --password $P --json
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
+
## Read paths dispatch only reads (#586)
|
|
150
|
+
|
|
151
|
+
A read sends only a `print`/`get`, even with `--validate=false`. Covered by
|
|
152
|
+
the CHR test `test/integration/read-path-guard.test.ts` (RP1–RP2, over REST
|
|
153
|
+
and native-api, for `retrieve` and `api`; the router's uptime must never go
|
|
154
|
+
backwards) and the offline grammar and REST-guard tests in
|
|
155
|
+
`test/unit/menu-path.test.ts` (RP3).
|
|
156
|
+
|
|
157
|
+
### RP1. A `#` or `?` in the path is refused before dispatch
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
centrs retrieve $R '/system/reboot#' --no-validate --attribute uptime --username $U --password $P --json
|
|
161
|
+
centrs api $R '/system/reboot?' --validate=false --attribute uptime --username $U --password $P --json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Envelope: `ok: false`, `error.code=input/invalid-routeros-path`,
|
|
165
|
+
`error.context.invalidSegments` names the bad segment, and nothing is sent.
|
|
166
|
+
Before #586 the first line posted `/rest/system/reboot` and rebooted the CHR.
|
|
167
|
+
|
|
168
|
+
### RP2. A well-formed command path is RouterOS's refusal, not a dispatch
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
centrs retrieve $R /system/reboot --no-validate --username $U --password $P --json
|
|
172
|
+
centrs retrieve $R /system/reset-configuration --no-validate --attribute uptime --username $U --password $P --json
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Envelope: `ok: false` with a `routeros/*` code. RouterOS answers
|
|
176
|
+
`<command>/print` and a `GET` of a command with "no such command", so the
|
|
177
|
+
appended `/print` is the guarantee once the path cannot cut it off.
|
|
178
|
+
|
|
179
|
+
### RP3. Menu-name grammar and the REST URL guard
|
|
180
|
+
|
|
181
|
+
Path segments are letters, digits and hyphens (case folds:
|
|
182
|
+
`/IP/Address` is accepted). Under every REST caller, a request path with
|
|
183
|
+
`?`, `#`, `\`, a control character or a `.`/`..` segment (including
|
|
184
|
+
`%2e%2e` and `.%2E`) fails `input/invalid-routeros-path` before `fetch`.
|
|
185
|
+
|
|
149
186
|
## Transport / error contract
|
|
150
187
|
|
|
151
188
|
### 12. Bad credentials
|
|
@@ -821,3 +858,50 @@ centrs retrieve $R /interface --until name=missing --wait 500ms --username $U --
|
|
|
821
858
|
One envelope goes to stdout, with `stopReason: "deadline-elapsed"`; exit 1.
|
|
822
859
|
No intermediate sample frames are printed. Unit anchors cover slow validation,
|
|
823
860
|
unfinished reads, cancellation and narrow retry classification.
|
|
861
|
+
|
|
862
|
+
## Record selection (`--match`, #583)
|
|
863
|
+
|
|
864
|
+
Covered by the named tests `MA1`–`MA3` in `test/integration/cdb-match.test.ts`
|
|
865
|
+
on disposable CHR; MA4 is the offline contract in the `MA4.` tests of
|
|
866
|
+
`test/unit/cdb-match.test.ts`, which drive `retrieve`/`api` through the CLI. `$CDB` holds
|
|
867
|
+
two `ipAdmin` records for the same target `$R`: user `$U` with a deliberately
|
|
868
|
+
wrong password, and a `read`-group user `reader` with its real password.
|
|
869
|
+
The same selector works on `execute`, `api`, `transfer`, `terminal` and
|
|
870
|
+
`btest client`.
|
|
871
|
+
|
|
872
|
+
### MA1. Two records for one target are ambiguous
|
|
873
|
+
|
|
874
|
+
```bash
|
|
875
|
+
centrs retrieve $R /system/identity --cdb-file $CDB --json
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Envelope: `ok: false`, `error.code=identity/ambiguous`, `context.matches`
|
|
879
|
+
lists both records, and the remediation names `--match user=<name>`.
|
|
880
|
+
|
|
881
|
+
### MA2. `--match user=` picks the record and its credentials
|
|
882
|
+
|
|
883
|
+
```bash
|
|
884
|
+
centrs retrieve $R /system/identity --cdb-file $CDB --match user=reader --json
|
|
885
|
+
centrs execute $R /system/identity/print --cdb-file $CDB --match user=reader --json
|
|
886
|
+
centrs api $R system/identity --cdb-file $CDB --match user=reader --json
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
Each is `ok: true` with `meta.target.recordIndex=1`. With
|
|
890
|
+
`--match user=$U` instead, each fails `transport/auth-failed`: the decoy's
|
|
891
|
+
wrong password went on the wire, so the selection is real.
|
|
892
|
+
|
|
893
|
+
### MA3. A selector that matches nothing
|
|
894
|
+
|
|
895
|
+
```bash
|
|
896
|
+
centrs retrieve $R /system/identity --cdb-file $CDB --match user=nobody --json
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
Envelope: `ok: false`, `error.code=identity/no-match`.
|
|
900
|
+
|
|
901
|
+
### MA4. `--match` is single-target and never ignored
|
|
902
|
+
|
|
903
|
+
With a fan-out selection (`--group`, `--where`, `--all`, several
|
|
904
|
+
positionals) or `--quickchr`, `--match` is `usage/conflicting-flags`. When no
|
|
905
|
+
CDB record is in play (no CDB file, an unregistered literal host, a
|
|
906
|
+
`__default__`-only fallback) it is `identity/no-match`, never a silent
|
|
907
|
+
literal-host read.
|
|
@@ -118,6 +118,31 @@ past 60 KB) rather than quietly switching to sftp. `scp`, `fetch`, and `ftp` are
|
|
|
118
118
|
explicit-only; `ftp` additionally requires `ALLOW_UNSAFE_PROTOCOLS=ftp` because
|
|
119
119
|
it is cleartext.
|
|
120
120
|
|
|
121
|
+
The method resolves `--via` → `CENTRS_TRANSFER_VIA` → the CDB record's `via=`
|
|
122
|
+
preference → config → auto ([constitution: settings](../../docs/CONSTITUTION.md)).
|
|
123
|
+
A record's `via=` names a *transport* (shared with `execute`), so transfer maps
|
|
124
|
+
it to a method (#591):
|
|
125
|
+
|
|
126
|
+
| CDB / `--via` transport name | Transfer method |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `ssh` | `sftp` |
|
|
129
|
+
| `rest-api` (also `--via rest`) | `rest` |
|
|
130
|
+
| `native-api` (also `--via native`) | `native` |
|
|
131
|
+
| `mac-telnet`, `romon`, `winbox-terminal`, `snmp`, `mndp`, `btest` | none — `settings/invalid-via` |
|
|
132
|
+
|
|
133
|
+
`--via ssh` (and `CENTRS_TRANSFER_VIA=ssh`) is accepted as an alias of
|
|
134
|
+
`sftp`. A record whose `via=` cannot carry files fails `settings/invalid-via`
|
|
135
|
+
naming the record, rather than being ignored; pass `--via sftp|rest|native`
|
|
136
|
+
for that transfer. A record `via=` holds a transport id: method names
|
|
137
|
+
(`sftp`, `rest`, `ftp`) and unknown values are rejected when the record is
|
|
138
|
+
read (`cdb/invalid-option` warning) and leave the method on auto. A record
|
|
139
|
+
preference is a setting, not an auto hop: a `via=rest-api` record uploading
|
|
140
|
+
more than 60 KB fails `transport/unsupported-operation` like a pinned
|
|
141
|
+
`--via rest` does, and the remediation names the record. The endpoint and
|
|
142
|
+
credentials still come from the same record; the mapping never selects a
|
|
143
|
+
different one. A record's `port=` travels with a `--via` pin, so if the
|
|
144
|
+
record's port belongs to another service, point `--port` at the pinned one.
|
|
145
|
+
|
|
121
146
|
**File contents are bytes on `rest`/`native`.** RouterOS carries `/file`
|
|
122
147
|
contents as raw bytes on both wires — REST puts bytes 0x80–0xff unescaped in the
|
|
123
148
|
JSON string, so the body is not UTF-8 — and stores the UTF-8 encoding of every
|
|
@@ -569,3 +569,49 @@ list succeeds over sftp: `meta.via=sftp`, `meta.target.source.kind=provider`,
|
|
|
569
569
|
handed over) carried as a provider-sourced `sshKey`. Otherwise the envelope is
|
|
570
570
|
`ok: false` with `error.code=quickchr/unsupported-via` and exit code 1 — a
|
|
571
571
|
typed gate, **never** a password prompt and never a silent fallback to REST.
|
|
572
|
+
|
|
573
|
+
## CDB record `via=` (#591)
|
|
574
|
+
|
|
575
|
+
A record's `via=` names a transport, shared with `execute`; transfer maps it
|
|
576
|
+
to a method (`ssh`→`sftp`, `rest-api`→`rest`, `native-api`→`native`). `$CDB`
|
|
577
|
+
holds one record for `127.0.0.1` whose comment is
|
|
578
|
+
`via=ssh port=$SSH_PORT ssh-key=$KEY insecure=true`; the key is imported as
|
|
579
|
+
in the sftp section. C1–C2 are covered by
|
|
580
|
+
`test/integration/transfer-cdb-via.test.ts`; C3–C4 are offline error
|
|
581
|
+
contracts covered by `test/unit/transfer.test.ts`
|
|
582
|
+
(`resolveTransferRequest takes the CDB record's via=`).
|
|
583
|
+
|
|
584
|
+
### C1. A `via=ssh` record runs execute over ssh and transfer over sftp
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
centrs execute 127.0.0.1 --cdb-file $CDB /system/identity/print --json
|
|
588
|
+
centrs transfer 127.0.0.1 --cdb-file $CDB upload $SRC centrs-591.txt --json
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Both `ok: true` with `meta.via="ssh"`, and no `--via` on either. The
|
|
592
|
+
transfer's `meta.settings.via` is `{kind: "comment-kv", key: "record:0:via"}`.
|
|
593
|
+
|
|
594
|
+
### C2. Download from the same record, with and without `--via sftp`
|
|
595
|
+
|
|
596
|
+
```bash
|
|
597
|
+
centrs transfer 127.0.0.1 --cdb-file $CDB download centrs-591.txt $TMP/c2-auto.txt --json
|
|
598
|
+
centrs transfer 127.0.0.1 --cdb-file $CDB --via sftp download centrs-591.txt $TMP/c2-sftp.txt --json
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Both `ok: true`, `meta.via="ssh"`; each file is byte-identical to `$SRC`.
|
|
602
|
+
`--via ssh` is accepted as the same method as `--via sftp`.
|
|
603
|
+
|
|
604
|
+
### C3. A record transport that cannot carry files
|
|
605
|
+
|
|
606
|
+
A record with `via=mac-telnet` (or `romon`, `snmp`, `winbox-terminal`,
|
|
607
|
+
`mndp`, `btest`) and no `--via`: `ok: false`, `error.code=settings/invalid-via`,
|
|
608
|
+
`error.context.source="record:N:via"`, and the remediation suggests
|
|
609
|
+
`--via sftp`, `--via rest` or `--via native`. An explicit `--via` wins over
|
|
610
|
+
the record and the transfer proceeds.
|
|
611
|
+
|
|
612
|
+
### C4. A `via=rest-api` record does not auto-hop a large upload
|
|
613
|
+
|
|
614
|
+
A `via=rest-api` record uploading more than 60 KB with no `--via`:
|
|
615
|
+
`ok: false`, `error.code=transport/unsupported-operation`, nothing sent. The
|
|
616
|
+
remediation names `record:N:via` and suggests `--via sftp`, matching a
|
|
617
|
+
pinned `--via rest`.
|
package/docs/CLI.md
CHANGED
|
@@ -64,6 +64,7 @@ Usage: centrs retrieve <target> <routeros-path> [flags] | centrs retrieve <targe
|
|
|
64
64
|
| `--max-results` | `<bytes>` | Fail instead of printing output larger than the given byte budget. |
|
|
65
65
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
66
66
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
67
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
67
68
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
68
69
|
| `--validate / --no-validate` | | Enable or disable inspect-backed preflight validation. |
|
|
69
70
|
| `--verbose` | | Show resolved setting sources in text output, on stderr. |
|
|
@@ -97,6 +98,7 @@ Usage: centrs execute <target> <command> [flags] | centrs execute <target...> --
|
|
|
97
98
|
| `--insecure` | | Disable SSH host-key verification (`--via ssh`: accepts changed/impersonated keys, not just new) or accept a self-signed `api-ssl` TLS cert. Default verifies. |
|
|
98
99
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
99
100
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
101
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
100
102
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
101
103
|
| `--timeout` | `<duration>` | Per-request timeout (for REST, max 60s). |
|
|
102
104
|
| `--validate / --no-validate` | | Run RouterOS :parse and /console/inspect validation before execution (default true; `--validate=false` also accepted). |
|
|
@@ -145,6 +147,7 @@ Usage: centrs api <router> <endpoint> [flags] | centrs api --host <host|url> <en
|
|
|
145
147
|
| `--insecure` | | Accept a self-signed `api-ssl`/REST TLS cert. Default verifies. |
|
|
146
148
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
147
149
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
150
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
148
151
|
| `--resolve` | <none\|arp> | Resolve a MAC-address target to an IP via the host ARP cache (default none). |
|
|
149
152
|
| `--timeout` | `<duration>` | Per-request timeout (for REST, max 60s). |
|
|
150
153
|
| `--validate / --no-validate` | | Run `/console/inspect` validation before the request (default true; `--validate=false` also accepted). |
|
|
@@ -162,7 +165,7 @@ Usage: centrs transfer <router>|--host <host> upload <local> [remote] | download
|
|
|
162
165
|
|
|
163
166
|
| Flag | Value | Description |
|
|
164
167
|
| ---- | ----- | ----------- |
|
|
165
|
-
| `--via` | `<method>` | Pin the method: rest, native, or sftp (large transfers); scp/fetch/ftp are not built yet. Auto picks the cheapest by size/direction. |
|
|
168
|
+
| `--via` | `<method>` | Pin the method: rest, native, or sftp (large transfers; `ssh` is an alias); scp/fetch/ftp are not built yet. Auto picks the cheapest by size/direction, after a CDB record's `via=` (ssh→sftp). |
|
|
166
169
|
| `--ssh-key` | `<path>` | sftp only: explicit private-key path. Falls back to CENTRS_SSH_KEY / the ssh-agent. |
|
|
167
170
|
| `--insecure` | | Accept a self-signed TLS cert (https/api-ssl) or a new SSH host key. Default verifies. |
|
|
168
171
|
| `--force / --overwrite` | | Skip the existing-destination check on the device for `upload`/`mkdir`/`copy` (`upload` then overwrites). Default refuses an existing target. `download` always replaces the local file. |
|
|
@@ -189,6 +192,7 @@ Usage: centrs transfer <router>|--host <host> upload <local> [remote] | download
|
|
|
189
192
|
| `--yaml` | | Shortcut for --format yaml. |
|
|
190
193
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
191
194
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
195
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
192
196
|
| `--validate / --no-validate` | | Enable or disable the existence-probe preflight. |
|
|
193
197
|
| `--verbose` | | Show the resolved transport in text output, on stderr. |
|
|
194
198
|
|
|
@@ -212,6 +216,7 @@ Usage: centrs terminal <router> [flags] | centrs terminal --host <host> [flags]
|
|
|
212
216
|
| `--insecure` | | `--via ssh`: disable host-key verification (accepts changed/impersonated keys). Default verifies. |
|
|
213
217
|
| `--cdb-file` | `<path>` | Read target credentials from a WinBox CDB file. |
|
|
214
218
|
| `--cdb-password` | `<password>` | Decrypt an encrypted WinBox CDB file. |
|
|
219
|
+
| `--match` | <user=name\|target=addr\|record-type> | Pick one of several CDB records saved for `<router>` (as `devices show --match`); single target only. |
|
|
215
220
|
| `--resolve` | <none\|arp> | `--via ssh` only: turn a MAC target into an IP. CDB-first; `arp` opts into the host ARP cache (default none). The mac-telnet default ignores it. |
|
|
216
221
|
| `--format` | <text\|json\|yaml> | Error-envelope format on failure. --json / --yaml shortcuts. |
|
|
217
222
|
| `--json` | | Shortcut for --format json. |
|
|
@@ -258,13 +263,15 @@ Usage: centrs explain [<router>] '<input>' [flags]
|
|
|
258
263
|
Inspect and mutate the CDB-backed device registry. `devices` is the only command that writes the CDB.
|
|
259
264
|
|
|
260
265
|
```text
|
|
261
|
-
Usage: centrs devices <init|list|show|groups|add|edit|set|remove> [args] [flags]
|
|
266
|
+
Usage: centrs devices <init|encrypt|list|show|groups|add|edit|set|remove> [args] [flags]
|
|
262
267
|
```
|
|
263
268
|
|
|
264
269
|
| Flag | Value | Description |
|
|
265
270
|
| ---- | ----- | ----------- |
|
|
271
|
+
| `--encrypt` | | `init` only — require encrypted creation with a non-empty CDB password; refuse an existing open CDB. |
|
|
272
|
+
| `--no-backup` | | `encrypt` only — skip the plaintext pre-conversion snapshot. Without that snapshot, a forgotten password cannot be recovered. |
|
|
266
273
|
| `--cdb-file` | `<path>` | Override the resolved CDB path (default `~/.config/tikoci/winbox.cdb`). |
|
|
267
|
-
| `--cdb-password` | `<secret>` | Decrypt an encrypted CDB. Falls back to `CENTRS_CDB_PASSWORD`. Also used to re-encrypt on writes. |
|
|
274
|
+
| `--cdb-password` | `<secret>` | Decrypt an encrypted CDB. Falls back to `CENTRS_CDB_PASSWORD`. Also used for `init --encrypt`, `encrypt`, and to re-encrypt on writes. |
|
|
268
275
|
| `--via` | `<protocol>` | Resolve and report the protocol source for provenance examples; no network IO is performed. |
|
|
269
276
|
| `--group` | `<name>` | `list` filter / `add`,`edit` first-class group field for the target entry. |
|
|
270
277
|
| `--where` | `<attr=value>` | `list` only — repeatable device-class filter over CDB facts (AND-combined), e.g. `--where lat=37.7749`. |
|
|
@@ -340,6 +347,7 @@ Usage: centrs btest <client <router>|server> [flags]
|
|
|
340
347
|
| `--port` | `<port>` | Control port. Default 2000. |
|
|
341
348
|
| `--cdb-file` | `<path>` | (client) Read target credentials from a WinBox CDB file. |
|
|
342
349
|
| `--cdb-password` | `<password>` | (client) Decrypt an encrypted WinBox CDB file. |
|
|
350
|
+
| `--match` | <user=name\|target=addr\|record-type> | (client) Pick one of several CDB records saved for `<router>` (as `devices show --match`); `btest server` rejects it. |
|
|
343
351
|
| `--format` | <text\|csv\|json\|yaml> | Output format. Default text. |
|
|
344
352
|
| `--csv` | | Shortcut for `--format csv`. |
|
|
345
353
|
|
package/docs/MATRIX.md
CHANGED
|
@@ -189,7 +189,9 @@ Wire, auth, TCP/UDP, and CI caveats live in `commands/btest/README.md` and the
|
|
|
189
189
|
Live `explain` (#236) is `CHR-passed` on REST and native API: the #491
|
|
190
190
|
foundation, #492's describe and completion facets, and #225's live value types,
|
|
191
191
|
with every applicable example green on stable and long-term CHR. Issue #90
|
|
192
|
-
owns the remaining sequencing, including #507 context
|
|
192
|
+
owns the remaining sequencing, including #507's context surface. Its bounded
|
|
193
|
+
submission matrix is grounded (#574); context selection and binding behavior
|
|
194
|
+
remain follow-up work.
|
|
193
195
|
|
|
194
196
|
**First: `explain`** (maintainer decision 2026-10-16 on
|
|
195
197
|
[#90](https://github.com/tikoci/centrs/issues/90)). In order: make the
|
|
@@ -253,8 +255,8 @@ applicable examples pass on CHR via `bun run test:integration`.
|
|
|
253
255
|
|
|
254
256
|
Open offline follow-ups remain explicit but do not reopen the verified baseline.
|
|
255
257
|
Issue #211 made an unlisted absolute bare path abstain (rule F, R14); rule
|
|
256
|
-
P's closed-world reading is still not adopted (#372).
|
|
257
|
-
|
|
258
|
+
P's closed-world reading is still not adopted (#372). The non-authoritative
|
|
259
|
+
scratch citation cleanup is complete (#272, shipped in #568). Issues #350 (the broad
|
|
258
260
|
optional `=`), #351 (a relative submenu brace read as a command operand)
|
|
259
261
|
and #355 (`'` is not a RouterOS token) are complete. A submenu block after a navigation statement
|
|
260
262
|
(`/ip⏎address { … }`) keeps the pre-#351 reading; see
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# `cdb/encrypt-failed`
|
|
2
|
+
|
|
3
|
+
The existing CDB could not be rewritten with encryption.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
Check directory permissions and available disk space, then retry
|
|
8
|
+
`centrs devices encrypt`. Preserve the original file and any timestamped backups
|
|
9
|
+
before recovery. A failed write may already have created a plaintext backup;
|
|
10
|
+
encryption does not protect that backup.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# `cdb/password-required`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A CDB password is required for decryption or explicit encryption.
|
|
4
4
|
|
|
5
5
|
## Fix
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Pass `--cdb-password` or set `CENTRS_CDB_PASSWORD` to the CDB password.
|
|
8
|
+
`devices init --encrypt` and `devices encrypt` require a non-empty password,
|
|
9
|
+
even when the file is currently open. A password alone never implicitly
|
|
10
|
+
converts an existing open CDB; use `devices encrypt` explicitly.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `cdb/plaintext-backups`
|
|
2
|
+
|
|
3
|
+
CDB backups may still contain plaintext credentials after encryption.
|
|
4
|
+
|
|
5
|
+
## Fix
|
|
6
|
+
|
|
7
|
+
Apply your own secure backup and retention policy to the `.bak.*` files beside
|
|
8
|
+
the CDB. `devices encrypt` protects only the active CDB and retains every backup,
|
|
9
|
+
including the plaintext snapshot created during conversion. It does not encrypt
|
|
10
|
+
or delete old copies. This warning also accompanies already-encrypted no-op runs,
|
|
11
|
+
since prior backup protection cannot be inferred from the active file.
|