instar 1.1.4 → 1.2.1

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.
@@ -0,0 +1,64 @@
1
+ # Side-effects review — Secret Drop hardening
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ **Before.** UNDER-block: a buggy consumer that called `/secrets/retrieve/<token>` once and dropped the response (parse error, exception in the writer step) lost the secret with no recovery path. The aggressive deletion-on-retrieve looked like a tight one-shot guarantee but actually created a silent-loss footgun, because the same 5-minute cleanup timer was already handling memory hygiene.
8
+
9
+ **After.** No new over-block: the default retrieval shape returns the same value the previous code did. Existing callers that don't pass `?consume=true` simply gain the ability to retry within the cleanup window — a safer state, not a different one. Callers that genuinely need one-shot semantics opt in with the query parameter.
10
+
11
+ The new stuck-consumer event closes the silent-failure gap: a submission that lingers unconsumed for 60s now produces a visible cue to the bound topic. Operators see the failure instead of waiting on a value that's already lost.
12
+
13
+ ## 2. Level-of-abstraction fit
14
+
15
+ Three changes at deliberately different altitudes:
16
+
17
+ - `SecretDrop.ts`: new methods (`peekReceived`, `consumeReceived`, `onStuckConsumer`) and the stuck-timer state machine. Pure logic, unit-tested.
18
+ - `routes.ts`: the route honors `?consume=true`; one default listener wires the stuck-consumer event to the topic-bound session via `injectPasteNotification`. Thin orchestration only.
19
+ - Back-compat alias: `getReceived` is kept as `consumeReceived`'s legacy synonym so no caller has to migrate in this PR. Callers migrate at their own pace by switching to `peekReceived` + `consumeReceived` explicitly.
20
+
21
+ ## 3. Signal vs Authority compliance
22
+
23
+ The Secret Drop request remains the authoritative declaration that this token may receive a submission (operator-issued via `/secrets/request`). The submission, once made, remains the authoritative payload — no agent code can mint a fake submission. The new methods change only the lifecycle of an already-authorized value:
24
+
25
+ - `peekReceived`: read-only, idempotent, returns the existing authority.
26
+ - `consumeReceived`: terminates the authority's lifetime explicitly.
27
+ - `onStuckConsumer`: signal-only emission to listeners; no listener gains write access to the submission.
28
+
29
+ No new authority is created. Existing CSRF, rate-limit, expiry, and one-time submission semantics are unchanged.
30
+
31
+ ## 4. Interactions with adjacent systems
32
+
33
+ - **The `/secrets/retrieve/<token>` route.** Default behavior shifts from destructive to non-destructive. Existing callers that don't add `?consume=true` retain the value across multiple calls — strictly safer; no callers depend on "second call returns 404" except as a footgun.
34
+ - **The `/secrets/drop/<token>` form-side route.** Untouched. Operator-facing flow is byte-identical.
35
+ - **`onReceive` callback on the request.** Untouched. Still fires once on submit.
36
+ - **Existing 5-minute auto-cleanup.** Untouched. Still the safety net.
37
+ - **System-message routing via `injectPasteNotification`.** Reused for the new stuck-consumer system message. Same pattern as `secret-drop-received`.
38
+ - **Existing `SecretDrop.test.ts` suite.** All 25 cases continue to pass. The new test file adds 13 cases for the new surface.
39
+
40
+ ## 5. Rollback cost
41
+
42
+ Low. Three files modified (`SecretDrop.ts`, `routes.ts`, one test file added). `git revert <merge-sha>` restores the pre-change atomic retrieve-and-delete behavior. Callers that adopted `?consume=true` continue to work after a revert (the parameter becomes a no-op against the pre-change atomic-delete path). The stuck-consumer event simply stops firing.
43
+
44
+ No state migration, no deployed-agent impact, no schema change. In-memory only.
45
+
46
+ ## 6. Backwards compatibility / drift surface
47
+
48
+ Fully backwards-compatible at the call-site level:
49
+
50
+ - `getReceived(token)` is preserved as an alias for `consumeReceived(token)`.
51
+ - The route default is non-destructive — strictly safer for existing callers.
52
+ - Callers that depend on one-shot semantics (the bridge script in the parallel telegram-history-backfill PR being one example) opt in via `?consume=true` explicitly.
53
+
54
+ Drift surface: small. Two new methods on `SecretDrop`. One new event interface. One new constant for the grace period. The grace period is a hard-coded constant for v1 ship; a future PR can make it configurable per-request if a real need surfaces.
55
+
56
+ ## 7. Authorization / Trust posture
57
+
58
+ No new authority. The two new methods read/write the same in-memory `received` map the previous code touched. The new listener-registration surface (`onStuckConsumer`) accepts callbacks from the route layer only — there is no external API to register a listener over HTTP. The route layer's default listener routes only to the topic-bound session (the same authority the `secret-drop-received` path already uses).
59
+
60
+ Trust ramifications: the operator gains a new visible signal (the stuck-consumer system message), which strengthens their ability to detect consumer-side failures. The agent gains a new structural primitive for safe-by-default retrieval, which reduces the surface for silent-loss bugs. Both directions of trust move in the safer direction.
61
+
62
+ ## Outcome
63
+
64
+ Ship. Incident-driven, three independent changes, fully unit-tested, fully backwards-compatible at the call-site level. The single highest-impact change is the default-non-destructive retrieve — that alone makes the 2026-05-20 lost-SMS-code failure mode structurally impossible to repeat without an explicit `?consume=true` from the buggy consumer.
@@ -0,0 +1,131 @@
1
+ # Side-effects review — Telegram history backfill (script tier)
2
+
3
+ Per L6. Seven dimensions.
4
+
5
+ ## 1. Over-block / under-block
6
+
7
+ **Before.** Echo had no path to retrieve Telegram history after a local
8
+ topic-memory.db truncation. The Bot API is forward-only, so any data loss
9
+ in the local mirror was permanent absent a Telegram-side export. The
10
+ 2026-05-20 native-module cascade exposed this gap.
11
+
12
+ **After.** The script-tier backfill closes the gap for the post-recovery
13
+ scenario. There is no over-block — the script runs only when invoked
14
+ explicitly (`--chat` flag required), never silently. No new automatic
15
+ behavior is added to any always-on path. Under-block protection: the
16
+ importer's UNIQUE-constraint-guarded INSERT OR IGNORE prevents duplicates
17
+ even under retries, FloodWait recoveries, and manual reruns.
18
+
19
+ ## 2. Level-of-abstraction fit
20
+
21
+ Three modules at deliberately different altitudes:
22
+
23
+ - `topic-memory-importer.mjs` — pure DB logic, unit-tested against the
24
+ real schema. No network, no auth, no gramjs dependency. Trivially testable.
25
+ - `telegram-historian.mjs` — gramjs wrapper. Exposes the minimum surface
26
+ needed (connect, listForumTopics, iterTopicMessages). No business logic.
27
+ - `telegram-history-backfill.mjs` — orchestration only. Parses CLI flags,
28
+ resolves credentials, ties the two libs together.
29
+
30
+ Each module is replaceable without touching the others.
31
+
32
+ ## 3. Signal vs Authority compliance
33
+
34
+ This is the central concern for this PR.
35
+
36
+ `.instar/secrets/...` is the credential signal. The operator's sealed
37
+ file is the authoritative declaration that this account may be accessed
38
+ via MTProto. The script reads the signal and acts on it; it has no
39
+ authority to mint or rotate credentials.
40
+
41
+ The session string, once captured, is the long-lived authority. Storing
42
+ it at mode 0600 outside any git path is the structural enforcement that
43
+ no other process can read it. The operator's Telegram Devices page is
44
+ the only revocation channel — exactly the right placement of authority,
45
+ because the operator alone owns that surface.
46
+
47
+ The script's read-only Telegram posture (iter/get only, no send/edit/
48
+ delete) is the third layer: even if everything else were compromised,
49
+ this code path cannot produce outbound messages or mutations against
50
+ Telegram.
51
+
52
+ ## 4. Interactions with adjacent systems
53
+
54
+ - **instar server (echo's own process).** Holds `topic-memory.db` open
55
+ under WAL mode. SQLite serializes writes across processes, so the
56
+ backfill can write while the server is reading without corruption. The
57
+ FTS triggers maintain the FTS index automatically; tested by
58
+ `tests/unit/topic-memory-importer.test.ts`'s "does not corrupt FTS
59
+ index across re-runs" case.
60
+ - **Bot-API polling (TelegramAdapter).** Unaffected. Live messages
61
+ continue to arrive through the bot. The backfill writes the same
62
+ schema with the same constraints, so the live path's INSERT OR IGNORE
63
+ (if any) and the backfill's INSERT OR IGNORE converge to the same
64
+ final state regardless of ordering.
65
+ - **Coherence gate / external-operation-gate hook.** The script is a
66
+ local Node process; it does not go through the MCP tool path the
67
+ external-operation-gate intercepts. The gate's authority remains over
68
+ agent-initiated network operations from the harness; this script is
69
+ operator-initiated from the CLI.
70
+ - **Migration parity / PostUpdateMigrator.** No agent-installed file is
71
+ modified by this PR. No template change, no hook change, no config
72
+ default change. Existing agents are unaffected by the merge — they
73
+ pick up the script only if they explicitly run it.
74
+
75
+ ## 5. Rollback cost
76
+
77
+ Trivial. Five files, two libraries, one test, one spec, one ELI16, one
78
+ side-effects review. `git revert <merge-sha>` removes the code. The
79
+ sealed credential file and the session string file are outside the repo
80
+ and unaffected by the revert. The `topic-memory.db` is touched only by
81
+ INSERT OR IGNORE, so already-imported rows are correct and stay — no
82
+ data restoration needed.
83
+
84
+ If the operator wants the imported rows undone too, a single SQL command
85
+ removes them: `DELETE FROM messages WHERE timestamp < '<backfill cutoff>'`.
86
+ This is safe because all writes go through the importer, which can be
87
+ disabled by removing the script.
88
+
89
+ ## 6. Backwards compatibility / drift surface
90
+
91
+ Fully backwards-compatible. The script is additive only — no existing
92
+ behavior is modified. The `topic-memory.db` schema is read but never
93
+ altered (no migrations). The CLI entry is a new file; nothing references
94
+ it from existing code paths.
95
+
96
+ Drift surface:
97
+ - The script targets the schema as it exists 2026-05-20 (read live for
98
+ this PR). A future schema change (e.g. adding a `chat_id` column)
99
+ would require updating `topic-memory-importer.mjs`. The narrow surface
100
+ and unit-test coverage make this easy to spot in code review.
101
+ - gramjs (`telegram` package) is a new dependency. Version pin is
102
+ `^2.26.22` — caret range allows non-breaking updates. If gramjs ever
103
+ ships a breaking 3.x, the import surface in `telegram-historian.mjs`
104
+ is small (4 imports, 3 method calls) so adaptation cost is bounded.
105
+
106
+ ## 7. Authorization / Trust posture
107
+
108
+ The single point where new authority is granted is the operator's
109
+ explicit hand-off of `api_id` + `api_hash` (2026-05-20T03:10:18Z via
110
+ Telegram topic 10873) and the live SMS-code exchange during `--auth`.
111
+ The script grants itself nothing — it only reads what the operator has
112
+ already authorized.
113
+
114
+ The session string, once captured, is the durable artifact of that
115
+ authorization. The operator can revoke it via Telegram's Devices UI at
116
+ any time; the script will then error on the next run and require a
117
+ fresh `--auth`. This places revocation entirely in the operator's hands
118
+ with no instar-side dependency.
119
+
120
+ The "read-only on Telegram" posture is enforced by **not exposing any
121
+ write method**, not by a guard the operator must trust. Adding a write
122
+ method would require a spec change AND a fresh operator approval — the
123
+ review-convergence + approved frontmatter tags would have to be
124
+ regenerated, which is the structural enforcement here.
125
+
126
+ ## Outcome
127
+
128
+ Ship. Incident-driven, single-purpose, read-only, idempotent, unit-tested.
129
+ Promotion to a full `src/messaging/TelegramHistorian.ts` feature is a
130
+ separate, follow-up PR after the recovery itself is verified end-to-end
131
+ against the live lifeline supergroup.