instar 1.3.257 → 1.3.259
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.map +1 -1
- package/dist/commands/server.js +97 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +18 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SecretSync.d.ts +116 -0
- package/dist/core/SecretSync.d.ts.map +1 -0
- package/dist/core/SecretSync.js +82 -0
- package/dist/core/SecretSync.js.map +1 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +5 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +33 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/eli16-pr-description-check.mjs +77 -0
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +5 -0
- package/upgrades/1.3.258.md +46 -0
- package/upgrades/1.3.259.md +32 -0
- package/upgrades/side-effects/cross-machine-secret-sync.md +112 -0
- package/upgrades/side-effects/eli16-pr-description-gate.md +48 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Finishes Phase 4 of the multi-machine arc: cross-machine secret sync. A secret given to the
|
|
9
|
+
agent on one machine can now be distributed, encrypted, to its other machines — so the same
|
|
10
|
+
agent can use it everywhere without the user re-entering it per machine.
|
|
11
|
+
|
|
12
|
+
- **`SecretSync` module** — `SecretProvisioner` (sender side: encrypts the secret set per
|
|
13
|
+
recipient to that machine's X25519 key and pushes a signed `secret-share` over the mesh) +
|
|
14
|
+
`SecretShareHandler` (receiver side: decrypts with this machine's own key and stores into
|
|
15
|
+
the local encrypted vault) + `secretKeyPaths` (names-only flattening for status).
|
|
16
|
+
- **Reuses the existing forward-secret crypto** (`encryptForSync`/`decryptFromSync`,
|
|
17
|
+
ephemeral-key-per-payload X25519 → AES-GCM); the mesh already enforces TLS, Ed25519
|
|
18
|
+
signing, and the registered-peer gate, so this adds application-layer confidentiality.
|
|
19
|
+
- **Two routes** (both behind auth): `GET /secrets/sync-status` reports which secret key
|
|
20
|
+
NAMES this machine holds plus the peers it would sync to (never a value); `POST
|
|
21
|
+
/secrets/sync-now` is the deterministic push lever (encrypts per online peer and pushes).
|
|
22
|
+
- **Config flag** `multiMachine.secretSync.enabled` — ships dark; defaults on only for the
|
|
23
|
+
development agent. A single-machine agent is a no-op.
|
|
24
|
+
|
|
25
|
+
## What to Tell Your User
|
|
26
|
+
|
|
27
|
+
- **A secret you give me on one machine can become usable on your other machines** — a bot
|
|
28
|
+
token, an API key, a login — without you setting it up again on each computer. It travels
|
|
29
|
+
encrypted to each of your machines and is never written down in plain text along the way.
|
|
30
|
+
- This is an early capability and ships turned off by default. When it is on, if you ever
|
|
31
|
+
start re-entering something you already gave me elsewhere, I can tell you it already synced.
|
|
32
|
+
|
|
33
|
+
## Summary of New Capabilities
|
|
34
|
+
|
|
35
|
+
- Cross-machine secret distribution: drop a secret once, use it on every paired machine.
|
|
36
|
+
- A read-only status view of which secrets a machine holds (names only, never values).
|
|
37
|
+
- A deterministic push lever to sync secrets on demand.
|
|
38
|
+
|
|
39
|
+
## Evidence
|
|
40
|
+
|
|
41
|
+
15 tests across all three tiers: 8 unit (real-crypto round-trip, per-recipient sealing,
|
|
42
|
+
wrong-key-fails confidentiality, best-effort fan-out, names-only flattening), 4 integration
|
|
43
|
+
(real SecretStore + real crypto encrypt→ship→decrypt through the HTTP routes, asserting no
|
|
44
|
+
secret value ever appears in a response, plus the 503-when-disabled paths), and 3 e2e (the
|
|
45
|
+
routes alive through the real AgentServer with a genuine round-trip into a peer vault, behind
|
|
46
|
+
auth). Spec: docs/specs/cross-machine-secret-sync-spec.md (approved).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Added a CI gate (`.github/workflows/eli16-pr-gate.yml` + the pure check
|
|
9
|
+
`scripts/eli16-pr-description-check.mjs`) that requires every PR's DESCRIPTION to carry a
|
|
10
|
+
plain-English ELI16 overview, and FAILS the PR when it is missing. Per Justin's standard
|
|
11
|
+
(2026-06-05): the PR description is what a reviewer reads and approves on the link, and the PRs "had
|
|
12
|
+
different formats." The ELI16 *file* (docs/specs/<slug>.eli16.md) is unchanged and still enforced at
|
|
13
|
+
commit time; this closes the PR-body half. Re-runs on `edited` (self-clears on a description fix);
|
|
14
|
+
exempts bot authors + the automated release-cut PR.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
None — internal change (no user-facing surface).
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
None — internal change (no user-facing surface).
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
|
|
26
|
+
- The decision is a PURE function (`checkPrDescriptionEli16`) with 10 unit tests covering both
|
|
27
|
+
sides (has-overview → pass; missing / too-short / next-heading-boundary / comment-stripped → fail)
|
|
28
|
+
and every exemption (bot, release-cut, title-mentions-release non-exemption, null inputs).
|
|
29
|
+
- `node --check scripts/eli16-pr-description-check.mjs` clean. New additive workflow — cannot affect
|
|
30
|
+
existing checks/builds/releases; a failing PR is visible + self-clears on a description edit.
|
|
31
|
+
- This PR's own description carries an ELI16 overview (dogfoods the gate). scripts + workflow + test
|
|
32
|
+
only, no `src/` (internal-only lane).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Side-Effects Review — Cross-Machine Secret Sync
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `cross-machine-secret-sync`
|
|
4
|
+
**Date:** `2026-06-04`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required` (Tier 2; single-author under Justin's explicit signoff)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Implements Phase 4 of the multi-machine arc: a secret given to the agent on one machine is
|
|
11
|
+
distributed, encrypted per-recipient, to its other registered machines so the same agent can
|
|
12
|
+
use it everywhere without re-entry. New `src/core/SecretSync.ts` holds a `SecretProvisioner`
|
|
13
|
+
(sender: encrypt the secret set to each online peer's X25519 key, push a signed `secret-share`
|
|
14
|
+
over the mesh), a `SecretShareHandler` (receiver: decrypt with this machine's own key, store
|
|
15
|
+
into the local encrypted vault), and `secretKeyPaths` (names-only flattening for status). The
|
|
16
|
+
inbound dispatcher handler was already wired in `src/commands/server.ts`; this change adds the
|
|
17
|
+
OUTBOUND provisioner (constructed after the MeshRpcClient), a route-facing `_secretSyncHandle`,
|
|
18
|
+
two routes in `src/server/routes.ts` (`GET /secrets/sync-status`, `POST /secrets/sync-now`)
|
|
19
|
+
plumbed through `AgentServer` + `RouteContext`, the `multiMachine.secretSync` config type, and
|
|
20
|
+
agent-awareness (CLAUDE.md template + `PostUpdateMigrator` section). Crypto is reused, not new
|
|
21
|
+
(`encryptForSync`/`decryptFromSync`, forward-secret ephemeral-key X25519→AES-GCM).
|
|
22
|
+
|
|
23
|
+
## Decision-point inventory
|
|
24
|
+
|
|
25
|
+
- `multiMachine.secretSync.enabled` gate (server.ts) — **add** — enables inbound handler +
|
|
26
|
+
outbound provisioner; resolves to `config.multiMachine.secretSync.enabled ?? !!developmentAgent`
|
|
27
|
+
(dark for normal agents, on for the dev agent).
|
|
28
|
+
- `SecretProvisioner.listPeers` online filter — **add** — only online, registered peers with a
|
|
29
|
+
resolvable encryption key receive a push.
|
|
30
|
+
- `secret-share` mesh acceptance — **pass-through** — authenticity, signing, registered-peer
|
|
31
|
+
gate, and nonce replay are already enforced by the existing MeshRpcDispatcher verify/RBAC
|
|
32
|
+
layer; this change adds only application-layer confidentiality on top.
|
|
33
|
+
|
|
34
|
+
## 1. Over-block
|
|
35
|
+
|
|
36
|
+
No block/allow surface — over-block not applicable. The two routes 503 only when the feature
|
|
37
|
+
is disabled or the handle is absent; they never reject a legitimate request when enabled.
|
|
38
|
+
|
|
39
|
+
## 2. Under-block
|
|
40
|
+
|
|
41
|
+
No block/allow surface — under-block not applicable. (Security property of note instead: a
|
|
42
|
+
`secret-share` payload sealed to machine A cannot be decrypted by machine B — GCM auth fails —
|
|
43
|
+
covered by the "wrong key fails" unit test.)
|
|
44
|
+
|
|
45
|
+
## 3. Level-of-abstraction fit
|
|
46
|
+
|
|
47
|
+
Right layer. The transport-level guarantees (TLS, Ed25519 signing, registered-peer gate, nonce
|
|
48
|
+
replay) live in the mesh and are reused, not re-implemented. This change is purely the
|
|
49
|
+
application-layer secret-distribution logic over that transport, plus thin route/handle wiring.
|
|
50
|
+
It USES the existing `encryptForSync`/`decryptFromSync` primitive rather than rolling new crypto,
|
|
51
|
+
and USES `SecretStore.read()`/`set()` rather than re-implementing vault access. No higher-level
|
|
52
|
+
gate is duplicated.
|
|
53
|
+
|
|
54
|
+
## 4. Signal vs authority compliance
|
|
55
|
+
|
|
56
|
+
- [x] No — this change has no block/allow surface.
|
|
57
|
+
|
|
58
|
+
The routes are read (status) and an explicit push lever; neither holds block authority over any
|
|
59
|
+
message or operation. The mesh acceptance layer (the existing smart authority) owns
|
|
60
|
+
accept/reject for `secret-share`.
|
|
61
|
+
|
|
62
|
+
## 5. Interactions
|
|
63
|
+
|
|
64
|
+
- **Shadowing:** none. The two new routes are new paths (`/secrets/sync-status`,
|
|
65
|
+
`/secrets/sync-now`) that don't sit before/after any existing check.
|
|
66
|
+
- **Double-fire:** the inbound `secret-share` handler is idempotent at the vault level — a
|
|
67
|
+
re-delivered share just re-writes the same decrypted value (rotate=overwrite, per spec).
|
|
68
|
+
Boot best-effort push + the deterministic route can both push the same set; the receiver
|
|
69
|
+
storing the same value twice is harmless.
|
|
70
|
+
- **Races:** `SecretStore.read`/`set` is read-modify-write to one encrypted file via an atomic
|
|
71
|
+
temp-file rename. Concurrent shares writing different key-paths could interleave a read with a
|
|
72
|
+
write; v1 syncs the whole set on provision and overwrites, so a lost interleaved write is
|
|
73
|
+
re-converged by the next push. Flagged as a known v1 limitation, not a correctness break for
|
|
74
|
+
the single-writer-at-a-time provision pattern.
|
|
75
|
+
- **Feedback loops:** none. A received share does not itself trigger a re-push (no boot/provision
|
|
76
|
+
event fires on inbound store), so shares don't ping-pong between machines.
|
|
77
|
+
|
|
78
|
+
## 6. External surfaces
|
|
79
|
+
|
|
80
|
+
- **Other agents on the same machine:** none — keyed to this agent's own mesh + vault.
|
|
81
|
+
- **Install base:** ships DARK; a normal agent and a single-machine agent are no-ops.
|
|
82
|
+
- **External systems:** none new. Uses the existing mesh transport between the user's own paired
|
|
83
|
+
machines; no third-party service involved.
|
|
84
|
+
- **Persistent state:** writes decrypted secrets into the RECEIVING machine's existing encrypted
|
|
85
|
+
vault (`config.secrets.enc`) — the same store Secret Drop / SecretMigrator already write. No
|
|
86
|
+
new on-disk plaintext; the wire payload is forward-secret encrypted.
|
|
87
|
+
- **Timing:** boot best-effort push is fire-and-forget and never blocks startup.
|
|
88
|
+
|
|
89
|
+
## 7. Rollback cost
|
|
90
|
+
|
|
91
|
+
Pure code change behind a dark flag — revert and ship a patch; no user-visible regression during
|
|
92
|
+
the rollback window (the feature is off for everyone except the dev agent). The only persistent
|
|
93
|
+
side effect is that secrets already pushed to a peer remain in that peer's vault after rollback —
|
|
94
|
+
which is the intended end-state (the user wanted them there) and requires no cleanup. No schema
|
|
95
|
+
or migration to unwind.
|
|
96
|
+
|
|
97
|
+
## Conclusion
|
|
98
|
+
|
|
99
|
+
The review surfaced one honest v1 limitation (whole-set overwrite means an interleaved partial
|
|
100
|
+
write is re-converged on the next push rather than merged) which is acceptable for the
|
|
101
|
+
push-on-provision model and is documented. No design changes were required. Crypto is reused, the
|
|
102
|
+
transport gate is reused, the feature ships dark, and rollback is a clean revert. Clear to ship
|
|
103
|
+
through the ceremony, with live-verification on the real laptop+mini pair as the final gate
|
|
104
|
+
before enabling it beyond the dev agent.
|
|
105
|
+
|
|
106
|
+
## Evidence pointers
|
|
107
|
+
|
|
108
|
+
- 15 tests green: `tests/unit/secret-sync.test.ts` (8), `tests/integration/secret-sync-routes.test.ts`
|
|
109
|
+
(4, real crypto round-trip through HTTP + no-value-leak asserts), `tests/e2e/secret-sync-alive.test.ts`
|
|
110
|
+
(3, real AgentServer alive + round-trip into a peer vault + auth).
|
|
111
|
+
- `tsc --noEmit` clean; `npm run lint` clean.
|
|
112
|
+
- Spec: `docs/specs/cross-machine-secret-sync-spec.md` (approved).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Side-Effects Review — ELI16-on-every-PR gate (PR description)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `eli16-pr-description-gate`
|
|
4
|
+
**Date:** `2026-06-05`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `required (a new gate that blocks EVERY PR)`
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
Justin's standard (2026-06-05): every PR's DESCRIPTION must carry a plain-English ELI16 overview,
|
|
11
|
+
because the description is what a reviewer reads and approves when they open the PR link — and the
|
|
12
|
+
current PRs "have different formats." This adds a CI gate (`.github/workflows/eli16-pr-gate.yml`)
|
|
13
|
+
that runs a pure check (`scripts/eli16-pr-description-check.mjs`) on every PR body and FAILS the PR
|
|
14
|
+
when no ELI16 overview is present. The ELI16 *file* (`docs/specs/<slug>.eli16.md`) is unchanged and
|
|
15
|
+
still enforced at commit time by the instar-dev gate; this is the missing PR-body half.
|
|
16
|
+
|
|
17
|
+
## Decision-point inventory
|
|
18
|
+
|
|
19
|
+
One decision: does the PR body contain an ELI16 overview? PASS when (a) the author is a Bot, or
|
|
20
|
+
(b) the title is a `chore: release` cut, or (c) the body has a heading whose text contains
|
|
21
|
+
`ELI16`/`ELI-16`/`ELI 16` followed by >= 200 chars of (comment-stripped) content. Otherwise FAIL.
|
|
22
|
+
|
|
23
|
+
## 1. Over-block (what it blocks)
|
|
24
|
+
|
|
25
|
+
A human/agent PR whose description lacks a real ELI16 overview is blocked — that's the point. The
|
|
26
|
+
check re-runs on `edited`, so adding the overview to the description clears it WITHOUT a new push.
|
|
27
|
+
The failure message is actionable (tells you exactly what to add + that the file is a good source).
|
|
28
|
+
|
|
29
|
+
## 2. Under-block (what it lets through)
|
|
30
|
+
|
|
31
|
+
Bot PRs and the automated `chore: release` PR (which has no human reviewer reading an ELI16). A PR
|
|
32
|
+
that has a real ELI16 heading + >= 200 chars passes. The check measures content only up to the next
|
|
33
|
+
heading, so a one-line ELI16 followed by a long "What Changed" does NOT slip through (tested).
|
|
34
|
+
|
|
35
|
+
## 3. Blast radius — CRITICAL
|
|
36
|
+
|
|
37
|
+
This gate blocks EVERY non-exempt PR, so a false-fail would block the whole fleet's PRs. Mitigations:
|
|
38
|
+
the decision is a PURE function with 10 unit tests covering both sides AND every exemption (bot,
|
|
39
|
+
release-cut, the "title mentions release" non-exemption, null/empty inputs, the next-heading
|
|
40
|
+
boundary, comment-stripping). The gate is a NEW, additive workflow — it cannot affect existing
|
|
41
|
+
checks, builds, or releases; a failing PR is VISIBLE and self-clears on a description edit (not a
|
|
42
|
+
silent jam like the manifest/publish class). No `src/` runtime change.
|
|
43
|
+
|
|
44
|
+
## 4. Reversibility
|
|
45
|
+
|
|
46
|
+
Fully reversible: delete the workflow + script + test. No state, no migration. Verified: the check
|
|
47
|
+
script parses (`node --check`); 10/10 unit tests pass. This PR's own description carries an ELI16
|
|
48
|
+
overview (dogfoods the gate).
|