@twinsearth/w2m-dsh-plugin 1.0.11 → 1.0.13
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 +98 -0
- package/package.json +5 -3
- package/plugin.json +1 -1
- package/src/plugin/tools.mjs +1 -1
- package/src/relay/state.mjs +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
|
|
5
|
+
## [1.0.13] — 2026-10-17
|
|
6
|
+
|
|
7
|
+
**Concurrent publishing is verified safe on one relay, and the reason it is safe is stated so a future change can
|
|
8
|
+
falsify it.**
|
|
9
|
+
|
|
10
|
+
### What v1.0.12 recorded as unverified, and is now verified
|
|
11
|
+
|
|
12
|
+
v1.0.12 closed the end-to-end gap **one machine at a time** and wrote concurrency down as open, with a specific
|
|
13
|
+
worry: the blob store keeps its ownership index as a read-modify-write, and a lost update there would leave **a stored
|
|
14
|
+
blob with no owner** -- readable by nobody, including the fleet that wrote it.
|
|
15
|
+
|
|
16
|
+
### Why it turned out to be safe, stated as a property rather than a hope
|
|
17
|
+
|
|
18
|
+
`RabbitState.putFleetBlob` and `Persistence.putBlob` **do no awaiting**: every filesystem call in that path is the
|
|
19
|
+
synchronous `fs` API. The request handler awaits the body first, and after that the whole read-mutate-write runs to
|
|
20
|
+
completion inside one Node job. **On a single-threaded event loop that makes each publish atomic with respect to
|
|
21
|
+
every other request on the same relay process.**
|
|
22
|
+
|
|
23
|
+
**That reasoning is the thing the tests falsify rather than assume.** If any part of that path becomes asynchronous,
|
|
24
|
+
they fail with a lost index entry -- which is much better than finding it in a fleet.
|
|
25
|
+
|
|
26
|
+
### What is verified
|
|
27
|
+
|
|
28
|
+
Four tests in `test/fleet-concurrency.test.mjs`:
|
|
29
|
+
|
|
30
|
+
* **ten publishes fired together** leave **ten** ownership entries, and every blob still readable and still owned -- a
|
|
31
|
+
lost update would show up as a 404;
|
|
32
|
+
* the **latest pointer names a document that was actually written** (not a torn value) and that document resolves;
|
|
33
|
+
* **two fleets publishing at the same moment stay separate** -- each fleet's pointer names its own document and the
|
|
34
|
+
index attributes each blob to the right fleet;
|
|
35
|
+
* the pointer is **stable once the writers stop** -- reading it twice gives the same hash and timestamp.
|
|
36
|
+
|
|
37
|
+
Full suite **2378 tests in 92 files, 2364 pass, 0 fail, 14 skip**. Pre-flight **GREEN, 15 of 15 checks**.
|
|
38
|
+
|
|
39
|
+
### The limit of this claim, which matters more than the claim
|
|
40
|
+
|
|
41
|
+
**One relay process.** The design is one relay per fleet. **Two processes sharing one state directory would need real
|
|
42
|
+
file locking**, and nothing here provides it. "Concurrency is tested" must not be read as "any topology is safe".
|
|
43
|
+
|
|
44
|
+
Also still unverified: **two machines merging while a third publishes.** The merge itself is a pure read of local bytes
|
|
45
|
+
plus one fetched blob, so it has no shared state to corrupt -- but that is reasoning, not a test, and it is recorded
|
|
46
|
+
as such.
|
|
47
|
+
|
|
48
|
+
## [1.0.12] — 2026-10-17
|
|
49
|
+
|
|
50
|
+
**The five fleet-memory actions are verified end to end, against a real relay, a real profile and a real
|
|
51
|
+
`fleet-memory.md` -- and the first attempt found a real defect.**
|
|
52
|
+
|
|
53
|
+
### What was unverified until now
|
|
54
|
+
|
|
55
|
+
`publish`, `pull`, `latest`, `diff` and `merge` had each been shipped marked **UNVERIFIED**. The merge *rule* was
|
|
56
|
+
proven exhaustively as a pure function, and the relay *routes* were proven against a real relay -- **but nothing had
|
|
57
|
+
ever executed the tool surface itself.** A caller with no execution is indistinguishable from a working one, which is
|
|
58
|
+
the same gap this project keeps restating in a different shape.
|
|
59
|
+
|
|
60
|
+
### The defect the end-to-end test found on its first run
|
|
61
|
+
|
|
62
|
+
`latest` reported `published_by: null` for a publication whose author the relay knew perfectly well.
|
|
63
|
+
|
|
64
|
+
`RabbitState.putFleetBlob` accepted a `publishedBy` argument that **nothing ever supplied**: the pointer store took
|
|
65
|
+
it, defaulted it to `null`, and the field looked implemented. Fixed by filling it from **the authenticated device's
|
|
66
|
+
`machine_id`, never from the request body** -- for the same reason the P2P announce route reads `machine_id` off the
|
|
67
|
+
token: a caller that could name itself in the body could attribute a publication to a machine that never made it.
|
|
68
|
+
|
|
69
|
+
**No unit test could have caught this**, because the unit under test was the pointer store, and it was behaving
|
|
70
|
+
exactly as written. The bug lived in the seam between two correct components, which is what an end-to-end test is for.
|
|
71
|
+
|
|
72
|
+
### What the test exercises, in the order a machine would
|
|
73
|
+
|
|
74
|
+
A real relay on an ephemeral port and a real profile, then:
|
|
75
|
+
|
|
76
|
+
1. **`publish`** — returns a 64-hex hash and the byte count.
|
|
77
|
+
2. **`latest`** — discovers that hash **without being told it**, and reports the publishing machine.
|
|
78
|
+
3. **`pull latest`** — fetches the bytes, re-hashes them, and they round-trip **exactly**.
|
|
79
|
+
4. **`diff`** — on an unchanged document: `same: true`, no difference, **and the local file untouched**.
|
|
80
|
+
5. the fleet's document is replaced out of band through the relay's own route;
|
|
81
|
+
6. **`diff`** — now reports the divergence, and the local file is **still untouched**;
|
|
82
|
+
7. **`merge`** — applies the declared rule (neither side declares a repair, so the **default layer** applies and the
|
|
83
|
+
newer publication wins), records the decision with a reason, carries the winning value into the merged text, and
|
|
84
|
+
**writes nothing to the local document.**
|
|
85
|
+
|
|
86
|
+
**`wrote_anything: false` is asserted three times**, because "these actions never edit the file they read" is the
|
|
87
|
+
property that makes it safe to let a network round trip participate at all.
|
|
88
|
+
|
|
89
|
+
### Verified
|
|
90
|
+
|
|
91
|
+
- the end-to-end suite: 1 test, passing, covering all five actions;
|
|
92
|
+
- `test/fleet-merge.test.mjs`: 13 tests over the rule itself;
|
|
93
|
+
- full suite: **2374 tests in 91 files, 2360 pass, 0 fail, 14 skip**;
|
|
94
|
+
- pre-flight **GREEN, 15 of 15 checks**.
|
|
95
|
+
|
|
96
|
+
### NOT verified, even now
|
|
97
|
+
|
|
98
|
+
**Concurrency.** Everything here is one machine at a time. Two machines publishing and merging simultaneously is
|
|
99
|
+
untested, and the pointer has no compare-and-swap, so the last writer to the pointer wins -- which is **the same rule
|
|
100
|
+
as the default layer**, but that is a coincidence rather than a guarantee. Recorded so the next person does not read
|
|
101
|
+
"end to end" as "under contention".
|
|
102
|
+
|
|
5
103
|
## [1.0.11] — 2026-10-17
|
|
6
104
|
|
|
7
105
|
**`w2m_memory {"action":"merge"}`: merge this machine's collective memory with the fleet's current blob, under the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twinsearth/w2m-dsh-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -117,13 +117,13 @@
|
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
119
|
"scripts": {
|
|
120
|
-
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
|
|
120
|
+
"test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
|
|
121
121
|
"test:e2e": "node --test --test-force-exit test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/p2p-agent.test.mjs",
|
|
122
122
|
"test:relay": "node --test --test-force-exit test/relay.test.mjs",
|
|
123
123
|
"test:agent": "node --test --test-force-exit test/agent.test.mjs test/memory.test.mjs",
|
|
124
124
|
"test:tools": "node --test --test-force-exit test/tools.test.mjs",
|
|
125
125
|
"test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
|
|
126
|
-
"test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
|
|
126
|
+
"test:all": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-agent.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/p2p-crypto.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs",
|
|
127
127
|
"pack": "node scripts/pack.mjs",
|
|
128
128
|
"verify:join": "node scripts/verify-join.mjs",
|
|
129
129
|
"lint": "eslint .",
|
|
@@ -138,6 +138,8 @@
|
|
|
138
138
|
"test:approval-index": "node --test --test-force-exit test/approval-index.test.mjs",
|
|
139
139
|
"test:fleet-sync": "node --test --test-force-exit test/fleet-sync.test.mjs",
|
|
140
140
|
"test:fleet-merge": "node --test --test-force-exit test/fleet-merge.test.mjs",
|
|
141
|
+
"test:fleet-memory-e2e": "node --test --test-force-exit test/fleet-memory-e2e.test.mjs",
|
|
142
|
+
"test:fleet-concurrency": "node --test --test-force-exit test/fleet-concurrency.test.mjs",
|
|
141
143
|
"test:health": "node --test --test-force-exit test/health.test.mjs",
|
|
142
144
|
"test:storage": "node --test --test-force-exit test/storage.test.mjs",
|
|
143
145
|
"test:exit-channel": "node --test --test-force-exit test/exit-channel.test.mjs",
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "twinsearth-w2m-dsh",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.13",
|
|
5
5
|
"description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
package/src/plugin/tools.mjs
CHANGED
|
@@ -211,7 +211,7 @@ const MAX_STAGES = 16;
|
|
|
211
211
|
* `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
|
|
212
212
|
* overrides it at runtime for tests and packagers.
|
|
213
213
|
*/
|
|
214
|
-
const PLUGIN_VERSION = '1.0.
|
|
214
|
+
const PLUGIN_VERSION = '1.0.13';
|
|
215
215
|
|
|
216
216
|
/** How long any single HTTP call may take before it is aborted, in milliseconds. */
|
|
217
217
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
package/src/relay/state.mjs
CHANGED
|
@@ -560,7 +560,20 @@ export class RabbitState {
|
|
|
560
560
|
}
|
|
561
561
|
const scope = this.scopeBlobFleet(device);
|
|
562
562
|
if (scope.ok !== true) return { ok: false, reason: scope.reason, detail: scope.text };
|
|
563
|
-
|
|
563
|
+
/**
|
|
564
|
+
* v1.0.12: `publishedBy` comes from **the authenticated device, never from the request body.**
|
|
565
|
+
*
|
|
566
|
+
* The relay knows which device is writing; the request does not get a say, for the same reason `machine_id` is
|
|
567
|
+
* read off the token in the P2P announce route -- a caller that could name itself in the body could attribute a
|
|
568
|
+
* publication to a machine that never made it.
|
|
569
|
+
*
|
|
570
|
+
* An end-to-end test caught this field being `null`: the pointer store accepted a `publishedBy` argument that
|
|
571
|
+
* nothing supplied, so `latest` reported `published_by: null` for a publication whose author was right there.
|
|
572
|
+
*/
|
|
573
|
+
return this._persist.putBlob(bytes, {
|
|
574
|
+
fleetId: scope.fleetId,
|
|
575
|
+
publishedBy: typeof device?.machine_id === 'string' ? device.machine_id : null,
|
|
576
|
+
});
|
|
564
577
|
}
|
|
565
578
|
|
|
566
579
|
/**
|