@patchstack/connect 0.5.0 → 0.5.2
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 +95 -2
- package/README.md +105 -1
- package/dist/cli.js +987 -145
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +9 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/protect/runtime/report-listeners.cjs +360 -0
- package/dist/protect/templates/express-guard.cjs +30 -12
- package/dist/protect/templates/express-guard.js +30 -12
- package/dist/protect/templates/express-guard.ts +35 -12
- package/dist/protect/templates/fastify-plugin.cjs +14 -1
- package/dist/protect/templates/fastify-plugin.js +14 -1
- package/dist/protect/templates/fastify-plugin.ts +14 -1
- package/dist/protect/templates/generic-guard.cjs +36 -12
- package/dist/protect/templates/generic-guard.js +36 -12
- package/dist/protect/templates/generic-guard.ts +41 -12
- package/dist/protect.cjs +43 -5
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.d.cts +14 -0
- package/dist/protect.d.ts +14 -0
- package/dist/protect.edge.js +30 -1
- package/dist/protect.edge.js.map +2 -2
- package/dist/protect.js +31 -2
- package/dist/protect.js.map +1 -1
- package/dist/{refresh-manifest-LDS35UKV.js → refresh-manifest-2MMPQN2A.js} +10 -3
- package/dist/refresh-manifest-2MMPQN2A.js.map +1 -0
- package/package.json +1 -1
- package/dist/refresh-manifest-LDS35UKV.js.map +0 -1
package/AGENT-INSTALL.md
CHANGED
|
@@ -9,7 +9,7 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
9
9
|
| Command | What it does | Reads your source? | Writes to your project | Sends over the network |
|
|
10
10
|
|---|---|---|---|---|
|
|
11
11
|
| `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 — 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 | `.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; 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 |
|
|
12
|
-
| `setup` | One bounded command: `scan` → manage the widget → install + verify `protect` → wire the install/build scans. Never runs the project build. | No | Config, widget tag, production marker, guard files, `package.json` scripts | Package names + versions and the site's public address and name (via `scan`) |
|
|
12
|
+
| `setup` | One bounded command: `scan` → manage the widget → install + verify `protect` → wire the install/build scans. Never runs the project build. | No | Config, widget tag, production marker, guard files, `package.json` scripts | Package names + versions and the site's public address and name (via `scan`); a claim token as a request header, only when you pass one |
|
|
13
13
|
| `map` | Local, read-only attack-surface analysis (entry points → inputs → sinks → evidence-backed flows). Never run by another command. | **Yes** — via the app's own TypeScript | Nothing (only the file named by `--out`) | Nothing — **unless `--upload`**: structure only (routes, parameter names, the package behind each sink, file:line). Never source code or env values |
|
|
14
14
|
| `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`. | No — writes guard files, does not analyze your code | Guard/framework files (e.g. `middleware.ts`, `src/patchstack/`) | Nothing |
|
|
15
15
|
| `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. | No | Same files as `scan` + `protect` | `scan` payload; polls the public Pulse rules endpoint (never the printed test requests) |
|
|
@@ -18,6 +18,7 @@ Every command at a glance — what it does, whether it reads your source, what i
|
|
|
18
18
|
| `status` | Re-print the site UUID + dashboard URL and check whether the site still exists (active / removed / could not verify). | No | Nothing | Site-existence check |
|
|
19
19
|
| `init <site-uuid>` | Optional: pre-seed `.patchstackrc.json` with an existing UUID. | No | `.patchstackrc.json` only | Nothing |
|
|
20
20
|
| `mark-build` | Stamp built HTML with a production flag + build fingerprint and ensure the widget tag in built pages. Run as a `postbuild` step. | No | Build output only (`dist/ build/ out/ .output/public`) — never source | Nothing |
|
|
21
|
+
| `claim` | Attach the site to a Patchstack account from the terminal: print a link the user opens to sign in (or sign up) and poll (10 min). Whoever approves becomes the owner. Does **not** rotate the credential. Same result as opening the dashboard link `scan` prints. Not usable in CI. | No | Nothing, unless the server issues a credential for a checkout that had none — then `.patchstackrc.local.json` | Device-code request + approval poll |
|
|
21
22
|
| `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 |
|
|
22
23
|
| `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 |
|
|
23
24
|
|
|
@@ -85,6 +86,8 @@ This is a request, not a mechanism: nothing in the install depends on it. Do it
|
|
|
85
86
|
|
|
86
87
|
This provisions or reuses the site, manages the widget, installs and verifies runtime protection, wires dependency-install and build scans, prints a dashboard link, and finishes with the same status shown by `guide`. Re-running it reuses existing configuration, widget tags, guards, and commands rather than duplicating them.
|
|
87
88
|
|
|
89
|
+
**If the request you were given includes a claim token**, pass it exactly as given: `npx @patchstack/connect setup --claim-token <token>`. The site is then created in the person's own Patchstack account and `setup` prints their dashboard link for it — there is no sign-in step to relay. The token comes only from the person's Patchstack dashboard; never invent one, never write it to a file, never print it back. If `setup` reports that the token had expired or was not recognised, the site is not connected: hand over the dashboard link it prints instead, and tell the person they can copy a fresh prompt from the dashboard.
|
|
90
|
+
|
|
88
91
|
In a hosted builder, run setup with `PATCHSTACK_ENVIRONMENT=sandbox` scoped to the workspace process/command, ensure the CLI's on-disk edits are adopted into the platform's persisted project state, then restart any already-running preview/server process so it loads the guard. Do not persist `"environment": "sandbox"` in `.patchstackrc.json`: deployed builds use the same committed files and should default to `production`. A client-only SPA has no server request path to guard; do not call it protected unless `protect --check` succeeds after a real server or edge seam is wired.
|
|
89
92
|
|
|
90
93
|
**Finish by telling the user to refresh their preview.** The widget's "Report a vulnerability" button loads with the page, so a preview that was already open still shows the HTML from before setup — the button is missing there until it reloads. Nothing in the CLI can reach the user's browser, so relaying this is your job. Phrase it as a check rather than a required step: a builder that hot reloads, or a preview server you restarted, may have refreshed it already.
|
|
@@ -140,6 +143,46 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
140
143
|
|
|
141
144
|
`setup` performs both steps automatically. The explicit commands are for manual setup or repair. If verification reports a generic or existing framework seam, complete the printed source edit and re-run `--check`; do not report protection as active until it exits successfully.
|
|
142
145
|
|
|
146
|
+
`--check` reads the app's source. It can establish that the guard is imported and called on a request
|
|
147
|
+
path; it cannot establish that a request ever reaches it — an app can wire the guard onto one server
|
|
148
|
+
and serve traffic from another, and that passes. To settle the difference there is an opt-in check
|
|
149
|
+
that **starts the application**:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
npx @patchstack/connect protect --check --runtime
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
It launches the project's entry with `node`, moves the HTTP listeners **that process** opens to an
|
|
156
|
+
ephemeral loopback port, sends one request per listener carrying a per-run challenge, and reports
|
|
157
|
+
whether the scaffolded guard seam answered it. Exit `0` runtime traversal reached the seam, `1` a
|
|
158
|
+
listener answered and the seam did not, `2` it could not be established — neither a pass nor a
|
|
159
|
+
failure, with the structural checks still standing on their own.
|
|
160
|
+
|
|
161
|
+
Exit `2` is the answer for everything this cannot speak for, and the reason is always printed. The
|
|
162
|
+
common one is an entry that needs the project's own toolchain (a TypeScript entry, a framework
|
|
163
|
+
launcher, a watcher, another runtime, anything reached through a package manager), which this never
|
|
164
|
+
installs, builds or invents. The others are about scope: **the run answers for one process, one
|
|
165
|
+
thread, and one discovery window.** If the app attempts to start another process, the launch is
|
|
166
|
+
refused and the answer is `2`. A child can daemonize after it starts without declaring that in its
|
|
167
|
+
launch options, so allowing it would make the end-of-run process-group cleanup a claim the verifier
|
|
168
|
+
cannot establish. The app sees `EPERM`. A worker thread is also `2`: it inherits the listener
|
|
169
|
+
handling, but it cannot report back, so its listeners can be neither counted nor asked. So is a
|
|
170
|
+
listener that bound an address other than loopback, one that cannot be probed, and anything the app
|
|
171
|
+
opens after the discovery window has closed — the app is asked to stop and its acknowledgement is
|
|
172
|
+
what closes that window, so a run that never gets one is `2` as well. An inherited `NODE_OPTIONS`
|
|
173
|
+
that would run code before the listener handling is in place — a `--require` or `--import` in your
|
|
174
|
+
environment — is `2` too, and is refused before the app is launched rather than after.
|
|
175
|
+
|
|
176
|
+
A worker handed a replacement environment that does not preserve the propagated `NODE_OPTIONS` is
|
|
177
|
+
refused outright, because it would not load the listener handling. The app sees `EPERM`, and the run
|
|
178
|
+
reports `2`.
|
|
179
|
+
|
|
180
|
+
What a pass says is exactly: **runtime traversal reached the scaffolded guard seam.** It does not say
|
|
181
|
+
rules were delivered, that the deployed app is wired, or that ordinary traffic is blocked.
|
|
182
|
+
|
|
183
|
+
Nothing else runs the application. `protect`, `protect --check`, `setup`, `guide`, `scan`, `status`
|
|
184
|
+
and `mark-build` only read and write files.
|
|
185
|
+
|
|
143
186
|
5. **Commit** `.patchstackrc.json`, the updated `package.json`, the guard/framework source changes, and the layout/HTML file carrying the widget tag (and the production marker, when `scan` wrote one into a JSX root), so every developer and CI run reports to the same site.
|
|
144
187
|
|
|
145
188
|
**Do not commit `.patchstackrc.local.json`.** That file holds the API key issued at provision; the scan writes it and adds it to `.gitignore`, and tells you if it could not. `.patchstackrc.json` holds only the site UUID and settings, and the UUID is public by design — it ships in the widget tag in served HTML.
|
|
@@ -149,6 +192,7 @@ It is server-only. Never put it in the widget tag, client bundles, or public env
|
|
|
149
192
|
## Rules
|
|
150
193
|
|
|
151
194
|
- Never invent or guess a UUID — the scan provisions it, the widget silently no-ops on a fake one.
|
|
195
|
+
- Never invent or guess a claim token either. One is only ever handed to you by the person, from their own Patchstack dashboard; pass it with `--claim-token` (or `PATCHSTACK_CLAIM_TOKEN`) and nowhere else — not into `.patchstackrc.json`, not into a committed file, not into your reply.
|
|
152
196
|
- The CLI never opens the dashboard link and never asks for Patchstack credentials.
|
|
153
197
|
- Label hosted workspace scans with `PATCHSTACK_ENVIRONMENT=sandbox` in that process only. Leave production builds unset (the default is `production`) and never commit a sandbox label into files shared with production.
|
|
154
198
|
- If a step fails, stop and report it. Don't proceed with placeholders.
|
|
@@ -388,7 +432,8 @@ Two more endpoints the package can call, for completeness:
|
|
|
388
432
|
## Verifying the install
|
|
389
433
|
|
|
390
434
|
- `npx @patchstack/connect status` re-prints the site UUID and dashboard URL, and checks whether the site still exists on Patchstack (`Site status: active / removed / could not be verified`).
|
|
391
|
-
- `npx @patchstack/connect protect --check` verifies the runtime guard is connected to the request path.
|
|
435
|
+
- `npx @patchstack/connect protect --check` verifies from the source that the runtime guard is connected to the request path. It does not run the app.
|
|
436
|
+
- `npx @patchstack/connect protect --check --runtime` additionally **starts the app** on a loopback port and sends it one request, to establish that a request reaches the guard seam. Opt-in, and the only command that runs the application; exit `0`/`1`/`2` as described in step 4.
|
|
392
437
|
- Load the site in a browser — the "Report a vulnerability" button should appear. Refresh a page that was already open before the tag was added: the button only loads with the page.
|
|
393
438
|
- On the deployed site, the button appears only after a deploy that includes these source changes.
|
|
394
439
|
|
|
@@ -401,6 +446,54 @@ These are **two independent states** — never conflate them:
|
|
|
401
446
|
|
|
402
447
|
Local files alone cannot tell you whether the site was removed from Patchstack. Run `npx @patchstack/connect status` and read the `Site status` line, then answer with both states. For example, when the site was removed but the local files remain, say: *"The site itself was removed from Patchstack — reporting has stopped and the widget no longer renders. The local integration code (widget tag, `.patchstackrc.json`, `.patchstackrc.local.json`, the dependency) is still in the project; want me to remove it?"* — not "Patchstack is still installed."
|
|
403
448
|
|
|
449
|
+
## Attaching the site to an account — `claim`
|
|
450
|
+
|
|
451
|
+
A scan provisions the site without an owner. It is monitored from that moment, but its reports are
|
|
452
|
+
only visible once someone attaches it to a Patchstack account. Opening the dashboard link that `scan`
|
|
453
|
+
and `status` print does that. `claim` does the same thing from the terminal, for when the link is
|
|
454
|
+
output nobody is looking at.
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
npx @patchstack/connect claim
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
Your code: BQDX-7ZKM
|
|
462
|
+
Claim at: https://api.patchstack.com/monitor/pulse/device?code=BQDX-7ZKM
|
|
463
|
+
|
|
464
|
+
Open that link and sign in to Patchstack — or create an account — to attach
|
|
465
|
+
this site to it. Whoever approves becomes the site's owner.
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**If you are an assistant running this, the sequence is three steps:**
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
1. npx @patchstack/connect claim → prints the link, exits straight away
|
|
472
|
+
2. Give the user the link. Wait for them to say they have done it.
|
|
473
|
+
3. npx @patchstack/connect claim → the SAME command again, after they confirm.
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
- **Step 1 exits immediately** when the output is piped or captured, rather than blocking for ten
|
|
477
|
+
minutes on a link you cannot see yet.
|
|
478
|
+
- **Step 3 is the same command.** While a request is still valid it resumes rather than restarting, so
|
|
479
|
+
running `claim` again never invalidates the link the user is looking at. If they have not finished
|
|
480
|
+
yet it says so, with the time remaining, and exits 0.
|
|
481
|
+
- **An already-claimed site exits 0, not 1.** It is the goal state. Re-running after the user claimed
|
|
482
|
+
in the browser reports that and stops; it is not a setup failure.
|
|
483
|
+
|
|
484
|
+
`claim --wait` is the blocking variant. Prefer the plain re-run — it keeps each command short, which
|
|
485
|
+
is what fits a conversation.
|
|
486
|
+
|
|
487
|
+
### What it does not do
|
|
488
|
+
|
|
489
|
+
- **It does not rotate the credential.** The project already holds one from provisioning, and CI,
|
|
490
|
+
deploys and other checkouts keep working. (`login` is the command that rotates; use it only to
|
|
491
|
+
recover a lost credential.) A credential is written only when the server issues one for a checkout
|
|
492
|
+
that had none.
|
|
493
|
+
- **It does not open a browser**, and it cannot claim on the user's behalf: the approval is a person
|
|
494
|
+
signing in to Patchstack.
|
|
495
|
+
- **It does not work in CI** — there is no browser and no one to sign in. It refuses and exits 1.
|
|
496
|
+
|
|
404
497
|
## Recovering a lost credential — `login`
|
|
405
498
|
|
|
406
499
|
Use this when the project **already has a site** but its credential is gone or rejected: `.patchstackrc.local.json` was deleted, the repo was cloned without it (it is git-ignored, so a fresh clone never has it), a container was recycled, or ingest started failing with 401. The site UUID `login` needs comes from the committed `.patchstackrc.json`.
|
package/README.md
CHANGED
|
@@ -73,6 +73,13 @@ patchstack-connect protect Install/reconcile the always-
|
|
|
73
73
|
guard. Auto-wires supported server stacks;
|
|
74
74
|
use --check to verify or --demo for local rules.
|
|
75
75
|
Also run by setup; never run by scan/guide/mark-build.
|
|
76
|
+
--check reads your source and never runs the app.
|
|
77
|
+
--check --runtime STARTS THE APP on a loopback
|
|
78
|
+
port and sends it one request, to establish that
|
|
79
|
+
a request reaches the guard seam. Opt-in, and the
|
|
80
|
+
only mode that runs the app. Exit 0 traversed,
|
|
81
|
+
1 a listener answered instead of the guard,
|
|
82
|
+
2 could not be established (see below).
|
|
76
83
|
patchstack-connect map [--dir p] [--out f] [--upload]
|
|
77
84
|
Print a JSON map of this project's attack
|
|
78
85
|
surface: server entry points, the inputs each
|
|
@@ -90,12 +97,32 @@ patchstack-connect demo node-serialize Production-backed walkthrough
|
|
|
90
97
|
patchstack-connect demo-guide node-serialize Read-only, state-aware instructions for the
|
|
91
98
|
local demo, including the next exact command,
|
|
92
99
|
expected proof, and cleanup.
|
|
100
|
+
patchstack-connect claim [--wait] Attach this site to a Patchstack account from
|
|
101
|
+
the terminal. Prints a link for the user to open
|
|
102
|
+
and sign in (or sign up); whoever approves becomes
|
|
103
|
+
the site's owner. Piped or captured, it prints the
|
|
104
|
+
link and exits — run it again once the user
|
|
105
|
+
confirms. Does not rotate the credential. Same
|
|
106
|
+
result as opening the dashboard link scan prints.
|
|
107
|
+
Not usable in CI
|
|
108
|
+
patchstack-connect login [--wait] Recover this site's credential when
|
|
109
|
+
.patchstackrc.local.json has been lost. Prints a
|
|
110
|
+
link for the site's OWNER to approve. Approving
|
|
111
|
+
ROTATES the credential, so CI, deploys and other
|
|
112
|
+
machines using the old one must be updated.
|
|
113
|
+
Not usable in CI
|
|
114
|
+
patchstack-connect uninstall [options] Signal Patchstack that this package is being
|
|
115
|
+
removed. An unclaimed site record is deleted; a
|
|
116
|
+
claimed one is flagged for its owner. Does NOT
|
|
117
|
+
touch local files
|
|
93
118
|
patchstack-connect help Print help
|
|
94
119
|
patchstack-connect --version Print the installed version
|
|
95
120
|
|
|
96
121
|
Options (for scan, setup, and status):
|
|
97
122
|
--site-uuid <uuid> Override the configured site UUID
|
|
98
123
|
--endpoint <url> Override the API endpoint
|
|
124
|
+
--claim-token <token> (scan, setup) Connect the site to the account that issued
|
|
125
|
+
the token (from the dashboard's "Connect website" prompt)
|
|
99
126
|
--dry-run (scan only) Print the payload without posting
|
|
100
127
|
|
|
101
128
|
Options (for demo and demo-guide):
|
|
@@ -103,11 +130,74 @@ Options (for demo and demo-guide):
|
|
|
103
130
|
(default: http://localhost:3000/api/tasks)
|
|
104
131
|
```
|
|
105
132
|
|
|
133
|
+
### Verifying the guard at runtime (opt-in)
|
|
134
|
+
|
|
135
|
+
`protect --check` reads the app's source. That establishes the guard is imported and called on a
|
|
136
|
+
request path — not that a request ever reaches it. An app can wire the guard onto one server and serve
|
|
137
|
+
its traffic from another, and the structural check passes.
|
|
138
|
+
|
|
139
|
+
`protect --check --runtime` settles that one question by **starting the application**:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
npx @patchstack/connect protect --check --runtime
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It runs the project's entry with `node`, moves the HTTP listeners **that process** opens to an ephemeral
|
|
146
|
+
loopback port (so a port already in use is not a failure), sends one request per listener carrying a
|
|
147
|
+
challenge generated for that run, and reports whether the scaffolded guard seam answered it. The child
|
|
148
|
+
is started in its own process group and killed with it — including if you interrupt the command. A
|
|
149
|
+
listener it cannot probe — a Unix socket, a file descriptor, a handed-over handle, an HTTP/2 server — is
|
|
150
|
+
reported and prevented from binding at all, rather than opened on the verifier's behalf.
|
|
151
|
+
|
|
152
|
+
**One process is the scope — one thread of it, and one discovery window.** If the app attempts to start
|
|
153
|
+
another process, the launch is refused and the answer is `2`, whatever that process is. A child can
|
|
154
|
+
daemonize after it starts without declaring that in its launch options, so allowing it would make the
|
|
155
|
+
end-of-run process-group cleanup a claim the verifier cannot establish. The app sees the launch fail
|
|
156
|
+
with `EPERM`. A **worker thread** is also `2`: it inherits the listener handling, but a worker has no
|
|
157
|
+
channel back, so its listeners can be neither counted nor asked. A worker handed a replacement
|
|
158
|
+
environment that does not preserve the propagated `NODE_OPTIONS` is refused, because it would not load
|
|
159
|
+
that handling at all.
|
|
160
|
+
|
|
161
|
+
Everything else the run finds also ends it this way: a listener that bound an address other than
|
|
162
|
+
loopback, a listener it cannot probe, and anything the app opens **after the discovery window closes** —
|
|
163
|
+
a second listener appearing while the first is still being asked cannot join a set that is already being
|
|
164
|
+
answered from. Closing that window is a handshake: the app is asked to stop opening listeners and its
|
|
165
|
+
acknowledgement is what proves nothing is still in flight, so a run that never gets one reports `2`
|
|
166
|
+
rather than passing. An inherited `NODE_OPTIONS` is checked before anything is launched, too: Node reads
|
|
167
|
+
that variable ahead of the command line, so a `--require` or `--import` sitting in your environment would
|
|
168
|
+
run before the listener handling was in place, and the run reports `2` rather than starting the app with
|
|
169
|
+
less containment than it claims. Recognised flags are passed through, with the reporter first.
|
|
170
|
+
|
|
171
|
+
A pass says exactly this: **runtime traversal reached the scaffolded guard seam.** It does not say
|
|
172
|
+
rules were delivered, that the deployed app is wired, or that ordinary traffic is blocked. The
|
|
173
|
+
challenge is generated per run, so a fixed response or a reflected header cannot answer it — but the
|
|
174
|
+
challenge does reach the whole app process, so this establishes traversal in a cooperating app rather
|
|
175
|
+
than against an app written to answer for itself.
|
|
176
|
+
|
|
177
|
+
| Exit | Meaning |
|
|
178
|
+
|---|---|
|
|
179
|
+
| `0` | A request reached the scaffolded guard seam. |
|
|
180
|
+
| `1` | A listener answered and the seam did not — or the structural checks failed, in which case the app is not started at all. |
|
|
181
|
+
| `2` | It could not be established. Neither a pass nor a failure; the structural checks still stand. |
|
|
182
|
+
|
|
183
|
+
Exit `2` is the common answer for entries this deliberately will not start. It runs `node <file>` on a
|
|
184
|
+
file the project already has, and nothing else — no package-manager scripts, no `node_modules/.bin`, no
|
|
185
|
+
build, no install. So a TypeScript entry, a framework launcher (`next start`), a watcher (`nodemon`),
|
|
186
|
+
another runtime (`bun`), or a script wrapped in an environment shim all report unavailable with the
|
|
187
|
+
reason printed. To make such a project verifiable, point a `start` script at a built, directly loadable
|
|
188
|
+
file — the check reports which entry it used and where it came from.
|
|
189
|
+
|
|
190
|
+
Windows reports exit `2` without starting anything: the cleanup this relies on is a POSIX process
|
|
191
|
+
group, and a verification that can leave a server running is worse than an unanswered question.
|
|
192
|
+
|
|
193
|
+
No other command runs your application. `protect`, `protect --check`, `setup`, `guide`, `scan`,
|
|
194
|
+
`status` and `mark-build` only read and write files.
|
|
195
|
+
|
|
106
196
|
## Configuration
|
|
107
197
|
|
|
108
198
|
Precedence (highest wins):
|
|
109
199
|
|
|
110
|
-
1. CLI flag (`--site-uuid`, `--endpoint`)
|
|
200
|
+
1. CLI flag (`--site-uuid`, `--endpoint`, `--claim-token`)
|
|
111
201
|
2. Environment variable
|
|
112
202
|
3. `.patchstackrc.local.json` in the current directory (the credential)
|
|
113
203
|
4. `.patchstackrc.json` in the current directory
|
|
@@ -118,6 +208,7 @@ Environment variables:
|
|
|
118
208
|
- `PATCHSTACK_ENDPOINT` — override the API endpoint (default `https://api.patchstack.com/monitor/pulse/manifest`)
|
|
119
209
|
- `PATCHSTACK_TIMEOUT_MS` — request timeout in milliseconds (default `30000`)
|
|
120
210
|
- `PATCHSTACK_ENVIRONMENT` — manifest label: `production` (default) or `sandbox`
|
|
211
|
+
- `PATCHSTACK_CLAIM_TOKEN` — connect the site straight to your account (see *Connecting straight to your account*)
|
|
121
212
|
|
|
122
213
|
Two files, because one value is public and the other is not.
|
|
123
214
|
|
|
@@ -152,6 +243,17 @@ The credential's file is never committed, so CI needs `PATCHSTACK_API_KEY` in th
|
|
|
152
243
|
|
|
153
244
|
A `pulseAuth` field is still read if present, and `PATCHSTACK_PULSE_AUTH` still overrides, for deployments that authenticate Pulse ingest with a different credential from block-logs. Neither is written by default, and neither is needed when the two share one.
|
|
154
245
|
|
|
246
|
+
### Connecting straight to your account
|
|
247
|
+
|
|
248
|
+
The dashboard's "Connect website" prompt carries a **claim token**. Pass it to the first `setup` (or `scan`) and the site it provisions is created in your account, so there is no dashboard link to open afterwards:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npx @patchstack/connect setup --claim-token <token>
|
|
252
|
+
# or: PATCHSTACK_CLAIM_TOKEN=<token> npx @patchstack/connect setup
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The token names your account, not the project: it is never written to `.patchstackrc.json` or the credential file, it is sent to Patchstack as a request header rather than in the manifest body, and it stops working within a day. A token that has expired (or one Patchstack does not recognise) leaves the site exactly as a scan without one would — unconnected, with the dashboard link printed — and `scan` says so. Re-running `setup` with the same token against a site already in your account is a no-op that says the site is already connected; a site that belongs to a different account is left alone.
|
|
256
|
+
|
|
155
257
|
### Sandbox and production manifests
|
|
156
258
|
|
|
157
259
|
Every `scan` sends an environment label with its dependency manifest. The default is `production`; 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.
|
|
@@ -244,6 +346,8 @@ The name is what the dashboard calls the site. It is taken from `name` in `.patc
|
|
|
244
346
|
|
|
245
347
|
You can see exactly what would be sent, without sending it, by running `npx @patchstack/connect scan --dry-run`: the preview it prints is the request body itself. Patchstack only applies either field to a site that does not have one yet: it never re-points a site whose address is real, and never replaces a name set in the dashboard.
|
|
246
348
|
|
|
349
|
+
One thing travels outside that body: a claim token, when you pass one (`--claim-token` / `PATCHSTACK_CLAIM_TOKEN`), is sent as the `X-Patchstack-Claim-Token` request header so that the site is created in your account. Without one, no such header is sent.
|
|
350
|
+
|
|
247
351
|
### `scan --install-paths` (opt-in)
|
|
248
352
|
|
|
249
353
|
Pass it and each entry also carries where that version is installed:
|