@twinsearth/w2m-dsh-plugin 0.33.0 → 0.34.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/CHANGELOG.md +48 -0
- package/docs/1.0.0-ACCEPTANCE.md +26 -15
- package/package.json +1 -1
- package/src/plugin/tools.mjs +36 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.34.0] — 2026-10-15
|
|
8
|
+
|
|
9
|
+
**`requireAuthorityLevel`: the policy that makes 分权限 real, and why it is a flag rather than a default.**
|
|
10
|
+
|
|
11
|
+
`docs/1.0.0-ACCEPTANCE.md` §3.11 recorded the gap plainly — the gate is **off unless configured**, so a
|
|
12
|
+
default install dispatches exactly as it did before the permission model existed, and **"off unless
|
|
13
|
+
configured" cannot be part of a 1.0.0 that claims 分权限.**
|
|
14
|
+
|
|
15
|
+
`requireAuthorityLevel: true` (or `W2M_REQUIRE_AUTHORITY_LEVEL`) closes it for a machine that asks. Measured,
|
|
16
|
+
the whole truth table:
|
|
17
|
+
|
|
18
|
+
| `requireAuthorityLevel` | level | result |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| off | unset | **starts**, gate unenforced |
|
|
21
|
+
| off | chosen | starts, gate enforced |
|
|
22
|
+
| **on** | **unset** | **refuses to start** — `AUTHORITY_CHOICE_NOT_CHOSEN` |
|
|
23
|
+
| **on** | **unusable** | **refuses to start** — `AUTHORITY_CHOICE_UNKNOWN` |
|
|
24
|
+
| on | chosen | starts, gate enforced |
|
|
25
|
+
|
|
26
|
+
A refusal names the four choices, so it is fixable from the message alone, and a machine that opted in says
|
|
27
|
+
so in `w2m_status` — a reader can tell "asked and answered" from "never asked".
|
|
28
|
+
|
|
29
|
+
### Why opt-in, which is a deliberate limit rather than a hedge
|
|
30
|
+
|
|
31
|
+
Refusing unconditionally would be the honest reading of *"a permission system that is off is untested in
|
|
32
|
+
production"* — and it would also **stop every machine running an earlier version the moment it upgraded**,
|
|
33
|
+
because none of them has a level. That trade is one silent failure for a loud outage, and **an outage is not
|
|
34
|
+
a security property.**
|
|
35
|
+
|
|
36
|
+
**So the gap is narrowed, not closed, and the document says so** rather than letting the flag's existence
|
|
37
|
+
imply otherwise. A fleet that never sets it is exactly as unenforced as before.
|
|
38
|
+
|
|
39
|
+
### `W2M_REQUIRE_AUTHORITY_LEVEL=0` means off
|
|
40
|
+
|
|
41
|
+
Asserted, because a truthiness test would read the string `"0"` as set-and-therefore-on and **refuse to start
|
|
42
|
+
a machine whose operator explicitly wrote zero.**
|
|
43
|
+
|
|
44
|
+
### My own errors, both of them syntax rather than logic
|
|
45
|
+
|
|
46
|
+
* I wrote `await` inside a **non-async arrow** passed to `assert.rejects`, which does not parse. `node --check`
|
|
47
|
+
caught it immediately; the fix is to compute the state directory first, because making the arrow async would
|
|
48
|
+
change what it does.
|
|
49
|
+
* I asserted refusal codes against the **message text**; the codes live on `error.reason_code`, which is the
|
|
50
|
+
field a consumer branches on. **The code was correct and the assertion was looking in the wrong place** —
|
|
51
|
+
now it pins the field.
|
|
52
|
+
|
|
53
|
+
**6 new tests, 0 failures.**
|
|
54
|
+
|
|
7
55
|
## [0.33.0] — 2026-10-15
|
|
8
56
|
|
|
9
57
|
**The authority level is chosen by name. Nobody types a number.**
|
package/docs/1.0.0-ACCEPTANCE.md
CHANGED
|
@@ -57,24 +57,35 @@ limiter down to `util`, **not** to add the edge.
|
|
|
57
57
|
| 3.8 | An escalated dispatch reaches a queue an approver can answer | `w2m_approvals`, asserted end to end | **yes** |
|
|
58
58
|
| 3.9 | The queue's front door meters **raising only** | `approval-gate.mjs`, wired in 0.31.0 | **yes** |
|
|
59
59
|
| 3.10 | Field-level memory acceptance is driven by the same model | `field-authority.mjs` | **mechanism yes, caller NO — see §4** |
|
|
60
|
-
| 3.11 | **The gate is ON by default** |
|
|
60
|
+
| 3.11 | **The gate is ON by default** | `requireAuthorityLevel`, asserted | **NARROWED — see below** |
|
|
61
|
+
| 3.12 | A level is chosen by name, never typed | `authority-choice.mjs`, asserted | **yes** |
|
|
62
|
+
| 3.13 | A misconfigured level is reported, not silently ignored | `w2m_status.actor_authority` | **yes** |
|
|
61
63
|
|
|
62
|
-
### `3.11`
|
|
64
|
+
### `3.11` — narrowed rather than closed, and the distinction is stated
|
|
63
65
|
|
|
64
66
|
`actorAuthorityLevel` is **unset by default**, and an unset level means the gate reports `enforced: false` and
|
|
65
|
-
changes nothing. So
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
changes nothing. So a default install dispatches exactly as it did before the permission model existed.
|
|
68
|
+
|
|
69
|
+
**0.34.0 adds `requireAuthorityLevel`** (or `W2M_REQUIRE_AUTHORITY_LEVEL`), which closes it for a machine that
|
|
70
|
+
asks for it to be closed. Measured, the whole table:
|
|
71
|
+
|
|
72
|
+
| `requireAuthorityLevel` | level | result |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| off | unset | **starts**, gate unenforced |
|
|
75
|
+
| off | chosen | starts, gate enforced |
|
|
76
|
+
| **on** | **unset** | **refuses to start** — `AUTHORITY_CHOICE_NOT_CHOSEN` |
|
|
77
|
+
| **on** | **unusable** | **refuses to start** — `AUTHORITY_CHOICE_UNKNOWN` |
|
|
78
|
+
| on | chosen | starts, gate enforced |
|
|
79
|
+
|
|
80
|
+
**Why it is opt-in, which is a deliberate limit rather than a hedge.** Refusing unconditionally would be the
|
|
81
|
+
honest reading of *"a permission system that is off is untested in production"* — and it would also **stop
|
|
82
|
+
every machine running an earlier version the moment it upgraded**, because none of them has a level. That
|
|
83
|
+
trades one silent failure for a loud outage, and **an outage is not a security property.**
|
|
84
|
+
|
|
85
|
+
**So the gap is narrowed, not closed.** A fleet that does not set this flag is exactly as unenforced as
|
|
86
|
+
before, and this document says so rather than letting the flag's existence imply otherwise. **The remaining
|
|
87
|
+
question for 1.0.0 is whether shipping a flag is enough, or whether the default must flip** — and that is a
|
|
88
|
+
decision about who this release is for, not a defect to fix.
|
|
78
89
|
|
|
79
90
|
---
|
|
80
91
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "DSH: Windows2MacOS — drive the same project on online Windows and macOS machines from one DeepSeek account, across networks and regions. P2P hole punching is the default path (relay + shared server as rendezvous, ledger and fallback), with a bundled STUN responder, Tailscale/public-VPS/tunnel shapes, Rabbit relay + per-machine Localside + eight DSH tools (replicate / split), nine aggregate verdicts and signed requests.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -198,7 +198,7 @@ const MAX_STAGES = 16;
|
|
|
198
198
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
199
199
|
* overrides it at runtime for tests and packagers.
|
|
200
200
|
*/
|
|
201
|
-
const PLUGIN_VERSION = '0.
|
|
201
|
+
const PLUGIN_VERSION = '0.34.0';
|
|
202
202
|
|
|
203
203
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
204
204
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
@@ -720,6 +720,40 @@ function readConfig(config = {}) {
|
|
|
720
720
|
? process.env.W2M_ACTOR_AUTHORITY.trim()
|
|
721
721
|
: undefined;
|
|
722
722
|
const resolved = resolveConfiguredLevel(fromEnv !== undefined ? fromEnv : config.actorAuthorityLevel);
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* v0.34.0: the policy that makes 分权限 real, and why it is a policy rather than a default.
|
|
726
|
+
*
|
|
727
|
+
* `docs/1.0.0-ACCEPTANCE.md` §3.11 records the gap plainly: the gate is **off unless configured**, so a
|
|
728
|
+
* default install dispatches exactly as it did before the permission model existed, and **"off unless
|
|
729
|
+
* configured" cannot be part of a 1.0.0 that claims 分权限.**
|
|
730
|
+
*
|
|
731
|
+
* `requireAuthorityLevel: true` closes it: with the policy on, a machine that has not chosen a level
|
|
732
|
+
* **refuses to start** rather than dispatching unenforced. `W2M_REQUIRE_AUTHORITY_LEVEL` supplies the
|
|
733
|
+
* same policy from the environment, so a fleet can require it without editing every profile.
|
|
734
|
+
*
|
|
735
|
+
* ## Why it is opt-in, which is a deliberate limit
|
|
736
|
+
*
|
|
737
|
+
* Refusing unconditionally would be the honest reading of "a permission system that is off is
|
|
738
|
+
* untested" — and it would also **stop every machine running an earlier version the moment it
|
|
739
|
+
* upgraded**, since none of them has a level. That trades one silent failure for a loud outage, and an
|
|
740
|
+
* outage is not a security property.
|
|
741
|
+
*
|
|
742
|
+
* So the policy is per-machine and explicit. **The gap is narrowed, not closed**: a fleet that does not
|
|
743
|
+
* turn this on is exactly as unenforced as before, and this comment says so rather than letting the
|
|
744
|
+
* existence of the flag imply otherwise.
|
|
745
|
+
*/
|
|
746
|
+
const required = config.requireAuthorityLevel === true
|
|
747
|
+
|| (typeof process.env.W2M_REQUIRE_AUTHORITY_LEVEL === 'string' && process.env.W2M_REQUIRE_AUTHORITY_LEVEL.trim() !== '' && process.env.W2M_REQUIRE_AUTHORITY_LEVEL.trim() !== '0');
|
|
748
|
+
if (required && !resolved.ok) {
|
|
749
|
+
throw configError(
|
|
750
|
+
'actorAuthorityLevel',
|
|
751
|
+
`is required on this machine (requireAuthorityLevel is set) and ${resolved.ok ? '' : resolved.detail}`,
|
|
752
|
+
`choose one of ${AUTHORITY_CHOICES.map((c) => c.value).join(', ')} as \`actorAuthorityLevel\`, or as \`W2M_ACTOR_AUTHORITY\`; to dispatch without a level, unset requireAuthorityLevel`,
|
|
753
|
+
resolved.code ?? 'W2M_AUTHORITY_LEVEL_REQUIRED',
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
723
757
|
return {
|
|
724
758
|
actorAuthorityLevel: resolved.ok ? resolved.level : null,
|
|
725
759
|
actorAuthorityChoice: {
|
|
@@ -728,6 +762,7 @@ function readConfig(config = {}) {
|
|
|
728
762
|
source: resolved.source,
|
|
729
763
|
reason_code: resolved.code,
|
|
730
764
|
detail: resolved.detail,
|
|
765
|
+
required,
|
|
731
766
|
},
|
|
732
767
|
};
|
|
733
768
|
})(),
|