Skip to content

[APPS-2945] Update: drop the in-process local execution guard in favor of Node's permission model - #563

Open
tyffical wants to merge 1 commit into
tiffany.trinh/apps-2945-follow-terrapin-flagfrom
tiffany.trinh/apps-2945-local-guard-messages
Open

tyffical wants to merge 1 commit into
tiffany.trinh/apps-2945-follow-terrapin-flagfrom
tiffany.trinh/apps-2945-local-guard-messages

Conversation

@tyffical

@tyffical tyffical commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

  • Where a call can't run under Node's permission model, an in-process guard refused writes and subprocesses the Node backend runtime allows, blocking code that runs in production. (APPS-2945, design page)
  • Comparable local runners don't build a JavaScript file-system guard: serverless-offline, Netlify dev and Vercel dev run unrestricted; SAM local and wrangler use the real runtime. dev:verify is the parity check.
  • Stacked on [APPS-2945 Update: switch the apps plugin's static checks to the Node backend runtime]([APPS-2945] Update: switch the apps plugin's static checks to the Node backend runtime #557).

Architecture

flowchart TD
    E["/__dd/executeAction"] --> Q{"Vite 6.0.4+ with vite/module-runner, no Yarn PnP?"}
    Q -->|"yes"| P{"Node's permission model usable?"}
    P -->|"yes"| C["call's own process under --permission"]
    C --> H["a refusal fails the call with a dev:verify hint"]
    P -->|"no, or --inspect without --allow-inspector"| U1["call's own process, unrestricted"]
    Q -->|"no: Vite 5, Vite 6.0.0 to 6.0.3, Yarn PnP"| U2["dev server's process, unrestricted"]
    U1 --> W["one warning per dev-server start"]
    U2 --> W
Loading

Changes

7 changes across network-guard.ts, index.ts, local-execution*.ts, package.json, README.md and tests
What changed File
Delete the in-process guard and its tests. src/vite/network-guard.ts, src/vite/network-guard.test.ts (deleted)
Stop installing the guard in the dev server and in a call's process. index.ts, local-execution.ts, local-execution-child.ts
Warn once per dev-server start when calls run unrestricted. index.ts, local-execution-process.ts
Map Node's refusals to messages with the hint and the refused path. local-execution-limits.ts
Drop the unused graceful-fs devDependency. package.json
Document the unrestricted paths and the hint. README.md
Test the warning, unrestricted calls and hinted refusals. local-execution-limits.test.ts, local-execution-process.integration.test.ts

QA Instructions

yarn typecheck:all && yarn lint && yarn cli integrity
# Expected: all exit 0, integrity leaves no file changes ✅ VERIFIED
yarn build:all && yarn test:unit
# Expected: Test Suites: 133 total; Tests: 1 skipped, 3485 total, all passed except 15 manifest-tags and parameters tests on macOS's /var TMPDIR ✅ VERIFIED
TMPDIR=<dir outside /var> yarn test:unit packages/plugins/apps/src/vite/manifest-tags.integration.test.ts packages/plugins/apps/src/vite/parameters.integration.test.ts
# Expected: Test Suites: 2 passed; Tests: 33 passed, 33 total ✅ VERIFIED
Fixtures and staging runs: Vite 8 with and without the permission model, Vite 5, dev:verify
  • src/guardRefusal.backend.ts:
import { execFileSync } from 'child_process';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

// Each lets its refusal reach the response instead of catching it.
const projectDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));

export async function spawnUncaught() {
    return execFileSync(process.execPath, ['-e', "process.stdout.write('spawned')"]).toString();
}

export async function writeProjectUncaught() {
    fs.writeFileSync(path.join(projectDir, 'qa-guard-probe.txt'), 'probe');
    return 'written';
}

export async function writeOutsideUncaught() {
    const outsideDir = path.join(path.dirname(projectDir), 'qa-outside-probe');
    fs.mkdirSync(outsideDir, { recursive: true });
    fs.writeFileSync(path.join(outsideDir, 'probe.txt'), 'probe');
    return 'written';
}

export async function writeRootUncaught() {
    fs.writeFileSync('/qa-limits-probe.txt', 'probe');
    return 'written';
}
  • guardRun, useLogger, cacheWrite and useFetch are unchanged; the Vite 5 app pins vite@5.4.21.
cd "$QA_DIR" && QA_APP=<vite 8 scaffold> ./install-plugin.sh 563 <build-plugins checkout of this branch>
QA_APP=<vite 8 scaffold> ./qa-run.sh development guardRefusal useFetch
# spawnUncaught and writeRootUncaught: HTTP 500 "Spawning a subprocess is blocked..." / "Writing outside the call's temp dirs is blocked... run `dev:verify`"
# writeProjectUncaught, writeOutsideUncaught: written (the scaffold sits under /tmp); getStatus 200; no unrestricted warning ✅ VERIFIED
QA_APP=<vite 8 scaffold> QA_NODE=<node 22.23.3> QA_NODE_OPTIONS=--inspect=127.0.0.1:0 ./qa-run.sh development guardRefusal
# spawnUncaught "spawned", both writes written, writeRootUncaught fails with the OS's EROFS;
# one "Backend functions run locally without Node's permission model..." warning ✅ VERIFIED
QA_APP=<vite 5 app> ./qa-run.sh development guardRun useLogger useLogger cacheWrite useFetch
# writeAndSpawn {"project":"written","tmp":"written","outside":"written","subprocess":"spawned"}; the logger gains a line each run;
# cache, package.json and node_modules/vite appends written; one unrestricted warning ✅ VERIFIED
QA_APP=<vite 8 scaffold> ./qa-run.sh dev-verify guardRun
# writeAndSpawn {"outside":"written","project":"written","subprocess":"spawned","tmp":"written"} on the real runtime ✅ VERIFIED

Blast Radius

  • Local execution only; cloud execution, dev:verify and builds are untouched.
  • On the fallback paths, calls the guard refused now run, with one warning per dev-server start.
  • Under the permission model, limits are unchanged; refusals gain the hint.
  • No feature flag.
  • Low risk: local only, and it removes refusals production doesn't make.

Out of Scope / Follow-ups

3 items deferred
Item Status Next step
The 2026-10-17 decision to keep blocking subprocesses locally is superseded Doc follow-up Update the design page's local execution section
A refusal the function catches, raises in a callback or wraps in another error shows Node's raw text Accepted None
A project under /tmp is writable from a call's own process, since the permission model grants /tmp Accepted None

Documentation

🤖 Generated with Claude Code

@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 9ff1de7 to 267c2ee Compare October 7, 2026 19:25
@tyffical
tyffical changed the base branch from master to tiffany.trinh/apps-2945-follow-terrapin-flag October 7, 2026 19:25
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 267c2ee to 7547a80 Compare October 7, 2026 19:37
@tyffical tyffical changed the title [APPS-2945] Fix: say local execution's guard blocks are local-only [APPS-2945] Fix: guard local execution by the org's backend runtime Oct 7, 2026
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from e898bea to 481364f Compare October 8, 2026 03:14
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 7547a80 to 96f4d63 Compare October 8, 2026 03:24
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 481364f to e574841 Compare October 8, 2026 03:44
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch 2 times, most recently from f05dc6f to 1bed9e6 Compare October 8, 2026 15:01
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from e574841 to 34a9189 Compare October 13, 2026 00:52
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 1bed9e6 to 1432b86 Compare October 13, 2026 01:22
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 34a9189 to abf34ad Compare October 13, 2026 20:08
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch 3 times, most recently from 2903b26 to b358421 Compare October 14, 2026 15:21
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Pipelines  Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 0b521ee | Docs | View more details | Give us feedback!

@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 10d99f9 to 121756b Compare October 14, 2026 23:54
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from abf34ad to 892bda9 Compare October 15, 2026 01:04
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 121756b to e2cb304 Compare October 15, 2026 02:04
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch 2 times, most recently from bd21790 to 2352fe9 Compare October 15, 2026 15:37
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from e2cb304 to c862d97 Compare October 15, 2026 16:46
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 2352fe9 to e7d5434 Compare October 15, 2026 19:07
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from c862d97 to 4154548 Compare October 15, 2026 21:09
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from e7d5434 to 1fa97d9 Compare October 15, 2026 22:20
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 4154548 to 7172f41 Compare October 15, 2026 23:14
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch 2 times, most recently from e44fe4a to 141d960 Compare October 16, 2026 00:47
@tyffical
tyffical marked this pull request as ready for review October 16, 2026 01:26
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 7172f41 to 8f17d92 Compare October 16, 2026 05:06
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 04671dd to 696d422 Compare October 16, 2026 16:37
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 8f17d92 to 2c8d204 Compare October 16, 2026 16:37
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 696d422 to a64a6ad Compare October 16, 2026 21:37
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 2c8d204 to dea67f3 Compare October 16, 2026 21:38
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from a64a6ad to 31ea49f Compare October 18, 2026 05:41
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from dea67f3 to a41bea5 Compare October 18, 2026 05:41
@tyffical tyffical changed the title [APPS-2945] Fix: guard local execution by the org's backend runtime [APPS-2945] Update: guard local execution with the Node backend runtime (v2) rules Oct 18, 2026
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 31ea49f to ea71187 Compare October 20, 2026 19:49
@tyffical
tyffical requested a review from a team as a code owner October 20, 2026 19:49
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from a41bea5 to 0febe46 Compare October 20, 2026 22:40
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from ea71187 to e59b683 Compare October 20, 2026 23:58
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 0febe46 to ea5cafa Compare October 20, 2026 23:59
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from e59b683 to c499254 Compare October 22, 2026 16:46
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from ea5cafa to 197d9ef Compare October 22, 2026 18:51
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from c499254 to 06018e9 Compare October 23, 2026 07:29
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 197d9ef to a24b4d7 Compare October 23, 2026 07:33
@tyffical tyffical changed the title [APPS-2945] Update: guard local execution with the Node backend runtime (v2) rules [APPS-2945] Update: guard local execution with the Node backend runtime's rules Oct 23, 2026
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 06018e9 to 7509268 Compare October 23, 2026 13:16
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from a24b4d7 to 1bc16e4 Compare October 23, 2026 13:20
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 7509268 to eb7a6fb Compare October 23, 2026 13:41
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 1bc16e4 to 55ce6f0 Compare October 23, 2026 13:43
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from eb7a6fb to 92afaf2 Compare October 23, 2026 14:30
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 55ce6f0 to 8f7e414 Compare October 23, 2026 15:34
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Live Debugger Runtime Benchmark

SDK-loaded dormant-probe runtime overhead, measured against an uninstrumented bundle in the same browser session.

Browser Workload Quality Per-call overhead upper
chrome Hot clean <= 2.55 ns
chrome Tiny clean <= 0.02 ns
firefox Hot clean <= 9.78 ns
firefox Tiny clean <= 0.00 ns
safari Hot clean <= 5.86 ns
safari Tiny clean <= 0.44 ns

What do the Tiny and Hot workloads represent?

Browser Debugger SDK: 7.16.1 · built 2026-10-21 · S3 ETag 1426183a

Full diagnostics
browser  workload  quality  per-call overhead upper  overhead upper           95% CI        A/A diag         block CI  acf(1)   baseline  instrumented                         samples
-------  --------  -------  -----------------------  --------------  ---------------  --------------  ---------------  ------  ---------  ------------  ------------------------------
chrome   Hot       clean                 <= 2.55 ns        <= 5.13%    2.51..2.55 ns  -0.01..0.01 ns    2.51..2.55 ns    0.03  52.655 ms     55.335 ms   102 (trim 20%, outliers 6.9%)
chrome   Tiny      clean                 <= 0.02 ns        <= 0.20%   -0.01..0.02 ns  -0.01..0.00 ns   -0.01..0.02 ns    0.08  43.300 ms     43.327 ms  102 (trim 20%, outliers 15.7%)
firefox  Hot       clean                 <= 9.78 ns       <= 38.86%    9.76..9.78 ns  -0.01..0.00 ns    9.76..9.78 ns   -0.11  39.900 ms     55.400 ms   102 (trim 20%, outliers 9.8%)
firefox  Tiny      clean                 <= 0.00 ns        <= 0.00%  -0.30..-0.29 ns  -0.00..0.01 ns  -0.30..-0.29 ns   -0.22  53.680 ms     52.620 ms   102 (trim 20%, outliers 8.8%)
safari   Hot       clean                 <= 5.86 ns       <= 23.44%    5.81..5.83 ns  -0.01..0.00 ns    5.81..5.86 ns    0.36  44.960 ms     55.420 ms  102 (trim 20%, outliers 11.8%)
safari   Tiny      clean                 <= 0.44 ns       <= 11.76%    0.42..0.44 ns  -0.00..0.00 ns    0.42..0.44 ns    0.00  51.360 ms     57.310 ms   102 (trim 20%, outliers 2.0%)

Raw samples are in the live-debugger-runtime-bench-results artifact.

@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-follow-terrapin-flag branch from 92afaf2 to b71e121 Compare October 23, 2026 16:19
… Node's permission model

Local execution's in-process JavaScript guard patched fs, child_process,
worker_threads and process.chdir to refuse writes and subprocesses on
the paths where a call can't run under Node's permission model. The
Node backend runtime itself allows those operations, and comparable
local runners don't build a JavaScript file-system guard, so the guard
blocked code that runs in production while not being a security
boundary.

The guard, its tests and its wiring are removed. On the fallback paths
(Vite 5, Vite 6.0.0 to 6.0.3, Yarn Plug'n'Play, an unresolvable
vite/module-runner, and a call's process on a Node without the
permission model or without --allow-inspector while debugging), calls
run unrestricted and the dev server warns once per start that
production's limits aren't enforced locally and that dev:verify runs
functions on the real runtime.

A call's own process under the permission model keeps Node's limits.
A refusal from one of Node's permission scopes that fails the call is
reported with a message naming what was refused, the refused path for
a file refusal, and the dev:verify hint, keeping the call's own stack
frames.

The unused graceful-fs devDependency goes with the guard's tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tyffical
tyffical force-pushed the tiffany.trinh/apps-2945-local-guard-messages branch from 8f7e414 to 0b521ee Compare October 23, 2026 16:22
@tyffical tyffical changed the title [APPS-2945] Update: guard local execution with the Node backend runtime's rules [APPS-2945] Update: drop the in-process local execution guard in favor of Node's permission model Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant