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.
Files changed (104) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1 -31
  3. package/package.json +29 -23
  4. package/plugin.mjs +3841 -0
  5. package/.editorconfig +0 -12
  6. package/.github/workflows/ci.yml +0 -75
  7. package/.github/workflows/release.yml +0 -50
  8. package/.node-version +0 -1
  9. package/AGENTS.md +0 -23
  10. package/INSTALL.md +0 -104
  11. package/docs/checkpoint.md +0 -68
  12. package/docs/history/1-spec-review.md +0 -233
  13. package/docs/history/10-p0-launch-prerequisites-review.md +0 -186
  14. package/docs/history/11-p0-job-containment-review.md +0 -188
  15. package/docs/history/12-p0-api-client.md +0 -15
  16. package/docs/history/13-p0-status-review.md +0 -122
  17. package/docs/history/14-p0-containment-final-review.md +0 -84
  18. package/docs/history/15-config-source-implementation.md +0 -27
  19. package/docs/history/16-sanitizer-implementation.md +0 -65
  20. package/docs/history/17-store-policy-implementation.md +0 -36
  21. package/docs/history/18-pure-mvp-review.md +0 -182
  22. package/docs/history/19-pure-mvp-re-review.md +0 -142
  23. package/docs/history/2-p0-runtime-prerequisites.md +0 -163
  24. package/docs/history/20-pure-mvp-final-review.md +0 -116
  25. package/docs/history/21-pure-mvp-review-final.md +0 -109
  26. package/docs/history/22-pure-mvp-signoff-review.md +0 -113
  27. package/docs/history/23-pure-mvp-signoff-review.md +0 -75
  28. package/docs/history/24-owned-tools-implementation.md +0 -27
  29. package/docs/history/25-v2-plugin-integration.md +0 -53
  30. package/docs/history/26-owned-tools-review.md +0 -138
  31. package/docs/history/27-owned-tools-final-review.md +0 -93
  32. package/docs/history/28-owned-tools-signoff.md +0 -98
  33. package/docs/history/3-p0-harness-foundation.md +0 -105
  34. package/docs/history/4-p0-source-opening.md +0 -78
  35. package/docs/history/5-qualification-gate-review.md +0 -133
  36. package/docs/history/6-p0-infrastructure-review.md +0 -275
  37. package/docs/history/7-p0-job-containment.md +0 -29
  38. package/docs/history/8-p0-evidence-support.md +0 -52
  39. package/docs/history/9-p0-launch-plan.md +0 -88
  40. package/docs/spec/design.md +0 -111
  41. package/docs/spec/intent.md +0 -81
  42. package/docs/spec/plan.md +0 -78
  43. package/docs/testing.md +0 -157
  44. package/scripts/build-plugin.mjs +0 -84
  45. package/scripts/install-opencode-ci.ps1 +0 -39
  46. package/scripts/install-opencode-ci.sh +0 -27
  47. package/scripts/smoke.mjs +0 -105
  48. package/scripts/verify-package.mjs +0 -18
  49. package/src/README.md +0 -22
  50. package/src/budgets/index.ts +0 -49
  51. package/src/config/README.md +0 -4
  52. package/src/config/options.ts +0 -76
  53. package/src/diagnostics/README.md +0 -4
  54. package/src/opencode/v2/filter.ts +0 -350
  55. package/src/opencode/v2/index.ts +0 -86
  56. package/src/opencode/v2/runtime.ts +0 -207
  57. package/src/opencode/v2/transports/README.md +0 -5
  58. package/src/runtime/http-transport.ts +0 -20
  59. package/src/sanitize/README.md +0 -4
  60. package/src/sanitize/index.ts +0 -454
  61. package/src/sources/README.md +0 -5
  62. package/src/sources/dotenv.ts +0 -133
  63. package/src/sources/native/README.md +0 -5
  64. package/src/store/index.ts +0 -64
  65. package/tests/README.md +0 -26
  66. package/tests/contract/plugin-v2.test.ts +0 -29
  67. package/tests/fixtures/README.md +0 -9
  68. package/tests/fixtures/job-child.mjs +0 -18
  69. package/tests/fixtures/plugins/README.md +0 -8
  70. package/tests/fixtures/process-child.mjs +0 -20
  71. package/tests/fixtures/services/README.md +0 -10
  72. package/tests/integration/components.test.ts +0 -5
  73. package/tests/integration/transparent-runtime.test.ts +0 -97
  74. package/tests/integration/windows-job.test.ts +0 -71
  75. package/tests/p0-host/README.md +0 -24
  76. package/tests/p0-host/linux-job.sh +0 -61
  77. package/tests/p0-host/windows-job.ps1 +0 -51
  78. package/tests/p0-native/README.md +0 -33
  79. package/tests/p0-native/windows-handle-loader.tests.ps1 +0 -191
  80. package/tests/scaffold/repository.test.ts +0 -105
  81. package/tests/smoke/README.md +0 -37
  82. package/tests/smoke/cases.json +0 -17
  83. package/tests/smoke/qualification.json +0 -20
  84. package/tests/smoke/runtime-candidate.json +0 -111
  85. package/tests/support/README.md +0 -32
  86. package/tests/support/child-process.ts +0 -235
  87. package/tests/support/host-api.ts +0 -123
  88. package/tests/support/opencode-environment.ts +0 -239
  89. package/tests/support/qualification.ts +0 -154
  90. package/tests/support/recording-endpoint.ts +0 -242
  91. package/tests/support/run-directory.ts +0 -179
  92. package/tests/support/smoke-cases.ts +0 -289
  93. package/tests/unit/budgets.test.ts +0 -35
  94. package/tests/unit/config.test.ts +0 -40
  95. package/tests/unit/dotenv.test.ts +0 -72
  96. package/tests/unit/filter-size.test.ts +0 -132
  97. package/tests/unit/harness.test.ts +0 -228
  98. package/tests/unit/host-api.test.ts +0 -122
  99. package/tests/unit/opencode-environment.test.ts +0 -161
  100. package/tests/unit/qualification.test.ts +0 -128
  101. package/tests/unit/recording-endpoint.test.ts +0 -188
  102. package/tests/unit/sanitize.test.ts +0 -159
  103. package/tests/unit/smoke-cases.test.ts +0 -20
  104. package/tsconfig.json +0 -20
@@ -1,116 +0,0 @@
1
- # 20 — Fresh adversarial review of pure MVP after R19
2
-
3
- Date: 2026-10-13
4
-
5
- ## Verdict
6
-
7
- **Not ready for MVP-owned tools or an MVP qualification claim.** The R19
8
- changes close the previously reported proxy-boundary failures in config,
9
- policy, sanitizer and budgets, and close store quota/collision and proof
10
- binding gaps. The pure suite passes, but three pure fail-closed/resource gates
11
- remain open. Host, source-opening, transport and qualification gates are also
12
- still unverified.
13
-
14
- ## Evidence run
15
-
16
- - `npm.cmd run typecheck` — pass.
17
- - `node --test tests/unit/config.test.ts tests/unit/dotenv.test.ts tests/unit/sanitize.test.ts tests/unit/store-policy.test.ts tests/unit/budgets.test.ts` — **35 passed, 0 failed**.
18
- - `npm.cmd run check` — typecheck and pure tests passed; the broader run then
19
- failed in 12 pre-existing private run-directory/ACL harness tests with
20
- `RUN_DIRECTORY_PERMISSION_FAILED`. This is not host qualification evidence
21
- and was not used as evidence for the pure result.
22
- - No source, OpenCode, real transport, source-opening, or qualification run
23
- was performed. No source or test edits were made.
24
-
25
- ## R19 fixes verified
26
-
27
- - Config and policy reject malformed plain records, accessors, proxies,
28
- symbols and cycles with fixed errors/denials.
29
- - Sanitizer public boundaries catch throwing object/pattern/budget traps;
30
- malformed projections return `null`, and admission returns the fixed safe
31
- failure.
32
- - Caller-supplied pattern representations are not trusted; the finite derived
33
- inventory is used. UUID-language and reserved-output collisions reject the
34
- complete admission.
35
- - Interval collection merges transitive overlaps, final output is rescanned,
36
- and default match/output/depth/node bounds deny without partial output.
37
- - Budget inspection is descriptor-based, rejects malformed boundaries, checks
38
- safe-integer addition and commits reservations atomically.
39
- - Store reservations are made before map mutation, session/handle quotas are
40
- enforced when a ledger is supplied, teardown releases them, and UUID
41
- collision retries have a finite bound.
42
- - Authorization proofs are issuer-held and bind session, agent, Location,
43
- generation, capability, operation and resource; forged or changed requests
44
- do not resolve.
45
-
46
- ## Must-fix before pure MVP sign-off
47
-
48
- ### High — sanitizer aggregate pattern work is not reserved or bounded by the operation budget
49
-
50
- `src/sanitize/index.ts` bounds each pattern and its derived representation,
51
- but `collectIntervalsBounded`, `sanitizeText`, `projectPlain` and
52
- `canonicalTextSink` do not enforce a total pattern/index/identity budget before
53
- deriving and repeatedly rescanning representations. `MAX_INDEX_ENTRIES` and
54
- `MAX_IDENTITY_ENTRIES` are declared but unused. `admitPatterns` limits the
55
- array to 4,096 entries, yet still permits a large aggregate inventory; the
56
- transform paths do not even apply that count limit consistently. A bounded
57
- input/output size therefore does not prove bounded allocation or work for an
58
- adversarial pattern array.
59
-
60
- Required: validate and reserve aggregate pattern bytes, representation/index
61
- entries, interval/identity storage and temporary copies before construction;
62
- apply the same finite limits to every public sanitizer entry point; deny the
63
- whole operation with no partial result. Add tests for large aggregate pattern
64
- text, many no-match patterns, representation expansion and reservation
65
- release.
66
-
67
- ### High — dotenv option boundary is not fail-closed
68
-
69
- `parseDotenv` checks the source, but reads `options.maxBytes`, `maxEntries` and
70
- `maxValueBytes` outside an exception boundary and does not validate those
71
- limits. A throwing proxy can escape its fixed `SourceParseError` boundary (a
72
- direct check with a proxy whose `get` trap throws produced the foreign message
73
- `foreign`); `Infinity`, negative or otherwise malformed limits can also defeat
74
- the intended bounded parser behavior.
75
-
76
- Required: normalize a plain, data-only options record, validate finite
77
- positive safe-integer limits, reject unknown fields, and catch all foreign
78
- object failures as the fixed source error. Add proxy/accessor/invalid-limit
79
- tests.
80
-
81
- ### Medium — store safety depends on an optional ledger and charges characters, not UTF-8 bytes
82
-
83
- `ActivationStore` permits construction without `budget`, making registration
84
- and grant map growth unbounded if an integration caller omits the ledger.
85
- Additionally, `secretBytes`, `indexBytes` and `workingBytes` use
86
- `value.length`, while the design requires UTF-8/actual buffer accounting.
87
- The finite collision loop and ledger-backed quota tests pass, but the released
88
- boundary must either require a validated ledger or impose safe defaults, and
89
- must reserve byte counts using UTF-8 bytes. Add non-ASCII quota tests and an
90
- unbudgeted-construction denial/default test.
91
-
92
- ## Residual host/integration gates
93
-
94
- 1. **P0/R8 source gate:** opened-handle, canonical containment, symlink/reparse,
95
- hard-link, ancestor replacement, filesystem identity, owner/ACL and tracked
96
- file tests on every advertised filesystem. No path-only or insecure fallback.
97
- 2. **P0/R7 deployment gate:** prove process-wide quota sharing across activation
98
- and package instances, or adopt the explicitly restricted single-instance/
99
- single-Location service envelope; measure actual RSS and reload overlap.
100
- 3. **P1/P2 integration:** pinned V2 config/plugin loading, activation closure
101
- ownership, source loading, immutable generations, cleanup, source rotation,
102
- and no durable secret-derived state.
103
- 4. **P3 sink gate:** independent interval oracle and final normalized/serialized
104
- sink-byte tests, scalar/structured echoes, cross-part spill, boundary
105
- creation, idempotence and actual allocation measurements.
106
- 5. **P4/P5 host/tool gate:** native Effect entrypoint, explicit enrollment,
107
- private executor, fixed status-only HTTPS operation, DNS/address/peer policy,
108
- cancellation/body withholding, and real sink/network evidence.
109
- 6. **Active-only limitations:** readiness/unload and fallback rollback,
110
- captured executors, late overlays, queued/resumed work, A→B→A moves,
111
- sessionless generation, provider-hosted tools, WebSocket/SDK routes,
112
- shell/progress/spill paths and host permission deny/ask remain unverified or
113
- expected gaps; they cannot be inferred from pure tests.
114
- 7. **P6+ enforced profile:** mandatory host-owned readiness/revision control,
115
- host-mediated authorization, final model-egress/publication gates and the
116
- isolated broker/sandbox are absent. No enforced-profile claim is permitted.
@@ -1,109 +0,0 @@
1
- # 21 — Final fresh-eye review of pure MVP after R20
2
-
3
- Date: 2026-10-13
4
-
5
- ## Verdict
6
-
7
- **NO-GO for the pure MVP-owned tools as a whole.** The dotenv boundary and
8
- store ledger/byte-accounting findings from R20 are closed. The sanitizer now
9
- reserves aggregate pattern inventory on every public transform path, but its
10
- caller-supplied work budgets are not capped by the hard resource ceilings.
11
- Consequently, an accepted `maxMatches` can permit very large interval
12
- materialization on a bounded input; analogous unbounded safe-integer values
13
- are accepted for traversal and output budgets. This is not sufficient evidence
14
- for bounded sanitizer work or for tools that depend on it.
15
-
16
- The config, dotenv parser, policy, budget ledger, and store components are
17
- **GO as pure component implementations**, subject to the host/integration
18
- gates below. Sanitizer-dependent owned tools remain **NO-GO** until the
19
- effective operation budgets are clamped to declared ceilings (or are charged
20
- against equivalent reservations) and adversarial tests prove that behavior.
21
-
22
- ## Evidence
23
-
24
- - `npm.cmd run typecheck` — pass.
25
- - `node --test tests/unit/config.test.ts tests/unit/dotenv.test.ts tests/unit/sanitize.test.ts tests/unit/store-policy.test.ts tests/unit/budgets.test.ts` — **38 passed, 0 failed**.
26
- - No source edits, tool implementation edits, OpenCode launch, ACL test,
27
- transport test, or real-host qualification run was performed.
28
-
29
- ## R20 findings
30
-
31
- ### Closed: dotenv proxy and limits
32
-
33
- `parseDotenv` places option normalization inside the fixed error boundary and
34
- uses a descriptor-based plain data record. It rejects proxies/accessors,
35
- symbols, unknown fields, malformed values, and non-positive/non-safe-integer
36
- limits with `SOURCE_INVALID`. The source byte, entry, and value limits are
37
- checked before accepting entries, with no partial snapshot returned.
38
-
39
- ### Closed: store mandatory/default ledger and UTF-8 bytes
40
-
41
- `ActivationStore` always installs a bounded default `BudgetLedger` when one is
42
- not supplied, while still accepting an injected validated ledger. Reservations
43
- precede map mutation and teardown releases them. Secret, index, and working
44
- charges use `Buffer.byteLength(value, "utf8")`; the focused non-ASCII quota test
45
- exercises the denial boundary.
46
-
47
- ### Partially closed: sanitizer aggregate reservations
48
-
49
- `reserveOperation` is now applied at every public sanitizer entry point and
50
- reserves aggregate pattern bytes, representation/index slots, identity slots,
51
- temporary input/pattern bytes, and releases reservations on every exit. The
52
- focused aggregate test covers interval collection, text sanitization, the
53
- canonical sink, projection, admission, and reservation reuse.
54
-
55
- Residual issue: `normalizedBudget` accepts any nonnegative safe integer for
56
- `maxMatches`, `maxNodes`, `maxDepth`, and `maxOutputBytes`; these values are
57
- not clamped to hard ceilings. For example, `collectIntervals` can accept a
58
- very large `maxMatches` and materialize a large `found` array from repeated
59
- matches in an otherwise bounded input. Output and traversal work likewise
60
- depend on caller-selected values rather than the declared hard ceilings.
61
- The aggregate reservation therefore does not by itself prove bounded work.
62
- This is a NO-GO finding, not a test failure in the current 38-test suite.
63
-
64
- ## Required follow-up before pure sign-off
65
-
66
- 1. Define hard effective ceilings for every sanitizer budget dimension and
67
- reject or clamp requests above them; preserve fail-closed behavior.
68
- 2. Add focused tests for oversized `maxMatches`, `maxNodes`, `maxDepth`, and
69
- `maxOutputBytes`, including repeated-match allocation and reservation
70
- release after denied operations.
71
- 3. Re-run typecheck and the focused suite, then independently review all
72
- public sanitizer paths for reservation-before-allocation and no partial
73
- output.
74
-
75
- ## Remaining host and integration gates
76
-
77
- Pure tests do not qualify the plugin or any OpenCode tool. Remaining blockers
78
- include:
79
-
80
- 1. P0 pinned V2 runtime/plugin compatibility, hook ordering, unload/fallback,
81
- and an isolated real-host smoke harness.
82
- 2. Handle-relative source loading on each advertised filesystem: containment,
83
- symlink/reparse and hard-link rejection, ancestor replacement, identity,
84
- ownership, and ACL evidence.
85
- 3. Shared process-wide quota accounting or an explicitly restricted
86
- single-instance deployment, including measured allocation/reload overlap.
87
- 4. Native tool and HTTP integration: explicit enrollment, private execution,
88
- fixed audience/transport policy, cancellation/body withholding, and safe
89
- status-only results.
90
- 5. Actual sink/publication evidence for model requests, structured/scalar
91
- echoes, persistence/spill, progress/errors, retries, and unsupported paths.
92
- 6. Rotation, generation invalidation, queued/resumed work, session/location
93
- changes, and all documented active-only expected gaps.
94
-
95
- No OpenCode, ACL, host, integration, production qualification, or enforced
96
- profile claim is made by this review.
97
-
98
- ## R21 remediation status
99
-
100
- The sole pure-MVP NO-GO finding is closed in the sanitizer implementation. Effective
101
- `maxMatches`, `maxNodes`, `maxDepth`, and `maxOutputBytes` values now reject explicit
102
- requests above fixed ceilings (50,000, 50,000, 32, and 2 MiB respectively), including
103
- otherwise safe integers such as `Number.MAX_SAFE_INTEGER`. Focused tests cover each
104
- oversized dimension, repeated-match denial before interval materialization, and
105
- reservation reuse after denial. Host and integration gates above remain unchanged.
106
-
107
- The subsequent R22 review identified and remediated two additional sanitizer boundary
108
- issues: canonical sink input inspection now occurs inside its fail-closed reservation
109
- boundary, and plain projection bounds key collection before sorting. See review 22.
@@ -1,113 +0,0 @@
1
- # 22 — Pure MVP sign-off review after R21
2
-
3
- Date: 2026-10-13
4
-
5
- ## Verdict
6
-
7
- **NO-GO for the pure component layer as a complete sign-off.** R21 closes the
8
- previous hard-ceiling finding: explicit sanitizer budgets above the fixed
9
- ceilings are denied, repeated matches are denied before interval materialization,
10
- and reservations are released. The dotenv parser, config, policy, budget
11
- ledger, and store remain GO as isolated pure implementations.
12
-
13
- Two sanitizer boundary/resource findings remain before the whole pure layer can
14
- be called signed off:
15
-
16
- 1. `canonicalTextSink` computes its initial reservation with `parts.reduce`
17
- outside the `try`/`finally` boundary. A hostile `Array` proxy can therefore
18
- throw a foreign error instead of returning the sanitizer's safe `null`
19
- result. This is a public sanitizer path and regresses the fail-closed
20
- boundary expected of the other paths.
21
- 2. `projectPlain` calls `Object.keys(current).sort()` before applying its
22
- `maxNodes` limit. A large plain object can force unbounded key-array
23
- allocation and sorting even when the caller requests `maxNodes: 0` or `1`.
24
- The traversal ceiling bounds visited values, but does not currently bound
25
- this pre-traversal work.
26
-
27
- These are pure-layer blockers, not host qualification claims.
28
-
29
- ## Evidence run
30
-
31
- - `npm.cmd run typecheck` — pass.
32
- - `node --test tests/unit/config.test.ts tests/unit/dotenv.test.ts tests/unit/sanitize.test.ts tests/unit/store-policy.test.ts tests/unit/budgets.test.ts` — **40 passed, 0 failed**.
33
- - Direct boundary probe: a throwing `parts.reduce` proxy causes
34
- `canonicalTextSink` to throw `foreign`; a 100,000-key object with
35
- `maxNodes: 1` returns `null` only after key enumeration/sorting.
36
- - No source or test edits, OpenCode launch, ACL test, transport test, or
37
- real-host qualification run was performed.
38
-
39
- ## R21 hard-ceiling review
40
-
41
- `normalizedBudget` is used by `collectIntervals`, `sanitizeText`,
42
- `canonicalTextSink`, and `projectPlain`, and rejects explicit values above the
43
- hard ceilings of 50,000 matches, 50,000 nodes, depth 32, and 2 MiB output.
44
- `reserveOperation` is present on all public sanitizer transforms, including
45
- `representationsFor`, `patternRepresentations`, `admitPatterns`, interval
46
- collection, text sanitization, canonical text sinks, and plain projection.
47
- The focused aggregate/repeated-match tests demonstrate release and reuse after
48
- denial. No R21 regression was observed in those paths.
49
-
50
- The reservation does not cure the two findings above: the canonical sink's
51
- pre-reservation traversal is outside its error boundary, and plain projection's
52
- key enumeration occurs before traversal accounting.
53
-
54
- ## Closed pure components
55
-
56
- - **Dotenv:** non-executing parsing, descriptor-based option normalization,
57
- finite positive limits, duplicate rejection, lexical preservation, and fixed
58
- `SOURCE_INVALID` failures are covered.
59
- - **Budget ledger:** descriptor validation, atomic reservations, safe-integer
60
- addition checks, idempotent disposal, and fixed denial behavior are covered.
61
- - **Store/policy:** bounded default ledger, UTF-8 byte charges, teardown release,
62
- finite handle collisions, generation/Location/session binding, and issuer-held
63
- authorization proofs are covered.
64
- - **Config:** plain-data/cycle/accessor/proxy rejection and hard configured
65
- ceilings are covered.
66
-
67
- ## Required follow-up
68
-
69
- 1. Put all canonical-sink input inspection and reservation calculation inside a
70
- fixed fail-closed boundary, with a regression test for throwing array/proxy
71
- traps and reservation reuse.
72
- 2. Bound plain-object key enumeration/sorting before materializing the complete
73
- key list, or otherwise reserve and enforce a finite key/temporary-work
74
- ceiling before that operation. Add a large-key object test at low node
75
- budgets.
76
- 3. Re-run typecheck and the focused pure suite, then repeat this review.
77
-
78
- ## Remaining host and tool gates
79
-
80
- Even after those pure blockers close, the plugin/tool layer remains unqualified:
81
-
82
- 1. Pinned V2 runtime/plugin compatibility, lifecycle ordering,
83
- unload/fallback behavior, and an isolated real-host smoke harness.
84
- 2. Handle-relative source loading with containment, symlink/reparse and
85
- hard-link rejection, identity/ancestor replacement, ownership, and ACL
86
- evidence on each advertised filesystem.
87
- 3. Process-wide quota sharing across activations/package copies, or the
88
- explicitly restricted single-instance/single-Location service envelope,
89
- including measured RSS and reload overlap.
90
- 4. Native tool and HTTP integration: explicit enrollment, private execution,
91
- fixed audience/transport policy, cancellation/body withholding, and safe
92
- status-only results.
93
- 5. Actual normalized/serialized sink evidence for model requests, structured
94
- and scalar echoes, persistence/spill, progress/errors, retries, and
95
- unsupported paths.
96
- 6. Rotation, generation invalidation, queued/resumed work, session/Location
97
- changes, and documented active-only gaps.
98
-
99
- No OpenCode, ACL, host, integration, production qualification, or enforced
100
- profile claim is made by this review.
101
-
102
- ## R22 remediation status
103
-
104
- Both residual sanitizer blockers are closed. `canonicalTextSink` now performs all
105
- parts inspection and initial reservation calculation inside its fail-closed
106
- `try`/`finally`, returning `null` for throwing array proxies and releasing any
107
- reservation on every path. `projectPlain` incrementally collects enumerable keys with
108
- a finite ceiling tied to the effective node ceiling before sorting, rejecting large
109
- objects without unbounded key-array materialization. Regression tests cover proxy
110
- containment, reservation reuse, and large-key objects with low `maxNodes`.
111
-
112
- The pure component implementation is GO subject to the host/tool gates listed above;
113
- this remediation does not claim host qualification.
@@ -1,75 +0,0 @@
1
- # 23 — Pure MVP sign-off review after R22 fixes
2
-
3
- Date: 2026-10-13
4
-
5
- ## Verdict
6
-
7
- **GO for the reviewed pure component layer, subject to the host and tool gates
8
- below.** The two R22 sanitizer blockers are closed. This is a pure-layer
9
- verdict only; it is not OpenCode, filesystem/ACL, integration, or production
10
- qualification.
11
-
12
- ## R22 fixes verified
13
-
14
- ### Canonical sink proxy boundary
15
-
16
- `canonicalTextSink` performs the `Array.isArray` check, part inspection, UTF-8
17
- input-byte accounting, reservation, join, and sanitization inside one
18
- fail-closed `try`/`finally` boundary. A throwing array proxy can therefore
19
- only produce `null`; it cannot escape a foreign trap error. If a reservation
20
- has already been acquired, the `finally` path releases it. The indexed second
21
- pass is also inside the same boundary, so a proxy that changes behavior
22
- between passes is contained as well.
23
-
24
- ### Plain projection key work
25
-
26
- `projectPlain` rejects non-plain values and collects enumerable own keys with a
27
- finite rejection probe before sorting. Key collection stops when it exceeds
28
- the effective `maxNodes` or the fixed `MAX_OBJECT_KEYS` ceiling, so a low node
29
- budget does not permit construction and sorting of an unbounded ordinary plain
30
- object key list. Descriptor reads, recursive visits, output construction, and
31
- the final scan remain inside the fail-closed boundary; cycles and reflection
32
- failures return `null`.
33
-
34
- This bounds the implementation's retained key list and sorting input for the
35
- supported plain-data boundary. It is not a claim that JavaScript can impose a
36
- global heap ceiling on arbitrary hostile proxy traps or the entire runtime;
37
- such resource and host behavior require the process/host gates below.
38
-
39
- ## Evidence
40
-
41
- - `npm.cmd run typecheck` — pass.
42
- - Focused pure suites — **42 passed, 0 failed, 0 skipped, 0 todo**:
43
- `tests/unit/config.test.ts`, `dotenv.test.ts`, `sanitize.test.ts`,
44
- `store-policy.test.ts`, and `budgets.test.ts`.
45
- - Regression coverage includes a throwing array proxy with reservation reuse
46
- and a 100,000-key plain object under `maxNodes: 1`.
47
- - `git diff --check` — pass (existing line-ending warnings only).
48
-
49
- The review inspected the implementation and tests directly. No OpenCode
50
- launch, source/ACL probe, integration qualification, or source edit was done.
51
-
52
- ## Remaining host/tool gates
53
-
54
- The pure verdict does not close these release blockers:
55
-
56
- 1. Pinned V2 runtime/plugin compatibility, lifecycle ordering, unload/fallback
57
- behavior, and an isolated real-host P0-BOOT/P0-HOOKS/P0-ABORT harness.
58
- 2. Handle-relative source loading with canonical containment, symlink/reparse
59
- and hard-link rejection, identity/ancestor replacement, ownership, and ACL
60
- evidence on each advertised filesystem.
61
- 3. Process-wide quota ownership across activations/package copies, or an
62
- explicitly restricted single-instance/single-Location envelope, including
63
- measured RSS and reload overlap.
64
- 4. Native tool and HTTP integration: explicit enrollment, private execution,
65
- fixed audience/transport policy, cancellation/body withholding, and safe
66
- status-only results.
67
- 5. Actual normalized/serialized sink evidence for model requests, structured
68
- and scalar echoes, persistence/spill, progress/errors, retries, and
69
- unsupported paths.
70
- 6. Rotation, generation invalidation, queued/resumed work, session/Location
71
- changes, active-only gaps, and the applicable plugin-only MVP qualification
72
- matrix.
73
-
74
- Until those gates pass on a compatible runner, do not claim host protection,
75
- production qualification, or an enforced profile.
@@ -1,27 +0,0 @@
1
- # P4 owned tool core
2
-
3
- Implemented the host-independent `credentials_read` and `credential_http` executors in
4
- `src/tools/index.ts`. Both use explicit session/agent/location/generation checks,
5
- `ActivationStore`, policy proofs, and bounded fixed-result publication. The read tool accepts
6
- only an immutable, generation-bound `admitSourceView` snapshot: handles are supplied by the
7
- snapshot, source/key/capability bindings carry session scope, and only entries authorized for
8
- the current session and optional capability filter are rendered. Catalogs require a separate
9
- immutable `admitCapabilityCatalog` boundary; identifiers, NUL/UTF-8 sizes, and plain data
10
- shapes are validated, and no store registration occurs while rendering. Enumeration accounts
11
- source IDs, keys, handles, separators, catalog names, arrays, and working copies before output
12
- allocation; source headers are included in both source and sink ceiling checks before lines are
13
- joined. Admission rejects duplicate source IDs, source entries, capability bindings, and catalog
14
- identities atomically. Caller budgets cannot exceed fixed ceilings and the canonical sink receives
15
- hard budgets.
16
-
17
- The HTTP tool supports only the fixed GitHub repository operation, injects the credential
18
- privately into a trusted transport request, and projects only `accessible`/`unavailable`
19
- outcomes. Its transport contract requires bounded cancellation completion for both in-flight
20
- timeout/abort and resolved-response cleanup; failure is a fixed operation error rather than a
21
- status result.
22
-
23
- Unit and integration tests use synthetic stores and an injected transport. This boundary does
24
- not qualify native OpenCode source adapters, host serialization, or a production HTTP transport.
25
- DNS/address selection and pinning, TLS peer verification, proxy and redirect policy,
26
- parser/header limits, and socket cancellation remain explicit trusted-transport responsibilities;
27
- no plugin entrypoint or OpenCode process is launched here.
@@ -1,53 +0,0 @@
1
- # 25 — Native V2 Effect plugin integration
2
-
3
- Date: 2026-10-13
4
-
5
- ## Implemented
6
-
7
- - Added the native `@opencode/plugin/effect` entrypoint with the required
8
- `Plugin.define({ id, effect })` default export.
9
- - Registered only documented V2 Effect surfaces: session request hooks for all
10
- four request kinds, native HTTP request/response hooks, tool before/after
11
- hooks, and synchronous tool transforms.
12
- - Kept the integration boundary typed and secret-free. Core policy, source
13
- loading, storage, sanitization and transport are supplied through
14
- `CoreRuntimeFactory`; no raw event value is copied into diagnostics or tool
15
- metadata.
16
- - Wired the existing `src/tools` executors through `makeToolExecutors`. The
17
- adapter passes only public arguments and V2 `Tool.Context` session/agent
18
- identity into core-owned closures, returning `Tool.Result` through Effect.
19
- - The default factory is explicitly unavailable and credential tools fail with
20
- the fixed `CREDENTIAL_RUNTIME_UNAVAILABLE` error. It is not a fake runtime
21
- or a qualification claim.
22
-
23
- This entrypoint still requires an injected core/source runtime factory and is
24
- not a functional installed MVP. No protection or shipping claim is made by
25
- this integration alone.
26
-
27
- ## Version evidence
28
-
29
- The package contract was inspected from the published `@opencode/plugin@2.0.28`
30
- tarball. Its Effect entrypoint defines scoped registrations and typed
31
- `Tool.Error` failure for `execute.before`; `effect@4.0.1` is the matching
32
- runtime dependency. Both are exact development pins in `package.json` and
33
- `package-lock.json`; installation was regenerated with `npm install
34
- --ignore-scripts`.
35
-
36
- The repository's strict compiler uses `skipLibCheck` only to isolate upstream
37
- declaration incompatibilities in the pinned OpenCode AI dependency graph; all
38
- project and plugin-integration declarations remain checked. No broad ambient
39
- `any` declaration was added.
40
-
41
- ## Deferred qualification and gaps
42
-
43
- No OpenCode host or user configuration was launched. The contract tests and
44
- TypeScript check validate package identity and public API typing only; they do
45
- not qualify hook execution, ordering, abort semantics, serialization sinks,
46
- or unload behavior. Real-host qualification remains deferred to the isolated
47
- smoke harness.
48
-
49
- The native source-opening/handle loader is not integrated here. The reviewed
50
- HTTP transport and WebSocket/SDK interception adapters are also not supplied by
51
- this boundary. Until those implementations and their host tests exist, this
52
- integration must not be described as protecting source files, provider traffic,
53
- or arbitrary tool execution.
@@ -1,138 +0,0 @@
1
- # 26 — Adversarial review of owned tools
2
-
3
- Date: 2026-10-13
4
-
5
- ## Scope and conclusion
6
-
7
- Reviewed `src/tools/index.ts`, `tests/unit/tools.test.ts`,
8
- `tests/integration/tools.test.ts`, the native registration in
9
- `src/opencode/v2/index.ts`, and [24](24-owned-tools-implementation.md) against
10
- the design and plan. No source or test files were changed.
11
-
12
- **Conclusion:** the HTTP executor is a useful *injected, trusted-transport
13
- boundary* for the narrow status-only operation. It is not suitable for plugin
14
- integration or a release claim yet. `credentials_read` is not safe as a
15
- general source-view boundary until snapshot validation, catalog authorization,
16
- and output/resource accounting are moved into this path (or guaranteed by a
17
- stronger runtime contract).
18
-
19
- ## Findings
20
-
21
- ### High — source view can partially register and is not atomic
22
-
23
- `credentialsRead` calls `store.register` while iterating source entries
24
- (`src/tools/index.ts:61-68`). If a later entry is malformed, registration
25
- throws, or final sink construction fails, earlier registrations remain in the
26
- store. This violates the whole-snapshot admission rule and can leave a
27
- partially initialized source/capability state. It also means the tool is
28
- performing source loading/registration rather than rendering an immutable,
29
- already-admitted snapshot.
30
-
31
- The executor accepts arbitrary `SourceView` values and does not enforce the
32
- config parser's admissibility predicate, supported lexical/UTF-8
33
- representations, duplicate rules, or source-generation invariants. A caller
34
- can therefore supply values that conflict with the handle language or fixed
35
- output inventory and still obtain a handle. The later `canonicalTextSink` call
36
- uses no patterns, so it cannot correct this.
37
-
38
- ### High — source-view and catalog disclosure are not capability-scoped
39
-
40
- An enrolled caller may select any supplied source ID and receives every entry
41
- in that source. Separately, every valid `runtime.capabilities` handle/name is
42
- published unless filtered by the optional name (`src/tools/index.ts:70-73`).
43
- There is no source/key-to-capability mapping and no per-capability policy
44
- authorization in `credentialsRead`. `ActivationStore.withSecret` correctly
45
- binds an HTTP proof to session, agent, Location, generation, operation and
46
- resource, but that proof path is absent from the read tool.
47
-
48
- This can disclose handles and capability names belonging to other configured
49
- operations or sessions. The handles do not automatically grant authority, but
50
- opaque-reference existence and catalog membership are still public reference
51
- disclosures. The registration in `src/opencode/v2/index.ts` makes this tool
52
- available with only `codemode: false`; it does not add this missing binding.
53
-
54
- ### High — read output and source-view work are unbounded by configured budgets
55
-
56
- The tool constructs an unbounded `lines` array/string from source IDs, keys,
57
- values-as-input-to-registration, and capability names. `canonicalTextSink` is
58
- called with its defaults rather than the configured `inputBytes`,
59
- `outputBytes`, traversal, and working-memory budgets. Store reservations cover
60
- individual registered values, not the aggregate source view, metadata, joins,
61
- or temporary copies. There is no quota reservation before enumeration.
62
-
63
- This is especially relevant for UTF-8: store accounting uses UTF-8 bytes, but
64
- the source-view construction has no equivalent byte bound and can allocate
65
- large JS strings before the sink rejects them. The tests only cover one small
66
- ASCII entry and do not exercise Unicode expansion, many entries, oversized
67
- keys/names, or failure after prior registration.
68
-
69
- ### Medium — timeout is not a completion bound for a non-cooperative transport
70
-
71
- `credentialHttp` aborts an internal signal on a timer, but awaits
72
- `transport.request` directly. A transport that ignores `AbortSignal` can leave
73
- the executor pending indefinitely; the timer does not force a fixed error or
74
- release the private callback. The `HttpTransport` interface does not state or
75
- enforce cancellation completion semantics.
76
-
77
- The response's `cancel()` is invoked only after the request promise resolves.
78
- If it throws, the code still publishes the status, despite the design
79
- requiring cancellation failure to terminate the call. The injected timeout
80
- test uses a cooperative transport and therefore does not prove either
81
- property.
82
-
83
- ### Medium — native network security is entirely delegated
84
-
85
- The core constructs a fixed HTTPS URL and headers, but the injected transport
86
- owns DNS resolution, address selection, TLS peer verification, proxy use,
87
- redirect handling, connection reuse, header limits, parser buffering, and
88
- actual socket cancellation. No interface contract prevents redirects or a
89
- second DNS lookup, and no tests in this scope observe those behaviors. The
90
- integration TODOs explicitly leave address pinning/TLS/no-proxy/redirect and
91
- slow-body cases unimplemented.
92
-
93
- Consequently, body/header withholding is proven only against a cooperative
94
- injected object: the executor does not read or return `body`/`headers`, but it
95
- cannot prevent a production transport or endpoint-facing logger from seeing
96
- the secret-bearing request/response. This is acceptable only when the
97
- transport is separately trusted and qualified.
98
-
99
- ### Medium — plugin integration is not currently typecheckable or qualified
100
-
101
- `npm run typecheck` fails at `src/opencode/v2/index.ts:55` (`makeToolExecutors`
102
- has a malformed return-type arrow). The native registration is therefore not
103
- a working integration artifact. Even after that syntax error, the available
104
- tests construct executors directly and do not verify V2 discovery,
105
- serialization, public input immutability, hook ordering, error publication,
106
- or unload/captured-executor behavior. The default factory also remains an
107
- explicit unavailable runtime per history 25.
108
-
109
- ## What is actually established
110
-
111
- - Public HTTP input is restricted to own plain properties and an opaque handle;
112
- policy authorization precedes the injected transport call and the store
113
- proof is bound to identity, Location, generation, capability, operation and
114
- resource.
115
- - The fixed URL/method and fixed outcome strings are constructed locally.
116
- - Returned response body/header fields are not copied into the result, and a
117
- resolved response is asked to cancel before status projection.
118
- - Malformed operation input is rejected before network invocation.
119
- - The focused five tests pass:
120
- `node --test tests/unit/tools.test.ts tests/integration/tools.test.ts`.
121
-
122
- The full unit command is not a clean tool qualification signal: it reaches the
123
- tool tests, but twelve unrelated Windows harness tests fail at private-run
124
- directory permission setup. Integration has three passing executable tests,
125
- one skip, and six TODOs. These results do not qualify a host or production
126
- transport.
127
-
128
- ## Required disposition
129
-
130
- Keep this implementation limited to an injected boundary with a trusted,
131
- separately qualified transport and pre-admitted immutable source snapshot.
132
- Before plugin integration, make source-view publication atomic and bounded,
133
- bind catalog entries to the configured source/capability policy, validate
134
- UTF-8/admissibility and all representations at load time, define
135
- cancellation-completion semantics, and add real transport tests for DNS,
136
- redirects, proxies, TLS peer/address binding, header limits, and aborts.
137
- Fix the native integration typecheck error and execute an isolated real-host
138
- registration/serialization test before making any plugin-integration claim.