instar 1.3.374 → 1.3.375
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/dist/commands/server.d.ts +6 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +42 -4
- package/dist/commands/server.js.map +1 -1
- package/dist/server/AgentServer.d.ts +9 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +10 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/users/TopicOperatorStore.d.ts.map +1 -1
- package/dist/users/TopicOperatorStore.js +8 -0
- package/dist/users/TopicOperatorStore.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/{1.3.374.md → 1.3.375.md} +34 -0
- package/upgrades/side-effects/topic-operator-polling-bind-inc2e.md +62 -0
- package/upgrades/topic-operator-polling-bind-inc2e.eli16.md +56 -0
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
|
|
6
6
|
## What Changed
|
|
7
7
|
|
|
8
|
+
The verified topic operator is now recorded automatically on BOTH inbound
|
|
9
|
+
ingress paths (Know Your Principal standard, security-build increment 2e — the
|
|
10
|
+
final operator-binding gap). Increment 2d covered only the lifeline-forward
|
|
11
|
+
route; the adapter long-poll (no-lifeline) path never bound. The bind now lives
|
|
12
|
+
at the shared `onTopicMessage` seam (`wireTelegramRouting`), guarded by
|
|
13
|
+
`isAuthorizedSender` — load-bearing there, because the seam also fires for
|
|
14
|
+
unauthorized senders. The seam resolves the server's own store instance
|
|
15
|
+
late-bound (a second instance on the same file would lose updates between the
|
|
16
|
+
two in-memory caches), and `TopicOperatorStore.setOperator` now skips the disk
|
|
17
|
+
write when the record is unchanged (per-message re-binds become pure reads).
|
|
18
|
+
|
|
8
19
|
`POST /pool/transfer` of a QUIET topic (never-seen or released ownership
|
|
9
20
|
record) previously journaled NOTHING: the placement pin is router-local and
|
|
10
21
|
the handler's release half only fires when the router itself held ownership.
|
|
@@ -25,6 +36,12 @@ other in-flight shape is left strictly untouched.
|
|
|
25
36
|
|
|
26
37
|
## What to Tell Your User
|
|
27
38
|
|
|
39
|
+
Nothing user-facing changes. Foundation wiring (experimental) for the identity
|
|
40
|
+
isolation security work: the agent now learns its verified operator from
|
|
41
|
+
authorized inbound messages no matter how its Telegram connection is set up.
|
|
42
|
+
Only authorized senders are ever recorded, and a recording failure never
|
|
43
|
+
affects message handling.
|
|
44
|
+
|
|
28
45
|
Nothing proactively — this is internal coherence plumbing. If they previously
|
|
29
46
|
moved a quiet conversation between machines and the receiving machine couldn't
|
|
30
47
|
fetch its files, that path now works: the move itself writes the ownership
|
|
@@ -35,6 +52,14 @@ record and it travels to the receiving machine automatically.
|
|
|
35
52
|
|
|
36
53
|
## Summary of New Capabilities
|
|
37
54
|
|
|
55
|
+
- Operator auto-bind at the `onTopicMessage` convergence seam (covers the
|
|
56
|
+
polling path; authorized senders only; fail-soft; pre-boot messages bind
|
|
57
|
+
nothing, fail-safe).
|
|
58
|
+
- `AgentServer.getTopicOperatorStore()` — public accessor for the seam's
|
|
59
|
+
late-bound store resolution.
|
|
60
|
+
- `TopicOperatorStore.setOperator` idempotency: identical record → no disk
|
|
61
|
+
write.
|
|
62
|
+
|
|
38
63
|
- Quiet-topic transfers journal ownership evidence (place + claim halves, real
|
|
39
64
|
epochs, `reason: 'user-move'`) that replicates to the pinned-to machine —
|
|
40
65
|
unblocking its working-set fetch reflex (#930 fallback finally has a producer).
|
|
@@ -45,6 +70,15 @@ record and it travels to the receiving machine automatically.
|
|
|
45
70
|
|
|
46
71
|
## Evidence
|
|
47
72
|
|
|
73
|
+
Verified by 6 Tier-1 unit tests (`topic-operator-polling-bind`: authorized
|
|
74
|
+
binds, unauthorized Caroline refusal, null store / getter throw / setOperator
|
|
75
|
+
throw all fail-soft with routing continuing, missing uid no-op), 2 Tier-1 store
|
|
76
|
+
tests (idempotent skip both sides), and 3 Tier-2 integration tests
|
|
77
|
+
(`topic-operator-polling-bind`: full Caroline replay through the seam,
|
|
78
|
+
single-instance no-clobber across two topics on disk, pre-construction →
|
|
79
|
+
post-construction lifecycle). All 60 existing topic-operator/session-context
|
|
80
|
+
canaries and 13 e2e lifecycle tests stay green. Clean `tsc --noEmit`.
|
|
81
|
+
|
|
48
82
|
- `tests/integration/pool-placement-transfer-routes.test.ts` — 6 new boundary
|
|
49
83
|
tests: quiet/never-seen (both halves journaled, record lands `active`),
|
|
50
84
|
self-owned (release + place + confirm, 3 entries), active-other (never
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Side-effects review — Topic Operator polling-path auto-bind (Know Your Principal #898, increment 2e)
|
|
2
|
+
|
|
3
|
+
## What this change does
|
|
4
|
+
Closes the auto-bind gap #909 documented: the adapter long-poll (no-lifeline)
|
|
5
|
+
ingress path never bound the verified operator. The bind now lives at the
|
|
6
|
+
`onTopicMessage` seam in `wireTelegramRouting` (src/commands/server.ts) — the
|
|
7
|
+
convergence point BOTH ingress paths reach — so a no-lifeline install learns its
|
|
8
|
+
operator too. Three coordinated pieces:
|
|
9
|
+
|
|
10
|
+
- `wireTelegramRouting` gains a late-bound `getTopicOperatorStore` parameter
|
|
11
|
+
(the `getHubDeps` precedent in the same function) and an additive bind block
|
|
12
|
+
early in the callback: an AUTHENTICATED + AUTHORIZED sender is recorded as the
|
|
13
|
+
topic's verified operator.
|
|
14
|
+
- `AgentServer.getTopicOperatorStore()` — public read-only accessor so the seam
|
|
15
|
+
resolves the server's OWN store instance at message-time.
|
|
16
|
+
- `TopicOperatorStore.setOperator` idempotency guard: an identical record skips
|
|
17
|
+
the disk write (both paths re-bind per message; unchanged = pure read).
|
|
18
|
+
|
|
19
|
+
## The load-bearing security property
|
|
20
|
+
The seam fires for unauthorized senders too — the lifeline path only skips its
|
|
21
|
+
own bind, it does not drop the message. So the `isAuthorizedSender` check INSIDE
|
|
22
|
+
the seam bind is load-bearing: without it, an unauthorized group member could
|
|
23
|
+
seat themselves as operator (the cross-principal "Caroline" bug). The
|
|
24
|
+
integration Caroline replay proves the refusal — an unauthorized sender with
|
|
25
|
+
`firstName: "Caroline"` cannot displace the bound operator.
|
|
26
|
+
|
|
27
|
+
## Why the SAME store instance (the lost-update hazard)
|
|
28
|
+
`TopicOperatorStore` caches its map in memory (`this.cache`). A second instance
|
|
29
|
+
on the same file would hold a divergent cache: a record written through one
|
|
30
|
+
instance disappears from the other's next full-map save. The original Inc-2e
|
|
31
|
+
scout suggested constructing a fresh store inside the callback — that design is
|
|
32
|
+
REJECTED here for exactly this reason. Instead the seam resolves
|
|
33
|
+
`AgentServer.getTopicOperatorStore()` late-bound (module-level `_agentServerRef`
|
|
34
|
+
assigned right after construction; the server is built long after routing is
|
|
35
|
+
wired). The integration no-clobber test pins the invariant.
|
|
36
|
+
|
|
37
|
+
## Blast radius (hot path — reviewed carefully)
|
|
38
|
+
- **Additive + fail-soft.** The bind block is wrapped in try/catch; a getter
|
|
39
|
+
error, store error, or missing uid logs and falls through — message routing
|
|
40
|
+
is never affected. Proven by unit tests (getter throws / setOperator throws /
|
|
41
|
+
null store → handleCommand still runs).
|
|
42
|
+
- **Pre-construction window.** Messages arriving before `_agentServerRef` is
|
|
43
|
+
assigned bind nothing (getter → null). Fail-safe: no binding means "unknown",
|
|
44
|
+
never a wrong operator. Lifecycle test covers the transition.
|
|
45
|
+
- **Lifeline double-bind.** On the lifeline path both the routes-side bind
|
|
46
|
+
(#909) and the seam bind run — same instance, same record; the new
|
|
47
|
+
idempotency guard makes the second a no-op read.
|
|
48
|
+
- **Disk-write reduction (behavior change, benign).** `setOperator` with a
|
|
49
|
+
byte-identical record no longer rewrites the file. Callers only ever read the
|
|
50
|
+
returned record or the store state — both unchanged. Existing store tests
|
|
51
|
+
(12) stay green; 2 new tests pin both sides (identical skips, changed
|
|
52
|
+
writes).
|
|
53
|
+
- **No new route / config key / dependency.** `wireTelegramRouting` is now
|
|
54
|
+
exported (test seam only; no runtime caller change beyond the two existing
|
|
55
|
+
callsites gaining the getter argument).
|
|
56
|
+
|
|
57
|
+
## No-allowlist trust model (unchanged, documented)
|
|
58
|
+
`isAuthorizedSender` semantics are #909's: with no `authorizedUserIds`
|
|
59
|
+
allowlist, every authenticated sender is accepted, so the operator is the
|
|
60
|
+
most-recent authenticated sender. Consistent with the existing trust model;
|
|
61
|
+
the uid is Telegram-authenticated, never a content name
|
|
62
|
+
(`TopicOperatorStore.setOperator` enforces that by construction, #904).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# ELI16 — Remembering the boss on the simpler phone line too
|
|
2
|
+
|
|
3
|
+
## The one-sentence version
|
|
4
|
+
The agent now writes down "this is the verified boss of this chat" no matter
|
|
5
|
+
which of its two message pipes the boss's message arrived through — before, one
|
|
6
|
+
pipe did this and the other forgot.
|
|
7
|
+
|
|
8
|
+
## The backstory
|
|
9
|
+
We've been closing a real security hole: an agent on a shared computer slowly
|
|
10
|
+
started treating a *different real person* (call her Caroline) as its boss. The
|
|
11
|
+
fix was a filing cabinet that records, per chat, who the verified boss is —
|
|
12
|
+
decided ONLY by the verified ID of whoever actually sent the message, never by a
|
|
13
|
+
name typed in a document. The last step (increment 2d) made the cabinet fill in
|
|
14
|
+
automatically — but only on the agent's MAIN message pipe (the relay most of the
|
|
15
|
+
fleet uses). A simpler setup, where the agent talks to Telegram directly without
|
|
16
|
+
the relay, still never filled in the cabinet. That was safe (an empty cabinet
|
|
17
|
+
just means "I don't know," never a wrong answer), but it meant some agents never
|
|
18
|
+
learned who their boss was.
|
|
19
|
+
|
|
20
|
+
## What this change adds
|
|
21
|
+
Both pipes eventually flow through one shared doorway in the code (the
|
|
22
|
+
`onTopicMessage` seam). This change puts the "write down the boss" step at that
|
|
23
|
+
doorway, so it runs no matter which pipe delivered the message. Two careful
|
|
24
|
+
details:
|
|
25
|
+
|
|
26
|
+
1. **The doorway still checks the allowed list.** The main pipe lets messages
|
|
27
|
+
from non-allowed people *through the doorway* (it just refuses to bind them
|
|
28
|
+
earlier) — so the doorway check is load-bearing. Without it, an outsider in
|
|
29
|
+
the group could seat themselves as boss — the exact Caroline bug. We test
|
|
30
|
+
this with a literal unauthorized "Caroline" message and prove nobody gets
|
|
31
|
+
written down.
|
|
32
|
+
2. **One cabinet, not two.** The cabinet keeps a copy of its contents in memory.
|
|
33
|
+
If the doorway opened its OWN second cabinet on the same file, the two copies
|
|
34
|
+
could silently overwrite each other's entries. So the doorway asks the server
|
|
35
|
+
for its existing cabinet (resolved late, because the server is built after
|
|
36
|
+
the doorway is wired) — same instance, no lost entries. A test proves an
|
|
37
|
+
entry written one way survives an entry written the other way.
|
|
38
|
+
|
|
39
|
+
## A small bonus fix
|
|
40
|
+
Since both pipes now re-write the boss on every message, the cabinet learned to
|
|
41
|
+
notice "this is exactly what I already have" and skip the disk write — a
|
|
42
|
+
re-delivered or repeated message is now a pure read instead of a pointless file
|
|
43
|
+
rewrite.
|
|
44
|
+
|
|
45
|
+
## Why it's safe
|
|
46
|
+
- Wrapped so any failure is logged and swallowed — recording the boss can never
|
|
47
|
+
break message handling.
|
|
48
|
+
- Before the server finishes booting, the doorway just skips the step (no
|
|
49
|
+
cabinet yet = nothing written = fail-safe).
|
|
50
|
+
- On the main pipe both the old write (increment 2d) and the new doorway write
|
|
51
|
+
run — same cabinet, same record, harmless.
|
|
52
|
+
|
|
53
|
+
## What's deliberately left for next time
|
|
54
|
+
Nothing in this family — the operator-binding loop is now closed on every
|
|
55
|
+
ingress path. The remaining Know Your Principal work is elsewhere (per-agent
|
|
56
|
+
credential isolation, Phase 3).
|