@voltro/plugin-storage 0.66.1 → 0.68.0

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/CHANGELOG.md CHANGED
@@ -39,6 +39,265 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.68.0] — 2026-09-09
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/devtools-ui, @voltro/cli, @voltro/plugin-queue** — The shared inspect pages now show reverse-ETL dead letters and replica handoff health, flag kill-switch history, and governance subject-graph limitations. Migration: provide `CdcOutPage.useDeadLetter`, `FlagsPage.useAudit`, and `GovernancePage.useSubjectGraph` as typed data-source hooks; preserve the complete CDC outbox/handoff response and handle `MetricRow.kind: 'plugin'`. Metrics accept both API invocation windows and web registry snapshots. Both dashboards share task navigation and add plugin inventory, data expectations, compute budgets, experiments, invariant checks, and agent-tool inventory. Production now exposes the same standing registries as development with process-scoped observation metadata. Schedules add bounded backfill with explicit confirmation and preserve HTTP 409 outcomes; hosts must supply `SchedulesPage.useBackfill`. Workflow run details support resuming from a recorded step, and the Flow view validates queued payloads and confirms intent retry/discard. Optional hooks are `WorkflowsPage.useResumeFromStep`, `useIntentValidation`, and `useOperateIntent`; both shipped hosts wire them. The shared dev/serve inspect router now reaches step resume, redrive, discard and synchronous update instead of returning 404.
47
+
48
+ The Data explorer row detail now offers last-write audit evidence through optional `DataViewerPage.useProvenance`, with separate missing-row, unattributed and unavailable states. Shared workflow controls fit narrow viewports and use theme-aware status text.
49
+
50
+ App metrics now include the full process metric registry with label/type filters and distribution details via `/_voltro/inspect/metrics/registry`. Hosts can supply `MetricsPage.useRegistry`; both dashboards do. Nonfinite histogram and summary readings remain explicit in JSON. Scope notices retain process identity even for a complete single-process answer, and row detail uses a keyboard-accessible native dialog with readable values for read-only columns.
51
+
52
+ Both dashboards add the transactional outbox queue, retained delivery attempts and a confirmed one-attempt resend with a required reason. Custom capability bags must provide `canResendOutbox`; privileged history reads use `canViewAuditLog`. The shared dev/serve routes enforce read and write credentials and omit payload/response bodies.
53
+
54
+ Mail preview now requires `Capabilities.canPreviewMail`; custom hosts must derive it from operator access and the app's inspect write credential. Mail views retain process/declaration scope, show failed reads explicitly and discard a preview that completes after its template or props changed.
55
+
56
+ Storage access writes now require `Capabilities.canManageStorage`. Derive it from operator authorization and the app's inspect write credential; read-only users can inspect grants but cannot create or revoke them. Plugin panels choose effective instances and clear previous selections' read/action state.
57
+
58
+ Queue inspection replaces `connected` with `producerSelected`: whether the selected instance owns the app-wide outbox/CDC producer slot. It is not a broker-health probe. Custom `QueueData` producers must provide `producerSelected` and remove `connected`.
59
+
60
+ Both dashboards compare remote SQL schemas through the real planner, with immediate results and explicit fetch failures. Dev and production wire the same comparison service; results are bounded to 20 per process. Custom hosts must provide `canCompareMigrationEnvironments` and return `CrossEnvDiffSnapshot` from `MigrationsPage.useCompareCrossEnv` and `InspectManifestApi.compareCrossEnv`. Cloud admins may compare with both inspect credentials; schema mutations remain owner-restricted.
61
+
62
+ Plugin snapshots include `httpRoutes` (method and path only). Custom inventory producers must provide this array. Pass `PluginsPage.appUrl` to link literal GET routes under the inspected app, including configured OpenAPI and Swagger mounts. Links never contain inspect credentials.
63
+
64
+ Data row pages preserve empty-page counts, upstream observation scope and connection state. `DataRowsPage.syncState` optionally reports `live` or `reconnecting`; disconnected views refresh snapshots every 15 seconds and reconcile after reconnect. Cloud commits page rows and status atomically and invalidates credential or organization changes. Local streams send the per-app bearer as an Authorization header on every handshake; the SSE proxy no longer accepts a shared stream cookie.
65
+
66
+ Cloud inspect schemas retain upstream scope, origin, completeness and capture time, including array-shaped web metrics. `DataSource.observation` optionally carries this envelope alongside a payload; `MetricsPage` supports it. The five standing-inspection pages display the sampled process and scope rather than only their collection timestamp.
67
+
68
+ Cloud RPCs preserve observation envelopes, optional log scopes and event delivery declarations. Cluster inspection maps actual scheduler claim IDs and owners. Local reads serialize refreshes and clear app/credential-stale snapshots; fleet responses require assembly evidence and retain departed replicas. Web metric arrays use the same JSON-safe nonfinite readings as the registry; custom hosts must accept `MetricReading` in metric values and histogram boundaries. Cloud and local operating surfaces use compact headings, responsive log/cluster rows and unobstructed mobile controls.
69
+
70
+ The inspector combines snapshot refresh and access guidance in a compact toolbar; expand the read-only notice to configure credentials. Log filters retain associated visible labels after entry. Cache hit rate stays unmeasured until the first request, while hit/miss counters remain zero. Cluster warnings reuse the shared callout icon.
71
+
72
+ **`voltro update` carries you across this** — codemod `0.68.0/01_dashboard-inspection-evidence`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.68.0).
73
+ - **@voltro/runtime, @voltro/voltro** — Outbox resends record an independent request timestamp, so manual attempts without an actor ID remain manual. Re-arming uses a conditional store update to refuse concurrent worker claims or competing requests. Migration: custom `makeOutboxFacade` store adapters must provide `updateMany` instead of `update`; the framework DataStore already does. Boot or `db apply` reconciles the nullable `resendRequestedAt` queue column.
74
+
75
+ **`voltro update` carries you across this** — codemod `0.68.0/01_dashboard-inspection-evidence`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.68.0).
76
+
77
+ ### Added
78
+
79
+ - **@voltro/ui-shadcn** — `Dialog` accepts a typed native dialog ref for `showModal()` and `close()`. Its props retain all `DialogHTMLAttributes<HTMLDialogElement>` and add React's native element ref; no existing attribute is removed or narrowed.
80
+ - Inspect declared rate-limit rules, RPC and HTTP decisions, SQL binding and fail-open observations in both dashboards, with explicit plugin-instance selection and process scope. Custom rate-limit stores can report unchecked requests using `RateLimitDecision.failOpenReason` (`unbound` or `store-error`).
81
+ - **@voltro/protocol, @voltro/runtime, @voltro/client, @voltro/web, @voltro/cli** — `voltro dev` tells you when the api client a page loaded is not the one the server binds, naming the procedures that disagree.
82
+
83
+ Every other freshness mechanism here predicts staleness from source bytes: hash what the browser's client is built from, re-bundle when it moves. Those have twice hashed a set smaller than what the browser actually loads, and the symptom each time was silence — a client decoding against an older schema drops the fields it does not know, and an unknown field is not a decode error, it is simply absent. This asks the question from the other end. The server publishes per-rpc-tag digests of the schemas it bound, the client computes the same over the group it loaded, and a disagreement is one console warning plus a `runtime.surface-mismatch` diagnostic. A gap in an import walk, an optimizer that did not re-run, a service worker and a stale cache all look the same from there: different schemas.
84
+
85
+ The digest is a STRUCTURAL walk of the schema AST, and the obvious implementation would have been useless. `String(schema.ast)` on a `Schema.TaggedError` renders the declared name, so two fields added to a typed error leave it identical — a check built on it would have passed cleanly through the incident it exists to catch. Tests pin that a field added to a tagged error, a tagged class, a nested struct, a union, a tuple and a record each move the digest, so an Effect version that breaks the walk fails loudly rather than going quiet.
86
+
87
+ Two limits stated rather than hidden. A recursive schema (`Schema.suspend`) renders opaque: following it would not terminate, so a field added inside one is invisible, though everything around it still counts. And a procedure only the CLIENT has is never reported — a browser group legitimately omits server-only routes, and a check that cries wolf about the normal case is one people switch off.
88
+
89
+ Dev only, by an asymmetry that is deliberate: `voltro serve` ships a built bundle where this staleness cannot arise from a dep optimizer, and a rolling deploy would make a transient mismatch normal.
90
+
91
+ `apiSurface: compatible` — the only replaced golden lines in `@voltro/client` and `@voltro/web` are one member added to the `ClientDiagnosticKind` union (`runtime.surface-mismatch`). Every existing member is unchanged, the framework is the only producer, and a reader that does not know the new kind sees one more event it can ignore. Only a switch exhaustive over the vocabulary needs an arm. `@voltro/web` is listed because it re-exports that type and its golden carries the same line.
92
+ - **@voltro/ui-shadcn** — Shared Signal opener classes unify responsive headlines, sublines and CTA rows across the Framework landing, Cloud landing and Docs index, including secondary-link and primary-button interactions.
93
+ - **@voltro/testing** — `ctx.asSubject(subject)` and `ctx.asTenant(tenantId)` return a re-scoped test context instead of passing one to a block — for a test with two actors alive at once.
94
+
95
+ `withSubject` nests, which reads well for "write as A, then assert B cannot see it" and badly for a conflict: two editors on one row, a lock contest, an undo the other person's write must refuse. Those want two named contexts and interleaved calls, and a block per alternation buries the sequence under test. Both forms call the same builder, so a context held side by side cannot differ from one handed to a callback: one store, one event bus, one cache, one clock.
96
+
97
+ The `store` option's documentation now says the seed is COPIED. Passing one seed to two `makeTestContext` calls reads like sharing a store and does not share one — each context builds its own from the rows, which is what keeps two tests in a file independent.
98
+
99
+ A test pins the trap that made this worth adding: spreading the context to replace `request.subject` moves the field you read and nothing that decides a write. `ctx.store` was wrapped for the original subject, so tenant scoping, row filters and the `audit()` stamp keep resolving to the first identity, and a write made that way lands with the wrong `updatedBy` — letting a test assert that one person touched a row while the row records another.
100
+
101
+ ### Fixed
102
+
103
+ - **@voltro/cli** — Server bundles no longer wrap every closure in an `Object.defineProperty`.
104
+
105
+ They were built with `minify: true, keepNames: true`. esbuild implements `keepNames` by emitting `__name(fn, "original")`, and `__name` is an `Object.defineProperty` on the function object. For a function DEFINITION that runs once; for a function EXPRESSION created inside a hot path it runs on every instance, and defining a property that way moves the instance's own properties out of its in-object slots into a separate `PropertyArray`.
106
+
107
+ Measured on the memory fixture with 200 open subscriptions, `heapUsed` after a forced GC:
108
+
109
+ minify + keepNames 168.0 KB/subscription, idle 49319 KB identifiers unmangled, no keepNames 135.4 KB/subscription, idle 44767 KB
110
+
111
+ A heap-snapshot diff of the same pair carries the fingerprint of the mechanism in its object counts — +28 200 `closure` nodes against +25 982 `array (object properties)` nodes, for the same 200 subscriptions.
112
+
113
+ The reason `keepNames` was there is unchanged and still correct: Effect's tags, error `name`s and the boot-refusal marker are read as STRINGS across the bundle boundary, and a mangled class name turns a precise refusal into an anonymous one. What was wrong was the estimate written beside it — "it costs a few percent of the saving" — which measured BYTES while the bill was paid in HEAP, once per live subscription, for as long as the subscription lives.
114
+
115
+ So the names are kept by NOT mangling identifiers, which is what created the need for a wrapper in the first place. Whitespace and syntax minification still apply, so the parse-time property these bundles exist for survives: the serve bundle grows 6184 KB → 8388 KB on disk while the cold-boot `modules` phase moves 139 ms → 137 ms, inside the noise. The idle-heap saving alone exceeds the bytes added, before a single subscription exists.
116
+
117
+ It applies to the api SERVE bundle and nothing else, because the bill scales with how many closures a process holds ALIVE. The web start bundle and the middleware bundle serve per-request closures that are collected, not live subscriptions, so the swap bought them nothing — and applying it there overran the start-bundle byte budget by 45 %, which is how the scope was found. They keep `keepNames`, as does the browser bundle, where download bytes over a user's connection dominate.
118
+
119
+ `serverBundleMinify.test.ts` asserts that each exemption is still real — an exemption list that describes nothing reads like a considered decision — and that `apiBuild.ts` being exempted for its precompiled-entry bag does not quietly exempt the serve bundle in the same file.
120
+
121
+ `serveBundleBytes` is re-pinned 6.58 → 9.42 MB, and it is the only artefact budget that moves. What the budget protects is unchanged: the cold-boot `modules` phase measured 139 → 137 ms, inside the noise, on the very number minification was bought for. Bytes are the proxy there; parse time is the property.
122
+ - **@voltro/cli** — `voltro build` on a web app now generates the rpc groups of the apis it imports.
123
+
124
+ `rpcGroup.generated.ts` is gitignored, so a clean clone has none — and only `voltro dev` and `voltro codegen` ever wrote it. The api branch of `voltro build` already self-healed its own, with the right reason recorded beside it: "a production build from a clean CI checkout is the exact case that never ran `voltro dev`". A WEB app imports that file from the API's package, where nothing was healing it, and the note two hundred lines up said so — "unlike the web case this file usually EXISTS, which is worse: a missing import fails the build" — while leaving the worse case alone.
125
+
126
+ What a user got instead was the bundler's:
127
+
128
+ Rolldown failed to resolve import "<pkg>/rpcGroup" from "<app>/.framework/main.tsx" If you do want to externalize this module explicitly add it to `build.rolldownOptions.external`
129
+
130
+ An internal path they did not write, and advice that is exactly wrong.
131
+
132
+ Only WORKSPACE apis: an external api is a URL with no local package, and `resolveApis` deliberately admits those.
133
+
134
+ Resolving the api's directory needs care, and the first attempt got it wrong in the quietest possible way. `${pkg}/package.json` is NOT resolvable when the package declares an `exports` map that omits it — which every voltro api does, because its map exists to expose `./rpcGroup`. That attempt caught the throw and continued, so the whole helper was a no-op that looked like it worked. It now resolves the subpath the bundler itself will use, and falls back to the workspace link (verifying the manifest's `name`) for the case this exists for: the file is not there yet, so every resolution into that package fails.
135
+ - **@voltro/cli** — `voltro dev` re-bundles the browser's api client when a shared file a descriptor IMPORTS changes, not only when a descriptor file itself does.
136
+
137
+ The dev server pre-bundles the generated rpc group's whole import graph, and Vite never re-optimizes a pre-bundled dependency on a content change — so the framework fingerprints that graph itself. It fingerprinted the descriptor files alone. A descriptor declaring `error: [SomeTypedError]` whose class lives in a shared `lib/` file therefore left the fingerprint untouched when the class gained a field, and the browser kept decoding the failure against the older class, dropping the new fields with nothing reported anywhere: an unknown field decodes as absent, not as an error, so there is no exception, no warning, and no request in the api log to find.
138
+
139
+ Three mechanisms carried that blind spot — the stamp codegen embeds in the generated file, the across-boot force, and the in-session watcher — so the fix is one shared definition rather than a third patch. `valueImportGraph.ts` now owns what an import is and where a specifier leads; `browserSafetyGuard` reads the same resolver instead of its own copy. The in-session watcher watches the closure it hashes, since hashing the right set and watching a smaller one leaves the same gap.
140
+
141
+ Type-only imports stay out (they are erased), executor halves stay out (the browser never loads them, and including them would re-bundle on every handler edit), and `@voltro/*` stays out — in an install those are built JavaScript that stops the walk anyway.
142
+
143
+ `descriptorSourceFingerprint` is deliberately unchanged. It answers whether the generated FILE is stale, which the descriptor set settles: an edit inside a shared `lib/` file changes no import line and no rpc tag, so the group it would regenerate is byte-identical.
144
+ - **@voltro/client** — `useConnectionStatus` no longer causes a React hydration mismatch on an SSR page.
145
+
146
+ It passed `getSnapshot` as `useSyncExternalStore`'s SERVER reader as well as its client one, so the server rendered whatever the live recovery coordinator held at that instant while the browser's hydration render read a coordinator that had already moved. React discards the server markup for that subtree and re-renders it on the client — the SSR benefit gone — and reports it only as a console error in a browser.
147
+
148
+ Both readers now agree on a frozen boot snapshot (`INITIAL_RECOVERY_SNAPSHOT`, exported from `@voltro/client`), and React swaps to the live value on the first commit after hydration, so nothing is lost but the mismatch. `navigator.onLine` went through the same door for the same reason.
149
+ - **@voltro/cli** — Three things `voltro dev` did silently.
150
+
151
+ **The dashboard auto-launch skipped without a word.** The launch was `if (resolveDashboardAppPath(root)) { … }` with no `else`, so a project that has never installed `@voltro/dashboard` — a separate package — watched the documented auto-launch not happen and learned nothing, while the same absence reached through `voltro dashboard` printed the exact install line. The boot prints that same sentence once now, from one constant both callers read, and stays quiet when `VOLTRO_DASHBOARD=off` or `--disable-dashboard` says the developer has already declined.
152
+
153
+ The launcher can also decline for five reasons of its OWN — each carrying a `reason`, all five dropped — so "it is installed and it did not come up" had no answer either, on the console or in the log file. A non-TTY stdout (a process manager, a piped run, CI) skips the launch by design, and by design is not the same as unexplained. The reason is logged at `debug`: three of the five are the developer's own decision or a peer process already doing the work, so a console line would be noise, but the log file answers the question when it is asked.
154
+
155
+ **A web app got no inspect token at all.** The mint ran on the api boot path only, so in a project with both, the api had a token and demanded it while the web app was fail-closed shut: `/_voltro/inspect/{app,routes,metrics}` all refused, its runtime-registry entry carried no token, and the dashboard listed the web app and could read nothing from it — the route list, the render modes and the web process's own metrics being exactly what only that half has. The web boot mints before it registers, so the entry carries the token.
156
+
157
+ It mints the INSPECT pair only. The framework's mintable set is split by what owns it now: a web dev boot has no store, so minting a field-encryption key there would write a real AES key into a `.env.local` that nothing can use. An api boot still mints all three.
158
+
159
+ The messages that described the token as minted "per project" said the wrong thing in four places; it is per APP, and an api and a web app each publish their own on their own registry entry.
160
+
161
+ **Orphaned registry temp files were never swept.** The atomic write is `writeFile` then `rename`, with a `catch` that unlinks its own tmp — and that catch cannot run when the process dies between the two, which is what Ctrl-C on a `voltro dev` does. Measured independently on two machines: 270 each, oldest a month old, 188 of them zero bytes, in the same directory the dashboard reads its app list from. A successful write now sweeps its own pattern, once per process, for files older than an hour — old enough that a live writer's tmp, which exists for microseconds, can never be reached.
162
+ - **@voltro/cli** — `voltro db …` now honours the `store` an app declares.
163
+
164
+ `dev.ts` states the rule for the whole framework — "env `$DB_DIALECT` → config.store → 'memory'" — and the db subcommands implemented `$DB_DIALECT` → `'postgres'`. The middle step was missing and the fallback was the wrong one, so an app declaring `store: 'sqlite'` (or mysql, mssql, turso) had every `voltro db` subcommand build a POSTGRES layer against a database that was not there.
165
+
166
+ What that looked like, in a project with no migration files at all:
167
+
168
+ db apply: a file-based migration FAILED — the schema diff was NOT run failed: SqlError: PgClient: Failed to connect
169
+
170
+ Two wrong statements in one message: the project has no file migrations, and the failure is not about migrations. The command was simply pointed at the wrong engine.
171
+
172
+ `$DB_DIALECT` still wins, because an operator acting on a running deployment outranks what the project declared. A declared `memory` store is deliberately not recorded: it is not a dialect these subcommands can operate on, and recording it would make the resolver refuse with a message naming `DB_DIALECT=memory` to someone who never set that variable.
173
+
174
+ The declared store is read once per command run rather than threaded through the resolver's twenty-one synchronous call sites; `resetDeclaredStore` exists so a suite can prove the value does not leak between runs.
175
+ - **@voltro/runtime** — The fleet view no longer reports a LIVE replica as departed.
176
+
177
+ `mergeFleetObservations` classified any observation row whose replica the local roster did not list as departed — a process that is gone. But a membership view is per process: replicas sharing a database and no broadcast bus each hold a roster of one, so two live replicas reported each other as departed while quoting an age of under two seconds, which is the same answer saying the process was alive moments ago.
178
+
179
+ Departure now needs age as well as roster-absence: a row is departed only when it is unrostered AND older than the freshness window, because a departed process cannot have written inside it. A fresh unrostered row is a responder whose existence the roster does not know about, which is what it always was.
180
+
181
+ The behaviour this replaces is kept: a row from a long-gone process is still old and still unrostered, so it still lands in `departed` rather than inflating `responded` or splitting the framework-version count.
182
+ - **@voltro/cli** — A 403 on the no-JS `/form/*` path now leaves a record saying which of the three refusal reasons fired.
183
+
184
+ `/form/*` and `POST /rpc` are two surfaces of ONE origin decision. The rpc listener has logged `refused cross-site request` under `voltro:security` with the reason and the offending origin since the guard shipped; the form path answered a terse `origin not allowed` to the browser, wrote nothing anywhere, and dropped the `decision.reason` it had just computed. So a refusal on the surface a user reaches WITHOUT JavaScript — the one where there is no console to inspect and no network panel to open — was the one with no diagnosis on either side.
185
+
186
+ The form path now logs the same message under the same scope, so an operator greps one scope for both surfaces. The response body is deliberately unchanged and still identical for every reason: the client learns it was refused, not which allowlist entry to guess at.
187
+
188
+ `host` is logged beside `origin` because it is part of the decision rather than context. Both `sameAuthority` and `bothLoopback` compare against the Host header and both return false when it is absent, so a request whose Origin reads as perfectly same-origin is refused the moment Host does not arrive — a case that, in a record carrying the origin alone, is indistinguishable from a genuine cross-site POST.
189
+ - **@voltro/cli** — The dashboard proxy sent its OWN inspect token to every app it opened.
190
+
191
+ Three sites forwarded `process.env.VOLTRO_INSPECT_TOKEN` — the token of whichever process was doing the forwarding — as the bearer for an arbitrary target: the dashboard proxy on the api boot path, the same proxy on the web boot path, and the in-page overlay's proxy to its api. `inspectFetch.ts` has resolved per registry entry all along, which is why `voltro logs` works from any directory; the three readers that forward on someone else's behalf never learned it.
192
+
193
+ That was invisible for exactly as long as every app on a machine shared one token. It stops being true the moment tokens are minted per app, and then the proxy opens precisely one app and is refused by every other — 401, `missing Authorization header`, against a registry entry carrying a perfectly good token of its own. In a project with an api and a web app, the half that answers is whichever one happens to own the forwarding process.
194
+
195
+ `inspectTokenForTarget` / `inspectTokenForTargetSync` resolve the bearer from the target's ORIGIN against the runtime registry, falling back to this process's token when the target is not registered — an operator-configured remote target has nothing else to offer, and a single-app machine has always used exactly that. A malformed target or an unreadable registry degrades to the same fallback rather than throwing, which matters for the sync form, used on a request path with no error boundary above it.
196
+
197
+ The write credential stays env-only and deliberately unpublished: the registry file is a per-app READ capability, and putting an action-authorising token in it would widen every entry into something worth stealing.
198
+
199
+ Three sites were wrong at once, so the guard is over the SET rather than over the line that was reported — a fourth forwarder is one paste away, and the paste looks locally correct, `process.env.VOLTRO_INSPECT_TOKEN` being, after all, this process's token.
200
+ - **@voltro/cli, @voltro/ui-shadcn** — The local dashboard JSON proxy forwards the separate inspect write credential on action requests in both development and production. Explicit read credentials are never upgraded with the proxy's local write token; runtime dashboard app configuration accepts an optional `writeToken`. The shared `Dialog` now accepts its native React ref so access forms can open with `showModal()` and retain native focus containment.
201
+ - **@voltro/cli** — `voltro e2e` reported a spec whose process was KILLED as passed.
202
+
203
+ Node gives a signal-killed child `code: null`, and the runner coerced that with `code ?? 0` — so an out-of-memory runner, a CI cancel or a harness timeout turned "this spec never finished" into "this spec succeeded", with nothing else in the run saying otherwise. A killed spec is a failure now, and the line names the signal, because `exit 1` for a SIGKILL sends the reader looking for an assertion that never ran.
204
+
205
+ `voltro typecheck` and the deploy command runner already failed closed on the same null; what they could not say is WHY. A typecheck failure with no diagnostics above it reads as a tsc bug rather than as a machine that ran out of memory, so both print the signal now.
206
+
207
+ The same discard sat in nine spawn helpers across the test suite, where it made a killed child fail as `expected null to be 1` — a message describing a behaviour defect that had not happened, on a rule that was working. All nine carry the signal into what the assertion prints, and `spawnedChildSignals.test.ts` keeps the tenth copy from being written: the one site where an exit code alone is provably enough is exempted BY NAME with its reason, and an exemption that stops matching fails the run.
208
+ - **@voltro/plugin-mail** — Mail inspection declares recent-send buffers as process-local and template lists as declarations, so dashboards retain replica identity and completeness instead of presenting them as a complete delivery history.
209
+ - **@voltro/web** — Closing an intercepting-route overlay returns focus to whatever opened it.
210
+
211
+ The overlay is a native `<dialog>` opened with `showModal()`, and the code relied on the platform for focus restoration — reasonably, because that is what the spec says `close()` does. The spec also says it only happens while the dialog is still in a document, and React deletes a subtree by detaching its topmost host node before walking the children to run their effect cleanups. So when the overlay is unmounted as part of a route change, its `close()` can land on an element that is already detached, and the restoration step silently does nothing.
212
+
213
+ Measured in a real chromium against `voltro start`: the trigger was focused (`activeElement=open-2`), `showModal()` moved focus into the dialog (`activeElement=DIALOG`), and after Back the dialog was gone with `document.activeElement` on `BODY`. The trigger itself was untouched — same node, still connected — so this was not a re-render replacing it. For anyone navigating by keyboard, Back dropped them at the top of the document.
214
+
215
+ The overlay now records what was focused before it opened and restores it on close. It FILLS THE GAP rather than fighting the platform: when `close()` restored focus itself this is a no-op on the same element, and a focus the app moved somewhere real while the overlay was open is left alone — only a focus that has fallen to nothing gets put back.
216
+
217
+ `e2e-fixtures/web-intercept` driven through a real browser goes from one failure to none, and both halves are pinned in jsdom, which has no `showModal` at all and is therefore the right harness for the explicit path.
218
+ - **@voltro/cli, @voltro/protocol** — Production now mounts plugin inspect endpoints through the same authenticated dispatcher as development. Plugin JSON GET endpoints can declare process, shared-store or declaration scope to retain origin and completeness at effective and aliased mounts. Plugin writes reject non-loopback browser origins in the shared dispatcher.
219
+ - Keep plugin operation targets, permissions and failures explicit in both dashboards. Confirm retention, subject erasure, search reindex/repair and moderation decisions; bind subject results to the selected subject and reject already-reviewed moderation items with HTTP 409. Report process/declaration/store scope without treating missing data as an empty or healthy state.
220
+ - Resolve dashboard plugin reads and writes against the selected effective instance. Distinguish failed reads from empty lists, show bounded storage and notification samples honestly, and retain declaration/process scope for billing configuration and queue counters.
221
+ - **@voltro/runtime** — The default `Referrer-Policy` made the framework refuse its own no-JS forms.
222
+
223
+ `no-referrer` was the shipped default, and under it a browser sends `Origin: null` on a same-origin state-changing request — the Fetch specification serialises the origin as `null` when the referrer policy is `no-referrer`. The origin guard refuses an opaque origin, correctly and by design. So every no-JS `<form method="post">` in every app answered **403**, on `voltro dev` and `voltro start` alike, for every user. Two security defaults, each defensible alone, cancelling each other: we asked the browser to withhold provenance and then refused the request for lacking it.
224
+
225
+ The same request also arrived without a `Referer`, which `/form/*` reads to choose the PRG redirect target and the 422 re-render's return path.
226
+
227
+ The default is now `strict-origin-when-cross-origin`. It withholds the path cross-origin — the privacy property `no-referrer` was chosen for — while leaving the `Origin` intact, and nulls it only on an https→http downgrade, which is the one case where withholding it is the point. It is also what a browser does with no header at all. `same-origin` would also fix the form path but nulls the `Origin` on a legitimate cross-origin post to an allowlisted api, which is exactly what a split deployment's `VOLTRO_ALLOWED_ORIGINS` exists to permit.
228
+
229
+ Measured in a real headless chromium against a bare `node:http` server, one header changed between rows:
230
+
231
+ (none) → Origin: http://127.0.0.1:45412 no-referrer → Origin: null ← and no Referer strict-origin-when-cross-origin → Origin: http://127.0.0.1:45412 same-origin → Origin: http://127.0.0.1:45412
232
+
233
+ An app that set `referrerPolicy: 'no-referrer'` explicitly keeps it and keeps the 403; `preservesSameOriginHeader` is exported so the incompatibility can be asserted rather than rediscovered.
234
+ - **@voltro/ui-shadcn** — The shared token stylesheet now supplies light/dark success, warning and info colors and their Tailwind utilities, so Callout status treatments render consistently in every consuming app without local token definitions.
235
+ - **@voltro/ui-shadcn** — Signal header actions use the navigation's resting border contrast, 16px icon size and text line height so equally sized controls also align visually.
236
+ - **@voltro/cli** — `voltro --help` stopped advertising a subcommand that was removed on purpose, and `voltro support --help` answers as help.
237
+
238
+ `voltro support sentry <eventId>` went away in 0.67.0 — sentry-cli 3 dropped `sourcemaps explain` — with a codemod carrying users across it. What outlived the removal was the command table's own summary, which went on listing `Subcommands: collect, sentry` while the implementation answered `unknown subcommand 'sentry'` with exit 2. The summary names the removal and where to look instead, so a reader arriving from an older help output or a runbook learns why rather than meeting a refusal.
239
+
240
+ `voltro support --help` also came back as `unknown subcommand '--help'` with exit 2. The command declares `handlesHelp`, so the flag arrives as the first argument and hit the subcommand check — an error for the one request that is never one, on the command somebody reaches for BECAUSE they do not yet know the subcommands. `--help`, `-h` and `help` all print usage and exit 0.
241
+ - **@voltro/cli** — `voltro test` no longer turns a TYPE-ONLY `tsconfig` `paths` mapping into a runtime alias. An app that maps `"react": ["./node_modules/@types/react"]` — the usual trick so `tsc` resolves ambient types in a hoisted monorepo — could not run a single test that imported React: vitest resolved the real `import 'react'` to a package of declarations and every such file died with `"." is not exported under the conditions ["node","development","import"] from @types/react`.
242
+
243
+ `voltro build` and `voltro dev` already skipped these mappings; the test runner and `voltro doctor` used a second alias reader that did not. The rule now has one definition (`isTypeOnlyPathTarget`) that all three import, and the two readers are pinned against each other in a test — the codemod path is deliberately excluded, since it hands `paths` back to a TypeScript compiler, which is who a type-only mapping is written for.
244
+
245
+ ### Internal (no consumer-facing effect)
246
+
247
+ - **@voltro/cli** — `scripts/browser-surface-mismatch.mjs` drives the schema cross-check in a real Chromium — a real `fetch` to the derived capabilities URL, a real console, the real `buildApiRuntime`.
248
+
249
+ The unit tests reach everything except the environment the check actually runs in, and this one's whole value is that it SAYS something where the alternative is silence: a version that had quietly stopped firing would look, from outside, exactly like a client that is always fresh. Three cases, and the last two are what keep the first honest — a disagreeing surface warns and names the procedure, an agreeing one is silent, and a server publishing no surface at all is silent too. Falsified by disconnecting the reporter: four cases go red, the two silence cases stay green.
250
+
251
+ It joins `browser-checks.mjs` by existing; the runner discovers `browser-*.mjs` by name.
252
+ - **@voltro/cli** — Synchronize the published dashboard-inspection codemod manifest and the development inspect-door regression with shared routing. Measure Redis delivery only after broker subscription readiness, establish file-watcher readiness in every workspace root, and run these real-process, watcher and Redis suites in the serial integration group.
253
+ - **@voltro/client** — `documentHidden` in the subscription cache reads `document` through `globalThis` with a local structural type, the way `formBinding` already does, instead of `typeof document !== 'undefined' && document.visibilityState`. Same value in every environment. The spelling mattered only to a program that compiles `@voltro/client` without the DOM lib: `@voltro/cli`'s test tsconfig does, and its `lint` reported TS2584 twice on that line while the client's own `lint` — DOM lib present — was green. A cross-package view of a file is its own check, and this one runs only in CI's Lint job.
254
+
255
+ ---
256
+
257
+ ## [0.67.0] — 2026-09-07
258
+
259
+ ### ⚠ BREAKING
260
+
261
+ - **@voltro/client, @voltro/protocol, @voltro/runtime, @voltro/cli, @voltro/web** — Confirmed optimistic writes now reconcile against a token-bound post-ACK primary snapshot, never an unrelated delta, changed row or timeout. Matching snapshots retire only covered previews; server normalisation and later writers remain authoritative. Same-tick acknowledgements coalesce, and affected cache entries preserve primary consistency across same-subject reconnects. Migration: deploy matching client/server framework versions together and account for an additional subscription read per affected acknowledgement group; affected entries use primary snapshots instead of replica reads/delta-resume for their cache lifetime. Native hooks need no source rewrite. Direct callers of the public `supersedesConfirmedPatches(event, baseBefore, baseAfter)` helper must migrate to `(event, reconciliationToken)` using the token of the post-ACK read, or use the native `SubscriptionCache`; row comparison cannot be mechanically rewritten into causal proof.
262
+
263
+ **`voltro update` carries you across this** — codemod `0.67.0/01_subscription-reconciliation`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.67.0).
264
+ - **@voltro/cli** — `voltro support sentry <eventId>` is removed. It wrapped `sentry-cli sourcemaps explain`, which sentry-cli 3 dropped in favour of Sentry's in-product source-map debugger; `@voltro/cli` now depends on `@sentry/cli` 3, whose `execute(args, live)` takes a boolean. Migration: delete the invocation from scripts, CI jobs and runbooks and read the event's source-map resolution in Sentry itself. `voltro support collect` is unchanged.
265
+
266
+ **`voltro update` carries you across this** — codemod `0.67.0/03_support-sentry-removed`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.67.0).
267
+ - **@voltro/cli, @voltro/workflow, @voltro/testing** — `voltro test` runs on vitest 5, and `@voltro/workflow` names `vitest ^5.0.0` as its peer. Migration: set `vitest` and `@vitest/coverage-v8` to `^5.0.0` together in every app that declares them (the coverage provider peer-pins the exact vitest version), install, and run the suite once — vitest 5 clears mocks before each test by default, fails a test whose async assertion was not awaited, and no longer looks a config file up in ancestor directories. Scaffolded templates ship on 5.
268
+
269
+ **`voltro update` carries you across this** — codemod `0.67.0/02_vitest-5`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.67.0).
270
+
271
+ ### Added
272
+
273
+ - **@voltro/ui-shadcn, @voltro/cli, @voltro/devtools, @voltro/devtools-ui** — Add the opt-in Signal brand stylesheet, accessible `SignalHeader` and pausable `SignalField` with reduced-motion and visibility handling. Development and production builds resolve the stylesheet from the same UI-kit installation; the in-browser devtools use the matching plum/violet palette and declare the Tailwind dependency required to compile their source stylesheet locally.
274
+
275
+ Code blocks use matching light/dark surfaces, code comparisons retain readable contrast, and the shared routes view uses a direct heading.
276
+
277
+ `DocsLayout` supports custom header and introduction slots and an optional desktop sidebar. Its mobile drawer uses native modal focus and Escape behavior; search contains keyboard focus and restores the opener when dismissed.
278
+
279
+ `VoltroWordmark` owns product badges and an operator warning tone. `SignalHeader` shares responsive brand geometry and an optional sticky reading surface; `AppShell` accepts a custom header slot. All first-party product headers and footers use this composition, including the compact local Devtools identity.
280
+
281
+ Shared `SignalPreferences`, `SignalSearch` and `SignalHeaderAction` align header controls across public and operating apps. Signal fields support both themes; `ProfileMenu` accepts a custom trigger and preserves an explicit System choice.
282
+
283
+ ### Changed
284
+
285
+ - **@voltro/cli, @voltro/runtime, @voltro/database, @voltro/protocol, @voltro/voltro, @voltro/sql-mssql, @voltro/sql-turso, @voltro/workflow, @voltro/plugin-sso-saml, @voltro/plugin-sentry** — Dependency refresh: Effect 3.22.1 with the matching `@effect/*` patch releases (`@effect/cluster` 0.60.2, `@effect/sql-mssql` 0.54.0, `@effect/platform` 0.97.1, `@effect/rpc` 0.76.2, `@effect/sql` 0.52.1, `@effect/workflow` 0.19.1), `@libsql/client` 0.18.0 — and `@effect/sql-mssql` 0.54.0 flipped its TLS default to encrypt-and-verify, so the mssql dialect now passes `encrypt: false` explicitly for an unset connection to keep the documented modes (unset → plaintext, `sslmode=require` → encrypt without verification) — `@sentry/cli` 3, `xml-encryption` 6, vite 8.2.2, and the tracked minor/patch lines. The mssql patch for `@effect/cluster` — the four SQL fixes plus the shard-release backoff — was regenerated against 0.60.2 (upstream refactored `SqlMessageStorage`, so the 0.60.0 hunks no longer applied) and ships as `patches/@effect__cluster@0.60.2.patch`; `voltro add mssql` writes the new key. Projects scaffolded by the CLI record the pnpm that ran it, which is 12.x on the framework's own toolchain; pnpm 11+ replaced `onlyBuiltDependencies` with `allowBuilds`, and the framework's workspaces declare it.
286
+
287
+ ### Fixed
288
+
289
+ - **@voltro/cli, @voltro/runtime, @voltro/database, @voltro/voltro** — The tunable readers that take `env = process.env` (`resolveEagerFallbackWarnIntervalMs`, `resolveWorkflowRecordingMode`, `readPollCeilingMs`, and the `SchedulingEnv` / `ReactiveEnv` / `InspectRingsEnv` shapes) accept a bare `NodeJS.ProcessEnv` again. They were typed as all-optional literals, which TypeScript's weak-type check rejects for `Dict<string>` the moment nothing in the compilation augments `ProcessEnv` — an app that compiles framework source with only `@types/node` (a mobile app, which declares no env) failed `tsc` on `dbMetrics.ts`. The parameters now carry an index signature.
290
+ - **@voltro/client, @voltro/web** — The subscription recovery watchdog knows when it was throttled. A browser delays timers in a hidden tab, so the stall timer could fire long after it was due, report the interval it was armed with as the interval waited, warn, and invalidate entries whose deliveries were queued behind the same throttling. It now compares its due time with its fire time: late beyond a tolerance, or firing in a hidden tab, it records one `subscription.recovery-timer-late` per attempt (with `timerLateMs` and `hidden`) and re-arms; the first on-time fire in a visible tab decides. `subscription.recovery-stuck` carries the time actually waited and the number of deferrals.
291
+
292
+ The client runtime yields to the event loop through a `MessageChannel` in the browser instead of `setTimeout`, so tasks queued behind Effect's periodic yield are not held to the hidden-tab timer grid. `runtime.resolved` records the channel in use as `scheduler`.
293
+
294
+ `socket.frames` marks frame-arrival bursts on the raw socket — the first frame after open and each frame that follows a gap of five seconds or more, at most eight per socket — so an export separates a late arrival from late processing.
295
+
296
+ `apiSurface: compatible` — the golden churn in `@voltro/client` and `@voltro/web` is two members added to the `ClientDiagnosticKind` union (`socket.frames`, `subscription.recovery-timer-late`); every existing member is unchanged, the framework is the only producer, and a reader that does not know the new kinds sees two more events it can ignore. Only a switch exhaustive over the vocabulary needs two arms.
297
+ - **@voltro/cli** — Web middleware is precompiled into the server production output and rooted by runtime pruning, preserving its behavior in source-free Docker deployments. Missing transitive imports now fail loudly instead of silently disabling a present middleware; rebuilding after removal clears the previous policy. Runtime tracing also preserves transitive dependencies when a deploy directory is reached through a symlink. Native bundle imports now propagate missing-library and initialization errors instead of returning a circular shim's empty exports. Rebuild and deploy the complete `.framework` output; no application API changes are required.
298
+
299
+ ---
300
+
42
301
  ## [0.66.1] — 2026-09-06
43
302
 
44
303
  ### Fixed
@@ -9,7 +9,7 @@ Generated from the resolved runtime dependency closure (118 packages).
9
9
 
10
10
  ---
11
11
 
12
- ## @aws-sdk/checksums@3.1000.26
12
+ ## @aws-sdk/checksums@3.1000.29
13
13
 
14
14
  License: Apache-2.0
15
15
 
@@ -217,7 +217,7 @@ Apache License
217
217
  limitations under the License.
218
218
  ```
219
219
 
220
- ## @aws-sdk/client-s3@3.1107.0
220
+ ## @aws-sdk/client-s3@3.1126.0
221
221
 
222
222
  License: Apache-2.0
223
223
 
@@ -425,7 +425,7 @@ Apache License
425
425
  limitations under the License.
426
426
  ```
427
427
 
428
- ## @aws-sdk/core@3.977.6
428
+ ## @aws-sdk/core@3.977.9
429
429
 
430
430
  License: Apache-2.0
431
431
 
@@ -633,7 +633,7 @@ Apache License
633
633
  limitations under the License.
634
634
  ```
635
635
 
636
- ## @aws-sdk/credential-provider-env@3.972.67
636
+ ## @aws-sdk/credential-provider-env@3.972.70
637
637
 
638
638
  License: Apache-2.0
639
639
 
@@ -841,13 +841,13 @@ Apache License
841
841
  limitations under the License.
842
842
  ```
843
843
 
844
- ## @aws-sdk/credential-provider-http@3.972.69
844
+ ## @aws-sdk/credential-provider-http@3.972.72
845
845
 
846
846
  License: Apache-2.0
847
847
 
848
848
  _(no bundled license text found in the package; see its npm page.)_
849
849
 
850
- ## @aws-sdk/credential-provider-ini@3.973.12
850
+ ## @aws-sdk/credential-provider-ini@3.973.15
851
851
 
852
852
  License: Apache-2.0
853
853
 
@@ -1055,13 +1055,13 @@ Apache License
1055
1055
  limitations under the License.
1056
1056
  ```
1057
1057
 
1058
- ## @aws-sdk/credential-provider-login@3.972.74
1058
+ ## @aws-sdk/credential-provider-login@3.972.77
1059
1059
 
1060
1060
  License: Apache-2.0
1061
1061
 
1062
1062
  _(no bundled license text found in the package; see its npm page.)_
1063
1063
 
1064
- ## @aws-sdk/credential-provider-node@3.972.78
1064
+ ## @aws-sdk/credential-provider-node@3.972.82
1065
1065
 
1066
1066
  License: Apache-2.0
1067
1067
 
@@ -1269,7 +1269,7 @@ Apache License
1269
1269
  limitations under the License.
1270
1270
  ```
1271
1271
 
1272
- ## @aws-sdk/credential-provider-process@3.972.67
1272
+ ## @aws-sdk/credential-provider-process@3.972.70
1273
1273
 
1274
1274
  License: Apache-2.0
1275
1275
 
@@ -1477,7 +1477,7 @@ Apache License
1477
1477
  limitations under the License.
1478
1478
  ```
1479
1479
 
1480
- ## @aws-sdk/credential-provider-sso@3.973.11
1480
+ ## @aws-sdk/credential-provider-sso@3.973.14
1481
1481
 
1482
1482
  License: Apache-2.0
1483
1483
 
@@ -1685,7 +1685,7 @@ Apache License
1685
1685
  limitations under the License.
1686
1686
  ```
1687
1687
 
1688
- ## @aws-sdk/credential-provider-web-identity@3.972.73
1688
+ ## @aws-sdk/credential-provider-web-identity@3.972.76
1689
1689
 
1690
1690
  License: Apache-2.0
1691
1691
 
@@ -1893,7 +1893,7 @@ Apache License
1893
1893
  limitations under the License.
1894
1894
  ```
1895
1895
 
1896
- ## @aws-sdk/lib-storage@3.1107.0
1896
+ ## @aws-sdk/lib-storage@3.1126.0
1897
1897
 
1898
1898
  License: Apache-2.0
1899
1899
 
@@ -2101,7 +2101,7 @@ Apache License
2101
2101
  limitations under the License.
2102
2102
  ```
2103
2103
 
2104
- ## @aws-sdk/middleware-sdk-s3@3.972.72
2104
+ ## @aws-sdk/middleware-sdk-s3@3.972.75
2105
2105
 
2106
2106
  License: Apache-2.0
2107
2107
 
@@ -2309,13 +2309,13 @@ Apache License
2309
2309
  limitations under the License.
2310
2310
  ```
2311
2311
 
2312
- ## @aws-sdk/nested-clients@3.997.41
2312
+ ## @aws-sdk/nested-clients@3.997.44
2313
2313
 
2314
2314
  License: Apache-2.0
2315
2315
 
2316
2316
  _(no bundled license text found in the package; see its npm page.)_
2317
2317
 
2318
- ## @aws-sdk/s3-request-presigner@3.1107.0
2318
+ ## @aws-sdk/s3-request-presigner@3.1126.0
2319
2319
 
2320
2320
  License: Apache-2.0
2321
2321
 
@@ -2523,7 +2523,7 @@ Apache License
2523
2523
  limitations under the License.
2524
2524
  ```
2525
2525
 
2526
- ## @aws-sdk/signature-v4-multi-region@3.996.43
2526
+ ## @aws-sdk/signature-v4-multi-region@3.996.46
2527
2527
 
2528
2528
  License: Apache-2.0
2529
2529
 
@@ -2731,7 +2731,7 @@ Apache License
2731
2731
  limitations under the License.
2732
2732
  ```
2733
2733
 
2734
- ## @aws-sdk/token-providers@3.1103.0
2734
+ ## @aws-sdk/token-providers@3.1116.0
2735
2735
 
2736
2736
  License: Apache-2.0
2737
2737
 
@@ -2939,7 +2939,7 @@ Apache License
2939
2939
  limitations under the License.
2940
2940
  ```
2941
2941
 
2942
- ## @aws-sdk/types@3.974.2
2942
+ ## @aws-sdk/types@3.974.5
2943
2943
 
2944
2944
  License: Apache-2.0
2945
2945
 
@@ -3147,7 +3147,7 @@ Apache License
3147
3147
  limitations under the License.
3148
3148
  ```
3149
3149
 
3150
- ## @aws-sdk/xml-builder@3.972.37
3150
+ ## @aws-sdk/xml-builder@3.972.40
3151
3151
 
3152
3152
  License: Apache-2.0
3153
3153
 
@@ -3704,7 +3704,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3704
3704
  SOFTWARE.
3705
3705
  ```
3706
3706
 
3707
- ## @azure/core-rest-pipeline@1.23.0
3707
+ ## @azure/core-rest-pipeline@1.24.0
3708
3708
 
3709
3709
  License: MIT
3710
3710
 
@@ -3900,7 +3900,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3900
3900
  SOFTWARE.
3901
3901
  ```
3902
3902
 
3903
- ## @effect/cluster@0.60.0
3903
+ ## @effect/cluster@0.60.2
3904
3904
 
3905
3905
  License: MIT
3906
3906
 
@@ -3928,7 +3928,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3928
3928
  SOFTWARE.
3929
3929
  ```
3930
3930
 
3931
- ## @effect/platform-node@0.108.0
3931
+ ## @effect/platform-node@0.108.1
3932
3932
 
3933
3933
  License: MIT
3934
3934
 
@@ -3956,7 +3956,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3956
3956
  SOFTWARE.
3957
3957
  ```
3958
3958
 
3959
- ## @effect/platform-node-shared@0.61.0
3959
+ ## @effect/platform-node-shared@0.61.1
3960
3960
 
3961
3961
  License: MIT
3962
3962
 
@@ -3984,7 +3984,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3984
3984
  SOFTWARE.
3985
3985
  ```
3986
3986
 
3987
- ## @effect/sql@0.52.0
3987
+ ## @effect/sql@0.52.1
3988
3988
 
3989
3989
  License: MIT
3990
3990
 
@@ -4012,7 +4012,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4012
4012
  SOFTWARE.
4013
4013
  ```
4014
4014
 
4015
- ## @effect/workflow@0.19.0
4015
+ ## @effect/workflow@0.19.1
4016
4016
 
4017
4017
  License: MIT
4018
4018
 
@@ -4129,13 +4129,13 @@ The above copyright notice and this permission notice shall be included in all c
4129
4129
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4130
4130
  ```
4131
4131
 
4132
- ## @img/sharp-libvips-linux-x64@1.3.2
4132
+ ## @img/sharp-libvips-linux-x64@1.3.3
4133
4133
 
4134
4134
  License: LGPL-3.0-or-later
4135
4135
 
4136
4136
  _(no bundled license text found in the package; see its npm page.)_
4137
4137
 
4138
- ## @img/sharp-linux-x64@0.35.3
4138
+ ## @img/sharp-linux-x64@0.35.4
4139
4139
 
4140
4140
  License: Apache-2.0
4141
4141
 
@@ -4576,7 +4576,7 @@ Apache License
4576
4576
  limitations under the License.
4577
4577
  ```
4578
4578
 
4579
- ## @opentelemetry/api-logs@0.221.0
4579
+ ## @opentelemetry/api-logs@0.222.0
4580
4580
 
4581
4581
  License: Apache-2.0
4582
4582
 
@@ -4784,7 +4784,7 @@ Apache License
4784
4784
  limitations under the License.
4785
4785
  ```
4786
4786
 
4787
- ## @opentelemetry/context-async-hooks@2.10.0
4787
+ ## @opentelemetry/context-async-hooks@2.11.0
4788
4788
 
4789
4789
  License: Apache-2.0
4790
4790
 
@@ -4992,7 +4992,7 @@ Apache License
4992
4992
  limitations under the License.
4993
4993
  ```
4994
4994
 
4995
- ## @opentelemetry/core@2.9.0
4995
+ ## @opentelemetry/core@2.10.0
4996
4996
 
4997
4997
  License: Apache-2.0
4998
4998
 
@@ -5200,7 +5200,7 @@ Apache License
5200
5200
  limitations under the License.
5201
5201
  ```
5202
5202
 
5203
- ## @opentelemetry/exporter-metrics-otlp-http@0.221.0
5203
+ ## @opentelemetry/exporter-metrics-otlp-http@0.222.0
5204
5204
 
5205
5205
  License: Apache-2.0
5206
5206
 
@@ -5408,7 +5408,7 @@ Apache License
5408
5408
  limitations under the License.
5409
5409
  ```
5410
5410
 
5411
- ## @opentelemetry/exporter-trace-otlp-http@0.221.0
5411
+ ## @opentelemetry/exporter-trace-otlp-http@0.222.0
5412
5412
 
5413
5413
  License: Apache-2.0
5414
5414
 
@@ -5616,7 +5616,7 @@ Apache License
5616
5616
  limitations under the License.
5617
5617
  ```
5618
5618
 
5619
- ## @opentelemetry/otlp-exporter-base@0.221.0
5619
+ ## @opentelemetry/otlp-exporter-base@0.222.0
5620
5620
 
5621
5621
  License: Apache-2.0
5622
5622
 
@@ -5824,7 +5824,7 @@ Apache License
5824
5824
  limitations under the License.
5825
5825
  ```
5826
5826
 
5827
- ## @opentelemetry/otlp-transformer@0.221.0
5827
+ ## @opentelemetry/otlp-transformer@0.222.0
5828
5828
 
5829
5829
  License: Apache-2.0
5830
5830
 
@@ -6032,7 +6032,7 @@ Apache License
6032
6032
  limitations under the License.
6033
6033
  ```
6034
6034
 
6035
- ## @opentelemetry/resources@2.10.0
6035
+ ## @opentelemetry/resources@2.11.0
6036
6036
 
6037
6037
  License: Apache-2.0
6038
6038
 
@@ -6240,7 +6240,7 @@ Apache License
6240
6240
  limitations under the License.
6241
6241
  ```
6242
6242
 
6243
- ## @opentelemetry/sdk-logs@0.221.0
6243
+ ## @opentelemetry/sdk-logs@0.222.0
6244
6244
 
6245
6245
  License: Apache-2.0
6246
6246
 
@@ -6448,7 +6448,7 @@ Apache License
6448
6448
  limitations under the License.
6449
6449
  ```
6450
6450
 
6451
- ## @opentelemetry/sdk-metrics@2.10.0
6451
+ ## @opentelemetry/sdk-metrics@2.11.0
6452
6452
 
6453
6453
  License: Apache-2.0
6454
6454
 
@@ -6656,7 +6656,7 @@ Apache License
6656
6656
  limitations under the License.
6657
6657
  ```
6658
6658
 
6659
- ## @opentelemetry/sdk-trace@2.10.0
6659
+ ## @opentelemetry/sdk-trace@2.11.0
6660
6660
 
6661
6661
  License: Apache-2.0
6662
6662
 
@@ -6864,7 +6864,7 @@ Apache License
6864
6864
  limitations under the License.
6865
6865
  ```
6866
6866
 
6867
- ## @opentelemetry/sdk-trace-base@2.10.0
6867
+ ## @opentelemetry/sdk-trace-base@2.11.0
6868
6868
 
6869
6869
  License: Apache-2.0
6870
6870
 
@@ -7072,7 +7072,7 @@ Apache License
7072
7072
  limitations under the License.
7073
7073
  ```
7074
7074
 
7075
- ## @opentelemetry/sdk-trace-node@2.10.0
7075
+ ## @opentelemetry/sdk-trace-node@2.11.0
7076
7076
 
7077
7077
  License: Apache-2.0
7078
7078
 
@@ -7544,7 +7544,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7544
7544
  SOFTWARE.
7545
7545
  ```
7546
7546
 
7547
- ## @smithy/core@3.31.1
7547
+ ## @smithy/core@3.33.3
7548
7548
 
7549
7549
  License: Apache-2.0
7550
7550
 
@@ -7960,7 +7960,7 @@ Apache License
7960
7960
  limitations under the License.
7961
7961
  ```
7962
7962
 
7963
- ## @smithy/fetch-http-handler@5.6.13
7963
+ ## @smithy/fetch-http-handler@5.8.0
7964
7964
 
7965
7965
  License: Apache-2.0
7966
7966
 
@@ -8168,7 +8168,7 @@ Apache License
8168
8168
  limitations under the License.
8169
8169
  ```
8170
8170
 
8171
- ## @smithy/node-http-handler@4.9.13
8171
+ ## @smithy/node-http-handler@4.12.1
8172
8172
 
8173
8173
  License: Apache-2.0
8174
8174
 
@@ -8584,7 +8584,7 @@ Apache License
8584
8584
  limitations under the License.
8585
8585
  ```
8586
8586
 
8587
- ## @smithy/types@4.16.1
8587
+ ## @smithy/types@4.18.0
8588
8588
 
8589
8589
  License: Apache-2.0
8590
8590
 
@@ -8820,7 +8820,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
8820
8820
  SOFTWARE.
8821
8821
  ```
8822
8822
 
8823
- ## @upstash/redis@1.38.2
8823
+ ## @upstash/redis@1.38.3
8824
8824
 
8825
8825
  License: MIT
8826
8826
 
@@ -9736,7 +9736,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9736
9736
  SOFTWARE.
9737
9737
  ```
9738
9738
 
9739
- ## jose@6.2.8
9739
+ ## jose@6.2.10
9740
9740
 
9741
9741
  License: MIT
9742
9742
 
@@ -9882,7 +9882,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9882
9882
  SOFTWARE.
9883
9883
  ```
9884
9884
 
9885
- ## multipasta@0.2.7
9885
+ ## multipasta@0.2.8
9886
9886
 
9887
9887
  License: MIT
9888
9888
 
@@ -9954,7 +9954,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
9954
9954
  SOFTWARE.
9955
9955
  ```
9956
9956
 
9957
- ## picomatch@4.0.5
9957
+ ## picomatch@4.0.7
9958
9958
 
9959
9959
  License: MIT
9960
9960
 
@@ -10142,7 +10142,7 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
10142
10142
  IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
10143
10143
  ```
10144
10144
 
10145
- ## sharp@0.35.3
10145
+ ## sharp@0.35.4
10146
10146
 
10147
10147
  License: Apache-2.0
10148
10148
 
@@ -10761,7 +10761,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
10761
10761
  SOFTWARE.
10762
10762
  ```
10763
10763
 
10764
- ## undici@8.9.0
10764
+ ## undici@8.10.0
10765
10765
 
10766
10766
  License: MIT
10767
10767
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-storage",
3
- "version": "0.66.1",
3
+ "version": "0.68.0",
4
4
  "description": "File-storage plugin — one StorageProvider contract, switchable backends (S3 / MinIO / Azure / filesystem / memory / database). Public (CDN-direct) + private (access policy + per-object grants) objects, upload constraints, virus-scan hook, on-the-fly image transforms.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -43,19 +43,19 @@
43
43
  "node": ">=24.0.0"
44
44
  },
45
45
  "dependencies": {
46
- "@voltro/database": "0.66.1",
47
- "@voltro/env": "0.66.1",
48
- "@voltro/protocol": "0.66.1"
46
+ "@voltro/database": "0.68.0",
47
+ "@voltro/env": "0.68.0",
48
+ "@voltro/protocol": "0.68.0"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@aws-sdk/client-s3": "^3.1107.0",
52
- "@aws-sdk/lib-storage": "^3.1107.0",
53
- "@aws-sdk/s3-request-presigner": "^3.1107.0",
51
+ "@aws-sdk/client-s3": "^3.1126.0",
52
+ "@aws-sdk/lib-storage": "^3.1126.0",
53
+ "@aws-sdk/s3-request-presigner": "^3.1126.0",
54
54
  "@azure/storage-blob": "^12.33.0",
55
- "sharp": "^0.35.3"
55
+ "sharp": "^0.35.4"
56
56
  },
57
57
  "peerDependencies": {
58
- "effect": "^3.22.0"
58
+ "effect": "^3.22.1"
59
59
  },
60
60
  "publishConfig": {
61
61
  "access": "public"