@wirecat/cli-base 0.40.4 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/docs/standards/README.md +111 -0
- package/docs/standards/accounts.md +148 -0
- package/docs/standards/architecture.md +214 -0
- package/docs/standards/ci.md +159 -0
- package/docs/standards/commands.md +302 -0
- package/docs/standards/docs.md +214 -0
- package/docs/standards/layout.md +128 -0
- package/docs/standards/lifecycle.md +143 -0
- package/docs/standards/mcp.md +207 -0
- package/docs/standards/new-cli.md +111 -0
- package/docs/standards/output.md +256 -0
- package/docs/standards/permissions.md +295 -0
- package/docs/standards/releases.md +191 -0
- package/docs/standards/security.md +260 -0
- package/docs/standards/store.md +177 -0
- package/docs/standards/style.md +119 -0
- package/docs/standards/testing.md +222 -0
- package/docs/standards/toolchain.md +232 -0
- package/package.json +4 -3
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
<!-- cspell:ignore flock lslocks OIDC whatsapp zizmor -->
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
How a CLI is released: `bin/release`, the release lock, version numbers, the release report and trusted publishing.
|
|
8
|
+
|
|
9
|
+
Rule ids on this page are `WC-REL-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
10
|
+
[index](README.md). The changelog a release dates is on the [Documents](docs.md) page. The checks a release runs are
|
|
11
|
+
on the [CI](ci.md) page.
|
|
12
|
+
|
|
13
|
+
## Rules
|
|
14
|
+
|
|
15
|
+
### WC-REL-1 · Release only with `bin/release`, from a clean `main` that matches `origin/main`
|
|
16
|
+
|
|
17
|
+
- **Why:** the workflow publishes `origin/main`, so a local commit or a dirty tree would not ship what was checked.
|
|
18
|
+
- **Enforced by:** `<app>: bin/release`.
|
|
19
|
+
- **Example:** raise `version` in `package.json` and date the pending changelog section in a pull request. Merge it.
|
|
20
|
+
Then run `bin/release` on `main`. It refuses another branch, uncommitted changes and a `main` ahead of
|
|
21
|
+
`origin/main`.
|
|
22
|
+
- **Exceptions:** none.
|
|
23
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
24
|
+
|
|
25
|
+
### WC-REL-2 · `main` changes only through a pull request, also during a release
|
|
26
|
+
|
|
27
|
+
- **Why:** the repository ruleset requires it, and every change to `main` then passes the pull request checks.
|
|
28
|
+
- **Enforced by:** review only.
|
|
29
|
+
- **Example:** when `bin/release` renumbers the version or folds the changelog, it opens a pull request, waits for its
|
|
30
|
+
checks and merges it before it publishes. The merge rule itself is
|
|
31
|
+
[WC-CI-12](ci.md#wc-ci-12--merge-to-main-only-through-a-pull-request-whose-checks-all-passed).
|
|
32
|
+
- **Exceptions:** none.
|
|
33
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
34
|
+
|
|
35
|
+
### WC-REL-3 · `bin/release` takes the machine release lock before it builds or publishes
|
|
36
|
+
|
|
37
|
+
- **Why:** two sessions published at the same time on one machine; one release at a time, across every package.
|
|
38
|
+
- **Enforced by:** `<app>: bin/release`.
|
|
39
|
+
- **Example:**
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
. ./config/wirecat/release-lock.sh
|
|
43
|
+
wirecat_release_lock
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The helper comes from the community presets (`adopt-standards.py --only release-lock`). It holds
|
|
47
|
+
`${XDG_RUNTIME_DIR:-/tmp}/leemour-release.lock` with `flock` on file descriptor 9 until the release ends.
|
|
48
|
+
- **Exceptions:** none. cli-testing still opens the lock with its own `flock` line and moves to the helper.
|
|
49
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** NEED-854.
|
|
50
|
+
|
|
51
|
+
### WC-REL-4 · Never break the release lock; wait for the holder and try again
|
|
52
|
+
|
|
53
|
+
- **Why:** a deleted lock file is a new file, so a second release can then run at the same time as the first.
|
|
54
|
+
- **Enforced by:** review only.
|
|
55
|
+
- **Example:** when the lock is held, the helper names the holder on stderr. Let that release finish, then run
|
|
56
|
+
`bin/release` again. Do not delete the lock file and do not kill the holder.
|
|
57
|
+
- **Exceptions:** none. A missing `lslocks` or an old holder label does not allow a release; only `flock` decides.
|
|
58
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
59
|
+
|
|
60
|
+
### WC-REL-5 · One releaser per version: npm decides which number is free
|
|
61
|
+
|
|
62
|
+
- **Why:** two sessions often prepare the same number; the second must not publish over the first or fail late.
|
|
63
|
+
- **Enforced by:** `tg-cli: bin/release` · `max-cli: bin/release` · `whatsapp-cli: bin/release` ·
|
|
64
|
+
`cli-memo: bin/release`.
|
|
65
|
+
- **Example:** before it publishes, `bin/release` asks npm. When npm already has the version, or a higher one, it
|
|
66
|
+
takes the next free number at the same level (after `x.y.0` the next minor, else the next patch). It lands that
|
|
67
|
+
number through a pull request and publishes it.
|
|
68
|
+
- **Exceptions:** a pre-release version is chosen by hand. drive's and zoom's `bin/release` do not renumber yet: they
|
|
69
|
+
refuse a version npm already has.
|
|
70
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
71
|
+
|
|
72
|
+
### WC-REL-6 · `package.json` holds the version, and the program reads a synced copy
|
|
73
|
+
|
|
74
|
+
- **Why:** a bundled command has no `package.json` to read, so `--version` reads a source file that must not drift.
|
|
75
|
+
- **Enforced by:** `tg-cli: pnpm version:check` · `max-cli: pnpm version:check` · `whatsapp-cli: pnpm version:check`.
|
|
76
|
+
- **Example:** edit only `package.json`, then run `pnpm version:sync` to update `src/version.ts`. `pnpm version:check`
|
|
77
|
+
fails when the two disagree.
|
|
78
|
+
- **Exceptions:** a shared library that prints no version has no `src/version.ts`. Today tg, max and wa have
|
|
79
|
+
`version:check`; memo, drive and zoom do not.
|
|
80
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
81
|
+
|
|
82
|
+
### WC-REL-7 · Versions are `x.y.z`; before 1.0.0 the changelog, not the number, marks a breaking change
|
|
83
|
+
|
|
84
|
+
- **Why:** before 1.0.0 any release may change the API, so a caller reads "Changed — may break callers" before an
|
|
85
|
+
upgrade.
|
|
86
|
+
- **Enforced by:** review only.
|
|
87
|
+
- **Example:** raise the minor (`0.40.4` to `0.41.0`) for a release with new features, and the patch for one with only
|
|
88
|
+
fixes. A breaking change, in either kind of release, has an entry under "Changed — may break callers".
|
|
89
|
+
- **Exceptions:** a pre-release version (`0.41.0-rc.1`) is chosen by hand.
|
|
90
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
91
|
+
|
|
92
|
+
### WC-REL-8 · A messenger CLI waits two hours between releases; a shared library does not wait
|
|
93
|
+
|
|
94
|
+
- **Why:** a broken CLI release must be noticed before the next one lands on top of it. Every CLI waits for a library
|
|
95
|
+
release, so a gap there only delays them.
|
|
96
|
+
- **Enforced by:** `<app>: bin/release`.
|
|
97
|
+
- **Example:** tg-cli, max-cli and whatsapp-cli's `bin/release` refuse to start within two hours of the last version
|
|
98
|
+
on npm. cli-base and cli-core release one after another with no gap.
|
|
99
|
+
- **Exceptions:** memo, drive and zoom have no gap in their `bin/release` today.
|
|
100
|
+
- **Applies to:** messenger CLIs (gap); shared libraries (no gap). **Since:** cli-base 0.41.0. **Rulings:** NEED-854.
|
|
101
|
+
|
|
102
|
+
### WC-REL-9 · A breaking change to a stable shared-library export ships at most once a week
|
|
103
|
+
|
|
104
|
+
- **Why:** each such change makes every CLI change too; batching them keeps the CLIs on one version.
|
|
105
|
+
- **Enforced by:** review only.
|
|
106
|
+
- **Example:** gather breaking export changes into one release a week. Bump the library in every CLI that uses it on
|
|
107
|
+
the same day.
|
|
108
|
+
- **Exceptions:** none.
|
|
109
|
+
- **Applies to:** shared libraries. **Since:** cli-base 0.41.0. **Rulings:** NEED-473.
|
|
110
|
+
|
|
111
|
+
### WC-REL-10 · A messenger CLI is released only with a release report the owner signed for that commit
|
|
112
|
+
|
|
113
|
+
- **Why:** the judgement half of a release (changelog wording, docs against the change, live checks) has no other
|
|
114
|
+
record that it ran for this exact commit.
|
|
115
|
+
- **Enforced by:** `max-cli: bin/release`.
|
|
116
|
+
- **Example:** max-cli's `bin/release` refuses unless the report has `Commit: <HEAD>` and `Signed off: <date>`. When
|
|
117
|
+
`bin/release` only renumbers or folds the changelog, it carries the report to the new commit. A fork passes its
|
|
118
|
+
own report with `bin/release --report <path>`.
|
|
119
|
+
- **Exceptions:** none. Today only max-cli's `bin/release` checks the report. tg-cli and whatsapp-cli have the
|
|
120
|
+
release skill, but their `bin/release` does not check a report yet.
|
|
121
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
122
|
+
|
|
123
|
+
### WC-REL-11 · Publish from GitHub Actions with npm trusted publishing
|
|
124
|
+
|
|
125
|
+
- **Why:** no npm token is stored in GitHub, and npm attaches provenance to the package.
|
|
126
|
+
- **Enforced by:** `release.yml:publish`.
|
|
127
|
+
- **Example:** `bin/release` starts `release.yml` and follows it. Only the `publish` job has the OIDC permission (the
|
|
128
|
+
short-lived identity npm trusts), and it runs no project code: it only runs `npm publish` on the validated file.
|
|
129
|
+
- **Exceptions:** npm trusts the workflow by file name: renaming `release.yml` stops publishing until npm's trusted
|
|
130
|
+
publisher settings are updated.
|
|
131
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
132
|
+
|
|
133
|
+
### WC-REL-12 · Publish only after full validation and the release security checks pass
|
|
134
|
+
|
|
135
|
+
- **Why:** pull request checks are bounded; the full suite and the heavy scanners run before anything reaches npm.
|
|
136
|
+
- **Enforced by:** `release.yml` · `release-checks.yml`.
|
|
137
|
+
- **Example:** `release.yml` runs full validation and `release-checks.yml` (dependency audit, workflow analysis with
|
|
138
|
+
zizmor, CodeQL). Validation packs the package once, and the `build` job checks that file's SHA-256 before it is
|
|
139
|
+
handed to `publish`.
|
|
140
|
+
- **Exceptions:** none.
|
|
141
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
142
|
+
|
|
143
|
+
### WC-REL-13 · Tag `v<version>` only after npm shows that version
|
|
144
|
+
|
|
145
|
+
- **Why:** a tag for a version npm does not have yet marks a release that may still fail.
|
|
146
|
+
- **Enforced by:** `release.yml:tag`.
|
|
147
|
+
- **Example:** the `publish` job waits until npm serves the new version; only then does the `tag` job push
|
|
148
|
+
`v<version>`. In the CLIs, the workflow also creates the GitHub release from that version's changelog section.
|
|
149
|
+
- **Exceptions:** none.
|
|
150
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
151
|
+
|
|
152
|
+
### WC-REL-14 · `bin/release --local` is the fallback, and it never prints the npm token
|
|
153
|
+
|
|
154
|
+
- **Why:** a release must still be possible when GitHub Actions is down, without leaking the token.
|
|
155
|
+
- **Enforced by:** `tg-cli: bin/release` · `max-cli: bin/release` · `whatsapp-cli: bin/release` ·
|
|
156
|
+
`cli-memo: bin/release`.
|
|
157
|
+
- **Example:** `bin/release --local` runs the same checks on this machine and reads the npm token from the keyring
|
|
158
|
+
into the publish command's environment. Neither success nor an error prints it.
|
|
159
|
+
- **Exceptions:** none. drive's and zoom's `bin/release` have no `--local` yet.
|
|
160
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
161
|
+
|
|
162
|
+
### WC-REL-15 · Fold a late pending section into the unpublished version before publishing
|
|
163
|
+
|
|
164
|
+
- **Why:** a pull request merged after the version was dated adds a pending section above it, and that change must
|
|
165
|
+
ship in the version it lands in.
|
|
166
|
+
- **Enforced by:** review only.
|
|
167
|
+
- **Example:** `bin/release` runs `cli-dev changelog fold --land --version <x.y.z>` while npm does not have the version.
|
|
168
|
+
It folds the pending section into the dated one, and merges any repeated `###` heading, through a pull request.
|
|
169
|
+
- **Exceptions:** none.
|
|
170
|
+
- **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
171
|
+
|
|
172
|
+
### WC-REL-16 · The package ships only the files it names
|
|
173
|
+
|
|
174
|
+
- **Why:** a stray file in the package can leak local data and grows every install.
|
|
175
|
+
- **Enforced by:** `tg-cli: pnpm release:check` · `max-cli: pnpm release:check` · `whatsapp-cli: pnpm release:check`.
|
|
176
|
+
- **Example:** `package.json` `files` lists what ships. The CLI's `pnpm release:check` runs `packContents` from
|
|
177
|
+
`@wirecat/cli-base/release` and fails on any other path in `npm pack --dry-run`.
|
|
178
|
+
- **Exceptions:** none. tg, max and wa run this check. memo's `release:check` has no package-contents step, drive and
|
|
179
|
+
zoom have no `release:check`, and zoom checks the packed file in a test instead.
|
|
180
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
181
|
+
|
|
182
|
+
## Sources
|
|
183
|
+
|
|
184
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
185
|
+
holds.
|
|
186
|
+
|
|
187
|
+
- [cli-base `RELEASING.md`](https://github.com/WireCatLabs/cli-base/blob/main/docs/dev/RELEASING.md), the step by step
|
|
188
|
+
for cli-base itself
|
|
189
|
+
- [community standards, "Release locking"](https://github.com/WireCatLabs/community/blob/main/standards/README.md),
|
|
190
|
+
the how-to for installing the lock helper
|
|
191
|
+
- each CLI's `bin/release` and its release skill, which carry out these rules
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
<!-- cspell:words zizmor -->
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
How a CLI keeps secrets and private data safe: secrets never in logs, fixtures or argv, the keyring, error text that
|
|
8
|
+
never repeats input, the scanners, workflow hardening, and how a vulnerability is reported.
|
|
9
|
+
|
|
10
|
+
Two words used on this page:
|
|
11
|
+
|
|
12
|
+
- A **secret** is anything that grants access: a session token, a bot token, an API key, a password, a 2FA code, a
|
|
13
|
+
proxy password.
|
|
14
|
+
- **Private data** is what people wrote or who they are: message text, chat titles, names, phone numbers.
|
|
15
|
+
|
|
16
|
+
Rule ids on this page are `WC-SECR-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
17
|
+
[index](README.md).
|
|
18
|
+
|
|
19
|
+
## Rules
|
|
20
|
+
|
|
21
|
+
### WC-SECR-1 · Private data and secrets never reach a log, a fixture or a committed document
|
|
22
|
+
|
|
23
|
+
Command output is the only place where a message the owner asked to read may appear. A log, a run record, a test
|
|
24
|
+
fixture, a document and a commit message never hold message text, a token or a phone number.
|
|
25
|
+
|
|
26
|
+
- **Why:** a log or a repository is copied and kept far longer than anyone remembers; private data in it is a leak.
|
|
27
|
+
- **Enforced by:** `node-ci.yml:secrets`.
|
|
28
|
+
- **Example:** a log line says `{ "event": "send", "outcome": "sent", "count": 1 }`, not the text that was sent.
|
|
29
|
+
- **Exceptions:** none. The scanners catch secrets of known shapes; message text and names are caught only in review.
|
|
30
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
31
|
+
|
|
32
|
+
### WC-SECR-2 · A log keeps only known attribute keys, and hides content keys
|
|
33
|
+
|
|
34
|
+
A log record writes only the attribute keys in cli-base's list (`LOG_ATTRIBUTE_KEYS`) and the ones a CLI adds by
|
|
35
|
+
name. Any other top-level key is written as `[not allowed]`. A key that names content or a secret (`text`, `title`,
|
|
36
|
+
`token`, `phone`, `chatTitle`, `accessToken` …) is written as `[redacted]`, at any depth.
|
|
37
|
+
|
|
38
|
+
- **Why:** redaction is the second line of defence; the first is never handing a secret to a logger.
|
|
39
|
+
- **Enforced by:** `src/logging.test.ts`.
|
|
40
|
+
- **Example:** `recordLog({ … redact: ["sessionKey"] })` adds a CLI's own secret field to the redacted list.
|
|
41
|
+
- **Exceptions:** none.
|
|
42
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
43
|
+
|
|
44
|
+
### WC-SECR-3 · Merged into [WC-TEST-8](testing.md#wc-test-8--prove-that-content-does-not-leak-with-a-marker-and-visibly-fake-data)
|
|
45
|
+
|
|
46
|
+
### WC-SECR-4 · Secrets come from the environment, then the OS keyring, then a private file
|
|
47
|
+
|
|
48
|
+
cli-base's `Credentials` reads the environment variable first (for CI), then the OS keyring, then a file with mode
|
|
49
|
+
`0600`. The keyring service is the command's name. When the config directory comes from the environment, the service
|
|
50
|
+
name is scoped to that directory (`isolated: true`).
|
|
51
|
+
|
|
52
|
+
- **Why:** the keyring is the safest store on the computer, and a throwaway config directory must not overwrite the
|
|
53
|
+
owner's real keyring entry.
|
|
54
|
+
- **Enforced by:** `src/credentials.test.ts`.
|
|
55
|
+
- **Example:** a bot token is read from `MAX_BOT_TOKEN`, then the keyring entry `bot:<profile>`, then the file.
|
|
56
|
+
- **Exceptions:** `auto` storage tries the keyring and, when it fails, warns once on stderr and uses the file.
|
|
57
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-295.
|
|
58
|
+
|
|
59
|
+
### WC-SECR-5 · A secret is never taken on the command line
|
|
60
|
+
|
|
61
|
+
A secret is typed at a terminal, without echo, or piped on stdin. A secret given on the command line is refused with
|
|
62
|
+
`validation_error`. It is stored only after the remote side accepted it.
|
|
63
|
+
|
|
64
|
+
- **Why:** the command line is visible to other processes and kept in shell history; a keyring entry cannot be read
|
|
65
|
+
back to check it.
|
|
66
|
+
- **Enforced by:** review only.
|
|
67
|
+
- **Example:** cli-core's `readSecret` reads one line from a terminal or a pipe, at most 64 KiB, and prompts on stderr.
|
|
68
|
+
`contacts import` reads phone numbers from stdin, never from the command line.
|
|
69
|
+
- **Exceptions:** none.
|
|
70
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-203.
|
|
71
|
+
|
|
72
|
+
### WC-SECR-6 · Error text never repeats a secret or what other people wrote
|
|
73
|
+
|
|
74
|
+
An error message names what failed and the next step. It never quotes a secret, a parameter the caller passed that
|
|
75
|
+
may hold private data, or text from the provider that can quote a chat title or a message. Provider errors are reduced
|
|
76
|
+
to their error name, or replaced with fixed wording.
|
|
77
|
+
|
|
78
|
+
- **Why:** errors reach logs, run records and agents' transcripts.
|
|
79
|
+
- **Enforced by:** review only.
|
|
80
|
+
- **Example:** tg's error mapper keeps only the provider's error name, and turns a name that tells a person nothing
|
|
81
|
+
into a fixed sentence with the next `tg …` command to run.
|
|
82
|
+
- **Exceptions:** the owner's own input that holds no private data, such as a file path, may be named.
|
|
83
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
84
|
+
|
|
85
|
+
### WC-SECR-7 · A token stays inside the transport
|
|
86
|
+
|
|
87
|
+
The address that holds a token is built in one place and never reaches an error, a trace or a rethrown network error.
|
|
88
|
+
Text that looks like a token is replaced with `[redacted]`. Attachments carry a file id, never a download address
|
|
89
|
+
that contains the token. Proxy credentials are passed as options, never inside a proxy address.
|
|
90
|
+
|
|
91
|
+
- **Why:** the token is the account or the bot; anything that prints it hands it away.
|
|
92
|
+
- **Enforced by:** review only.
|
|
93
|
+
- **Example:** a failed Bot API call reports the method and the API's own description with tokens replaced, never
|
|
94
|
+
the request address.
|
|
95
|
+
- **Exceptions:** none.
|
|
96
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
97
|
+
|
|
98
|
+
### WC-SECR-8 · Phone numbers are masked unless the owner asks for the whole number
|
|
99
|
+
|
|
100
|
+
Output shows the last four digits (`***1234`). `--show-phone` prints the whole number, on the command line only.
|
|
101
|
+
|
|
102
|
+
- **Why:** a phone number identifies a person and is the login of a messenger account.
|
|
103
|
+
- **Enforced by:** review only.
|
|
104
|
+
- **Example:** `max work account show --show-phone`.
|
|
105
|
+
- **Exceptions:** none.
|
|
106
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-209.
|
|
107
|
+
|
|
108
|
+
### WC-SECR-9 · A command does not send the CLI's own secret files unless the owner says so
|
|
109
|
+
|
|
110
|
+
A command that sends a local file refuses known credential files, browser profiles, the WireCat apps' own folders and
|
|
111
|
+
the message store. The owner adds `--allow-any-file` to send one anyway.
|
|
112
|
+
|
|
113
|
+
- **Why:** an agent talked into "send me your config" must not be able to send the session.
|
|
114
|
+
- **Enforced by:** review only.
|
|
115
|
+
- **Example:** `max work messages send @me --file ~/.config/max-cli/credentials.json` is refused.
|
|
116
|
+
- **Exceptions:** ordinary hidden developer folders, such as `.cache` or `.worktrees`, are not refused.
|
|
117
|
+
- **Applies to:** messenger CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-274.
|
|
118
|
+
|
|
119
|
+
### WC-SECR-10 · Private files are written atomically with private modes
|
|
120
|
+
|
|
121
|
+
A file that holds a secret or private state is written with `writeSecurely`: atomically, mode `0600`, in a directory
|
|
122
|
+
created with mode `0700`. A temporary credential file, such as a login QR image, is `0600` and removed in `finally`.
|
|
123
|
+
tg's `doctor` reports a wrong mode and prints the `chmod` fix without applying it.
|
|
124
|
+
|
|
125
|
+
- **Why:** another user on the same computer must not read the session.
|
|
126
|
+
- **Enforced by:** `src/config.test.ts`.
|
|
127
|
+
- **Example:** `writeSecurely(path, JSON.stringify(state), 0o600)`.
|
|
128
|
+
- **Exceptions:** Windows ignores both modes.
|
|
129
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
130
|
+
|
|
131
|
+
### WC-SECR-11 · Text from outside is made safe before a terminal or an agent sees it
|
|
132
|
+
|
|
133
|
+
Display text and agent text pass through cli-base's sanitizer. It escapes control characters, terminal escapes and
|
|
134
|
+
the characters that reverse text direction.
|
|
135
|
+
|
|
136
|
+
- **Why:** a chat title can hold `\x1b[2K` and overwrite what the tool already printed, or make `invoicefdp.exe`
|
|
137
|
+
look like a PDF.
|
|
138
|
+
- **Enforced by:** `src/sanitize.test.ts`.
|
|
139
|
+
- **Example:** a title with an escape sequence prints as visible `\x1b` text.
|
|
140
|
+
- **Exceptions:** machine output (`--json`) keeps the original strings, as a contract with scripts.
|
|
141
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
142
|
+
|
|
143
|
+
### WC-SECR-12 · Nothing goes to an outside model or service without explicit config
|
|
144
|
+
|
|
145
|
+
Private data goes to an outside AI model only when the owner configured it, through cli-core's one model gateway. A
|
|
146
|
+
feature that sends a chat's text asks once for consent and can be turned off per chat. Embeddings use the local model
|
|
147
|
+
by default. Nothing is downloaded unasked either: attachment text is extracted from a remote file only with
|
|
148
|
+
`--download`, and a meaning search with no model falls back to words and says `meaning: "unavailable"`.
|
|
149
|
+
|
|
150
|
+
- **Why:** messages belong to the people who wrote them; sending them to a third party is the owner's decision.
|
|
151
|
+
- **Enforced by:** review only.
|
|
152
|
+
- **Example:** reply rewording is off until the profile turns it on, and asks once per profile.
|
|
153
|
+
- **Exceptions:** none.
|
|
154
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-545, NEED-552, NEED-755, NEED-756, NEED-757, ss:10.
|
|
155
|
+
|
|
156
|
+
### WC-SECR-13 · A CLI sends no telemetry
|
|
157
|
+
|
|
158
|
+
- **Why:** the tool must not report on its owner; for a messenger CLI, extra traffic also stops it looking like the
|
|
159
|
+
official client.
|
|
160
|
+
- **Enforced by:** review only.
|
|
161
|
+
- **Example:** a pull request that adds a call to a WireCat server, or to an analytics service, is refused in review.
|
|
162
|
+
- **Exceptions:** a messenger CLI may later copy what the official client reports, and nothing more.
|
|
163
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-16.
|
|
164
|
+
|
|
165
|
+
### WC-SECR-14 · Certificate checks stay on, and the computer's trust store is never changed
|
|
166
|
+
|
|
167
|
+
A CLI never turns off TLS verification. A root certificate that a provider needs is trusted inside the process only.
|
|
168
|
+
|
|
169
|
+
- **Why:** changing the computer's trust store weakens every other program on it.
|
|
170
|
+
- **Enforced by:** review only.
|
|
171
|
+
- **Example:** for its Bot API calls, max adds the root certificate it ships to that process alone.
|
|
172
|
+
- **Exceptions:** none.
|
|
173
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-293, sc:5.
|
|
174
|
+
|
|
175
|
+
### WC-SECR-15 · Security work keeps ordinary workflows working
|
|
176
|
+
|
|
177
|
+
Prefer compatibility over guesses about attacks. Fix a confirmed critical issue at once. A new restriction that is not
|
|
178
|
+
critical goes to the owner for review first.
|
|
179
|
+
|
|
180
|
+
- **Why:** users run VPNs, proxies and unusual DNS; a speculative guard that breaks them gets switched off.
|
|
181
|
+
- **Enforced by:** review only.
|
|
182
|
+
- **Example:** attachment downloads use the configured transport and normal redirects, with no private-address block
|
|
183
|
+
list.
|
|
184
|
+
- **Exceptions:** none.
|
|
185
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** sc:3, sc:5, sc:6, sc:8.
|
|
186
|
+
|
|
187
|
+
### WC-SECR-16 · Keep the secret scanners on, and run each one with `--redact`
|
|
188
|
+
|
|
189
|
+
- gitleaks, in the pre-commit hook on staged changes, in the pull request job on the new commits, and on the whole
|
|
190
|
+
history in full validation, at release and every month.
|
|
191
|
+
- Biome's `noSecrets` rule, from the shared preset, off in tests only
|
|
192
|
+
([WC-TOOL-10](toolchain.md#wc-tool-10--keep-the-secret-lint-on-and-suppress-a-finding-only-with-its-reason)).
|
|
193
|
+
- At release and every month: a dependency audit, zizmor for workflows and CodeQL.
|
|
194
|
+
- **Why:** a secret removed in a later commit is still a leaked secret; `--redact` keeps a finding from leaking it
|
|
195
|
+
again into the log.
|
|
196
|
+
- **Enforced by:** `node-ci.yml:secrets` · `release-checks.yml` · `standards.yml:standards` · `pnpm standards:check`.
|
|
197
|
+
- **Example:** `gitleaks git --staged --no-banner --redact .`. When each scanner runs is on the CI page:
|
|
198
|
+
[WC-CI-1](ci.md#wc-ci-1--run-only-staged-lint-and-a-staged-secret-scan-before-a-commit),
|
|
199
|
+
[WC-CI-3](ci.md#wc-ci-3--run-the-fast-standards-job-on-every-pull-request-and-every-push-to-main),
|
|
200
|
+
[WC-CI-7](ci.md#wc-ci-7--run-full-validation-once-on-each-merge-to-main-on-linux-without-publishing) and
|
|
201
|
+
[WC-CI-10](ci.md#wc-ci-10--run-heavy-security-checks-only-at-release-monthly-or-on-request).
|
|
202
|
+
- **Exceptions:** the pre-commit hook warns, and does not block, when gitleaks is not installed; CI scans anyway.
|
|
203
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
204
|
+
|
|
205
|
+
### WC-SECR-17 · A gitleaks rule covers each secret shape a CLI handles
|
|
206
|
+
|
|
207
|
+
A CLI adds a gitleaks rule for each token, device id or personal identifier that the default rules do not know. An
|
|
208
|
+
allowlist names synthetic values by value, never a whole file, so the file stays scanned.
|
|
209
|
+
|
|
210
|
+
- **Why:** the default rules know AWS and GitHub keys, not a messenger's session token.
|
|
211
|
+
- **Enforced by:** review only.
|
|
212
|
+
- **Example:** max's `.gitleaks.toml` has `max-session-token`, `max-device-identity` and `phone-number-e164`, and
|
|
213
|
+
allows only `+71234567890` and other sanctioned fakes.
|
|
214
|
+
- **Exceptions:** `pnpm-lock.yaml` may be allowed by path. Today only max and wa have a phone number rule.
|
|
215
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
216
|
+
|
|
217
|
+
### WC-SECR-18 · Report a vulnerability privately
|
|
218
|
+
|
|
219
|
+
Every repository has a `SECURITY.md`. It names `hello@wirecat.dev` and GitHub's private vulnerability reporting, and
|
|
220
|
+
asks never to send a token, a session or anyone's messages.
|
|
221
|
+
|
|
222
|
+
- **Why:** a public issue tells attackers before users can upgrade.
|
|
223
|
+
- **Enforced by:** review only.
|
|
224
|
+
- **Example:** [max-cli's `SECURITY.md`](https://github.com/WireCatLabs/max-cli/blob/main/SECURITY.md).
|
|
225
|
+
- **Exceptions:** none.
|
|
226
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-555.
|
|
227
|
+
|
|
228
|
+
### WC-SECR-19 · Audit findings stay out of public places
|
|
229
|
+
|
|
230
|
+
An audit follows the [cli-testing security method](https://github.com/WireCatLabs/cli-testing/tree/main/docs/security).
|
|
231
|
+
Its results go to a private repository, not to a public backlog or issue. A fix is described in the changelog in
|
|
232
|
+
general words.
|
|
233
|
+
|
|
234
|
+
- **Why:** a public finding is a guide to attacking users who have not upgraded.
|
|
235
|
+
- **Enforced by:** review only.
|
|
236
|
+
- **Example:** "Fixed: an error could include part of a request" — not the steps to make it happen.
|
|
237
|
+
- **Exceptions:** none.
|
|
238
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-271.
|
|
239
|
+
|
|
240
|
+
### WC-SECR-20 · Pin each third-party action to a commit, and give each workflow read-only defaults
|
|
241
|
+
|
|
242
|
+
- **Why:** a moved tag or a write token in a workflow is how an outside change reaches a release.
|
|
243
|
+
- **Enforced by:** review only.
|
|
244
|
+
- **Example:** `uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1` with
|
|
245
|
+
`persist-credentials: false`, and `permissions: contents: read` at the top of the workflow. A job that needs more
|
|
246
|
+
asks for it on that job only. The `zizmor` scan in `release-checks.yml` reports high findings at release, but does
|
|
247
|
+
not fail on them.
|
|
248
|
+
- **Exceptions:** none.
|
|
249
|
+
- **Applies to:** the ten repositories: the six CLIs, cli-base, cli-core, cli-testing, cli-docs and community.
|
|
250
|
+
**Since:** cli-base 0.41.0. **Rulings:** none.
|
|
251
|
+
|
|
252
|
+
## Sources
|
|
253
|
+
|
|
254
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
255
|
+
holds. The audit procedure stays in cli-testing; this page links it and does not copy it.
|
|
256
|
+
|
|
257
|
+
- [The security audit method](https://github.com/WireCatLabs/cli-testing/tree/main/docs/security): what an audit
|
|
258
|
+
checks, how a run is done, and where its results go.
|
|
259
|
+
- [The organisation's security policy](https://github.com/WireCatLabs/.github/blob/main/SECURITY.md): how to report a
|
|
260
|
+
problem.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Store
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
How local data is kept: one shared store, its areas, account scope, migrations and store versions, what a delete
|
|
6
|
+
and a repair do to the data, and the rule that tests never touch the owner's store.
|
|
7
|
+
|
|
8
|
+
Rule ids on this page are `WC-STORE-<n>`. How to read a rule, how ids work and which rule wins are in the
|
|
9
|
+
[index](README.md).
|
|
10
|
+
|
|
11
|
+
Related pages: which account a read covers, and how a read across accounts is asked for, is in
|
|
12
|
+
[Accounts](accounts.md); the general test sandbox is in [Testing](testing.md); where secrets live is in
|
|
13
|
+
[Security](security.md).
|
|
14
|
+
|
|
15
|
+
### Words used here
|
|
16
|
+
|
|
17
|
+
- **The store:** the one SQLite file that cli-core opens for every CLI, `wirecat.db`.
|
|
18
|
+
- **Area:** one part of the store for one kind of record, such as `store.messages` or `store.chats`.
|
|
19
|
+
- **Store version:** the number of the newest migration the file has run. It is the `version` field of `MANIFEST`
|
|
20
|
+
in cli-core's `src/store/sqlite/manifest.ts`.
|
|
21
|
+
- **Tombstone:** the mark a deleted message keeps in place of its content.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
### WC-STORE-1 · Keep local data in the one shared store that cli-core opens
|
|
26
|
+
|
|
27
|
+
- **Why:** one file for every messenger and account lets search, backup and export see everything in one place.
|
|
28
|
+
- **Enforced by:** review only.
|
|
29
|
+
- **Example:** `openStore` opens `wirecat.db` in the WireCat folder (`~/.local/share/WireCat/` on Linux,
|
|
30
|
+
`%LOCALAPPDATA%\WireCat\Data\` on Windows). Rows are keyed by provider and account. `storePath` is the only code
|
|
31
|
+
that reads `MESSAGING_STORE` and `WIRECAT_STATE_DIR`.
|
|
32
|
+
- **Exceptions:** none. Older documents that name `messages.db` describe a store that is no longer used.
|
|
33
|
+
- **Applies to:** CLIs that keep local data through cli-core (tg, max, wa, memo, drive, zoom). **Since:** cli-base 0.41.0.
|
|
34
|
+
**Rulings:** NEED-383.
|
|
35
|
+
|
|
36
|
+
### WC-STORE-2 · Reach the data through its area, and add a new kind of record as a new area
|
|
37
|
+
|
|
38
|
+
- **Why:** an area keeps one kind of record in one file, so the store does not grow back into one long interface.
|
|
39
|
+
- **Enforced by:** `cli-core: biome noExcessiveLinesPerFile`.
|
|
40
|
+
- **Example:** `store.chats.saveChats(key, …)`, `store.messages.find(…)`, `store.tasks`, `store.mail`. Each area is a
|
|
41
|
+
getter built on first use from its own file (`xStoreOver(context)`). `MessageStore` has no flat methods.
|
|
42
|
+
- **Exceptions:** none. cli-core's `biome.json` caps `src/store/store.ts` at 438 lines so that it does not grow again.
|
|
43
|
+
- **Applies to:** cli-core, and every CLI that calls the store. **Since:** cli-base 0.41.0 (store areas since
|
|
44
|
+
cli-core 0.307.0). **Rulings:** none.
|
|
45
|
+
|
|
46
|
+
### WC-STORE-3 · A service of one account works through the store bound to that account
|
|
47
|
+
|
|
48
|
+
- **Why:** when no call takes an account, a row of another account cannot be read or written by mistake.
|
|
49
|
+
- **Enforced by:** `cli-core: src/services/account-isolation.test.ts`.
|
|
50
|
+
- **Example:** `accountStore(deps)` returns `store.forAccount(key)`; then `.messages.messages(chatId, …)` takes no
|
|
51
|
+
account, and a row id of another account (an attachment, a mail thread, a meeting) is "not found". A read across
|
|
52
|
+
accounts on purpose asks for the whole store with `crossAccount(deps, reason)`; see [Accounts](accounts.md).
|
|
53
|
+
- **Exceptions:** `forAccount` binds only the account areas (accounts, chats, sync, messages, attachments, roster,
|
|
54
|
+
people, conversations, vectors, counters, tags, mentions). The `chats`, `conversations` and `attachments` services
|
|
55
|
+
use the bound store; the other services still pair `deps.store()` with `deps.account()`. New code uses the bound
|
|
56
|
+
store.
|
|
57
|
+
- **Applies to:** cli-core and every CLI that adds a service. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
58
|
+
|
|
59
|
+
### WC-STORE-4 · Reach the store through its public module, never a table module
|
|
60
|
+
|
|
61
|
+
- **Why:** the table modules are the SQL layer; a caller that imports them skips the transaction and the account
|
|
62
|
+
binding.
|
|
63
|
+
- **Enforced by:** review only.
|
|
64
|
+
- **Example:** in cli-core, `biome.json` is meant to refuse an import of `src/store/sqlite/*` from `src/services/`,
|
|
65
|
+
`src/sends/`, `src/mcp/` and `src/cli/`; today it does not, because a later override replaces that rule
|
|
66
|
+
([WC-ARCH-1](architecture.md#wc-arch-1--call-only-the-layers-below-you-interface-then-service-then-port-then-adapter)). They import from `src/store/store.ts`, which re-exports what they need.
|
|
67
|
+
- **Exceptions:** tests of a table module.
|
|
68
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
69
|
+
|
|
70
|
+
### WC-STORE-5 · A CLI writes no SQL against the shared store
|
|
71
|
+
|
|
72
|
+
- **Why:** the schema belongs to cli-core; SQL in a CLI breaks on the next migration.
|
|
73
|
+
- **Enforced by:** review only.
|
|
74
|
+
- **Example:** a CLI that needs a new query asks cli-core for an area method, and the method lands in cli-core with
|
|
75
|
+
its test. drive and zoom state "no SQL here" in their own architecture pages.
|
|
76
|
+
- **Exceptions:** a provider engine's own file of keys and device state, outside `wirecat.db`
|
|
77
|
+
([WC-STORE-13](#wc-store-13--keep-keys-and-device-state-out-of-the-shared-store)).
|
|
78
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-383.
|
|
79
|
+
|
|
80
|
+
### WC-STORE-6 · One store method is one whole operation, with no `await` inside a transaction
|
|
81
|
+
|
|
82
|
+
- **Why:** the driver is synchronous; an `await` inside a transaction lets the commit run before the awaited part.
|
|
83
|
+
- **Enforced by:** review only.
|
|
84
|
+
- **Example:** a write goes through `write` and a read of several queries through `read`
|
|
85
|
+
(`src/store/sqlite/transaction.ts`), never Drizzle's `transaction` or a hand-written `BEGIN`. A large write runs
|
|
86
|
+
in bounded batches, because it blocks the event loop while it runs.
|
|
87
|
+
- **Exceptions:** `replaceConversations` writes a new build in several short transactions on purpose. Readers see
|
|
88
|
+
only the current build.
|
|
89
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
90
|
+
|
|
91
|
+
### WC-STORE-7 · Migrations only go forward: add, never edit or rebuild
|
|
92
|
+
|
|
93
|
+
- **Why:** a file that a newer build migrated must still open in the build already installed.
|
|
94
|
+
- **Enforced by:** `cli-core: src/store/sqlite/manifest.test.ts`.
|
|
95
|
+
- **Example:** a change is a new column that is nullable or has a default, or a new table. Triggers, full-text tables
|
|
96
|
+
and data fills go in a custom migration. A mistake in a released migration is fixed by the next version. cli-core's
|
|
97
|
+
manifest tests refuse a rebuilt base table, a gap in the versions and a bundle that differs from what drizzle-kit
|
|
98
|
+
wrote.
|
|
99
|
+
- **Exceptions:** none.
|
|
100
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** NEED-383.
|
|
101
|
+
|
|
102
|
+
### WC-STORE-8 · Take the migration number before you write the migration
|
|
103
|
+
|
|
104
|
+
- **Why:** two branches with the same number leave one migration unapplied on stores that ran the other.
|
|
105
|
+
- **Enforced by:** review only.
|
|
106
|
+
- **Example:** edit the "next free version" line in cli-core `docs/dev/COORDINATION.md` in a pull request of its
|
|
107
|
+
own, and wait for it to merge. Versions are the `version` field of `MANIFEST`; the initial migration is 1.
|
|
108
|
+
- **Exceptions:** none.
|
|
109
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
110
|
+
|
|
111
|
+
### WC-STORE-9 · Ship each migration with its schema spec, an upgrade test and a changelog line
|
|
112
|
+
|
|
113
|
+
- **Why:** the schema page, the code and the files already on disk must agree after every version.
|
|
114
|
+
- **Enforced by:** `cli-core: pnpm schema:check`.
|
|
115
|
+
- **Example:** change `scripts/schema/spec.mjs` first, then `schema.ts` until `pnpm schema:check` passes, then
|
|
116
|
+
`pnpm db:generate` and `pnpm db:bundle`. Add a `src/store/version-<n>.test.ts` that opens a file at version n−1 and
|
|
117
|
+
reads what version n added. The changelog entry names the store version, as in "Chat members in the store (store
|
|
118
|
+
version 7)".
|
|
119
|
+
- **Exceptions:** none.
|
|
120
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
121
|
+
|
|
122
|
+
### WC-STORE-10 · An older build refuses a store it cannot read and says to upgrade
|
|
123
|
+
|
|
124
|
+
- **Why:** an older build that reads a file it does not understand corrupts it or answers wrongly.
|
|
125
|
+
- **Enforced by:** review only.
|
|
126
|
+
- **Example:** each migration keeps `minCompatible`. An additive change leaves it as it is (1 today). A change that
|
|
127
|
+
drops what an older build reads raises it; that older build then refuses the file and asks to be upgraded.
|
|
128
|
+
- **Exceptions:** raising `minCompatible` needs the owner's approval. It is a major version of cli-core, and the CLIs
|
|
129
|
+
ship their upgrade the same day.
|
|
130
|
+
- **Applies to:** cli-core and the CLIs that use the store. **Since:** cli-base 0.41.0. **Rulings:** NEED-867.
|
|
131
|
+
|
|
132
|
+
### WC-STORE-11 · A deleted message keeps a tombstone and loses its content
|
|
133
|
+
|
|
134
|
+
- **Why:** a delete must remove what the owner wanted gone, while the history still shows that something was there.
|
|
135
|
+
- **Enforced by:** review only.
|
|
136
|
+
- **Example:** a delete keeps a mark in the message row. Triggers erase its text, revisions, transcript, file text and
|
|
137
|
+
tags, so a build that knows nothing about a newer table cannot leave content behind.
|
|
138
|
+
- **Exceptions:** none.
|
|
139
|
+
- **Applies to:** cli-core. **Since:** cli-base 0.41.0. **Rulings:** NEED-393.
|
|
140
|
+
|
|
141
|
+
### WC-STORE-12 · A repair never deletes data
|
|
142
|
+
|
|
143
|
+
- **Why:** the store is the owner's only copy of much of this data.
|
|
144
|
+
- **Enforced by:** review only.
|
|
145
|
+
- **Example:** `store repair` keeps a misshapen table as a copy, builds the correct one and copies the columns.
|
|
146
|
+
- **Exceptions:** none.
|
|
147
|
+
- **Applies to:** cli-core and the CLIs that expose `store repair`. **Since:** cli-base 0.41.0. **Rulings:** NEED-626.
|
|
148
|
+
|
|
149
|
+
### WC-STORE-13 · Keep keys and device state out of the shared store
|
|
150
|
+
|
|
151
|
+
- **Why:** a store that holds keys turns every backup and export into a secret.
|
|
152
|
+
- **Enforced by:** review only.
|
|
153
|
+
- **Example:** WhatsApp's linked-device keys live in a separate file per profile, `private.db`, which only the
|
|
154
|
+
provider engine writes. Tokens go to the keyring ([Security](security.md)).
|
|
155
|
+
- **Exceptions:** none.
|
|
156
|
+
- **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-1395.
|
|
157
|
+
|
|
158
|
+
### WC-STORE-14 · Tests never open the owner's store
|
|
159
|
+
|
|
160
|
+
- **Why:** migrations only go forward; a test that opened the real file would migrate it, and nothing undoes that.
|
|
161
|
+
- **Enforced by:** review only.
|
|
162
|
+
- **Example:** the vitest `setupFiles` sandbox points `MESSAGING_STORE`, `WIRECAT_STATE_DIR` and each app's own
|
|
163
|
+
directory variables at a temporary directory before any test runs. A test that defines a new app prefix adds it to
|
|
164
|
+
the sandbox. A test that needs its own store is
|
|
165
|
+
[WC-TEST-9](testing.md#wc-test-9--give-each-test-its-own-profile-or-its-own-store). The rest of the sandbox is
|
|
166
|
+
[WC-TEST-1](testing.md#wc-test-1--point-every-path-a-test-could-write-at-a-temporary-directory).
|
|
167
|
+
- **Exceptions:** none.
|
|
168
|
+
- **Applies to:** all CLIs that use the store, and cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
|
|
169
|
+
|
|
170
|
+
## Sources
|
|
171
|
+
|
|
172
|
+
These documents explain the rules on this page in more detail. Where one of them disagrees with this page, this page
|
|
173
|
+
holds.
|
|
174
|
+
|
|
175
|
+
- [cli-core `ARCHITECTURE.md`, "The store"](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/ARCHITECTURE.md#the-store)
|
|
176
|
+
- [cli-core `COORDINATION.md`, "Store migrations"](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/COORDINATION.md#store-migrations)
|
|
177
|
+
- [cli-core `TESTING.md`](https://github.com/WireCatLabs/cli-core/blob/main/docs/dev/TESTING.md)
|