opencode-secrets-obfuscation 0.1.0 → 0.1.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.
- package/LICENSE +21 -21
- package/README.md +1 -31
- package/package.json +29 -23
- package/plugin.mjs +3841 -0
- package/.editorconfig +0 -12
- package/.github/workflows/ci.yml +0 -75
- package/.github/workflows/release.yml +0 -50
- package/.node-version +0 -1
- package/AGENTS.md +0 -23
- package/INSTALL.md +0 -104
- package/docs/checkpoint.md +0 -68
- package/docs/history/1-spec-review.md +0 -233
- package/docs/history/10-p0-launch-prerequisites-review.md +0 -186
- package/docs/history/11-p0-job-containment-review.md +0 -188
- package/docs/history/12-p0-api-client.md +0 -15
- package/docs/history/13-p0-status-review.md +0 -122
- package/docs/history/14-p0-containment-final-review.md +0 -84
- package/docs/history/15-config-source-implementation.md +0 -27
- package/docs/history/16-sanitizer-implementation.md +0 -65
- package/docs/history/17-store-policy-implementation.md +0 -36
- package/docs/history/18-pure-mvp-review.md +0 -182
- package/docs/history/19-pure-mvp-re-review.md +0 -142
- package/docs/history/2-p0-runtime-prerequisites.md +0 -163
- package/docs/history/20-pure-mvp-final-review.md +0 -116
- package/docs/history/21-pure-mvp-review-final.md +0 -109
- package/docs/history/22-pure-mvp-signoff-review.md +0 -113
- package/docs/history/23-pure-mvp-signoff-review.md +0 -75
- package/docs/history/24-owned-tools-implementation.md +0 -27
- package/docs/history/25-v2-plugin-integration.md +0 -53
- package/docs/history/26-owned-tools-review.md +0 -138
- package/docs/history/27-owned-tools-final-review.md +0 -93
- package/docs/history/28-owned-tools-signoff.md +0 -98
- package/docs/history/3-p0-harness-foundation.md +0 -105
- package/docs/history/4-p0-source-opening.md +0 -78
- package/docs/history/5-qualification-gate-review.md +0 -133
- package/docs/history/6-p0-infrastructure-review.md +0 -275
- package/docs/history/7-p0-job-containment.md +0 -29
- package/docs/history/8-p0-evidence-support.md +0 -52
- package/docs/history/9-p0-launch-plan.md +0 -88
- package/docs/spec/design.md +0 -111
- package/docs/spec/intent.md +0 -81
- package/docs/spec/plan.md +0 -78
- package/docs/testing.md +0 -157
- package/scripts/build-plugin.mjs +0 -84
- package/scripts/install-opencode-ci.ps1 +0 -39
- package/scripts/install-opencode-ci.sh +0 -27
- package/scripts/smoke.mjs +0 -105
- package/scripts/verify-package.mjs +0 -18
- package/src/README.md +0 -22
- package/src/budgets/index.ts +0 -49
- package/src/config/README.md +0 -4
- package/src/config/options.ts +0 -76
- package/src/diagnostics/README.md +0 -4
- package/src/opencode/v2/filter.ts +0 -350
- package/src/opencode/v2/index.ts +0 -86
- package/src/opencode/v2/runtime.ts +0 -207
- package/src/opencode/v2/transports/README.md +0 -5
- package/src/runtime/http-transport.ts +0 -20
- package/src/sanitize/README.md +0 -4
- package/src/sanitize/index.ts +0 -454
- package/src/sources/README.md +0 -5
- package/src/sources/dotenv.ts +0 -133
- package/src/sources/native/README.md +0 -5
- package/src/store/index.ts +0 -64
- package/tests/README.md +0 -26
- package/tests/contract/plugin-v2.test.ts +0 -29
- package/tests/fixtures/README.md +0 -9
- package/tests/fixtures/job-child.mjs +0 -18
- package/tests/fixtures/plugins/README.md +0 -8
- package/tests/fixtures/process-child.mjs +0 -20
- package/tests/fixtures/services/README.md +0 -10
- package/tests/integration/components.test.ts +0 -5
- package/tests/integration/transparent-runtime.test.ts +0 -97
- package/tests/integration/windows-job.test.ts +0 -71
- package/tests/p0-host/README.md +0 -24
- package/tests/p0-host/linux-job.sh +0 -61
- package/tests/p0-host/windows-job.ps1 +0 -51
- package/tests/p0-native/README.md +0 -33
- package/tests/p0-native/windows-handle-loader.tests.ps1 +0 -191
- package/tests/scaffold/repository.test.ts +0 -105
- package/tests/smoke/README.md +0 -37
- package/tests/smoke/cases.json +0 -17
- package/tests/smoke/qualification.json +0 -20
- package/tests/smoke/runtime-candidate.json +0 -111
- package/tests/support/README.md +0 -32
- package/tests/support/child-process.ts +0 -235
- package/tests/support/host-api.ts +0 -123
- package/tests/support/opencode-environment.ts +0 -239
- package/tests/support/qualification.ts +0 -154
- package/tests/support/recording-endpoint.ts +0 -242
- package/tests/support/run-directory.ts +0 -179
- package/tests/support/smoke-cases.ts +0 -289
- package/tests/unit/budgets.test.ts +0 -35
- package/tests/unit/config.test.ts +0 -40
- package/tests/unit/dotenv.test.ts +0 -72
- package/tests/unit/filter-size.test.ts +0 -132
- package/tests/unit/harness.test.ts +0 -228
- package/tests/unit/host-api.test.ts +0 -122
- package/tests/unit/opencode-environment.test.ts +0 -161
- package/tests/unit/qualification.test.ts +0 -128
- package/tests/unit/recording-endpoint.test.ts +0 -188
- package/tests/unit/sanitize.test.ts +0 -159
- package/tests/unit/smoke-cases.test.ts +0 -20
- package/tsconfig.json +0 -20
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
# P0 infrastructure adversarial review
|
|
2
|
-
|
|
3
|
-
Date: 2026-10-13
|
|
4
|
-
Scope: `tests/support/run-directory.ts`, `child-process.ts`,
|
|
5
|
-
`recording-endpoint.ts`; their unit tests; `tests/p0-native/*`; and the P0
|
|
6
|
-
history documents. `qualification.ts` and runtime-pin work were intentionally
|
|
7
|
-
excluded.
|
|
8
|
-
|
|
9
|
-
## Verdict
|
|
10
|
-
|
|
11
|
-
The support code is useful synthetic fixture infrastructure, but it is not yet
|
|
12
|
-
a safe real-host harness. In particular, child-process teardown is not awaited,
|
|
13
|
-
pre-cancellation still reaches `spawn`, Windows executable/source trust is not
|
|
14
|
-
established, and cleanup is vulnerable to races. The native Windows fixture is
|
|
15
|
-
correctly labelled partial in the history document, but one of its reparse
|
|
16
|
-
tests does not exercise the advertised protection. No P0 native or real-host
|
|
17
|
-
qualification claim is supported.
|
|
18
|
-
|
|
19
|
-
## Must fix
|
|
20
|
-
|
|
21
|
-
### 1. Child failures settle before the child exits (critical)
|
|
22
|
-
|
|
23
|
-
`tests/support/child-process.ts:96-105` sets `settled = true`, calls
|
|
24
|
-
`child.kill()`, and rejects immediately. The `close` handler at lines 132-138
|
|
25
|
-
is consequently ignored. This applies to timeout, cancellation, startup
|
|
26
|
-
timeout, output limit, and child error paths. The helper therefore does not
|
|
27
|
-
provide the claimed “owned child has terminated” boundary; it also does not
|
|
28
|
-
wait for stdout/stderr streams to close before discarding the private result.
|
|
29
|
-
|
|
30
|
-
Reproduction: `node --test tests/unit/harness.test.ts` on the review Windows
|
|
31
|
-
host failed the output-limit case during cleanup with:
|
|
32
|
-
`EBUSY ... rmdir ...run-...`. The test had already received `OUTPUT_LIMIT`, but
|
|
33
|
-
the direct Node child still held the run directory. This is a deterministic
|
|
34
|
-
false pass opportunity: callers can proceed, remove the run directory, or
|
|
35
|
-
launch another fixture while the failed child is still running. Fix by making
|
|
36
|
-
failure enter a terminating state, perform bounded escalation, and reject only
|
|
37
|
-
after the direct child's `close`/stream teardown (with a bounded cleanup
|
|
38
|
-
failure code if it cannot be proven dead).
|
|
39
|
-
|
|
40
|
-
### 2. “Pre-cancelled does not spawn” is not tested and is false
|
|
41
|
-
|
|
42
|
-
`child-process.ts:66-75` calls `spawn` before `child-process.ts:106-110`
|
|
43
|
-
checks `signal.aborted`. The test named
|
|
44
|
-
`pre-cancelled child execution does not spawn` only checks the returned error
|
|
45
|
-
(`tests/unit/harness.test.ts:89-107`); it has no spawn oracle. A pre-aborted
|
|
46
|
-
signal therefore starts the executable and then kills it. A cancellation race
|
|
47
|
-
after validation has the same issue. Check the signal before `spawn`, and test
|
|
48
|
-
with a fixture that leaves an observable synthetic marker (or injected spawn
|
|
49
|
-
adapter), not merely the error code.
|
|
50
|
-
|
|
51
|
-
### 3. Direct-child kill is not a process-tree boundary
|
|
52
|
-
|
|
53
|
-
The documented limitation in `docs/history/3-p0-harness-foundation.md:24-29`
|
|
54
|
-
is honest, but it blocks the real-service P0 harness. `child-process.ts:102-104`
|
|
55
|
-
only calls `child.kill()` on the returned PID. Descendants can survive,
|
|
56
|
-
retain handles to the private directory, continue network activity, or outlive
|
|
57
|
-
cleanup. There is no escalation if the direct child ignores the first signal,
|
|
58
|
-
and no Windows Job Object / POSIX process-group ownership proof. The P0 exit
|
|
59
|
-
gate requires owned-process cleanup; this helper cannot satisfy it until a
|
|
60
|
-
platform-qualified tree boundary is added and tested, including descendants,
|
|
61
|
-
timeouts, cancellation, and forced escalation.
|
|
62
|
-
|
|
63
|
-
### 4. Executable trust is only an absolute-path check
|
|
64
|
-
|
|
65
|
-
`child-process.ts:46-55` accepts any absolute executable. It does not resolve
|
|
66
|
-
the final object, reject symlinks/reparse points, verify ownership/ACLs,
|
|
67
|
-
validate the expected file identity/hash, or ensure the executable remains the
|
|
68
|
-
same object through launch. An absolute path to a writable or replaced binary
|
|
69
|
-
is not a trusted executable resolution. This is especially material because
|
|
70
|
-
the child environment is otherwise deliberately minimal. The harness needs an
|
|
71
|
-
explicit, pinned artifact identity and platform-appropriate opened-object or
|
|
72
|
-
equivalent launch verification; otherwise a test can pass while running an
|
|
73
|
-
unexpected executable.
|
|
74
|
-
|
|
75
|
-
### 5. Run-directory privacy and cleanup have TOCTOU gaps
|
|
76
|
-
|
|
77
|
-
On Windows, `run-directory.ts:48-64` removes inheritance and grants the
|
|
78
|
-
current SID, but does not verify the complete ACL or remove pre-existing
|
|
79
|
-
explicit broad/unknown ACEs. The check only requires one `(F)` line and no
|
|
80
|
-
`(I)` line (`:59-60`). Thus an existing root with explicit broad access can be
|
|
81
|
-
reported private. Children created later are not independently checked, and
|
|
82
|
-
the unit suite does not inspect child ACLs (`harness.test.ts:10-22`).
|
|
83
|
-
|
|
84
|
-
`run-directory.ts:124-127` validates the marker and then recursively walks by
|
|
85
|
-
path. Between `assertOwned` and each `lstat/readdir/rmdir` (`:75-89`), an
|
|
86
|
-
attacker or concurrent process can replace a directory with a junction,
|
|
87
|
-
symlink, or mount/reparse object, or replace the marker/tree. `lstat` avoids
|
|
88
|
-
following a link at the instant it runs but is not a race-free deletion
|
|
89
|
-
boundary. Cleanup must use an owned directory handle / no-follow traversal (or
|
|
90
|
-
an equivalent OS primitive), revalidate identity, and refuse unsafe races.
|
|
91
|
-
|
|
92
|
-
The root itself is created or reused at `:91-95` and is not marker-owned;
|
|
93
|
-
`mkdir(..., recursive: true)` plus chmod can operate on a caller-supplied
|
|
94
|
-
existing path. The ownership contract is only for the run child, so this must
|
|
95
|
-
be explicit in the API and documentation rather than treated as private-root
|
|
96
|
-
proof.
|
|
97
|
-
|
|
98
|
-
### 6. Native Windows reparse test disables its own protection
|
|
99
|
-
|
|
100
|
-
In `tests/p0-native/windows-handle-loader.tests.ps1:30-35`, `OpenRelative`
|
|
101
|
-
builds `options` with `0x00200000` (`FILE_OPEN_REPARSE_POINT`) and then passes
|
|
102
|
-
`options & ~0x00200000` to `NtCreateFile`. `OBJECT_ATTRIBUTES.Attributes`
|
|
103
|
-
contains only `OBJ_CASE_INSENSITIVE` at line 32; `OBJ_DONT_REPARSE` is declared
|
|
104
|
-
but never used. Consequently the fixture does not prove that component opens
|
|
105
|
-
reject reparse points. The history claim at
|
|
106
|
-
`docs/history/4-p0-source-opening.md:23-27` overstates this specific evidence,
|
|
107
|
-
even though the overall document correctly remains partial. Correct the native
|
|
108
|
-
flags and add a setup-independent result that distinguishes unavailable link
|
|
109
|
-
creation from a genuine rejection test.
|
|
110
|
-
|
|
111
|
-
## Additional findings / false-pass risks
|
|
112
|
-
|
|
113
|
-
- `child-process.ts:46-55` assumes typed options at runtime. `null`, missing
|
|
114
|
-
`args`/`allowedEnv`, non-string environment values, or non-array values can
|
|
115
|
-
throw `TypeError` or reach `spawn` instead of returning `INVALID_OPTIONS`.
|
|
116
|
-
Malformed-option tests are absent. Also, `Buffer.concat` at `:123`/`:130`
|
|
117
|
-
repeatedly copies all prior data; the byte check bounds retained data but not
|
|
118
|
-
transient allocation or chunk size.
|
|
119
|
-
- The output-limit unit test (`harness.test.ts:52-69`) proves only an error
|
|
120
|
-
code. It does not prove process exit, descendant cleanup, no post-failure
|
|
121
|
-
output allocation, or that cleanup can safely run. Its current `EBUSY` is
|
|
122
|
-
evidence that the test itself exposes the lifecycle defect rather than a
|
|
123
|
-
qualification pass.
|
|
124
|
-
- `recording-endpoint.ts:61-105` bounds request capture and clears pending
|
|
125
|
-
buffers on close, and the focused recording tests passed. However, the close
|
|
126
|
-
test (`recording-endpoint.test.ts:88-99`) asserts only zero public captures;
|
|
127
|
-
it does not independently prove that a request handler is no longer pending,
|
|
128
|
-
that all sockets are closed before `close()` resolves, or that a late
|
|
129
|
-
`data/end/close` event cannot repopulate capture state. Add a post-close
|
|
130
|
-
event/barrier assertion and a bounded close failure path. Invalid runtime
|
|
131
|
-
shapes such as `options = null`, `responses = null`, or non-string bodies
|
|
132
|
-
also produce incidental TypeErrors rather than the fixed diagnostic.
|
|
133
|
-
- `recording-endpoint.ts:109` increments `rejected` for `clientError`, while
|
|
134
|
-
`attempted` is incremented only in the request handler (`:52`). Counts can
|
|
135
|
-
therefore describe different populations during malformed-client tests;
|
|
136
|
-
define the counter contract before using it as a zero-egress oracle.
|
|
137
|
-
- The Windows native fixture uses `Remove-Item -Recurse -Force` in its finalizer
|
|
138
|
-
(`windows-handle-loader.tests.ps1:100`). This is acceptable for its exact
|
|
139
|
-
generated synthetic path only after the fixture has proved no handles remain,
|
|
140
|
-
but it is not a substitute for the production owned-tree cleanup boundary.
|
|
141
|
-
|
|
142
|
-
## Tests and gate status
|
|
143
|
-
|
|
144
|
-
- `node --test tests/unit/harness.test.ts tests/unit/recording-endpoint.test.ts`:
|
|
145
|
-
13 passed, 1 failed. The failed output-limit test ended in `EBUSY` during
|
|
146
|
-
run-directory cleanup, as described above.
|
|
147
|
-
- `npm.cmd run check`: typecheck passed; the full test command had the two
|
|
148
|
-
known `qualification.ts` failures (malformed external evidence and accessor
|
|
149
|
-
handling), which are outside this review scope. The remaining suites include
|
|
150
|
-
26 TODO tests; TODO/scaffold passes are not security evidence.
|
|
151
|
-
- The Windows native script was not rerun as part of this review. The prior
|
|
152
|
-
recorded result is deliberately partial with reparse and hard-link setup
|
|
153
|
-
gaps, and the flag defect above makes its reparse result non-qualifying.
|
|
154
|
-
- No OpenCode host, user config, credential, shared service, or live endpoint
|
|
155
|
-
was accessed. Focused tests used only synthetic values and loopback/local
|
|
156
|
-
fixtures.
|
|
157
|
-
|
|
158
|
-
## Remaining P0 blockers
|
|
159
|
-
|
|
160
|
-
1. Pin and verify the runtime/artifacts (separate review work), then build a
|
|
161
|
-
real isolated host harness with private config/data/home and a qualified
|
|
162
|
-
process-tree boundary.
|
|
163
|
-
2. Make cancellation, timeout, output overflow, and startup failure settle
|
|
164
|
-
only after owned process and stream teardown; test forced escalation and
|
|
165
|
-
descendants on each target OS.
|
|
166
|
-
3. Establish trusted executable identity and race-safe private run-directory
|
|
167
|
-
creation/cleanup, including effective child ACL inheritance.
|
|
168
|
-
4. Repair and rerun the Windows native spike, add the required Linux/ext4
|
|
169
|
-
spike, and test ACLs, ancestor replacement, swaps, links, filesystem
|
|
170
|
-
identity, and post-read identity/size consistency.
|
|
171
|
-
5. Implement actual P0-BOOT/P0-HOOKS/P0-ABORT/quotas evidence. The inventory in
|
|
172
|
-
`tests/smoke/cases.json` is planned only; no real-host case is executable.
|
|
173
|
-
6. Keep the release unqualified. A partial native fixture, unit harness pass,
|
|
174
|
-
or expected-gap/TODO result must not be converted into platform or security
|
|
175
|
-
qualification.
|
|
176
|
-
|
|
177
|
-
## Re-review: P0-B/P0-C primitive repairs
|
|
178
|
-
|
|
179
|
-
Date: 2026-10-13
|
|
180
|
-
Scope: repaired run-directory/child-process helpers, their 8-test harness,
|
|
181
|
-
the synthetic child fixture, and `tests/p0-native/*`. The host Job Object and
|
|
182
|
-
environment-builder work was excluded, as were qualification and recording
|
|
183
|
-
helpers.
|
|
184
|
-
|
|
185
|
-
### Verification result
|
|
186
|
-
|
|
187
|
-
- `node --test tests/unit/harness.test.ts`: **8 passed, 0 skipped**.
|
|
188
|
-
- `npm.cmd exec -- tsc --noEmit --pretty false`: passed.
|
|
189
|
-
- `pwsh -NoProfile -File tests/p0-native/windows-handle-loader.tests.ps1`:
|
|
190
|
-
returned the intentional partial result
|
|
191
|
-
`P0-C-WINDOWS: PARTIAL (NTFS/relative/identity/read/post-read/hardlink/junction-rejection=True; gaps=reparse-create-1314,junction-native-create-4392)`.
|
|
192
|
-
The nonzero exit is appropriate for the setup gaps. No user configuration,
|
|
193
|
-
credential, shared service, or non-generated path was accessed.
|
|
194
|
-
|
|
195
|
-
### Prior findings verified repaired
|
|
196
|
-
|
|
197
|
-
1. **Failure teardown:** `child-process.ts:166-181,220-230` now enters a
|
|
198
|
-
terminating state, stops accepting output, escalates after a grace period,
|
|
199
|
-
and rejects only after child/stdout/stderr close. The former Windows `EBUSY`
|
|
200
|
-
cleanup failure did not recur.
|
|
201
|
-
2. **Pre-abort:** `child-process.ts:90-95,184-191` checks cancellation before
|
|
202
|
-
hashing/spawn. `harness.test.ts:102-114` uses the fixture marker and proves
|
|
203
|
-
no launch occurred.
|
|
204
|
-
3. **Executable identity and malformed options:** runtime validation and a
|
|
205
|
-
caller-supplied SHA-256 are enforced at `child-process.ts:53-95`; the
|
|
206
|
-
mismatch and malformed-input cases are exercised at
|
|
207
|
-
`harness.test.ts:116-126`.
|
|
208
|
-
4. **Output bounds:** output is counted before retention at
|
|
209
|
-
`child-process.ts:202-218`; the test now proves teardown completes before
|
|
210
|
-
cleanup (`harness.test.ts:80-89`). This bounds retained synthetic output,
|
|
211
|
-
not the stream chunk allocated by Node before the handler sees it.
|
|
212
|
-
5. **Run-root safety:** existing roots are verified but not chmod/ACL-modified
|
|
213
|
-
(`run-directory.ts:118-128`). POSIX ancestor ownership/write checks and
|
|
214
|
-
Windows current-SID ACL inspection are present. Windows new-directory ACL
|
|
215
|
-
setup removes prior rules and adds the current SID with container/object
|
|
216
|
-
inheritance (`run-directory.ts:83-99`). Child verification is exposed and
|
|
217
|
-
tested (`harness.test.ts:27-36`).
|
|
218
|
-
6. **Cleanup scope:** cleanup now refuses anything other than the exact
|
|
219
|
-
marker-only directory (`run-directory.ts:110-116`), including nested links;
|
|
220
|
-
it does not recursively delete arbitrary content. Marker replacement and
|
|
221
|
-
outside-file preservation are tested (`harness.test.ts:27-49,52-61`).
|
|
222
|
-
7. **Native no-reparse/open checks:** `OpenRelative` now passes both
|
|
223
|
-
`OBJ_DONT_REPARSE` and `FILE_OPEN_REPARSE_POINT` and rejects a returned
|
|
224
|
-
reparse handle (`windows-handle-loader.tests.ps1:45-50`). The probe tests
|
|
225
|
-
handle identity, same-handle read, post-swap identity/read, hard-link count,
|
|
226
|
-
owner/DACL policy, broad inherited ACEs, and an actual junction rejection
|
|
227
|
-
when setup permits (`:121-185`).
|
|
228
|
-
|
|
229
|
-
### Must-fix or gate blockers remaining
|
|
230
|
-
|
|
231
|
-
- **P0-B is still direct-child only.** `child-process.ts:172-176` explicitly
|
|
232
|
-
signals only the PID returned by `spawn`; the comments correctly make no
|
|
233
|
-
tree claim. The excluded Job Object/process-group work must establish and
|
|
234
|
-
test descendant containment before this can launch a real service. The
|
|
235
|
-
`TEARDOWN_FAILED` timer proves only that this helper eventually refuses to
|
|
236
|
-
claim teardown, not that descendants are gone.
|
|
237
|
-
- **Hash-before-spawn is not race-proof.** `child-process.ts:79-87` uses
|
|
238
|
-
`lstat`/`readFile` and then launches by pathname. It is an adequate
|
|
239
|
-
synthetic artifact identity check and the history now says so, but a hostile
|
|
240
|
-
replacement between hashing and spawn can run another file. A real-host gate
|
|
241
|
-
still needs an OS-qualified launch/open identity strategy or trusted artifact
|
|
242
|
-
boundary. The hash read itself is also not size-bounded before allocation;
|
|
243
|
-
production artifact validation should impose a size limit or streaming
|
|
244
|
-
policy.
|
|
245
|
-
- **Cleanup is intentionally not race-free.** The marker-only check followed
|
|
246
|
-
by `rm(marker)`/`rmdir(path)` (`run-directory.ts:110-116`) narrows damage and
|
|
247
|
-
refuses unexpected content, but it is path-based. A hostile concurrent
|
|
248
|
-
replacement can race the checks. This is acceptable as a synthetic fixture
|
|
249
|
-
primitive with an explicit limitation, not as private real-host evidence;
|
|
250
|
-
real qualification still needs handle-relative/no-follow cleanup or an
|
|
251
|
-
equivalent owned OS boundary.
|
|
252
|
-
- **Windows ACL evidence is shape-limited.** The helper requires exactly one
|
|
253
|
-
current-SID rule for its private run directories (`run-directory.ts:70-80`),
|
|
254
|
-
and the source probe’s native policy covers the tested current SID,
|
|
255
|
-
SYSTEM/Administrators, unknown/broad/deny ACE, owner, and replacement-mask
|
|
256
|
-
shapes. It does not establish all effective-token/group/ACE/reparse-tag
|
|
257
|
-
combinations. The source probe’s `OpenDirectory` remains path-based for the
|
|
258
|
-
fixture root and uses share-write/delete; this is not a production
|
|
259
|
-
race-free source loader.
|
|
260
|
-
- **P0-C remains Windows partial.** The successful junction fallback is useful
|
|
261
|
-
evidence of actual ancestor rejection, but native symlink creation (1314)
|
|
262
|
-
and native junction creation (4392) were unavailable on this worker. Linux
|
|
263
|
-
ext4, concurrent ACL changes, final size consistency under mutation, and
|
|
264
|
-
alternate reparse tags remain untested. No platform qualification follows.
|
|
265
|
-
|
|
266
|
-
### Scope conclusion
|
|
267
|
-
|
|
268
|
-
The repaired code now provides credible bounded **synthetic test primitives**
|
|
269
|
-
for the previously failing direct-child lifecycle, pre-cancellation,
|
|
270
|
-
artifact-hash, private-directory setup, cleanup refusal, and Windows handle
|
|
271
|
-
policy cases. It must not be described as hostile real-host isolation,
|
|
272
|
-
process-tree cleanup, race-free executable launch, race-free cleanup, or
|
|
273
|
-
Windows/Linux source-loader qualification. P0-B/P0-C release gates therefore
|
|
274
|
-
remain open pending the excluded host process boundary plus the native/runtime
|
|
275
|
-
and Linux evidence listed above.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# P0 job containment
|
|
2
|
-
|
|
3
|
-
The Windows-only test harness now has a native Job Object supervisor in
|
|
4
|
-
`tests/p0-host/windows-job.ps1`. It uses `CreateProcessW(CREATE_SUSPENDED)`, assigns
|
|
5
|
-
the process before `ResumeThread`, sets `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` and
|
|
6
|
-
`JOB_OBJECT_LIMIT_PROCESS_MEMORY`, and records bounded output privately. Assignment
|
|
7
|
-
failure terminates the still-suspended process and never resumes it.
|
|
8
|
-
|
|
9
|
-
`tests/integration/windows-job.test.ts` uses only explicit executables and an owned
|
|
10
|
-
request root. It covers exact argv backslashes/spaces, descendant timeout cleanup,
|
|
11
|
-
assignment failure before resume, output overflow termination, an empty native
|
|
12
|
-
environment, and supervisor cancellation. Node's synthetic fixture creates a
|
|
13
|
-
grandchild and runs only with private test directories plus essential OS variables;
|
|
14
|
-
no user profile, application-data, PATH, or credential values are copied.
|
|
15
|
-
|
|
16
|
-
This is a Windows infrastructure prerequisite, not an OpenCode smoke test or a
|
|
17
|
-
qualification result; Linux/macOS have no equivalent qualification.
|
|
18
|
-
|
|
19
|
-
The current Windows ARM64 worker rejects the mandatory atomic job-list launch
|
|
20
|
-
with `ERROR_INVALID_PARAMETER` (87), so it is explicitly blocked rather than
|
|
21
|
-
using the weaker assign-after-create fallback. Windows x64 qualification remains
|
|
22
|
-
pending on a worker supporting `PROC_THREAD_ATTRIBUTE_JOB_LIST`.
|
|
23
|
-
|
|
24
|
-
The ARM64 integration case executes the supervisor and records
|
|
25
|
-
`platformBlocked: true` only when its stderr contains
|
|
26
|
-
`ATOMIC_JOB_LIST_UNSUPPORTED` and the supervisor exits nonzero. It also asserts
|
|
27
|
-
that no child PID was created. This is a passing infrastructure observation,
|
|
28
|
-
not a skipped case or a qualification result; any other supervisor failure, or
|
|
29
|
-
any child creation on the blocked path, still fails the suite.
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# P0 evidence-support implementation
|
|
2
|
-
|
|
3
|
-
Date: 2026-10-13. Scope: **test infrastructure only**, not product code or a P0
|
|
4
|
-
exit-gate pass.
|
|
5
|
-
|
|
6
|
-
## Implemented
|
|
7
|
-
|
|
8
|
-
- `tests/support/qualification.ts`: bounded plain-data validation and selected-case
|
|
9
|
-
evidence assessment. Rejects missing, duplicate, unexpected, TODO/skipped,
|
|
10
|
-
stale-build/run and mismatched-profile evidence. Expected gaps require an explicit
|
|
11
|
-
active-only expectation and never satisfy an enforced profile.
|
|
12
|
-
- `tests/support/recording-endpoint.ts`: scripted loopback HTTP fixture with bounded
|
|
13
|
-
request captures, request/connection counters, deadlines and explicit socket/buffer
|
|
14
|
-
cleanup. Supports JSON/SSE response content types for later protocol fixtures but
|
|
15
|
-
is not itself a model protocol implementation.
|
|
16
|
-
|
|
17
|
-
## Test-first and independent review evidence
|
|
18
|
-
|
|
19
|
-
Initial behavior tests were written before either helper existed and failed with
|
|
20
|
-
missing-module errors. After implementation, the qualification suite passed 13
|
|
21
|
-
cases and the recording suite passed 6, later 8 cases.
|
|
22
|
-
|
|
23
|
-
Fresh-eye reviews in [review 5](5-qualification-gate-review.md) and
|
|
24
|
-
[review 6](6-p0-infrastructure-review.md) required runtime-shape validation,
|
|
25
|
-
accessor rejection and stronger close/counter semantics. Added regression tests
|
|
26
|
-
failed against the previous implementations before repairs. The repaired focused
|
|
27
|
-
command passes **24 tests** (15 evidence assessor, 9 recording endpoint):
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
node --test tests/unit/qualification.test.ts tests/unit/recording-endpoint.test.ts
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
The reviewer independently reran those 24 tests and found no new must-fix bug in
|
|
34
|
-
these two helpers. Earlier full-suite/typecheck failures in review reports include
|
|
35
|
-
parallel in-progress files and are preserved as historical observations, not a
|
|
36
|
-
claim that those files remain broken.
|
|
37
|
-
|
|
38
|
-
## Boundaries still open
|
|
39
|
-
|
|
40
|
-
- Evidence assessment does not establish actual execution, correct/complete case
|
|
41
|
-
selection, artifact provenance/checksums, capture ownership or independent oracle
|
|
42
|
-
truth. The real smoke runner must supply and validate those before qualification.
|
|
43
|
-
- The recorder is synthetic, unauthenticated loopback HTTP. It neither validates
|
|
44
|
-
GitHub TLS/address policy nor blocks unrelated outbound network traffic.
|
|
45
|
-
- `attempted` counts parsed HTTP requests; `connections` counts accepted sockets
|
|
46
|
-
and `malformed` is separate. Zero parsed requests is not zero network attempts.
|
|
47
|
-
- Explicit per-instance recorder bounds allow up to 64 MiB of captured body data
|
|
48
|
-
at the maximum settings, plus copies/overhead. A future combined real-host
|
|
49
|
-
harness must reserve aggregate budgets; these local limits are not a process RSS
|
|
50
|
-
guarantee. Captures remain private in memory and are never printed by helpers.
|
|
51
|
-
- No product module or OpenCode service was launched by these tests. Runtime,
|
|
52
|
-
source-opening and process-tree prerequisites remain independent P0 gates.
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# P0-BOOT launch plan (not yet executable)
|
|
2
|
-
|
|
3
|
-
Status: prepared; launch is intentionally blocked until the process-containment
|
|
4
|
-
agent completes its review. This plan owns no host process and reads no user
|
|
5
|
-
configuration.
|
|
6
|
-
|
|
7
|
-
## Candidate identity
|
|
8
|
-
|
|
9
|
-
Use the architecture-matching explicit unqualified candidate in
|
|
10
|
-
`tests/smoke/runtime-candidate.json`: OpenCode `2.0.28`, official tag commit
|
|
11
|
-
`026b106bc5d41b679ffc413264229c340871e4d3`. On this ARM64 runner the selected
|
|
12
|
-
member is `@opencode/cli-windows-arm64@2.0.28`, PE machine `AA64`, executable
|
|
13
|
-
member SHA-256
|
|
14
|
-
`65F0E7ACCC7D5FD77CA7380FDE87E3289D0CF27687851E5240BBDE6CAA7BDB66`.
|
|
15
|
-
The x64 candidate remains recorded for a matching worker. Each downloaded
|
|
16
|
-
archive SHA-256 is distinct and must not be used as the executable hash.
|
|
17
|
-
|
|
18
|
-
## Source-verified launch contract
|
|
19
|
-
|
|
20
|
-
At tag `v2.0.28`, `packages/cli/src/server-process.ts` supports foreground
|
|
21
|
-
`serve` with `--hostname` and `--port`; `packages/cli/src/services/standalone.ts`
|
|
22
|
-
uses `serve --stdio --port 0` and emits one JSON readiness line
|
|
23
|
-
`{"url":"..."}`. `serve` itself prints `server listening on ...` in foreground
|
|
24
|
-
mode. The exact password variable is `OPENCODE_PASSWORD` (legacy
|
|
25
|
-
`OPENCODE_SERVER_PASSWORD` is also accepted): `standalone.ts` creates a random
|
|
26
|
-
password and passes it to the child as `OPENCODE_PASSWORD`. The client sends
|
|
27
|
-
HTTP Basic authentication with username `opencode` and that password. The
|
|
28
|
-
first probe must use the standalone stdio contract or an explicit foreground
|
|
29
|
-
`serve` with a synthetic `OPENCODE_PASSWORD`, never shared-service discovery.
|
|
30
|
-
|
|
31
|
-
The local API identity endpoint is authenticated `GET /api/info`, documented by
|
|
32
|
-
the V2 API reference and defined by the tag's `packages/protocol/src/groups/server.ts`.
|
|
33
|
-
Its `ServerInfo` schema requires `version: string`, `pid: nonnegative integer`,
|
|
34
|
-
`urls: string[]`, and `paths.tmp: string`, with optional capabilities. The probe
|
|
35
|
-
must decode this schema, assert version `2.0.28`, assert `pid >= 0`, require a
|
|
36
|
-
run-owned URL and `paths.tmp` beneath the private root, and never print the
|
|
37
|
-
password or raw response.
|
|
38
|
-
|
|
39
|
-
## Environment construction
|
|
40
|
-
|
|
41
|
-
`tests/support/opencode-environment.ts` is a pure builder. It accepts a private
|
|
42
|
-
absolute run root and caller-supplied trusted OS values, and returns:
|
|
43
|
-
|
|
44
|
-
* a fixture-owned cwd and data/cache/config/state/temp/database paths;
|
|
45
|
-
* an explicit allowlist containing only OS runtime values and source-verified
|
|
46
|
-
OpenCode controls; and
|
|
47
|
-
* synthetic JSON config with only supported fields: `snapshots: false`,
|
|
48
|
-
`warming: false`, and `update: "disable"`.
|
|
49
|
-
|
|
50
|
-
After review 10, the builder requires an explicit `password` (64 lowercase hex
|
|
51
|
-
characters). The future runner generates 32 CSPRNG bytes for each run and passes
|
|
52
|
-
them only through the private environment/request channel, never user credentials
|
|
53
|
-
or command-line arguments. `OPENCODE_PASSWORD` is in the fixed allowlist. The
|
|
54
|
-
database choice is a run-owned file, not `:memory:`, so later sink evidence can be
|
|
55
|
-
inspected. Candidate metadata now records the same database and auth controls.
|
|
56
|
-
|
|
57
|
-
The builder never reads `process.env`. It omits provider keys, `NODE_OPTIONS`,
|
|
58
|
-
proxy variables, plugin-path variables, credential-agent variables, and all
|
|
59
|
-
other ambient values. The caller must supply any executable-required OS values
|
|
60
|
-
explicitly. `OPENCODE_CONFIG_PROJECT_DISABLE=1` prevents the ancestor/project
|
|
61
|
-
walk; `ProgramData` is redirected so system config discovery is also inside the
|
|
62
|
-
fixture. `XDG_*`, `TEMP`/`TMP`, `OPENCODE_CONFIG_DIR`, `OPENCODE_CONFIG_CONTENT`,
|
|
63
|
-
and `OPENCODE_DB` implement the tag-source isolation controls.
|
|
64
|
-
|
|
65
|
-
## Next-agent execution sequence
|
|
66
|
-
|
|
67
|
-
1. Wait for the containment decision and create an ACL-protected run root.
|
|
68
|
-
2. Materialize only the exact candidate executable member, verify regular-file,
|
|
69
|
-
non-link, single-link identity and the corrected SHA-256 before spawn.
|
|
70
|
-
3. Build the environment using the pure builder and pass it to the existing
|
|
71
|
-
child runner with `shell: false`, exact cwd, bounded stdout/stderr, startup
|
|
72
|
-
and overall deadlines, and owned-process containment.
|
|
73
|
-
4. Start the exact foreground/stdio command selected by the containment agent.
|
|
74
|
-
Do not use `opencode`, `service`, `api`, or any installed executable by name.
|
|
75
|
-
5. Parse only the bounded readiness line, call authenticated `GET /api/info`,
|
|
76
|
-
assert candidate identity, then stop the owned process through the approved
|
|
77
|
-
containment mechanism. Any mismatch, shared-service signal, unexpected
|
|
78
|
-
filesystem path, inherited variable, timeout, or unauthenticated endpoint
|
|
79
|
-
is a hard failure.
|
|
80
|
-
6. Preserve raw evidence only under the private run root; publish case ID,
|
|
81
|
-
safe status, and validated identity fields.
|
|
82
|
-
|
|
83
|
-
This document is a launch prerequisite, not P0 evidence. No host startup or
|
|
84
|
-
real smoke pass is claimed until the sequence is implemented and executed.
|
|
85
|
-
|
|
86
|
-
Parent added red/green malformed-input, accessor, cross-platform separator and
|
|
87
|
-
mandatory-auth regressions after initial implementation. The six environment
|
|
88
|
-
tests exercise only pure construction/validation, not filesystem or host isolation.
|
package/docs/spec/design.md
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Design: transparent OpenCode V2 substitution MVP
|
|
2
|
-
|
|
3
|
-
Status: authoritative implementation design, revised 2026-10-16.
|
|
4
|
-
|
|
5
|
-
## Architecture
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
configured dotenv source
|
|
9
|
-
|
|
|
10
|
-
v
|
|
11
|
-
private activation map: raw value -> secret-UUID handle
|
|
12
|
-
|
|
|
13
|
-
+--> session/model/http/tool hooks --> sanitized existing host objects
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
There is no tool transform, capability registry, credential executor, private
|
|
17
|
-
HTTP transport, or network request owned by this plugin.
|
|
18
|
-
|
|
19
|
-
## Activation lifecycle
|
|
20
|
-
|
|
21
|
-
The Effect activation loads sources and admits a complete snapshot before hooks
|
|
22
|
-
can publish substitutions. The map and matcher state belong to that activation
|
|
23
|
-
closure. Activation close disposes the map and matcher state. No raw values are
|
|
24
|
-
stored in OpenCode storage, project metadata, logs, environment variables or
|
|
25
|
-
spills created by the plugin.
|
|
26
|
-
|
|
27
|
-
The current implementation uses a bounded in-memory map and a CSPRNG-generated
|
|
28
|
-
UUIDv4-shaped handle. Handles are obfuscation identities only; there is no
|
|
29
|
-
reverse-resolution API exposed to the host or model.
|
|
30
|
-
|
|
31
|
-
## Source contract
|
|
32
|
-
|
|
33
|
-
MVP sources are explicit Location-relative dotenv files with an allowlisted key
|
|
34
|
-
set. No shell sourcing, interpolation, environment indirection or arbitrary
|
|
35
|
-
path is allowed. A required source failure, unsafe value, admission collision,
|
|
36
|
-
or budget failure disables substitution rather than publishing a partial map.
|
|
37
|
-
|
|
38
|
-
The path-opening implementation and filesystem security claims remain a
|
|
39
|
-
qualification gate. Until each platform is tested, the release must name only
|
|
40
|
-
the supported platform/filesystem.
|
|
41
|
-
|
|
42
|
-
## Substitution algorithm
|
|
43
|
-
|
|
44
|
-
For each admitted raw value, construct the finite supported representation set
|
|
45
|
-
(semantic text, dotenv spellings, JSON escaping, percent encoding and base64
|
|
46
|
-
forms). Scan host values with bounded interval collection, replace matches with
|
|
47
|
-
the corresponding opaque handle, and rescan the complete output. Overlaps are
|
|
48
|
-
unioned before replacement. If the result cannot be bounded or validated,
|
|
49
|
-
withhold the affected sink instead of returning raw input.
|
|
50
|
-
|
|
51
|
-
The replacement is applied to exact host objects before publication. For
|
|
52
|
-
serialized provider payloads, the implementation must preserve valid protocol
|
|
53
|
-
shape and validate the final encoded bytes; leaf-only scans are insufficient.
|
|
54
|
-
|
|
55
|
-
## Hook boundary
|
|
56
|
-
|
|
57
|
-
The MVP wires these existing V2 surfaces:
|
|
58
|
-
|
|
59
|
-
- `session.prompt`
|
|
60
|
-
- `session.context`
|
|
61
|
-
- `session.compaction`
|
|
62
|
-
- `session.generate`
|
|
63
|
-
- `session.title`
|
|
64
|
-
- `session.model.request`
|
|
65
|
-
- `session.http.request`
|
|
66
|
-
- `session.http.response`
|
|
67
|
-
- `tool.execute.before`
|
|
68
|
-
- `tool.execute.after`
|
|
69
|
-
|
|
70
|
-
A hook only proves the boundary it actually observes. It cannot retroactively
|
|
71
|
-
protect values emitted before the hook, internal writes by ordinary tools,
|
|
72
|
-
provider-hosted tools, WebSocket/SDK routes, or host behavior after unload.
|
|
73
|
-
Those are recorded as uncovered or deferred, never silently considered safe.
|
|
74
|
-
|
|
75
|
-
## Configuration
|
|
76
|
-
|
|
77
|
-
```json
|
|
78
|
-
{
|
|
79
|
-
"version": 1,
|
|
80
|
-
"sources": [
|
|
81
|
-
{
|
|
82
|
-
"id": "project",
|
|
83
|
-
"type": "file",
|
|
84
|
-
"format": "dotenv",
|
|
85
|
-
"path": ".env",
|
|
86
|
-
"required": true,
|
|
87
|
-
"keys": ["EXAMPLE_TOKEN"]
|
|
88
|
-
}
|
|
89
|
-
],
|
|
90
|
-
"limits": { "...": "all bounded limit keys are required" }
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
No authorization, capabilities, operation IDs, resource URLs, tool names or
|
|
95
|
-
transport endpoints belong in the MVP options.
|
|
96
|
-
|
|
97
|
-
## Explicit non-goals
|
|
98
|
-
|
|
99
|
-
- no model-visible credential tools;
|
|
100
|
-
- no plugin-owned requests;
|
|
101
|
-
- no GitHub-specific behavior;
|
|
102
|
-
- no DNS/proxy/egress policy for requests the plugin does not make;
|
|
103
|
-
- no universal protection claim against an untrusted or compromised host.
|
|
104
|
-
|
|
105
|
-
## E2E proof
|
|
106
|
-
|
|
107
|
-
`P5-TRANSPARENT-ROUNDTRIP` runs a real OpenCode 2.0.28 subprocess with a
|
|
108
|
-
synthetic dotenv source and a loopback provider. It verifies existing provider
|
|
109
|
-
traffic contains an opaque `secret-<uuid>` replacement and not the raw canary,
|
|
110
|
-
while the plugin makes no independent request. Additional sink cases are
|
|
111
|
-
required before release qualification.
|
package/docs/spec/intent.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Intent: transparent OpenCode V2 secret substitution
|
|
2
|
-
|
|
3
|
-
Status: authoritative product intent, revised 2026-10-16.
|
|
4
|
-
|
|
5
|
-
## Problem
|
|
6
|
-
|
|
7
|
-
Project-local configuration may contain credentials that must not appear in
|
|
8
|
-
OpenCode prompts, model requests, tool-visible values, errors, logs, or durable
|
|
9
|
-
host payloads. The plugin should protect those values **behind the scenes**;
|
|
10
|
-
the model and ordinary OpenCode tools should continue using their existing
|
|
11
|
-
interfaces.
|
|
12
|
-
|
|
13
|
-
## MVP product boundary
|
|
14
|
-
|
|
15
|
-
The plugin is a native OpenCode V2 Effect plugin that:
|
|
16
|
-
|
|
17
|
-
1. Loads explicitly configured dotenv values into private activation memory.
|
|
18
|
-
2. Assigns each admitted value an opaque `secret-<UUIDv4>` replacement.
|
|
19
|
-
3. Applies the replacement through existing OpenCode session/model/http/tool
|
|
20
|
-
hooks before public or provider-facing publication.
|
|
21
|
-
4. Withholds or fails closed when a bounded sink cannot be safely transformed.
|
|
22
|
-
5. Disposes the private map when the plugin activation closes.
|
|
23
|
-
|
|
24
|
-
The plugin **does not**:
|
|
25
|
-
|
|
26
|
-
- register tools or change tool schemas;
|
|
27
|
-
- expose a credential catalog to the model;
|
|
28
|
-
- resolve handles for a private operation;
|
|
29
|
-
- perform credential, GitHub, or other plugin-owned network requests;
|
|
30
|
-
- implement authorization for an operation it owns;
|
|
31
|
-
- claim protection for host routes that its hooks do not observe.
|
|
32
|
-
|
|
33
|
-
## What the model sees
|
|
34
|
-
|
|
35
|
-
Where a configured value would otherwise appear, the public value is an opaque
|
|
36
|
-
handle such as `secret-550e8400-e29b-41d4-a716-446655440000`. The raw value
|
|
37
|
-
never enters model-visible arguments or provider request bodies through a
|
|
38
|
-
qualified path. Handles are references for obfuscation only; this MVP does not
|
|
39
|
-
resolve them.
|
|
40
|
-
|
|
41
|
-
## MVP configuration
|
|
42
|
-
|
|
43
|
-
Options contain only:
|
|
44
|
-
|
|
45
|
-
- `version: 1`;
|
|
46
|
-
- relative, explicitly keyed dotenv `sources`;
|
|
47
|
-
- bounded `limits` for source size, traversal, output and working memory.
|
|
48
|
-
|
|
49
|
-
Paths must remain inside the OpenCode Location. `${env:...}` and `${file:...}`
|
|
50
|
-
indirections are rejected. Values are synthetic in tests and never persisted by
|
|
51
|
-
the plugin.
|
|
52
|
-
|
|
53
|
-
## Supported boundary
|
|
54
|
-
|
|
55
|
-
The first qualified boundary is the existing provider request path plus the
|
|
56
|
-
session hooks for prompt, context, compaction, generate and title, and tool
|
|
57
|
-
before/after results. Each sink requires its own evidence. Ordinary shell,
|
|
58
|
-
MCP, provider-hosted tools, WebSocket/SDK routes, screenshots, transformed or
|
|
59
|
-
covert values, and host behavior after plugin unload are not automatically
|
|
60
|
-
protected.
|
|
61
|
-
|
|
62
|
-
## Security posture
|
|
63
|
-
|
|
64
|
-
This is an **active-plugin-only** MVP. It trusts the OpenCode host, process,
|
|
65
|
-
OS account and plugin ordering. It does not claim protection against a
|
|
66
|
-
compromised host, a malicious same-process plugin, or an unobserved route.
|
|
67
|
-
Broad production enforcement is a later profile requiring host-owned readiness
|
|
68
|
-
and route mediation; it is not part of this plugin's MVP.
|
|
69
|
-
|
|
70
|
-
## E2E acceptance
|
|
71
|
-
|
|
72
|
-
`P5-TRANSPARENT-ROUNDTRIP` launches pinned OpenCode V2 in a private sandbox,
|
|
73
|
-
loads a synthetic `.env`, sends one normal `opencode run` prompt through a
|
|
74
|
-
loopback provider, and proves:
|
|
75
|
-
|
|
76
|
-
- the existing provider path completes normally;
|
|
77
|
-
- provider bytes contain an opaque handle instead of the raw canary;
|
|
78
|
-
- public output and sandbox host data contain no raw canary;
|
|
79
|
-
- no plugin-owned tool or network request occurs.
|
|
80
|
-
|
|
81
|
-
This is a first E2E boundary, not exhaustive qualification.
|