requestshield 0.1.2 → 0.1.4

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.
@@ -0,0 +1,182 @@
1
+ # `requestshield` CLI reference
2
+
3
+ Every RequestShield platform action goes through this CLI. There is no supported way
4
+ to create keys, read the contract, or check traffic by hand-crafting HTTP calls, so if
5
+ a task needs one of these values, run the command rather than guessing.
6
+
7
+ Commands marked **auth** require a signed-in session (`requestshield signin`). The CLI
8
+ also exposes local commands that do not need platform API access, such as `--help`,
9
+ `--version`, and agent setup. Check once with `auth status` before a multi-step
10
+ platform task instead of discovering it halfway through an edit.
11
+
12
+ Most commands print JSON shaped `{"ok": true, "data": {...}}`. Parse `data`; treat
13
+ `ok: false` as a hard stop and surface the message rather than continuing with edits.
14
+
15
+ ## Contents
16
+
17
+ - [General](#general)
18
+ - [Authentication](#authentication)
19
+ - [Key management](#key-management)
20
+ - [Integration contract](#integration-contract)
21
+ - [Applications](#applications)
22
+ - [Credentials](#credentials)
23
+ - [Service and monitoring](#service-and-monitoring)
24
+ - [Billing](#billing)
25
+ - [Agent integration](#agent-integration)
26
+
27
+ ## General
28
+
29
+ | Command | Purpose |
30
+ | --- | --- |
31
+ | `requestshield --help` | Usage and the full command list. Run this if a command below is rejected — the installed CLI may be older or newer than this file. |
32
+ | `requestshield --version` | Installed CLI version, e.g. `requestshield 1.0.0`. |
33
+ | `requestshield update check` | Latest available version. Use it when behaviour disagrees with this reference. |
34
+
35
+ ## Authentication
36
+
37
+ ```bash
38
+ requestshield signin
39
+ requestshield auth status
40
+ ```
41
+
42
+ **auth/API.**
43
+
44
+ `signin` is interactive and stores a session on the machine. **Never try to automate
45
+ it or ask for the user's credentials** — if the user is signed out, tell them to run it
46
+ themselves and wait.
47
+
48
+ `auth status` returns `{"ok":true,"data":{"authenticated":true,"customer_id":"customer_456"}}`.
49
+ The `customer_id` is useful context to echo back so the user can confirm they are
50
+ operating on the right account before you create or deactivate anything.
51
+
52
+ ## Key management
53
+
54
+ **The user runs these, not you.** They print the API Secret or change state for a live
55
+ App Key, so hand over the exact command and let them run it in their own terminal. See
56
+ "Key and secret management" in `SKILL.md` for the hand-off and the follow-up
57
+ presence check.
58
+
59
+ ```bash
60
+ requestshield keys create --app-name <name>
61
+ requestshield keys deactive --app-key <app-key> # or --app-name <name>
62
+ requestshield app key rotate --app-key <app-key> # or --app-name <name>
63
+ ```
64
+
65
+ `keys create` returns both halves:
66
+
67
+ ```
68
+ App Key: app_123
69
+ Secret Key: rs_sk_xxxxx
70
+ Save this Secret Key now. It will not be shown again.
71
+ ```
72
+
73
+ The Secret Key appears exactly once. Warn the user *before* they run the command that
74
+ they need somewhere to put it, and afterwards point them at the environment variable
75
+ rather than repeating the value. Never ask them to paste the secret back — confirm it
76
+ only by presence. See the secret-handling rules in `SKILL.md`.
77
+
78
+ `keys deactive` (spelled that way in the CLI) needs at least one of `--app-key` or
79
+ `--app-name`; if both are given, `--app-key` wins. Deactivation stops traffic being
80
+ verified for that key, so name the affected app explicitly when you hand the command
81
+ over and make sure the user means that key.
82
+
83
+ `app key rotate` issues a new Secret Key for an existing App Key and deactivates the
84
+ previous one. This is the correct response to a leaked or lost secret — it keeps the
85
+ App Key stable, so no browser or config change is needed beyond the new secret. Like
86
+ `keys create`, the user runs it.
87
+
88
+ ## Integration contract
89
+
90
+ ```bash
91
+ requestshield contract
92
+ ```
93
+
94
+ **auth.** The authoritative integration surface — run this before writing any
95
+ integration code. The block below is an **illustration of the shape only**; never read
96
+ values out of it, and in particular do not treat its `available_modes` as the modes your
97
+ customer has:
98
+
99
+ ```json
100
+ {"ok":true,"data":{
101
+ "contract_version":"2026-08-27",
102
+ "browser":{"script_url":"https://.../intellifend.js","token_header":"X-IntelliFend-Token",
103
+ "available_modes":["manual"]},
104
+ "backend":{"supported_languages":["java"],"min_jdk":17},
105
+ "release_state":"..."}}
106
+ ```
107
+
108
+ Use `script_url` and `token_header` verbatim. Check `available_modes` before committing
109
+ to seamless or manual — a mode absent from that list is not released, and writing an
110
+ integration against it produces code that will not work. Check `supported_languages`
111
+ before promising a backend integration for a stack that is not listed; the browser side
112
+ alone provides no protection, so an unsupported backend means the honest answer is
113
+ "not yet supported here".
114
+
115
+ ## Applications
116
+
117
+ ```bash
118
+ requestshield apps list
119
+ requestshield apps get <app-key>
120
+ ```
121
+
122
+ **auth.** `apps list` returns the applications the current user can access, each with
123
+ `app_key`, `name`, and `status`. Use it to resolve a human-supplied app name to a key
124
+ rather than asking the user to retype one.
125
+
126
+ `apps get <app-key>` returns `app_id`, `app_name`, and `status`. Status distinguishes a
127
+ key that has never seen traffic (`ready`) from one in normal service (`active`) — which
128
+ is exactly the difference between "the integration is not wired up yet" and "it is
129
+ working", so check it before spending time debugging code.
130
+
131
+ ## Credentials
132
+
133
+ ```bash
134
+ requestshield credentials status <app-key>
135
+ ```
136
+
137
+ **auth.** Returns metadata only — `provisioned`, `active`, `shared` — and never the
138
+ secret value. Use it to answer "is the secret configured?" without anyone having to
139
+ paste a credential. A key that is `provisioned: false` explains backend failures far
140
+ faster than reading code.
141
+
142
+ ## Service and monitoring
143
+
144
+ ```bash
145
+ requestshield server
146
+ requestshield challenge volume <app-key> [--from <time>] [--to <time>] [--granularity <value>]
147
+ ```
148
+
149
+ **auth.** `server` reports `healthy` or `degraded`. Check it first when an integration
150
+ that was working starts behaving oddly — a degraded platform explains fail-open
151
+ behaviour on the backend without anything being wrong in the customer's code.
152
+
153
+ `challenge volume` is runtime evidence that the browser/platform half is active. It is
154
+ not proof that the protected backend enforces a blocking decision; the controlled
155
+ negative test in `SKILL.md` provides that evidence. Times are ISO-8601
156
+ (`2026-08-01T00:00:00Z`); `--granularity` takes values such as `hour` or `day`. A
157
+ `challenge_count` of zero after the user has exercised the endpoint means the browser
158
+ half is not firing — that is a browser-side bug, not a backend one, so start with the
159
+ mode's reference file.
160
+
161
+ ## Billing
162
+
163
+ ```bash
164
+ requestshield get billing
165
+ ```
166
+
167
+ **auth.** Current plan and usage for the application. Read-only; surface it when the
168
+ user asks about cost or limits.
169
+
170
+ ## Agent integration
171
+
172
+ ```bash
173
+ requestshield agent setup
174
+ requestshield agent setup --agent claude
175
+ requestshield agent setup --agent codex
176
+ ```
177
+
178
+ Local setup. Installs this skill for the named agent. With no `--agent`, the CLI detects
179
+ which agent is in use. Run it when the user wants the skill available in another repo or
180
+ for the other agent; it is the supported alternative to copying files by hand. It should
181
+ not require a RequestShield App Key or API Secret, and it must not create, rotate, or
182
+ deactivate customer credentials.
@@ -0,0 +1,389 @@
1
+ # Integration planning
2
+
3
+ Use this reference before writing or removing RequestShield integration code. It owns the
4
+ planning steps that are too detailed for `SKILL.md`.
5
+
6
+ ## Workflow by operation
7
+
8
+ - **Install** -> `integration-planning.md` full flow: **Credentials check** ->
9
+ **Detect existing integration** -> **Contract check** -> **Gate** -> **Then act on
10
+ what you found** -> **Choosing the mode** -> **Installing** -> **Verifying** ->
11
+ **Negative test**.
12
+ - **Verify / Troubleshoot** -> `integration-planning.md` (Detect existing integration
13
+ only) -> **Verifying**. If a reason code, degraded result, browser mode, backend path,
14
+ or unknown state is involved, read only the matching reference files.
15
+ - **Uninstall** -> `integration-planning.md` (Detect existing integration only) ->
16
+ **Uninstalling**.
17
+
18
+ Do not couple browser mode to backend path. They are independent axes:
19
+
20
+ - Browser mode: Seamless (`browser-seamless.md`) or Manual (`browser-manual.md`).
21
+ - Backend path: Spring Boot 3 starter (`backend-spring-boot.md`) or Java core SDK
22
+ (`backend-java-core.md`).
23
+
24
+ Either browser mode may pair with either supported backend path. The backend stack
25
+ selects the backend path. The way the browser issues the protected request selects the
26
+ browser mode.
27
+
28
+ ## Credentials check
29
+
30
+ The **pre-edit** check: confirm a credential source exists before touching backend
31
+ integration code. This step belongs to the Install full flow.
32
+
33
+ - **Install** — always, before editing backend code.
34
+ - **Verify / Troubleshoot** — do not run this as part of the operation route. Start with
35
+ **Detect existing integration only**, then use `troubleshooting.md` if the evidence
36
+ points to a credential failure.
37
+ - **Uninstall** — do not run this step; detect the existing integration only.
38
+
39
+ Diagnosing a credential *failure* is a different question, and `troubleshooting.md`
40
+ owns it — go there for `consume_unauthorized`, `unknown_customer`, or a backend
41
+ rejecting every token, whether or not this check has already passed.
42
+
43
+ RequestShield requires:
44
+
45
+ - an App Key
46
+ - an API Secret
47
+
48
+ Check for credential sources without printing, reading, or echoing their values. Confirm
49
+ presence only; never print, cat, or echo either value:
50
+
51
+ ```bash
52
+ # Presence check only. Never print secret values.
53
+
54
+ if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY)='; then
55
+ echo "RequestShield App Key: present"
56
+ else
57
+ echo "RequestShield App Key: not found in current environment"
58
+ fi
59
+
60
+ if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(API_SECRET|SECRET)='; then
61
+ echo "RequestShield API Secret: present"
62
+ else
63
+ echo "RequestShield API Secret: not found in current environment"
64
+ fi
65
+ ```
66
+
67
+ Common variable names are `INTELLIFEND_REQUESTSHIELD_APP_KEY`,
68
+ `REQUESTSHIELD_APP_KEY`, `REQUESTSHIELD_KEY`,
69
+ `INTELLIFEND_REQUESTSHIELD_API_SECRET`, `REQUESTSHIELD_API_SECRET`, and
70
+ `REQUESTSHIELD_SECRET`. A matching non-canonical name is only a credential candidate.
71
+ Confirm that the backend actually reads or maps that variable before treating it as a
72
+ valid RequestShield credential.
73
+
74
+ If the App Key is known, also confirm server-side credential state without exposing the
75
+ secret:
76
+
77
+ ```bash
78
+ requestshield credentials status <app-key>
79
+ ```
80
+
81
+ A credential that is not present in the current shell is not necessarily missing from
82
+ the deployed backend. It may be injected at runtime by a secret manager, CI/CD system,
83
+ container runtime, Kubernetes Secret, or deployment environment.
84
+
85
+ Report only what was confirmed:
86
+
87
+ | Finding | Action |
88
+ | --- | --- |
89
+ | Both credential sources are confirmed | Continue. Never read their values. |
90
+ | A non-canonical variable is found and its backend mapping is confirmed | Report the mapping and continue. |
91
+ | Credentials are provisioned, but the runtime source is unknown | Ask how the deployed backend receives them before editing backend integration code. |
92
+ | No credential source can be confirmed | Report the uncertainty and ask before proceeding. |
93
+ | No credential pair has been provisioned | Ask the user to provision credentials before continuing with backend setup. |
94
+
95
+ If no pair has been provisioned yet, the user creates it. You may run the read-only
96
+ `requestshield apps list` to see whether an application already exists. Everything that
97
+ mints or changes a secret stays with the user; see **Key and secret management** in
98
+ `SKILL.md`.
99
+
100
+ ## Detect existing integration
101
+
102
+ Search for existing RequestShield code before install, verify/troubleshoot, or uninstall.
103
+ Use `rg` when available. Use `rg -l` for filename-only discovery.
104
+
105
+
106
+ Browser markers:
107
+
108
+ ```bash
109
+ rg -l "data-app-key|data-protect|IntelliFend|X-IntelliFend-Token" \
110
+ -g "*.html" -g "*.htm" -g "*.js" -g "*.jsx" -g "*.ts" -g "*.tsx" \
111
+ -g "*.vue" -g "*.svelte" -g "*.astro" \
112
+ -g "*.erb" -g "*.haml" -g "*.php" -g "*.jinja*" -g "*.j2" -g "*.twig" \
113
+ -g "*.hbs" -g "*.ejs" -g "*.pug" -g "*.blade.php" \
114
+ -g "!node_modules/**" -g "!**/skills/**" -g "!**/.requestshield/**" .
115
+ ```
116
+
117
+ The tag often lives in a server-rendered layout rather than a static `.html` — a Django,
118
+ Rails, Laravel, or Thymeleaf base template — so a scan limited to `.html`/`.js` reports
119
+ "not integrated" on a codebase that already has one, and the install then adds a second
120
+ tag. If the app's templates use an extension not listed above, add it before concluding.
121
+
122
+ Backend markers:
123
+
124
+ ```bash
125
+ rg -l "RequestShieldClient|RequestShieldProtected|intellifend|requestshield" \
126
+ -g "*.java" -g "*.kt" -g "*.xml" -g "*.yml" -g "*.yaml" \
127
+ -g "*.gradle" -g "*.gradle.kts" \
128
+ -g "!**/skills/**" -g "!**/.requestshield/**" .
129
+ ```
130
+
131
+ `rg -l` returns candidate filenames only. Do not follow it with `cat`, an unrestricted
132
+ `sed`, plain `rg -n` without `-o`, or another command that exposes complete matching
133
+ lines or the entire file.
134
+
135
+ Locate known markers without returning the rest of their lines:
136
+
137
+ ```bash
138
+ rg -n -o \
139
+ "data-app-key|data-protect|IntelliFend|getToken|X-IntelliFend-Token" \
140
+ <browser-candidate-file>
141
+
142
+ rg -n -o \
143
+ "RequestShieldClient|RequestShieldProtected|verify|isAllowed|intellifend|requestshield" \
144
+ <backend-candidate-file>
145
+
146
+ rg -n -o \
147
+ "(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY|API_SECRET|SECRET)" \
148
+ <candidate-configuration-file>
149
+ ```
150
+ -o prints only the matched marker or environment-variable name, not the surrounding
151
+ value. Environment-variable names are safe to report; their values are not.
152
+
153
+ When configuration context is required, inspect it only through a secret-aware parser
154
+ or redacted view that masks sensitive values before stdout reaches the agent. Its output
155
+ may contain the filename, line number, configuration key, environment-variable name,
156
+ App Key when needed to identify the integration, and whether the source is an
157
+ environment reference, secret-manager reference, or literal.
158
+
159
+ Replace every API Secret, RequestShield token, session value, CSRF value,
160
+ authorization credential, and other credential literal with <redacted>.
161
+
162
+ If no secret-aware parser or redacted view is available, do not read the candidate
163
+ configuration file verbatim. Ask the user for a sanitized excerpt instead. Switching
164
+ to rg -l and then reading the entire candidate file is not safe.
165
+
166
+ The exclusions matter when this skill is installed inside the repo being scanned —
167
+ browser and backend reference files contain marker strings and would otherwise read as
168
+ an existing integration.
169
+
170
+ Report one of:
171
+
172
+ - **Not integrated**: no browser or backend markers.
173
+ - **Browser only**: tokens may be obtained or attached, but no backend enforcement was
174
+ found. This looks protected and is not.
175
+ - **Backend only**: enforcement exists, but no page sends a token. In BLOCK mode this can
176
+ reject legitimate users.
177
+ - **Fully integrated**: browser mode and backend path are both present. Name both.
178
+
179
+ If a script tag already exists, update it instead of adding a second one. The SDK
180
+ initializes from `document.currentScript`; two tags mean two configurations, and the
181
+ last one to run wins rather than the two merging.
182
+
183
+ ## Contract check
184
+
185
+ Run this **after** detection and **before** the Gate: detection says what the codebase
186
+ already does, the contract says what the platform currently allows, and the browser-mode
187
+ decision needs both.
188
+
189
+ ```bash
190
+ requestshield contract
191
+ ```
192
+
193
+ It is authoritative for `browser.script_url`, `browser.token_header`,
194
+ `browser.available_modes`, `backend.supported_languages`, and `backend.min_jdk`. Use
195
+ those values verbatim; a stale hardcoded script URL produces a page that loads nothing
196
+ and fails silently.
197
+
198
+ Treat every array as an **allow-list**. There is no "unavailable" flag, so something is
199
+ unavailable when it is absent — and an absent, empty, or unparseable array is
200
+ unavailable, not permission. If the contract is missing or returns `ok: false`, stop and
201
+ report what the CLI returned.
202
+
203
+ ### Backend consequences — these feed the Gate
204
+
205
+ - `backend.supported_languages` missing the customer's backend language → **stop.** No
206
+ backend path exists, and a browser-only install protects nothing.
207
+ - `backend.min_jdk` above the project's Java target → **stop.** The JDK upgrade is the
208
+ prerequisite; do not fall back to an older SDK version.
209
+
210
+ ### Browser-mode consequences — these decide the mode
211
+
212
+ Read `browser.available_modes` together with what detection found in the codebase:
213
+
214
+ | Released modes | Codebase today | Do this |
215
+ | --- | --- | --- |
216
+ | **One mode** | already uses that mode | Keep it. Nothing to change on the browser side. |
217
+ | **One mode** | uses the *other* mode | **This is a finding, not a preference.** The mode in the codebase is not released, so it cannot be relied on. Report it and migrate the client to the released mode. |
218
+ | **One mode** | not integrated | No choice to make. Install that mode, and say which one and why. |
219
+ | **Both modes** | already uses one of them | **Keep the mode already in use.** Change it only for a concrete reason — the transport is one Seamless cannot cover, or the user asks. A mode switch rewrites working call sites and risks leaving an endpoint half-migrated. |
220
+ | **Both modes** | not integrated | A genuine choice. Go to **Choosing the mode** below. |
221
+ | **Neither** | any | **Stop.** No browser mode is released; report the contract output. |
222
+
223
+ Per-endpoint, not per-repo: a page may legitimately run Seamless on one endpoint and
224
+ Manual on another. Apply the table to the operation being worked on, not to the codebase
225
+ as a whole.
226
+
227
+ ## Gate
228
+
229
+ The backend path is decided by the backend stack, not by browser preference. Establish
230
+ this before proposing edits. Discovering a backend blocker after the browser half is
231
+ written leaves tokens flowing with nothing enforcing them.
232
+
233
+ An integration is one backend path plus one browser mode, and they are independent
234
+ choices. A non-Spring-MVC backend does not rule out Seamless mode; it only rules out the
235
+ Spring Boot 3 starter backend path.
236
+
237
+ Backend path:
238
+
239
+ | Backend path | Requirement | Reference |
240
+ | --- | --- | --- |
241
+ | **Spring Boot 3 starter** | Spring Boot 3 with Spring MVC, and Java 17+ | `backend-spring-boot.md` |
242
+ | **Java core SDK** | Java 17+ | `backend-java-core.md` |
243
+
244
+ Browser mode:
245
+
246
+ | Browser mode | Use when | Reference |
247
+ | --- | --- | --- |
248
+ | **Seamless** | The page reaches the endpoint with `fetch` or async `XMLHttpRequest` | `browser-seamless.md` |
249
+ | **Manual** | The call needs explicit token timing, or a non-header carrier | `browser-manual.md` |
250
+
251
+ Either browser mode pairs with either backend path. The token travels in
252
+ `X-IntelliFend-Token` regardless. Java below 17 blocks both backend paths, and no amount
253
+ of browser-side work substitutes for backend enforcement.
254
+
255
+ ### Run the check
256
+
257
+ Find the build file for the module that serves the protected endpoint, not the first one
258
+ in the tree. In a monorepo, locate the route first:
259
+
260
+ ```bash
261
+ find . -maxdepth 4 \( -name pom.xml -o -name "build.gradle*" \) \
262
+ -not -path "*/node_modules/*" -not -path "*/build/*" -not -path "*/target/*"
263
+ rg -n "/api/register" -g "*.java" -g "*.kt" .
264
+ ```
265
+
266
+ One build file, inspect it. Several, and the route cannot be tied to one of them — or
267
+ several backends could plausibly serve it — **stop and ask which service owns the
268
+ endpoint.** Two backends in a monorepo can differ in both Java version and framework, so
269
+ a guess here invalidates the whole integration rather than needing a tweak.
270
+
271
+ Use repo-wide build-file scans only for discovery. Do not combine a route found in one
272
+ module with Java or Spring evidence from another module.
273
+
274
+ #### Java target version
275
+
276
+ Read the *project's target*, never the machine's JDK.
277
+
278
+ ```bash
279
+ rg -n "maven\.compiler\.(release|source|target)|<release>|<java\.version>" <owning-pom.xml>
280
+ rg -n "languageVersion|sourceCompatibility|jvmToolchain|JavaVersion" <owning-build.gradle*>
281
+ ```
282
+
283
+ - **Maven** — `maven.compiler.release`, or the compiler plugin's `<release>`, is
284
+ authoritative. Spring Boot's parent POM exposes `<java.version>`, which is the
285
+ idiomatic place in a Boot app. **Check the parent POM too** when the module inherits;
286
+ the value is often not in the module's own file.
287
+ - **Gradle** — `java.toolchain.languageVersion = JavaLanguageVersion.of(21)` and
288
+ `kotlin { jvmToolchain(21) }` are both authoritative, and both easy to miss because
289
+ they sit inside a block rather than on a property line.
290
+ - **When the build file says nothing** — `.java-version`, `.sdkmanrc`, `.tool-versions`,
291
+ a `setup-java` step's `java-version` in `.github/workflows/`, or a JDK base image in a
292
+ `Dockerfile`. These are **hints, not answers**: they describe an environment, which can
293
+ differ from what the build compiles against. A build file that yields only hints is the
294
+ ambiguous row below — ask.
295
+ - `java -version` reports the **local** JDK. A repo targeting 11 builds fine on a machine
296
+ with 21 installed, so it is a last-resort hint and never evidence.
297
+
298
+ Java below 17 blocks **both** backend paths. Report the version and its source and name
299
+ the upgrade as the prerequisite; do not fall back to an older SDK version or write a
300
+ partial browser-only integration.
301
+
302
+ #### Spring Boot 3, and separately Spring MVC
303
+
304
+ Two conditions. Both must hold for the Spring Boot starter.
305
+
306
+ ```bash
307
+ rg -n "spring-boot-starter-parent|spring-boot-dependencies|org\.springframework\.boot" \
308
+ <owning-pom.xml-or-build.gradle*> | rg "3\.[0-9]+"
309
+ rg -n "spring-boot-starter-web[\"' :<]|spring-boot-starter-webflux|spring-webmvc" \
310
+ <owning-pom.xml-or-build.gradle*>
311
+ ```
312
+
313
+ Read the parent version, the `spring-boot-dependencies` BOM version, or the Gradle plugin
314
+ version. **Spring Boot 2.x does not qualify** — the starter is a Boot 3 artifact, so a
315
+ 2.x app needs the core SDK path even though it is Spring.
316
+
317
+ Then MVC versus WebFlux — the trap worth checking every time:
318
+
319
+ - `spring-boot-starter-web` → Spring MVC. The starter is available when the app is also
320
+ Spring Boot 3 on Java 17+.
321
+ - `spring-boot-starter-webflux` with no `-web` → **not** Spring MVC. The starter protects
322
+ Spring MVC handler methods, so `@RequestShieldProtected` does not apply. Such an app is
323
+ Spring Boot 3 on Java 17+ and still needs the Java core SDK — say so explicitly,
324
+ because "Spring Boot 3, Java 21" reads like a starter green light and quietly is not.
325
+ Browser mode is unaffected; Seamless remains available when the request transport
326
+ matches.
327
+ - **Both present** → determine which stack serves the protected route. A
328
+ `@RestController` returning `Mono`/`Flux`, or a `RouterFunction`, indicates the
329
+ reactive path. Ask if it stays unclear.
330
+
331
+ Also not Spring MVC, and therefore the Java core SDK backend path: **Quarkus, Micronaut,
332
+ Dropwizard, Helidon, Ktor, Vert.x, Jakarta EE / JAX-RS on its own, and a plain servlet
333
+ application.**
334
+
335
+ ## Then act on what you found
336
+
337
+ This decision chooses the backend path only. Pick the browser mode separately from how
338
+ the page issues the protected request.
339
+
340
+ | Detected | Do this |
341
+ | --- | --- |
342
+ | Java < 17, or no Java backend | Stop before editing. Report the finding and its source; no backend path is available, so nothing can enforce. Name the upgrade as the prerequisite. |
343
+ | Java 17+, no Spring Boot 3 MVC | Use Java core SDK. It is the only backend option; state it rather than asking a fake choice. |
344
+ | Java 17+ and Spring Boot 3 MVC | Both backend paths are viable. Recommend the starter and confirm once. |
345
+ | Ambiguous module, missing build file, version only in environment hints, or unclear MVC/WebFlux ownership | Ask, showing what you found. |
346
+
347
+ Always report the evidence with file and line references:
348
+
349
+ ```text
350
+ Backend: theair-customer-backend/build.gradle.kts
351
+ Java 21 - java.toolchain.languageVersion (line 14)
352
+ Spring Boot 3.2.1 - org.springframework.boot plugin (line 3)
353
+ Spring MVC - spring-boot-starter-web (line 20)
354
+ Backend path: Spring Boot 3 starter. Browser mode still depends on released modes and request transport.
355
+ ```
356
+
357
+ Detection is inference from build files. If any line would read "unknown", ask rather
358
+ than assume.
359
+
360
+ ## Choosing the mode
361
+
362
+ Reach this section only when **Contract check** left a genuine choice: both modes
363
+ released, and this operation not already integrated. If it landed on any other row of
364
+ that table, the mode is already decided — do not re-open it here.
365
+
366
+ This is the browser half. Backend path does not constrain it. Both browser modes deliver
367
+ the token in `X-IntelliFend-Token`, which either backend path reads.
368
+
369
+ | | **Seamless** | **Manual** |
370
+ | --- | --- | --- |
371
+ | Browser | `data-protect` lists exact endpoints; the SDK attaches tokens to matching `fetch` and async XHR requests. | `await IntelliFend.getToken()` at each protected call site, attached in application code. |
372
+ | Requires | The request travels by `fetch` or async XHR from page scope. | Nothing beyond the SDK and a supported carrier. |
373
+ | Code touched | One line of HTML. | Every protected call site, plus every retry path. |
374
+ | Backend work | Identical either way: whichever backend path the Gate selected. | Identical either way: whichever backend path the Gate selected. |
375
+
376
+ Released-mode availability was already settled at **Contract check**. One question is
377
+ left: does the protected request travel by `fetch` or asynchronous `XMLHttpRequest` from
378
+ page scope? Native form POST, `sendBeacon`, WebSocket/EventSource, synchronous XHR,
379
+ `no-cors`, and service-worker-owned requests are not intercepted — read
380
+ `browser-seamless.md` for the full coverage list.
381
+
382
+ Recommend Seamless when that check passes. It touches one line instead of every call
383
+ site, so there is no missed call site or retry path. If it fails, use Manual and give
384
+ the reason; that is a finding, not a preference.
385
+
386
+ A given protected operation uses one mode, not both. Different endpoints on the same
387
+ page may use different modes when necessary. Never use two backend verification paths on
388
+ one request; `@RequestShieldProtected` and manual `RequestShieldClient.verify()` both
389
+ consume the token, so the second returns `token_replayed`.