@patchstack/connect 0.5.26 → 0.5.30
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/AGENT-INSTALL.md +34 -19
- package/README.md +73 -17
- package/dist/{chunk-YG5C7HYB.js → chunk-52IV7LN7.js} +1 -1
- package/dist/{chunk-YG5C7HYB.js.map → chunk-52IV7LN7.js.map} +1 -1
- package/dist/cli.js +2755 -371
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +33 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +33 -12
- package/dist/index.js.map +1 -1
- package/dist/protect/templates/astro-middleware.ts +5 -4
- package/dist/protect/templates/express-guard.cjs +5 -8
- package/dist/protect/templates/express-guard.js +5 -8
- package/dist/protect/templates/express-guard.ts +5 -8
- package/dist/protect/templates/fastify-plugin.cjs +52 -6
- package/dist/protect/templates/fastify-plugin.js +52 -6
- package/dist/protect/templates/fastify-plugin.ts +53 -7
- package/dist/protect/templates/fetch-guard.ts +52 -0
- package/dist/protect/templates/generic-guard.cjs +6 -5
- package/dist/protect/templates/generic-guard.js +6 -5
- package/dist/protect/templates/generic-guard.ts +5 -8
- package/dist/protect/templates/guard.ts +4 -7
- package/dist/protect/templates/next-guard.ts +5 -4
- package/dist/protect/templates/next-middleware.ts +5 -4
- package/dist/protect/templates/nuxt-middleware.ts +5 -4
- package/dist/protect/templates/sveltekit-hooks.ts +5 -4
- package/dist/protect.cjs +33 -12
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.d.cts +1 -1
- package/dist/protect.d.ts +1 -1
- package/dist/protect.edge.js +2 -2
- package/dist/protect.edge.js.map +1 -1
- package/dist/protect.js +4 -4
- package/dist/protect.js.map +1 -1
- package/dist/{refresh-manifest-OMRDCNFD.js → refresh-manifest-EBORKM75.js} +33 -12
- package/dist/refresh-manifest-EBORKM75.js.map +1 -0
- package/package.json +5 -2
- package/dist/refresh-manifest-OMRDCNFD.js.map +0 -1
package/AGENT-INSTALL.md
CHANGED
|
@@ -43,7 +43,7 @@ The pages get a build step, even though nothing is compiled. The build is what r
|
|
|
43
43
|
npx @patchstack/connect setup
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`setup` creates the site, writes its `siteUuid` to `.patchstackrc.json`, adds the Patchstack widget to `public/index.html`, adds `"postinstall": "patchstack-connect scan"`, and wires `"prebuild": "patchstack-connect scan"` and `"postbuild": "patchstack-connect mark-build"` around the build. In a hosted builder, scope `PATCHSTACK_ENVIRONMENT=sandbox` to the `setup` command, as in "Automated setup".
|
|
46
|
+
`setup` creates the site, writes its `siteUuid` to `.patchstackrc.json`, adds the Patchstack widget to `public/index.html`, adds `"postinstall": "patchstack-connect scan"`, and wires `"prebuild": "patchstack-connect scan && patchstack-connect map --upload"` and `"postbuild": "patchstack-connect mark-build"` around the build. In a hosted builder, scope `PATCHSTACK_ENVIRONMENT=sandbox` to the `setup` command, as in "Automated setup".
|
|
47
47
|
2. Add `dist` to `.gitignore` next to the entries `setup` wrote.
|
|
48
48
|
3. Put the widget on the other pages. `setup` adds the tag only to `index.html`, `public/index.html` or `src/app.html`. For any other page it lists the widget under `Missing` and prints the tag to add. Add one tag before `</body>` on each page, or in the shared layout, exactly as printed — no `data-build-mode`.
|
|
49
49
|
4. When the person names where the site is published, add that host's build settings so it publishes `dist/` and runs the build. See "Deploying" below; for Netlify that is a `netlify.toml` with `command = "npm run build"` and `publish = "dist"`.
|
|
@@ -69,8 +69,8 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
69
69
|
| Command | What it does | Reads your source? | Writes to your project | Sends over the network |
|
|
70
70
|
|---|---|---|---|---|
|
|
71
71
|
| `scan` | Provision (or reuse) the site and POST the dependency list for vulnerability matching. Also runs automatically via `setup` and the install/build hooks. | No source analysis — lockfile only; `node_modules/` is enumerated when no lockfile can be read (e.g. `bun.lockb`) or when the lockfiles present disagree. It also reads the `<title>` of the root `index.html` and the `name` in `package.json`, to report what the site is called. During `prebuild` only, it reads the scaffolded guard and its co-located rules JSON to remove a previous map stamp. | `.patchstackrc.json` (public: site UUID + settings); `.patchstackrc.local.json` (the API key, created owner-only) and a `.gitignore` entry for it — the CLI says so if it could not add one; during `prebuild`, removal of a previous `_patchstack.build_id` from the guard's own rules file so a later build cannot carry stale coordinates; the widget `<script>` tag in the root HTML shell — only after a successful post; the production marker in a code root shell — before the post, since it needs no site UUID | Package names + versions; this site's public address and name, where the project or build environment states them |
|
|
72
|
-
| `setup` | One bounded command:
|
|
73
|
-
| `map` | Local attack-surface analysis
|
|
72
|
+
| `setup` | One bounded command: scan, widget, install + verify guard, upload map, pull live rules, wire build hooks. Never starts the app or runs a build. | Local integration and structural source analysis | Config, widget, guard/framework files, scripts, map identity in the guard rules JSON, git-ignored `.patchstack/` cache | Package inventory and site identity; structural map (routes, input names, packages, file:line, identity); authenticated Pulse rules lookup. No source code or environment values |
|
|
73
|
+
| `map` | Local attack-surface analysis; setup invokes it with upload, and wires prebuild uploads. | Server source parsed with TypeScript (app compiler or CLI dependency) | `--out` file if requested; an uploaded map is stamped into the guard rules JSON during setup or a pre-bundle build hook | Standalone command sends nothing unless `--upload`. Setup and its build hook upload structure only, never source text or environment values |
|
|
74
74
|
| `protect` | Install the always-on runtime guard; auto-wire known stacks, or scaffold a generic guard + print a wiring plan. `--check` verifies the guard is wired (exit 1 if not); `--demo` seeds a broad sample rule set. Runs automatically **only** via `setup` — never by `scan`, `guide`, `status`, or `mark-build`. | Reads local wiring/route source for integration; does not produce an attack-surface map | Guard/framework files (e.g. `middleware.ts`, `src/patchstack/`, supported Next App Router handlers) | Nothing |
|
|
75
75
|
| `demo node-serialize` | Production-backed walkthrough: confirm the vulnerable package is present, scan, wait for live rule `18843`, install + verify the guard, print test requests. Does not install the package or start/restart the app. | Local integration reads via `scan` and `protect` | Same files as `scan` + `protect` | `scan` payload; polls the public Pulse rules endpoint (never the printed test requests) |
|
|
76
76
|
| `demo-guide node-serialize` | Read-only companion: explains the prepare/run/prove/cleanup sequence and prints the next command. | Reads local protection wiring for verification | Nothing | Nothing |
|
|
@@ -82,27 +82,27 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
82
82
|
| `login` | Recover a lost credential for an existing site: print an owner-approval link and poll (10 min). Approving **rotates** the credential. Not usable in CI. | No | New credential into `.patchstackrc.local.json` on approval | Device-code request + approval poll |
|
|
83
83
|
| `uninstall` | Signal Patchstack that the package is being removed: an unclaimed record is deleted, a claimed one is flagged. Does **not** touch local files. | No | Nothing local | Removal signal |
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
`setup` uploads a structural attack-surface map after installing the guard; the build hooks it installs upload a fresh map before bundling. Standalone `map` remains local without `--upload`. `protect` reads and edits integration source but transmits nothing. `scan` sends package names + versions and the declared public site identity, not source text. Map uploads additionally include routes, parameter names, package attribution, relative file:line locations, coverage limitations, and a policy-map digest. `scan --install-paths` remains separately opt-in for package installation locations.
|
|
86
86
|
|
|
87
87
|
## Package and command behavior
|
|
88
88
|
|
|
89
89
|
- Package: [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect), MIT-licensed, source at https://github.com/patchstack/connect. `npm view @patchstack/connect` shows the live registry metadata.
|
|
90
|
-
-
|
|
90
|
+
- **`scan` sends the dependency list plus this site's public address and name** — read from lockfiles or installed package metadata. It sends no source code or git history. Setup additionally uploads the structural map described below.
|
|
91
91
|
- **`scan --install-paths` is the one exception, and it is opt-in.** It adds where each package sits in the dependency tree — repo-relative paths made of `node_modules` segments, plus a workspace directory name when a workspace pins its own copy. They are read from the lockfile's own keys or from the `node_modules` walk, **never from your source tree**: no path to a file you wrote is sent by either form of `scan`.
|
|
92
92
|
- Why it exists: the same package is routinely installed twice at different versions, and without the locations an advisory affecting only one of them cannot be matched to the copy your code actually loads. Node resolves an import by walking up from the importing file, so the location is what distinguishes "you are running the vulnerable copy" from "the vulnerable copy is installed but nothing reaches it". Absent them, every installed version has to be treated as if the app used it — warnings about code you never call, and protection rules pinned to routes that run the safe copy.
|
|
93
93
|
- Why it is off by default: it widens what leaves the machine, so it is your explicit choice and not a consequence of upgrading the package. (`mark-build` additionally stamps built HTML with a coarse stack descriptor that may include hosting-related env variable *names* — e.g. `VERCEL`, `CF_PAGES` — never their values.)
|
|
94
94
|
- **Only `scan` looks for the address and the name.** They are resolved in the one code path that reports them, so `guide`, `status`, `login`, `uninstall`, `mark-build`, `init`, `protect`, `demo-guide` and `map` neither read the host's URL variables nor open `index.html` or `package.json` for this. `setup` and `demo` do, because both run `scan`.
|
|
95
95
|
- **The address is the one your visitors use, and, apart from the tool's own `PATCHSTACK_*` settings, it is the only env var value read.** A site provisioned by a scan from a developer machine has no address, so the dashboard shows a placeholder and Patchstack cannot check that the published page still carries what was scanned. `scan` therefore sends `url` when — and only when — it can know it: `url` in `.patchstackrc.json` or `PATCHSTACK_SITE_URL` if you set one, otherwise the single variable a host publishes to name its own **production** URL (`VERCEL_PROJECT_PRODUCTION_URL` on a Vercel production deployment, Netlify's `URL` in the production context, `RENDER_EXTERNAL_URL`, `RAILWAY_PUBLIC_DOMAIN` in a production environment). Preview and branch deployments are excluded, as are hosts that publish no production signal. An address that is not how the public reaches a website is dropped: any IP address (in either family, however it is written), any single-label host such as `localhost` or `production`, and the reserved suffixes (`.local`, `.internal`, `.test`, `.invalid`, `.home.arpa`, …). A `url` you set explicitly that fails those checks is refused with an error rather than replaced by a guess. When nothing qualifies, `url` is omitted from the payload rather than guessed. Patchstack only ever applies it to a site that still has no address; it never re-points a site whose address is already real.
|
|
96
96
|
- **The name is read from your project, never from the host environment.** `name` in `.patchstackrc.json` (or `PATCHSTACK_SITE_NAME`) if you set one; otherwise the `<title>` of the project's root `index.html` (`public/index.html` if there is no root one), read from the file as text — a title your app sets from script is not seen; otherwise the `name` in `package.json`, unless it is a template placeholder such as `vite_react_shadcn_ts`. It is omitted when nothing qualifies, and it only ever fills in a site that has no name yet — a name set in the dashboard is never replaced.
|
|
97
|
-
- **
|
|
97
|
+
- **Mapping is automatic in `setup`, and in its prebuild hook.** It parses server source and sends structure, not source text. A standalone `map` command prints locally and uploads only with `--upload`. `protect`, `scan`, `guide`, `status` and `mark-build` do not invoke mapping.
|
|
98
98
|
- **`scan` makes up to three source edits:** the Patchstack Connector's `<script>` tag, the production marker, and — during `prebuild` only — removal of a previous `_patchstack.build_id` from the existing guard rules file. None runs on `--dry-run`; all are idempotent. `"widget": false` disables the first two, while stale-stamp removal is independent because it prevents old coordinates being attributed to a new build.
|
|
99
99
|
- The **widget tag** goes in the root HTML shell — the first of `index.html`, `public/index.html`, or `src/app.html` that exists — and only after a successful post, because it carries the site UUID.
|
|
100
100
|
- The **production marker** goes in a root shell that is JSX rather than HTML (e.g. `src/routes/__root.tsx`, `app/layout.tsx`), inside a `{/* #region patchstack */}` block placed above the widget tag. It is written *before* the post: it carries no site UUID and needs no network, and build scripts commonly chain `patchstack-connect scan || true`, where waiting on the server would mean an offline build silently ships without the flag. The marker is guarded by the framework's own production expression (`import.meta.env.PROD`, or `process.env.NODE_ENV === 'production'`), so it is inert in dev and preview builds. Without it a server-rendered site has no built HTML for `mark-build` to stamp, and the widget treats the published site as build mode. `mark-build` writes to build output only (`dist/`, `build/`, `out/`, `.output/public`), never to source. `guide`, `status`, and `init` write nothing except `init`'s own `.patchstackrc.json`.
|
|
101
|
-
- **`setup` runs `scan
|
|
102
|
-
- The package also exposes **`protect`** directly (runtime exploit guard; its templates live under `dist/protect/`). `setup` invokes it automatically; `scan`, `guide`, `status`, and `mark-build` do not. It writes only local files and auto-wires known stacks — **TanStack Start + Supabase** (patches the Supabase client + `src/start.ts`), **Next.js** (scaffolds or composes middleware and adds request/response checks to supported App Router handlers), **SvelteKit** (`src/hooks.server.ts`), **Astro** (`src/middleware.ts`), **Nuxt** (`server/middleware/`), **NestJS** (`app.use(patchstackMiddleware)` in the bootstrap), **Fastify** (`app.register(patchstackFastify)`), and **Express** (`app.use(patchstackMiddleware)`). On **any other stack** it scaffolds a framework-agnostic guard under `src/patchstack/` and prints a wiring plan — then you finish the install by importing that guard into your server entry (`protectFetch(handler)` for a Web-Fetch server, or `app.use(patchstackMiddleware)` for Node/Express) and running `patchstack-connect protect --check` to confirm it is wired (exit 1 until it is). Passing `--demo` seeds a broad sample rule set (for demonstrations, not production).
|
|
101
|
+
- **`setup` runs `scan` → `protect` → map upload → live-rule lookup**, then reports the outcome. Provisioning precedes guard installation. The map is stamped into the guard source for the NEXT startup/build; restart an already-running preview/server to load it. The rule lookup uses the runtime validator and source-scoped local cache without creating a running guard or installing global hooks. A successful empty policy is reported as zero assigned rules, not proof of protection. Failed uploads/pulls appear under Missing and can be retried by rerunning setup. It also wires install scans, prebuild scans + map uploads, and postbuild marking (explicit build chains for Yarn, pnpm and Bun), preserving existing commands. It never starts, builds or deploys the app. Ambiguous/custom integration code still requires review rather than being overwritten.
|
|
102
|
+
- The package also exposes **`protect`** directly (runtime exploit guard; its templates live under `dist/protect/`). `setup` invokes it automatically; `scan`, `guide`, `status`, and `mark-build` do not. It writes only local files and auto-wires known stacks — **TanStack Start + Supabase** (patches the Supabase client + `src/start.ts`), **TanStack Start** (documented server Fetch entry without requiring Supabase), **Next.js** (scaffolds or composes middleware/proxy and adds request/response checks to supported App Router handlers), **SvelteKit** (`src/hooks.server.ts`), **Astro** (`src/middleware.ts`), **Nuxt** (`server/middleware/`), **NestJS** (`app.use(patchstackMiddleware)` in the bootstrap), **Fastify** (`app.register(patchstackFastify)`), and **Express** (`app.use(patchstackMiddleware)`). On **any other stack** it scaffolds a framework-agnostic guard under `src/patchstack/` and prints a wiring plan — then you finish the install by importing that guard into your server entry (`protectFetch(handler)` for a Web-Fetch server, or `app.use(patchstackMiddleware)` for Node/Express) and running `patchstack-connect protect --check` to confirm it is wired (exit 1 until it is). Passing `--demo` seeds a broad sample rule set (for demonstrations, not production).
|
|
103
103
|
- **`demo node-serialize` is an explicit production-backed walkthrough.** It requires `node-serialize@0.0.4` to already be present in the lockfile; it does not install the vulnerable dependency. It runs the same production `scan`, polls the configured site's public Pulse rules endpoint until rule `18843` is served, runs `protect`, verifies the generated guard, and prints exploit/benign test requests. It writes the same manifest/widget and guard files as those underlying commands. It does not start/restart the app and does not send the printed requests.
|
|
104
|
-
-
|
|
105
|
-
- **`map --upload`
|
|
104
|
+
- **Standalone `map` is local unless you pass `--upload`.** It skips dependencies, build output and hidden directories and does not follow external symlinks by default. It reports detected entry points, inputs, sinks, dependency calls and evidence tiers with coverage limitations. Static analysis is best-effort, not a completeness guarantee. `setup` runs this analysis after guard integration and uploads it automatically.
|
|
105
|
+
- **`map --upload` and `setup` send structural analysis** to `monitor/pulse/input-map/<your site uuid>`. The upload includes no source code, file contents or environment variable values. Setup and pre-bundle uploads derive `_patchstack.build_id` from the policy map and write it into the existing guard rules file. A standalone upload outside a build remains unbound and changes no guard file. No recognized routes still means useful import/coverage evidence and is uploaded. API failures are reported without failing a production build. Setup then retrieves Pulse rules, including delivered response-phase policies, using the same build identity. Only a matching server verdict permits scoped enforcement. The local cache is not a client bundle or a replacement for runtime rule refresh; filesystem-less deployments fetch at runtime and need server-side credentials.
|
|
106
106
|
- **`demo-guide node-serialize` is the read-only companion.** It checks the Host-created site configuration and vulnerable lockfile entry, explains the complete local prepare/run/restart/prove/cleanup sequence, and prints the next exact command. It does not require a deployment and does not change files or contact Patchstack.
|
|
107
107
|
- Patchstack is not WordPress-only. Connect monitors any JS/Node project — Vite, Next.js, plain vanilla JS, anything with a lockfile.
|
|
108
108
|
|
|
@@ -293,16 +293,16 @@ Handle it in this order:
|
|
|
293
293
|
```jsonc
|
|
294
294
|
{
|
|
295
295
|
"scripts": {
|
|
296
|
-
"prebuild": "patchstack-connect scan",
|
|
296
|
+
"prebuild": "patchstack-connect scan && patchstack-connect map --upload",
|
|
297
297
|
"postbuild": "patchstack-connect mark-build",
|
|
298
298
|
"postinstall": "patchstack-connect scan"
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
-
If a lifecycle hook already exists, chain instead of replacing it, e.g. `"prebuild": "existing-command && patchstack-connect
|
|
303
|
+
If a lifecycle hook already exists, chain instead of replacing it, e.g. `"prebuild": "patchstack-connect scan && existing-command && patchstack-connect map --upload"`. The `postinstall` scan reports dependencies added during an iterative sandbox session and covers applications with no build command.
|
|
304
304
|
|
|
305
|
-
**Bun
|
|
305
|
+
**Yarn, pnpm and Bun projects:** use an explicit build chain instead of assuming npm-style `pre`/`post` hooks run: `"build": "patchstack-connect scan && patchstack-connect map --upload && <existing build command> && patchstack-connect mark-build"`. Modern Yarn and Bun skip those hooks; pnpm behavior depends on version and configuration. `setup` uses this chain for all three managers, including Yarn Classic, and preserves existing custom hooks.
|
|
306
306
|
|
|
307
307
|
**Checking a build yourself:** run it through the package manager (`npm run build`), never the framework's own CLI (`astro build`, `vite build`, `next build`). Calling the CLI directly skips the `prebuild`/`postbuild` hooks, so the build is not scanned, not marked and not reported, and it tells you nothing about what the deployed build will carry.
|
|
308
308
|
|
|
@@ -336,14 +336,29 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
336
336
|
check requests and filter returned responses. It writes a shared server-only `patchstack.next`
|
|
337
337
|
helper alongside `patchstack.rules.json`. Unsupported exports, complex matchers or handlers are
|
|
338
338
|
left unchanged and reported by `--check`; re-run `protect` after adding routes. An existing
|
|
339
|
-
`proxy.ts`/`proxy.js`
|
|
340
|
-
|
|
339
|
+
`proxy.ts`/`proxy.js` is composed on Next 16+ using the same conservative export/matcher rules;
|
|
340
|
+
a new Next 16+ install uses `proxy.ts`. Conflicting entries stay untouched. Middleware/proxy alone
|
|
341
341
|
cannot filter downstream page bodies. Rendered pages, Server Actions and Pages API response
|
|
342
342
|
filtering are not verified by this adapter. Keep Next.js patched: a framework middleware bypass
|
|
343
343
|
also bypasses a guard in middleware. Edge middleware needs `PATCHSTACK_API_KEY` in the server
|
|
344
344
|
environment; it cannot read `.patchstackrc.local.json`. Do not put credentials in public variables
|
|
345
345
|
or commit them. Source checks do not verify rule delivery or blocking in the running deployment.
|
|
346
346
|
|
|
347
|
+
**TanStack Start:** the documented `src/server.ts` Fetch entry can be scaffolded without Supabase
|
|
348
|
+
or `src/start.ts`. Supported literal `createServerEntry({ fetch: ... })` configurations are wrapped
|
|
349
|
+
without changing host arguments or application error handling. The installed framework must expose
|
|
350
|
+
`server-entry`; custom entry paths, spreads, getters and competing files need manual integration.
|
|
351
|
+
The existing TanStack/Supabase adapter screens native requests by default and filters the response
|
|
352
|
+
inside TanStack's middleware result. No `PATCHSTACK_ROUTE_WAF` switch is needed. Browser-direct
|
|
353
|
+
services and separately deployed functions are not protected by guarding the frontend server;
|
|
354
|
+
keep backend authorization/RLS and install a guard at each independently exposed backend.
|
|
355
|
+
|
|
356
|
+
Generated guards refresh live rules every five minutes (15 seconds in an explicit sandbox).
|
|
357
|
+
Express/Node guards enable bounded response filtering; Fastify filters buffered `onSend` output,
|
|
358
|
+
leaving streams and bodyless replies untouched. Unmodified recognized helpers can be upgraded;
|
|
359
|
+
customized helpers are preserved and flagged for manual review. Wiring checks inspect executable
|
|
360
|
+
statements, not just marker comments, and cannot establish live delivery or complete coverage.
|
|
361
|
+
|
|
347
362
|
`--check` reads the app's source. It can establish that the guard is imported and called on a request
|
|
348
363
|
path; it cannot establish that a request ever reaches it — an app can wire the guard onto one server
|
|
349
364
|
and serve traffic from another, and that passes. To settle the difference there is an opt-in check
|
|
@@ -449,13 +464,13 @@ which integration API is available.
|
|
|
449
464
|
| [Solid](https://docs.solidjs.com/quick-start) | Separate the UI library from SolidStart or a custom server; keep protection out of client components. |
|
|
450
465
|
| [Qwik](https://qwik.dev/docs/qwikcity/) | Inspect Qwik City and the deployment adapter; component resumability does not identify the request entry. |
|
|
451
466
|
| [Ember](https://guides.emberjs.com/release/getting-started/quick-start/) | Inspect the deployed backend or SSR host separately; browser routes and the development server are not production coverage. |
|
|
452
|
-
| [Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) | Inspect root or `src/` middleware/proxy, matchers, APIs and Server Actions.
|
|
467
|
+
| [Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) | Inspect root or `src/` middleware/proxy, matchers, APIs and Server Actions. Connect uses `proxy.ts` for new Next 16+ installs and composes supported existing proxies. Conflicting entries and complex routing require manual review. |
|
|
453
468
|
| [Nuxt](https://nuxt.com/docs/4.x/directory-structure/server) | Inspect the configured server directory and Nitro server middleware, not client navigation middleware. Distinguish a server deployment from generated static output. |
|
|
454
469
|
| [SvelteKit](https://svelte.dev/docs/kit/hooks) | Compose the existing server `handle` hook; check endpoints, actions, prerendering and the deployed adapter. |
|
|
455
470
|
| [Astro](https://docs.astro.build/en/guides/middleware/) | Compose `onRequest` in server middleware; distinguish execution during prerendering from on-demand routes behind an adapter. |
|
|
456
471
|
| [Remix](https://v2.remix.run/docs/discussion/runtimes/) | Inspect the adapter around `createRequestHandler`; cover document requests, loaders, actions and resource routes, not only `entry.server` rendering. |
|
|
457
472
|
| [React Router](https://reactrouter.com/how-to/middleware) | Determine library versus framework/SSR mode. Inspect the server adapter and version-specific server middleware; client middleware cannot guard loaders/actions on the server. |
|
|
458
|
-
| [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/guide/middleware) | Inspect the server entry and global request middleware, including server functions. The
|
|
473
|
+
| [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/guide/middleware) | Inspect the server entry and global request middleware, including server functions. The native Fetch-entry adapter does not require Supabase. Custom entry paths need manual review; the separate TanStack/Supabase adapter matches a specific layout. |
|
|
459
474
|
| [SolidStart](https://docs.solidjs.com/solid-start/v1/advanced/middleware) | Inspect configured server middleware and adapter; verify API and server action paths separately rather than assuming rendering middleware covers them. |
|
|
460
475
|
| [Qwik City](https://qwik.dev/docs/middleware/) | Inspect deployment entry and request middleware, including endpoints, loaders and actions. Confirm route/layout scope and static output. |
|
|
461
476
|
| [Gatsby](https://www.gatsbyjs.com/docs/reference/functions/) | Static pages need no request guard, but `src/api` functions and SSR deployments need their own server entry review. |
|
|
@@ -503,7 +518,7 @@ read from: rename the field two deploys later and the rule addresses something t
|
|
|
503
518
|
while still reporting as active protection. Coverage that is not there is worse than a known gap.
|
|
504
519
|
|
|
505
520
|
Such a rule carries a `build_scope` naming the policy map its coordinate came from, and it blocks only
|
|
506
|
-
when Patchstack **confirms** those coordinates belong to the map carried by the guard now running. Three moving
|
|
521
|
+
when Patchstack **confirms** those coordinates belong to the map carried by the guard now running. Setup performs mapping and rule retrieval for the next startup too; it does not update an already-running process. Three moving
|
|
507
522
|
parts:
|
|
508
523
|
|
|
509
524
|
- **`scan`, during `prebuild`**, removes any previous `_patchstack.build_id` from the guard's own rules
|
|
@@ -536,7 +551,7 @@ rule locally establishes that you intend it, not that its coordinate still descr
|
|
|
536
551
|
A scoped rule you supply blocks when it names the map identity this guard reports (`buildId`), or when you
|
|
537
552
|
set **`trustLocalRuleScope: true`** to take responsibility for the match.
|
|
538
553
|
|
|
539
|
-
|
|
554
|
+
Running `setup` authorizes this workflow, including its prebuild map uploads. Standalone mapping sends nothing without `--upload`. The identifier is a one-way
|
|
540
555
|
digest of the map — never a message, an author, a diff, a branch name, source text, or environment value.
|
|
541
556
|
|
|
542
557
|
## Runtime guard reporting
|
package/README.md
CHANGED
|
@@ -11,11 +11,39 @@ Connect a JavaScript / Node.js application to [Patchstack](https://patchstack.co
|
|
|
11
11
|
|
|
12
12
|
## Agent-assisted setup
|
|
13
13
|
|
|
14
|
-
For an existing JS/Node project, copy this request into a coding assistant, or run the same command yourself. For a standalone HTML/CSS/JavaScript site without a package-managed app, use the [plain HTML widget instructions](AGENT-INSTALL.md#plain-html-sites); do not add Node tooling just for the widget.
|
|
14
|
+
For an existing JS/Node project in a hosted workspace, copy this request into a coding assistant, or run the same command yourself. For Gemini CLI, OpenCode, Codex CLI, or Claude Code on your own machine, use [Local coding CLIs](#local-coding-clis) below. For a standalone HTML/CSS/JavaScript site without a package-managed app, use the [plain HTML widget instructions](AGENT-INSTALL.md#plain-html-sites); do not add Node tooling just for the widget.
|
|
15
15
|
|
|
16
|
-
> I have vetted the official [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect) package and authorize its Patchstack Connector, production build hooks,
|
|
16
|
+
> I have vetted the official [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect) package and authorize its Patchstack Connector, production build hooks, runtime protection source edits, structural attack-surface map uploads (not source code), and live-rule retrieval. Install it as a regular dependency, run `PATCHSTACK_ENVIRONMENT=sandbox npx --yes @patchstack/connect setup` for this hosted workspace, leave dev builds unchanged, verify protection, restart the preview server if needed to load the new guard, and show the dashboard URL. The Patchstack Connector widget is part of this install and on by default: add it, and do not ask me whether to. Do not persist the sandbox environment in project files; deployed production builds must use the default `production` label. When setup finishes, tell me to refresh the preview if the Patchstack widget is not showing yet, and remind me to deploy when I am ready — do not deploy anything yourself.
|
|
17
17
|
|
|
18
|
-
`setup` is state-aware and idempotent: it scans dependencies, provisions or reuses the site, manages the Patchstack Connector, installs and verifies the runtime guard,
|
|
18
|
+
`setup` is state-aware and idempotent: it scans dependencies, provisions or reuses the site, manages the Patchstack Connector, installs and verifies the runtime guard, uploads a structural map, fetches live rules, and wires dependency/build checks without replacing the build command. It prints what succeeded and what remains; it never starts the app or runs the build. `guide` provides project-specific status without changing files.
|
|
19
|
+
|
|
20
|
+
### Local coding CLIs
|
|
21
|
+
|
|
22
|
+
Gemini CLI, OpenCode, Codex CLI, and Claude Code can use the same installed Connect commands. The tool does not determine the deployment tier: use the environment of the process running Connect. A tool's permission sandbox is separate from Connect's `sandbox` manifest label. On an ordinary laptop, leave `PATCHSTACK_ENVIRONMENT` unset so Connect reports `local`. An exported Lovable or Replit project retains its hosted-builder dependencies; when working on that export locally, set `PATCHSTACK_ENVIRONMENT=local` in the local process only to override the builder assumption.
|
|
23
|
+
|
|
24
|
+
Have the assistant work in the application's package directory, reuse any existing site UUID, and install Connect in `dependencies` with the package manager that owns the project. Then run the installed CLI:
|
|
25
|
+
|
|
26
|
+
| Package manager | Install if absent | Set up | Check source wiring |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| npm | `npm install --save @patchstack/connect` | `npx --no-install patchstack-connect setup` | `npx --no-install patchstack-connect protect --check` |
|
|
29
|
+
| pnpm | `pnpm add @patchstack/connect` | `pnpm exec patchstack-connect setup` | `pnpm exec patchstack-connect protect --check` |
|
|
30
|
+
| Yarn | `yarn add @patchstack/connect` | `yarn exec patchstack-connect setup` | `yarn exec patchstack-connect protect --check` |
|
|
31
|
+
| Bun | `bun add @patchstack/connect` | `bun run patchstack-connect setup` | `bun run patchstack-connect protect --check` |
|
|
32
|
+
|
|
33
|
+
Use the same invocation with `guide --verbose` to inspect the environment, build hooks, widget, and remaining setup work, and with `status` to recover the dashboard URL. Read the actual command results before reporting completion. A declared dependency, generated scaffold, or proposed command alone does not establish a completed install. Resolve source-check failures on server applications; for a client-only site, state the protection limitation. Restart an already-running app to load the new guard and refresh the page to check the widget.
|
|
34
|
+
|
|
35
|
+
Before an authorized deployment, preserve `.patchstackrc.json`, the dependency and lockfile changes, the install/build hooks, and the generated guard and layout edits. Keep `.patchstackrc.local.json` out of Git and configure `PATCHSTACK_API_KEY` through the deployment host's secret settings. Remove any local or workspace-only environment override from the deployment process; use the host's tier signals or the explicit [DigitalOcean build settings](#sandbox-and-production-manifests). Run the project's existing build command so both `scan` and `mark-build` execute. A successful source check establishes wiring, not that the deployed server loaded it; verify the deployed widget and server protection separately. Setup itself does not deploy the application.
|
|
36
|
+
|
|
37
|
+
To keep future agent sessions aware of the setup, add a short note to the application's existing project instructions. Preserve the instructions already there. Record the package manager, the installed Connect invocation, the existing site configuration, the verification command, and the deployment environment requirements. Refer to `node_modules/@patchstack/connect/AGENT-INSTALL.md` for the installed version's reference. Keep credentials and a fixed workspace tier out of these notes.
|
|
38
|
+
|
|
39
|
+
| Coding tool | Project instructions |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Codex CLI | [AGENTS.md](https://learn.chatgpt.com/docs/agent-configuration/agents-md) |
|
|
42
|
+
| OpenCode | [AGENTS.md](https://opencode.ai/docs/rules/) |
|
|
43
|
+
| Claude Code | [CLAUDE.md, or a shared AGENTS.md via the documented configuration/import](https://code.claude.com/docs/en/memory) |
|
|
44
|
+
| Gemini CLI | [GEMINI.md, or configure it to load the shared AGENTS.md](https://geminicli.com/docs/cli/gemini-md/) |
|
|
45
|
+
|
|
46
|
+
Project instructions provide context; command permissions still belong to the coding tool. If execution is declined, use the handoff below and report which setup steps remain unverified.
|
|
19
47
|
|
|
20
48
|
### If your coding tool blocks the command
|
|
21
49
|
|
|
@@ -87,7 +115,7 @@ npm install --save @patchstack/connect && npx @patchstack/connect setup
|
|
|
87
115
|
|
|
88
116
|
> **Use your project's own package manager.** On Bun-managed projects (including many Lovable projects) install with `bun add @patchstack/connect` instead — running `npm install` there plants a `package-lock.json` that the platform's native dependency flow never updates again, leaving a stale lockfile next to the live one. Connect detects and works around that (see *Stale lockfiles* below), but not creating the fossil is better. Protection imports `@patchstack/connect/protect` at runtime, so deployments that prune dev dependencies need the package in `dependencies`.
|
|
89
117
|
|
|
90
|
-
> **Hosted builders:** set `PATCHSTACK_ENVIRONMENT=sandbox` in the workspace process environment (or scope it to the setup command above), persist every file written by `setup`, and restart any already-running server so it loads the new middleware. Do not write `"environment": "sandbox"` to the committed `.patchstackrc.json`: the same project files reach production, where scans should inherit no override and default to `production`. TanStack Start + Supabase (the server shape emitted by Lovable) is auto-wired: browser Supabase traffic is tunneled through a same-origin guard, server-function arguments are inspected, and responses are screened. A client-only SPA has no server request path to protect; setup will leave a generic scaffold and `protect --check` will remain red until the host adds a server/edge seam.
|
|
118
|
+
> **Hosted builders:** set `PATCHSTACK_ENVIRONMENT=sandbox` in the workspace process environment (or scope it to the setup command above), persist every file written by `setup`, and restart any already-running server so it loads the new middleware. Do not write `"environment": "sandbox"` to the committed `.patchstackrc.json`: the same project files reach production, where scans should inherit no override and default to `production`. TanStack Start + Supabase (the server shape emitted by Lovable) is auto-wired: browser Supabase traffic is tunneled through a same-origin guard, server-function arguments are inspected, and responses are screened. A client-only SPA has no server request path to protect; setup will leave a generic scaffold and `protect --check` will remain red until the host adds a server/edge seam. Native TanStack requests are screened by default; no additional route-WAF switch is needed. New Start apps without that Supabase layout use the documented server Fetch entry. Browser-direct services and separately deployed functions still require their own protection.
|
|
91
119
|
|
|
92
120
|
That's it. `setup`:
|
|
93
121
|
|
|
@@ -98,8 +126,11 @@ That's it. `setup`:
|
|
|
98
126
|
5. Connect installs the Patchstack Connector's `<script>` tag into your root HTML shell (see *The Patchstack Connector* below) so the widget shows up on the next preview reload — as the "Connect this website" panel until the site is claimed, then as the "Report a vulnerability" button. On a server-rendered root it also adds the production marker, which is what tells the widget to switch from build mode to visitor report intake on the published site.
|
|
99
127
|
6. Installs the runtime guard after provisioning, bakes the site UUID into it, and verifies the framework seam. Known server stacks are auto-wired; unmatched or conflicting layouts get a generic scaffold and exact manual checks.
|
|
100
128
|
7. Adds `postinstall: patchstack-connect scan`, preserving any existing command, so dependencies added during a sandbox session and build-less production installs are reported immediately.
|
|
101
|
-
8.
|
|
102
|
-
9.
|
|
129
|
+
8. Uploads a structural attack-surface map (routes, input names, package attribution, relative file:line locations and coverage notes; no source text or environment values), stamps its identity into the guard for the next startup/build, and fetches live request/response rules. Empty policy, upload failures and rule-fetch failures are reported separately.
|
|
130
|
+
9. Wires `scan` followed by `map --upload` before builds and `mark-build` after builds, preserving existing commands. npm uses lifecycle hooks; Yarn, pnpm and Bun use explicit build chains independent of lifecycle settings.
|
|
131
|
+
10. Prints a dashboard link — open it in a browser to attach the new site to your Patchstack account. You can re-display it any time with `npx @patchstack/connect status`.
|
|
132
|
+
|
|
133
|
+
If the server is already running, **restart it** to load the new guard and map identity. Setup does not start, build or deploy your app. Rule delivery does not prove runtime enforcement: scoped rules still need a matching server verdict, and unsupported/custom entries remain reported gaps.
|
|
103
134
|
|
|
104
135
|
Then **refresh your preview**. The widget loads with the page, so a preview that was already open still shows the HTML from before setup. Builders that hot reload will have refreshed it for you; if the widget is missing, refresh it once. Until the site is claimed it shows the "Connect this website" panel. `setup` prints the same reminder, and the CLI has no way to reload a browser itself.
|
|
105
136
|
|
|
@@ -128,7 +159,8 @@ patchstack-connect scan [options] Scan the lockfile and POST to
|
|
|
128
159
|
.patchstackrc.json)
|
|
129
160
|
patchstack-connect setup [options] Run scan, manage the widget, and idempotently
|
|
130
161
|
install + verify runtime protection and wire
|
|
131
|
-
dependency/build scans.
|
|
162
|
+
dependency/build scans + map uploads. Uploads the map and
|
|
163
|
+
fetches live rules; never starts the app or runs the build
|
|
132
164
|
patchstack-connect init <site-uuid> Optional: pre-seed .patchstackrc.json with
|
|
133
165
|
an existing site UUID
|
|
134
166
|
patchstack-connect status [options] Show current configuration
|
|
@@ -202,16 +234,26 @@ Options (for demo and demo-guide):
|
|
|
202
234
|
|
|
203
235
|
### Next.js request and response protection
|
|
204
236
|
|
|
237
|
+
Framework detection follows the exported application's server entry, not the builder's name.
|
|
238
|
+
[Lovable documents TanStack Start for new projects and React/Vite for older ones](https://docs.lovable.dev/introduction/faq);
|
|
239
|
+
[Hostinger Horizons offers a hosted backend](https://www.hostinger.com/blog/horizons-integrated-backend/),
|
|
240
|
+
and [Airo exports React/TypeScript applications](https://airo-builder.godaddy.com/discover/features).
|
|
241
|
+
Those frontends do not establish where backend requests execute. Browser-direct APIs, Supabase Edge
|
|
242
|
+
Functions and other separately deployed services need their own server-side integration; a browser
|
|
243
|
+
tunnel does not replace backend authorization or RLS. Tests use synthetic framework-shaped apps,
|
|
244
|
+
not proprietary builder templates, and do not certify a builder's live hosting environment.
|
|
245
|
+
|
|
205
246
|
`protect` composes straightforward existing middleware instead of replacing its authentication or
|
|
206
247
|
redirect logic. The request guard gets a catch-all matcher; the application's middleware still runs
|
|
207
248
|
only within its original scope. Automatic composition accepts directly exported handlers and literal
|
|
208
249
|
path matchers (including a terminal `/:path*`). Complex matchers, re-exports and custom URL routing
|
|
209
|
-
are left untouched with an integration message. Source-aware edits use
|
|
210
|
-
|
|
250
|
+
are left untouched with an integration message. Source-aware edits use a TypeScript parser supplied
|
|
251
|
+
by the app or Connect's CLI dependency; configuration files are never executed.
|
|
211
252
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
253
|
+
On Next 16+, the installer creates `proxy.ts` for new wiring and composes supported existing
|
|
254
|
+
`proxy.ts`/`proxy.js` handlers (including under `src/`). Older versions retain middleware. Conflicting
|
|
255
|
+
entries, unsupported proxy exports, and custom normalization stay untouched and are reported by
|
|
256
|
+
`protect --check`. Middleware and proxy are never created alongside one another.
|
|
215
257
|
|
|
216
258
|
For App Router `app/**/route.ts` or `route.js` files, it also adds request checks and screens each
|
|
217
259
|
returned response. The shared server-only `patchstack.next` helper initializes one policy per module
|
|
@@ -308,7 +350,7 @@ Environment variables:
|
|
|
308
350
|
- `PATCHSTACK_SITE_UUID` — the site UUID from your Patchstack dashboard
|
|
309
351
|
- `PATCHSTACK_ENDPOINT` — override the API endpoint (default `https://api.patchstack.com/monitor/pulse/manifest`)
|
|
310
352
|
- `PATCHSTACK_TIMEOUT_MS` — request timeout in milliseconds (default `30000`)
|
|
311
|
-
- `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the build platform's own variables. Where the platform names the tier (Vercel, Netlify, Render, Railway, GitLab CI), production reports `production` and a preview `sandbox`. Where it names only the branch (Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier), a branch named `main`, `master`, `production`, `prod`, `release` or `live` reports `production` and any other branch or a pull request `sandbox`. A Replit Deployment reports `production`, the Replit workspace `sandbox`. Failing all of that, a project a hosted builder generated and builds for itself (Lovable, Replit) reports `production`, because on those platforms the edit preview is a dev server and a build only happens when the owner publishes. Anything else — a developer machine, a CI runner this does not know (`CI=true` alone), a platform this does not know — reports `local`
|
|
353
|
+
- `PATCHSTACK_ENVIRONMENT` — manifest label: `production`, `sandbox` or `local`. Unset, the label comes from the build platform's own variables. Where the platform names the tier (Vercel, Netlify, Render, Railway, GitLab CI), production reports `production` and a preview `sandbox`. Vercel reads `VERCEL_TARGET_ENV` before `VERCEL_ENV`; custom targets report `sandbox`. Netlify Preview Servers report `sandbox`; Netlify Dev reports `local`, even with production settings. Vercel or Netlify with a platform marker but no tier reports `local`, without falling back to a CI branch or hosted builder. Where it names only the branch (Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier), a branch named `main`, `master`, `production`, `prod`, `release` or `live` reports `production` and any other branch or a pull request `sandbox`. A Replit Deployment reports `production`, the Replit workspace `sandbox`. Failing all of that, a project a hosted builder generated and builds for itself (Lovable, Replit) reports `production`, because on those platforms the edit preview is a dev server and a build only happens when the owner publishes. Anything else — a developer machine, a CI runner this does not know (`CI=true` alone), a platform this does not know — reports `local`
|
|
312
354
|
- `PATCHSTACK_CLAIM_TOKEN` — connect the site straight to your account (see *Connecting straight to your account*)
|
|
313
355
|
|
|
314
356
|
Two files, because one value is public and the other is not.
|
|
@@ -357,9 +399,23 @@ The token names your account, not the project: it is never written to `.patchsta
|
|
|
357
399
|
|
|
358
400
|
### Sandbox and production manifests
|
|
359
401
|
|
|
360
|
-
Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the build platform's own variables. Platforms that name the tier answer directly: Vercel's `VERCEL_ENV
|
|
402
|
+
Every `scan` sends an environment label with its dependency manifest. When nothing sets one, the label comes from the build platform's own variables. Platforms that name the tier answer directly: Vercel's `VERCEL_TARGET_ENV` (falling back to `VERCEL_ENV`), Netlify's `CONTEXT`, Render's pull-request flag, Railway's environment name, GitLab's `CI_ENVIRONMENT_TIER`. Production reports `production`; a preview those platforms name as such reports `sandbox`. Platforms that name only the branch — Cloudflare Pages and Workers Builds, AWS Amplify, GitHub Actions, GitLab CI without a tier — are decided by the branch name: `main`, `master`, `production`, `prod`, `release` and `live` report `production`, any other branch reports `sandbox`, and a pull-request build reports `sandbox` whatever the branch. That is an assumption about naming, so the line `scan` prints says the decision rests on the branch name; set `PATCHSTACK_ENVIRONMENT` where the live branch is called something else. A Replit Deployment reports `production` and the Replit workspace `sandbox`. Where no platform answers, the project itself gets the last word: one a hosted builder generated and builds for itself — Lovable, Replit — reports `production`, because the edit preview there is a dev server, so a build running at all is the owner publishing. Everything else reports `local` — a developer's machine, a CI runner this does not know (`CI=true` alone proves automation, not deployment), and a platform whose build environment carries no such signal. A local manifest is inventory — it tells Patchstack what the app is built from — and never counts as contact with a live site, so an app that has only been set up on a laptop shows in the dashboard as **Configured locally**, not as connected or deployed. The label also decides whether `mark-build` stamps the live-site marker, so a deployment that reads `local` ships its pages without one: set `PATCHSTACK_ENVIRONMENT=production` where builds run on a platform this list does not know. Sandboxed builders should set `PATCHSTACK_ENVIRONMENT=sandbox` in the sandbox process only. Patchstack stores and deduplicates manifests per environment, so an iterative workspace scan does not replace the last production manifest.
|
|
403
|
+
|
|
404
|
+
The environment is re-evaluated on every scan and `mark-build` run; setup does not persist an inferred tier. Hosting signals take precedence over CI branch guesses and hosted-builder dependencies. A Vercel or Netlify marker without its tier stays `local`; `scan --verbose` explains the missing signal. Vercel's target variable also works without the separate `VERCEL` marker, and custom targets report `sandbox`. Netlify's `NETLIFY_PREVIEW_SERVER=true` reports `sandbox` even if `CONTEXT` says production. Outside a hosted Preview Server, `NETLIFY_DEV=true` reports `local` even when Netlify Dev loads production settings. See the [Vercel system variables](https://vercel.com/docs/environment-variables/system-environment-variables) and [Netlify build variables](https://docs.netlify.com/build/configure-builds/environment-variables/), and [Netlify Dev implementation](https://github.com/netlify/cli/blob/main/src/commands/dev/dev.ts).
|
|
405
|
+
|
|
406
|
+
For DigitalOcean App Platform or Droplets, set the tier explicitly in the environment of the process that builds the app:
|
|
407
|
+
|
|
408
|
+
| Deployment | Build setting |
|
|
409
|
+
|---|---|
|
|
410
|
+
| Production | `PATCHSTACK_ENVIRONMENT=production` |
|
|
411
|
+
| Staging, preview, or sandbox | `PATCHSTACK_ENVIRONMENT=sandbox` |
|
|
412
|
+
| Local development | Leave unset, or set `PATCHSTACK_ENVIRONMENT=local` |
|
|
413
|
+
|
|
414
|
+
In App Platform, make the variable available at build time (`BUILD_TIME` or `RUN_AND_BUILD_TIME`); a runtime-only setting cannot label the prebuild scan or stamp built HTML. Docker builds must pass it into the build steps that run Connect. DigitalOcean's documented app URL and ID variables identify an app, not its deployment tier; `NODE_ENV=production` also does not distinguish a local optimized build from a deployment. See [DigitalOcean environment configuration](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/).
|
|
415
|
+
|
|
416
|
+
An explicit `PATCHSTACK_ENVIRONMENT` overrides `.patchstackrc.json`, and the file's `environment` overrides automatic detection. When production keeps reporting sandbox after deployment, remove a workspace-only override from the committed config and from the production build environment, then rebuild and deploy. Apply sandbox overrides only to preview processes or preview deployment settings.
|
|
361
417
|
|
|
362
|
-
Do not commit `"environment": "sandbox"` to `.patchstackrc.json` when the same files are deployed to production. Scope the variable to the sandbox command/process instead:
|
|
418
|
+
Do not commit `"environment": "sandbox"` to `.patchstackrc.json` when the same files are deployed to production. If one is committed anyway, a build its platform identifies as production is still treated as production, and `scan` and `mark-build` print a warning naming the file. Scope the variable to the sandbox command/process instead:
|
|
363
419
|
|
|
364
420
|
```bash
|
|
365
421
|
PATCHSTACK_ENVIRONMENT=sandbox npx @patchstack/connect setup
|
|
@@ -371,7 +427,7 @@ During a build, the `prebuild` scan removes any previous map stamp. A later `map
|
|
|
371
427
|
|
|
372
428
|
### `scan` as a build hook
|
|
373
429
|
|
|
374
|
-
`setup` wires `scan` into `postinstall`, `prebuild`, or
|
|
430
|
+
`setup` wires `scan` into `postinstall`, npm's `prebuild`, or an explicit `build` chain for Yarn, pnpm and Bun. Run from one of those, a report Patchstack cannot accept — no credential in the build environment, a rejected credential, a site that no longer exists, an outage — is printed on stderr and `scan` exits 0, so the install or build it is attached to carries on. Patchstack keeps the last manifest it accepted for the site until a scan that can report. Run directly (`npx @patchstack/connect scan`), the same failure exits 1.
|
|
375
431
|
|
|
376
432
|
A deploy never has `.patchstackrc.local.json`, so the usual cause is a missing `PATCHSTACK_API_KEY` in the platform's environment (see *Configuration*). The hook is recognised through `npm_lifecycle_event`, which npm, pnpm, Yarn and `bun run` set to the running script's name. `bun install` does not set it, so a `postinstall` scan under Bun still fails the install when it cannot report.
|
|
377
433
|
|
|
@@ -411,7 +467,7 @@ The Patchstack Connector is a floating control whose form follows the site's cla
|
|
|
411
467
|
|
|
412
468
|
Re-runs update the tag in place (the `data-patchstack-connect-widget` attribute marks it as managed by Connect); a pre-existing manual widget tag is left untouched. `--dry-run` never edits anything; a failed post still skips the widget tag (it needs the site UUID) but the production marker may already have been written, since it runs before the post. Projects whose root layout is code that Connect does not edit (Nuxt, …) get the exact snippet and target file printed instead — `guide` shows framework-specific placement.
|
|
413
469
|
|
|
414
|
-
- **`mark-build`** ensures the same tag in built HTML output, covering builds whose source shell Connect couldn't edit, and stamps `window.__PATCHSTACK_PROD__` so the widget hides the claim/login UI on the published site (owners reach it by appending `#patchstack` to the live URL). It then reports what it did — `stamped`, `withheld`, `no-pages` for a server-rendered build, or `no-output` — alongside the same manifest `scan` sent before the bundler ran, so the dashboard can say why a published app is or is not reporting its build. That report is the second half of one build, not a second build: Patchstack keeps one copy of the manifest and reads the two together. It is sent only for a site that is already registered, and never carries the site's address or name, which `mark-build` does not resolve. The marker says the page is the live site, so **only a production build carries it**: the environment is read the same way `scan` reads it (the build platform's own tier or branch name, then the hosted builder the project belongs to), and a local or preview build gets the widget tag, no marker, and any marker an earlier build left behind removed. Publishing a static build by hand from your machine is the case that needs `--production` (or `PATCHSTACK_ENVIRONMENT=production`), because nothing in that environment can say the build is a deployment.
|
|
470
|
+
- **`mark-build`** ensures the same tag in built HTML output, covering builds whose source shell Connect couldn't edit, and stamps `window.__PATCHSTACK_PROD__` so the widget hides the claim/login UI on the published site (owners reach it by appending `#patchstack` to the live URL). It then reports what it did — `stamped`, `withheld`, `no-pages` for a server-rendered build, or `no-output` — alongside the same manifest `scan` sent before the bundler ran, so the dashboard can say why a published app is or is not reporting its build. That report is the second half of one build, not a second build: Patchstack keeps one copy of the manifest and reads the two together. It is sent only for a site that is already registered, and never carries the site's address or name, which `mark-build` does not resolve. The marker says the page is the live site, so **only a production build carries it**: the environment is read the same way `scan` reads it (the build platform's own tier or branch name, then the hosted builder the project belongs to), and a local or preview build gets the widget tag, no production marker, and any marker an earlier build left behind removed — including an inline script that only sets `window.__PATCHSTACK_PROD__` and was added by hand. A `sandbox` build gets `window.__PATCHSTACK_ENV__="sandbox"` in its place, so the widget shows the owner's panel on a preview host it cannot recognise by name. Publishing a static build by hand from your machine is the case that needs `--production` (or `PATCHSTACK_ENVIRONMENT=production`), because nothing in that environment can say the build is a deployment.
|
|
415
471
|
|
|
416
472
|
- **Opting out:** persist `"widget": false` in `.patchstackrc.json` to disable both the widget tag and the production marker (dependency scanning only). Without it, the next successful scan re-adds the managed tag, and the next scan re-adds the marker on a JSX root or Astro layout.
|
|
417
473
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protect/safe-origin.js","../src/types.ts","../src/endpoint-policy.ts","../src/bounded-response.ts","../src/pulse-token.ts","../src/build-id.ts"],"mappings":";AAKO,SAAS,aAAa,OAAO;AAClC,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK;AACvB,QAAI,EAAE,aAAa,SAAU,QAAO;AACpC,WAAO,EAAE,aAAa,YAAY,EAAE,aAAa,eAAe,EAAE,aAAa,eAAe,EAAE,aAAa,WAAW,EAAE,aAAa;AAAA,EACzI,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,YAAY,WAAW,UAAU,OAAO;AACtD,MAAI,OAAO,cAAc,YAAY,cAAc,GAAI,QAAO;AAC9D,MAAI,aAAa,SAAS,EAAG,QAAO;AACpC,WAAS,OAAO,gCAAgC,KAAK,cAAc,SAAS,qDAAqD;AACjI,SAAO;AACT;AAEA,IAAM,SAAS,oBAAI,IAAI;AACvB,SAAS,SAAS,KAAK,SAAS;AAC9B,MAAI,OAAO,IAAI,GAAG,EAAG;AACrB,SAAO,IAAI,GAAG;AAEd,UAAQ,KAAK,OAAO;AACtB;;;
|
|
1
|
+
{"version":3,"sources":["../src/protect/safe-origin.js","../src/types.ts","../src/endpoint-policy.ts","../src/bounded-response.ts","../src/pulse-token.ts","../src/build-id.ts"],"mappings":"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","names":[]}
|