Repository navigation
Conversation
Replaces the 'server-side HTTP is not available yet' caveat with the request() reference: options, response shape, limits, error codes and the no-logging guarantee. Regenerated the AI-doc indexes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying fliplet-cli with
|
| Latest commit: |
9b8f8ee
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://bc54bc95.fliplet-cli.pages.dev |
| Branch Preview URL: | https://feature-dev-2042-server-http.fliplet-cli.pages.dev |
zeryabkhan91
left a comment
There was a problem hiding this comment.
Code review (via /fliplet-pr-review)
Review basis: subject=e7d2bb8e46049994b4a44f176c465b0cad541131 standards=fliplet-engineering@0.18.0 revision=11c68cdf718d75681ad0fc323e57b80bb36e2fa9 source=fliplet-pr-review digest=sha256:40c49b60a8bd5384
Summary: Documents Fliplet.App.V3.Actions.request() and regenerates the LLM index files.
Regression risk: Docs only. Merging publishes and the AI builder reads these docs, so the release-order note in the description applies.
Acceptance criteria: The limits, error codes and response shape match the runner code in Fliplet/fliplet-service-browser#53, and the new in-page anchor resolves on the Cloudflare preview.
CI: Green, but the PR currently conflicts with master.
Two things to address:
- Merge conflict. The only conflict is the
generatedAtline indocs/.well-known/llms-v3-libraries.json(master moved on 5 October). Merge master and re-runbuild-agent-indexes.mjs. - One claim is not true of the runner as it stands, see the inline comment.
| - **The body is text.** Use `JSON.parse(response.body)` for a JSON response. Binary responses are not supported. | ||
| - **HTTPS only, public destinations only.** Private, loopback and link-local addresses are refused, including when a public hostname resolves or redirects to one. | ||
| - **Limits.** 30 seconds by default (50 at most) for the whole request including redirects, 5 MB for the response body and for the request body, and 5 redirects. | ||
| - **Redirects to another host drop every request header**, so a credential is never forwarded to a host you did not name. Call the final URL directly if the provider needs the header there. |
There was a problem hiding this comment.
"a credential is never forwarded to a host you did not name" is not true today for a credential in the body.
The runner drops headers on a cross-host redirect, but on a 307/308 it resends the request body to the new host (details on Fliplet/fliplet-service-browser#53). This page also tells authors a credential can go "in a header (or the body)".
Either the runner stops resending the body, in which case this sentence can stay, or it needs softening to headers only. docs/.well-known/llms-full.txt carries the same text, so regenerate after the edit.
There was a problem hiding this comment.
The runner no longer resends the body (Fliplet/fliplet-service-browser#53, e231719), so the sentence holds now. I added a bullet that spells out the 307/308 case so it isn't a surprise: the request resolves with the 3xx and its location.
Also in d1e9458: the body is always decoded as UTF-8 (other encodings not supported), compressed responses are decompressed, master merged and the index files regenerated.
…rver-http-transport # Conflicts: # docs/.well-known/llms-v3-libraries.json
Follows the runner review fixes: a 307/308 to another host that would resend the body is not followed, the body is always decoded as UTF-8, and compressed responses are decompressed. Regenerated the AI-doc indexes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # docs/.well-known/llms-v3-libraries.json
QA found the runner's Chromium runs with web security off (@sparticuz/chromium default), so fetch() already reaches CORS-less providers. Reword: request() is the call that refuses private addresses, caps limits and stays out of the logs. An any action run on the server now also gets UNAVAILABLE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zeryabkhan91
left a comment
There was a problem hiding this comment.
Code review (via /fliplet-pr-review)
Review basis: subject=9b8f8ee57c480e9dbba97e450b031fa51de50914 standards=fliplet-engineering@0.18.0 revision=11c68cdf718d75681ad0fc323e57b80bb36e2fa9 source=fliplet-pr-review digest=sha256:40c49b60a8bd5384
Approving. Both points from my last review are sorted: the conflict is gone, and the 307/308 body case is documented and matches the runner now. Checked llms-full.txt too, it has the new wording and none of the old CORS lines.
A few non-blocking comments inline. One more for the description: the deploy order still says #53, then #8732, then this. The any action line only holds once api #8790 and runner #56 are live (and studio #9041 for the AI guidance), so worth adding those to the order. The body also still mentions regenerating llms-v3-libraries.json, which isn't in the diff any more.
| <p class="warning"><strong>Never let a setting value leave the action.</strong> Never return it, never log it (<code>console.*</code>), never put it in a thrown or returned error message, and never put it in a URL or query string — send it in a request header or body instead. Return values and error messages are stored in the app action logs and returned to the caller of <code>runWithResult()</code>; console output and the URL of every <code>fetch</code> or XHR request are captured in the server runner logs. A provider that only accepts its key in the URL query string must be called with <a href="#flipletappv3actionsrequest-server-actions-only"><code>Fliplet.App.V3.Actions.request()</code></a>, which is the one request the runner does not log.</p> | ||
|
|
||
| <p class="warning">Server actions run in a headless browser, so <code>fetch</code> from a server action is a cross-origin browser request. Provider APIs that do not allow cross-origin browser requests (CORS) reject it. Server-side HTTP requests without this restriction are not available yet.</p> | ||
| <p class="warning">Call third-party provider APIs from a server action with <a href="#flipletappv3actionsrequest-server-actions-only"><code>Fliplet.App.V3.Actions.request()</code></a>, not <code>fetch</code>. Both reach the provider, but only <code>request()</code> refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the server runner logs. The URL of every <code>fetch</code> is captured in those logs.</p> |
There was a problem hiding this comment.
Non-blocking: this repo and developers.fliplet.com are public, and this line spells out that a server action's own fetch() isn't address-filtered. QA saw it try to connect to 10.0.0.1 inside the runner's VPC. I'd just say what request() does without comparing it to fetch:
| <p class="warning">Call third-party provider APIs from a server action with <a href="#flipletappv3actionsrequest-server-actions-only"><code>Fliplet.App.V3.Actions.request()</code></a>, not <code>fetch</code>. Both reach the provider, but only <code>request()</code> refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the server runner logs. The URL of every <code>fetch</code> is captured in those logs.</p> | |
| <p class="warning">Call third-party provider APIs from a server action with <a href="#flipletappv3actionsrequest-server-actions-only"><code>Fliplet.App.V3.Actions.request()</code></a>. It refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the server runner logs. The URL of every <code>fetch</code> is captured in those logs.</p> |
Same idea at L328 and in app-settings.md, then regenerate llms-full.txt. The VPC reachability itself needs its own ticket, not something for this PR.
|
|
||
| #### `Fliplet.App.V3.Actions.request()` (server actions only) | ||
|
|
||
| `Fliplet.App.V3.Actions.request()` makes an HTTPS request from the server runner instead of from the headless browser the action runs in. Unlike `fetch`, it refuses private network addresses, enforces timeout, size and redirect limits, and is never written to the runner logs. The request leaves from the same static IP address as every other server action request, so provider keys restricted to an allow-listed IP keep working. |
There was a problem hiding this comment.
Non-blocking, same as L270:
| `Fliplet.App.V3.Actions.request()` makes an HTTPS request from the server runner instead of from the headless browser the action runs in. Unlike `fetch`, it refuses private network addresses, enforces timeout, size and redirect limits, and is never written to the runner logs. The request leaves from the same static IP address as every other server action request, so provider keys restricted to an allow-listed IP keep working. | |
| `Fliplet.App.V3.Actions.request()` makes an HTTPS request from the server runner instead of from the headless browser the action runs in. It refuses private network addresses, enforces timeout, size and redirect limits, and is never written to the runner logs. The request leaves from the same static IP address as every other server action request, so provider keys restricted to an allow-listed IP keep working. |
| | `TIMEOUT` | No complete response within `timeoutMs` | | ||
| | `TOO_LARGE` | Response body over 5 MB | | ||
| | `TOO_MANY_REDIRECTS` | More than 5 redirects | | ||
| | `NETWORK_ERROR` | DNS, connection or TLS failure | |
There was a problem hiding this comment.
Non-blocking: QA saw a header value with a CRLF in it come back as NETWORK_ERROR, but this table points malformed input at INVALID_REQUEST. Either mention invalid header values here or map them to INVALID_REQUEST in the runner.
|
|
||
| - Call the provider with [`Fliplet.App.V3.Actions.request()`](../core/app-actions-v3#flipletappv3actionsrequest-server-actions-only). Unlike a plain `fetch`, it refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the runner logs. |
There was a problem hiding this comment.
Non-blocking: the blank line above makes this a separate list from the two bullets before it. And same wording point as app-actions-v3.md L270, I'd drop the comparison with fetch:
| - Call the provider with [`Fliplet.App.V3.Actions.request()`](../core/app-actions-v3#flipletappv3actionsrequest-server-actions-only). Unlike a plain `fetch`, it refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the runner logs. | |
| - Call the provider with [`Fliplet.App.V3.Actions.request()`](../core/app-actions-v3#flipletappv3actionsrequest-server-actions-only). It refuses private network addresses, caps the timeout, response size and redirects, and keeps the URL, headers and body out of the runner logs. |
Product areas affected
Developer docs (developers.fliplet.com) — App Actions V3 and the V3 app settings page. No CLI code changes.
What does this PR do?
Documents
Fliplet.App.V3.Actions.request(), the new way for a server action to call a provider API that rejects cross-origin browser requests (CORS).docs/API/core/app-actions-v3.md: newrequest()section (options, response shape, limits, error codes, what is and isn't logged). Thecontext.settingsexample now usesrequest()instead offetch. The two warnings that said server-side HTTP isn't available, and that a key-in-the-query-string provider can't be called, are updated.docs/API/v3/app-settings.md: points torequest()from the "Reading settings in a server action" section.docs/.well-known/llms-full.txtandllms-v3-libraries.json.JIRA ticket
DEV-2042
Result
node bin/build-agent-indexes.mjs --strict: passes.npm run test:unit: 163/163 passing.Checklist
Docs only, so no new tests.
Deployment instructions
Don't merge until the feature is live in production. Merging publishes the docs, and Studio's AI builder reads them, so it would start generating
request()calls that don't work yet.Order: Fliplet/fliplet-service-browser#53, then Fliplet/fliplet-api#8732, then this.
Author concerns
bin/check-v3-catalog-drift.mjsreports 1 error (fliplet-service-workernot in/v1/widgets/assets). It's the same on untouchedmaster, so it isn't from this PR.build-agent-indexes.mjsdoes nothing when run on Windows (its "run as script" check comparesimport.meta.urlwith a Windows path and never matches). I ran it in a Linux container instead. Worth a separate small fix.🤖 Generated with Claude Code