@needmoretruth/nmts-cli 0.36.1 → 0.36.2

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 CHANGED
@@ -3,6 +3,15 @@
3
3
  Each version's entry is what changed for the person or the program using `nmts`. The product's own
4
4
  update history, which covers the site and the server too, is at https://nmts.me/updates.
5
5
 
6
+ ## 0.36.2 — 2026-09-19
7
+
8
+ - On Windows, `platform keygen` now restricts the key file instead of only reporting that no file
9
+ mode applies: it removes the permissions inherited from the folder and grants your account
10
+ alone (`icacls`). The file is written wherever the command runs, which may be a shared folder.
11
+ If that step fails the command says so and the file is still written.
12
+ - Three filler words are gone from the advice printed by `share`, a resumed upload and a wallet
13
+ agreement that asks for too long a term.
14
+
6
15
  ## 0.36.1 — 2026-09-19
7
16
 
8
17
  - On Windows, `platform keygen` says that the key file inherits its folder's permissions, because
package/README.ko.md CHANGED
@@ -50,7 +50,7 @@ nmts --help
50
50
 
51
51
  ```sh
52
52
  npm install -g github:needmoretruth/nmts-cli # 기본 브랜치
53
- npm install -g github:needmoretruth/nmts-cli#v0.36.1 # 버전을 고정할 때
53
+ npm install -g github:needmoretruth/nmts-cli#v0.36.2 # 버전을 고정할 때
54
54
  npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
55
55
  ```
56
56
 
package/README.md CHANGED
@@ -51,7 +51,7 @@ default branch, from a pinned version, or from the tarball attached to the
51
51
 
52
52
  ```sh
53
53
  npm install -g github:needmoretruth/nmts-cli # the default branch
54
- npm install -g github:needmoretruth/nmts-cli#v0.36.1 # a pinned version
54
+ npm install -g github:needmoretruth/nmts-cli#v0.36.2 # a pinned version
55
55
  npm install -g https://github.com/needmoretruth/nmts-cli/releases/latest/download/nmts.tgz
56
56
  ```
57
57
 
@@ -11,7 +11,7 @@ export declare const CRYPTO_SPEC_URL = "https://github.com/needmoretruth/nmts-re
11
11
  * is the field's own contract — what the writer says about itself — and a person holding two
12
12
  * copies of one account's artefacts can then tell which program made each.
13
13
  */
14
- export declare const WRITTEN_BY = "nmts-cli 0.36.1";
14
+ export declare const WRITTEN_BY = "nmts-cli 0.36.2";
15
15
  /** Which of the three artefacts a wrapper is. A reader holding several can sort them. */
16
16
  export type ArtifactKind = "recovery-list" | "file-list" | "recovery-kit";
17
17
  /**
@@ -12,7 +12,9 @@
12
12
  // ⛔ AND THE FILE IS NEVER REPLACED. `--force` does not open it: the file it would overwrite may
13
13
  // hold the only copy of a key a business is already registered under, and replacing it locks
14
14
  // that business out of its own door with nothing able to bring the key back.
15
+ import { spawnSync } from "node:child_process";
15
16
  import { mkdirSync, statSync, writeFileSync } from "node:fs";
17
+ import { userInfo } from "node:os";
16
18
  import { isAbsolute, resolve } from "node:path";
17
19
  import { modesAreEnforced } from "../credentials.js";
18
20
  import { NmtsError } from "../errors.js";
@@ -45,14 +47,42 @@ function keygen(out, say) {
45
47
  writeKeyFile(path, keys);
46
48
  say(`Public key: ${keys.publicKey}`);
47
49
  say(`Written to: ${path}`);
48
- // The same sentence `nmts doctor` uses for a stored key: where no mode applies, say so.
49
50
  if (!modesAreEnforced()) {
50
- say(`Windows applies no POSIX file mode, so this file inherits its folder's permissions ` +
51
- `rather than being restricted to one user.`);
51
+ // THIS FILE LANDS WHEREVER THE COMMAND WAS RUN, which may be a shared folder unlike the
52
+ // stored NMTS key, which lives under the user's own profile. So on Windows the mode is not
53
+ // merely reported as absent: the file is cut off from the folder's permissions.
54
+ say(restrictToCurrentUser(path)
55
+ ? `Inherited permissions were removed; only your Windows account can open this file.`
56
+ : `Windows applies no POSIX file mode, and restricting the file with icacls failed, so it ` +
57
+ `inherits its folder's permissions. Move it somewhere only you can read.`);
52
58
  }
53
59
  say(`Register the public key at ${HOME_URL} — ${REGISTER_PATH}. The private half stays in that file.`);
54
60
  return 0;
55
61
  }
62
+ /**
63
+ * Windows only: drop the inherited entries and grant the current account alone.
64
+ *
65
+ * ⛔ `icacls`, NOT A HAND-ROLLED ACL. It ships with every supported Windows, and this is the shape
66
+ * OpenSSH asks of a private key there. One invocation does both halves, so there is no moment
67
+ * with the inheritance gone and nobody granted.
68
+ * ⚠ A FAILURE IS REPORTED, NOT THROWN: the pair is already written and is the only copy, so the
69
+ * command must still print the public half and say plainly what protects the file.
70
+ */
71
+ function restrictToCurrentUser(path) {
72
+ try {
73
+ const name = userInfo().username;
74
+ const domain = process.env.USERDOMAIN;
75
+ const account = domain === undefined || domain === "" ? name : `${domain}\\${name}`;
76
+ const done = spawnSync("icacls", [path, "/inheritance:r", "/grant:r", `${account}:F`], {
77
+ windowsHide: true,
78
+ stdio: "ignore",
79
+ });
80
+ return done.status === 0;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
56
86
  /**
57
87
  * Say where registering happens, and stop.
58
88
  *
@@ -101,9 +131,8 @@ function targetFor(out) {
101
131
  * ⚠ `wx` FAILS IF THE NAME APPEARED SINCE THE CHECK ABOVE, which is the point of using it rather
102
132
  * than trusting that check: between the two, something else may have written there.
103
133
  *
104
- * ⚠ ON WINDOWS THE MODE IS IGNORED and the file inherits the folder's permissions — the same limit
105
- * every other file this tool writes has, and claiming otherwise would claim a guarantee the
106
- * platform does not give.
134
+ * ⚠ ON WINDOWS THE MODE IS IGNORED; `keygen` restricts the file afterwards with `icacls` and says
135
+ * which of the two happened.
107
136
  */
108
137
  function writeKeyFile(path, keys) {
109
138
  mkdirSync(resolve(path, ".."), { recursive: true, mode: 0o700 });
@@ -114,7 +114,7 @@ export async function share(target, typedAddress, options = {}) {
114
114
  if (typeof identityB64 !== "string") {
115
115
  throw new NmtsError("That public code has never published an identity to share to.", {
116
116
  exitCode: 4,
117
- nextStep: "Nothing was shared. The code may be right and simply unused — somebody has to open " +
117
+ nextStep: "Nothing was shared. The code may be right and unused — somebody has to open " +
118
118
  "their account once before anything can be encrypted to them.",
119
119
  });
120
120
  }
package/dist/product.d.ts CHANGED
@@ -9,7 +9,7 @@ export declare const BINARY_NAME = "nmts";
9
9
  * beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
10
10
  * command importing the entry point is a cycle waiting to bite.
11
11
  */
12
- export declare const VERSION = "0.36.1";
12
+ export declare const VERSION = "0.36.2";
13
13
  /** Where the product lives, for messages that need to send somebody somewhere real. */
14
14
  export declare const HOME_URL = "https://nmts.me";
15
15
  /** The source, so a person holding only the built program can find what it was built from. */
package/dist/product.js CHANGED
@@ -18,7 +18,7 @@ export const BINARY_NAME = "nmts";
18
18
  * beside it. Here rather than in `main.ts` because the MCP server has to say it too, and a
19
19
  * command importing the entry point is a cycle waiting to bite.
20
20
  */
21
- export const VERSION = "0.36.1";
21
+ export const VERSION = "0.36.2";
22
22
  /** Where the product lives, for messages that need to send somebody somewhere real. */
23
23
  export const HOME_URL = "https://nmts.me";
24
24
  /** The source, so a person holding only the built program can find what it was built from. */
@@ -49,7 +49,7 @@ export async function pushPart(input, step) {
49
49
  message: why(error),
50
50
  paid: true,
51
51
  nextStep: "The bytes are on the network. Certifying moves no money, so running the same command " +
52
- "again simply finishes the job.",
52
+ "again finishes the job.",
53
53
  });
54
54
  }
55
55
  }
@@ -80,7 +80,7 @@ export function parseWalletGrant(input, now, version) {
80
80
  if (expiresMs > ceiling) {
81
81
  throw new NmtsError(`A wallet agreement lasts at most ${MAX_GRANT_DAYS} days.`, {
82
82
  exitCode: 2,
83
- nextStep: `Grant it again when it runs out. That is the point: a grant nobody remembers giving should run out on its own.`,
83
+ nextStep: `Grant it again when it runs out. An agreement ends on its own so that one nobody remembers giving does not stay open.`,
84
84
  });
85
85
  }
86
86
  return {
@@ -4,7 +4,8 @@ Commands: platform
4
4
  Tiers: platform=none · platform.keygen=medium
5
5
 
6
6
  `platform keygen` makes an Ed25519 key pair and writes both halves to `nmts-business-key.json`, or
7
- to `--out <file>`, with mode 0600 (Windows applies no file mode, and the command says so). It
7
+ to `--out <file>`, with mode 0600 (on Windows, where no file mode applies, it removes the inherited
8
+ permissions with `icacls` and leaves your account alone on the file, and says so if that fails). It
8
9
  refuses a name that already exists, whatever `--force` says, and prints only the public half.
9
10
  `platform register` cannot register it: that needs a signed-in browser session at nmts.me, under
10
11
  Settings › Developer › Platform, and the command prints that path and exits 2.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needmoretruth/nmts-cli",
3
- "version": "0.36.1",
3
+ "version": "0.36.2",
4
4
  "description": "Command-line and MCP access to NMTS (NeedMoreTruthStorage, https://nmts.me): open-source, end-to-end encrypted storage on the Walrus network. No fee to NMTS; you pay the network from your own wallet. For people and for their agents.",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {