<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dave Kurian</title>
    <description>The latest articles on DEV Community by Dave Kurian (@davekurian).</description>
    <link>https://dev.to/davekurian</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3962819%2Ff0a481b6-456b-476e-bd6b-0aeb82ce4c1c.jpg</url>
      <title>DEV Community: Dave Kurian</title>
      <link>https://dev.to/davekurian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davekurian"/>
    <language>en</language>
    <item>
      <title>Replace the generic AI landing page with evidence visitors can verify</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Fri, 16 Oct 2026 11:50:23 +0000</pubDate>
      <link>https://dev.to/davekurian/replace-the-generic-ai-landing-page-with-evidence-visitors-can-verify-2n14</link>
      <guid>https://dev.to/davekurian/replace-the-generic-ai-landing-page-with-evidence-visitors-can-verify-2n14</guid>
      <description>&lt;p&gt;Your AI page can look finished before it has proved anything. A polished gradient is not a product claim.&lt;/p&gt;

&lt;p&gt;A useful signal appeared in a 16 October discussion on r/SideProject: a builder described a detector for generic AI-page tells such as purple gradients, stock fonts and placeholder text; a reply observed that landing pages are starting to look alike. That is one conversation, not a survey. But it points to a practical launch question: what should you replace before sending real visitors?&lt;/p&gt;

&lt;p&gt;If you need a ready-made starting point, &lt;a href="https://otf-kit.dev/pricing" rel="noopener noreferrer"&gt;the OTF Everything Bundle is $149 and includes 15 landing-page templates plus three kits&lt;/a&gt;. You can &lt;a href="https://summit.otf-kit.dev" rel="noopener noreferrer"&gt;preview the Summit landing page&lt;/a&gt;. A template supplies a layout and code; it does not supply your positioning, product evidence or customer proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the promise, not the palette
&lt;/h2&gt;

&lt;p&gt;Open the page as if you have never heard of the product. Give yourself five seconds and answer three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is this?&lt;/li&gt;
&lt;li&gt;Who is it for?&lt;/li&gt;
&lt;li&gt;What can I do next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the first screen answers only “this is modern software,” the visual treatment is carrying a promise that the copy has not earned. Write one plain sentence that names a specific user, the job they are trying to finish and the result your product actually provides. For example: “A weekly meal planner for two-person households that turns saved recipes into a shared grocery list.” Use that only if it describes your product; do not borrow the example as a claim.&lt;/p&gt;

&lt;p&gt;Then check the verbs. “Transform,” “speed up” and “reimagine” can sound confident while explaining nothing. Replace them with actions a visitor can picture: import a CSV, compare two plans, review a draft, schedule a session. Do not add benefits your product does not deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit the first screen in five passes
&lt;/h2&gt;

&lt;p&gt;Treat the hero as a short product walkthrough. It should let a visitor identify the product, recognize their problem, see a credible next step and find what happens after they click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass 1: Name the audience.&lt;/strong&gt; Avoid “for everyone” unless that is genuinely true. Say which role or situation the page serves. A narrower, accurate sentence gives a qualified visitor a better reason to continue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass 2: Show the product.&lt;/strong&gt; Replace a decorative gradient or generic stock image with a real screen, a small interaction, or a concrete before-and-after from your own product. If a screenshot is not possible, use a diagram that explains a real workflow. Do not use a fake customer portrait, made-up testimonial or an AI-generated interface as if it were live software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass 3: Make the action specific.&lt;/strong&gt; “Get started” is vague when the visitor does not know what will happen. Use “Open the demo,” “See the sample project,” or “Download the starter” only when the button really does that. Make the destination visible before the click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass 4: Check every number.&lt;/strong&gt; A count, rating, percentage, logo wall or “trusted by” line is a factual claim. Find the source and confirm it is current, representative and approved for public use. If you cannot verify it, remove it. A sample dashboard can contain illustrative data, but label the view as a demo and never imply those numbers came from customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pass 5: Read the page on a phone.&lt;/strong&gt; Check that the headline wraps cleanly, the primary action is visible, the body text is readable and the key visual still explains the product. A page that looks convincing only at desktop width is not finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace filler with evidence you can stand behind
&lt;/h2&gt;

&lt;p&gt;For each section, ask: what would a skeptical visitor need to see here? The answer may be a real product view, a short explanation of the workflow, a documented integration, a transparent limitation, or a link to a working demo. It does not have to be a testimonial.&lt;/p&gt;

&lt;p&gt;A feature list is more useful when it is tied to a task. “Role-based access” is a capability. “Invite a teammate and limit which records they can edit” shows what it means. Use that second sentence only if the product supports it. If the capability is planned, say so plainly rather than describing it in the present tense.&lt;/p&gt;

&lt;p&gt;If you have a case study, quote, customer count or performance result, keep its source close. Confirm that you have permission to publish it and that the statement is still accurate. If you have no such proof yet, do not fill the gap with invented social proof. Show the product working and state what it does today.&lt;/p&gt;

&lt;p&gt;The same check applies to template screenshots. The Summit preview below is a layout example, not evidence that a real travel company achieved the figures shown in the sample design. Replace every illustrative label or number with your own substantiated details before launch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz38h7veazbt1stu2bsob.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz38h7veazbt1stu2bsob.jpg" alt="Summit landing-page template preview with an alpine hero, sample headline and illustrative statistic cards." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give each section one job
&lt;/h2&gt;

&lt;p&gt;A long page can still feel generic when every section repeats “simple,” “capable” or “beautiful.” Assign each section one question to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hero: What does this product do for me?&lt;/li&gt;
&lt;li&gt;Product view: What will I actually use?&lt;/li&gt;
&lt;li&gt;How it works: What are the steps?&lt;/li&gt;
&lt;li&gt;Proof: What can I verify?&lt;/li&gt;
&lt;li&gt;Pricing: What will I pay, and what is included?&lt;/li&gt;
&lt;li&gt;FAQ: What might stop me from trying it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remove a section when it does not answer one of those questions. If two sections make the same claim with different gradients, keep the clearer one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad918ynnft2gtxww91vb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fad918ynnft2gtxww91vb.jpg" alt="Summit template region cards show how a landing page can organize distinct examples instead of repeating abstract benefits." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A “proof” section does not have to claim popularity. It can show a product state, a documented compatibility list, a changelog entry, a sample output labeled as such, or a clear explanation of a limitation. The key is that the visitor can check what you mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the click match the promise
&lt;/h2&gt;

&lt;p&gt;A landing page is a path, not a poster. Follow the primary action from the first screen through the destination. Does the button open the demo you named? Does the next page preserve the same product language? Is a purchase action clear about what the buyer receives?&lt;/p&gt;

&lt;p&gt;A good launch check uses a clean browser session and test data. Click every primary action. Follow a signup or checkout only as far as your test account permits. Confirm that the page does not lose the visitor’s context, show stale pricing or promise a feature that is missing. Never test with real customer records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3oietxdbrusbrr6phyv0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3oietxdbrusbrr6phyv0.jpg" alt="The Summit preview’s lower-page layout uses cards and a secondary action; check that each card leads to the destination its label promises." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use a starter or template, audit its sample content before publishing. Search for placeholder names, generic quotes, stock counters, decorative partner logos and buttons that do not work. A polished template can make unsupported copy look more authoritative, so the responsibility to verify it remains yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  A 15-minute pre-traffic checklist
&lt;/h2&gt;

&lt;p&gt;Before you share the page, write down the answers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can a new visitor explain the product and audience after reading the hero?&lt;/li&gt;
&lt;li&gt;Does the first screen show a real product view or a clearly labeled illustration?&lt;/li&gt;
&lt;li&gt;Can every factual statement be traced to something you can substantiate?&lt;/li&gt;
&lt;li&gt;Are sample metrics, fake testimonials and placeholder logos removed or unmistakably labeled?&lt;/li&gt;
&lt;li&gt;Does the main action lead to the destination its label promises?&lt;/li&gt;
&lt;li&gt;Is the price, what is included and any important limitation clear?&lt;/li&gt;
&lt;li&gt;Does the page remain readable on a phone?&lt;/li&gt;
&lt;li&gt;Is there one next step for someone who is not ready to buy?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a readiness check, not a conversion guarantee. A page can be clear and still fail to find its audience, price or channel. Publish only what you can prove, then measure qualified visits and the actions people actually take.&lt;/p&gt;

&lt;p&gt;For another view of what to inspect when an AI-built project moves from a prompt into a repo, see &lt;a href="https://otf-kit.dev/blog/a-founder-who-cant-code-can-still-ship" rel="noopener noreferrer"&gt;how a founder who cannot code can still ship&lt;/a&gt;. For landing-page builders who want code they can edit, the &lt;a href="https://otf-kit.dev/pricing" rel="noopener noreferrer"&gt;Everything Bundle&lt;/a&gt; brings together all 15 landing templates and the three kits for $149; the &lt;a href="https://summit.otf-kit.dev" rel="noopener noreferrer"&gt;Summit preview&lt;/a&gt; is one example, not a substitute for custom proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/SideProject/comments/1x6tmly/what_are_you_building_right_now_drop_your_project/" rel="noopener noreferrer"&gt;r/SideProject discussion, “What are you building right now? Drop your project below,” 16 October 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://otf-kit.dev/pricing" rel="noopener noreferrer"&gt;OTF pricing and bundle contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://summit.otf-kit.dev" rel="noopener noreferrer"&gt;Summit landing-page preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>templates</category>
      <category>designsystem</category>
      <category>kits</category>
    </item>
    <item>
      <title>How to bring lifts, runs, and steps into one fitness app history</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Fri, 16 Oct 2026 09:56:19 +0000</pubDate>
      <link>https://dev.to/davekurian/how-to-bring-lifts-runs-and-steps-into-one-fitness-app-history-25bl</link>
      <guid>https://dev.to/davekurian/how-to-bring-lifts-runs-and-steps-into-one-fitness-app-history-25bl</guid>
      <description>&lt;p&gt;A fitness app can track a lifting session, a run, and a day of steps, yet still leave the user with three disconnected histories. If you are building that experience, the hard part is deciding what “one activity history” means before the first integration ships.&lt;/p&gt;

&lt;p&gt;Building the app shell too? &lt;a href="https://otf-kit.dev/templates/fitness-kit" rel="noopener noreferrer"&gt;See the $99 Fitness Kit&lt;/a&gt;, a mobile Expo + Hono starter with workout tracking, activity rings, sign-in, and a Postgres-backed app foundation. &lt;a href="https://fitness-preview.otf-kit.dev" rel="noopener noreferrer"&gt;Open the live demo&lt;/a&gt;. Health-data sync and payments are not included, so treat the kit as a starting point and plan those integrations separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the user's question
&lt;/h2&gt;

&lt;p&gt;A person opening a fitness app usually wants a useful answer, not a database browser: what did I do, when did I do it, and how does it fit with the rest of my week? A barbell session, a run recorded by another app, and steps captured by a phone are different kinds of records. Putting them on one screen does not make them the same kind of event.&lt;/p&gt;

&lt;p&gt;A builder's first decision is whether the history is a timeline, a set of summaries, or both. A timeline answers “what happened and when?” Summaries answer “how much did I do over this period?” These views can share data, but they should not share a vague definition of activity.&lt;/p&gt;

&lt;p&gt;A useful first release can show three distinct event types in chronological order. Each entry should keep its original type and source visible. A strength session can show its exercises and duration; a run can show distance and duration when those values are available; steps can show a daily total. The interface can unify navigation and date grouping without pretending that the underlying measurements are interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep events separate from aggregates
&lt;/h2&gt;

&lt;p&gt;Start with an event model that preserves the detail the app actually owns. For example, a manually logged workout might have a stable identifier, activity type, start and end times, time-zone offset, and the user who created it. Exercise sets can live as child records with their own repetitions, weight, and order. An imported run or step total should preserve its external source and source identifier when the integration provides them.&lt;/p&gt;

&lt;p&gt;Then derive summaries for the screens that need them. A weekly “sessions completed” card can count workout sessions. A “distance this week” card can sum run records with compatible units. A step chart can aggregate steps by local calendar day. These are separate calculations with explicit input rules, rather than a single score that silently adds unlike values together.&lt;/p&gt;

&lt;p&gt;This distinction helps with edits too. If someone corrects a manually entered workout, update that event and recompute the affected summaries. If an imported record is removed or changed by its source, process that change as an update to the imported record. Avoid copying every incoming value into a second, indistinguishable manual event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmq2jmrnmzxw1zxh23hwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmq2jmrnmzxw1zxh23hwx.png" alt="A developer organizing lifting sessions, runs, and steps as separate event types on one timeline" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Record provenance before syncing
&lt;/h2&gt;

&lt;p&gt;Once data can arrive from multiple places, provenance becomes part of the product. Store the origin for each imported event and retain whatever stable source ID the provider gives you. That gives the app a way to recognize an update instead of showing the same run twice after a later sync.&lt;/p&gt;

&lt;p&gt;Do not deduplicate by matching only date and duration. Two workouts can happen on the same day and last the same amount of time. Prefer a provider's record identifier. When no stable identifier exists, define a cautious matching rule using several fields, keep the original payload available for debugging where privacy policy permits, and make the rule visible in code.&lt;/p&gt;

&lt;p&gt;Also separate “recorded at” from “happened at.” A workout may be uploaded later, or a phone may cross a time zone between the workout and the sync. Store the event's start and end timestamps and preserve the relevant offset when available. Grouping by the user's local day is a display decision; changing the underlying timestamp to make a chart look neat can move an event into the wrong date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat permissions and empty states as product flows
&lt;/h2&gt;

&lt;p&gt;Health data is sensitive. Request access when the user tries a feature that needs it, explain the value in plain language, and handle denial without blocking the rest of the app. Users can revoke access later, so the app should have an explicit connected, not-connected, and permission-revoked state instead of treating a missing response as zero activity.&lt;/p&gt;

&lt;p&gt;On Android, Health Connect exposes different record types for data such as steps and exercise sessions. Its documentation also describes the permissions and records developers need to account for; use the data types your feature requires rather than requesting everything by default. A permission grant does not guarantee that a source has supplied records, so distinguish “no data yet” from “we could not read data.”&lt;/p&gt;

&lt;p&gt;The first screen matters most. A brand-new account may have no workouts, integrations, or step history. Give that user a clear first action: log a workout, connect a source, or explore a sample screen. Do not fill a real account with invented personal history. Sample content belongs in a visibly labeled demo state; real users should see an honest empty state with a useful next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pfbptfsvnel1zzfwav9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4pfbptfsvnel1zzfwav9.png" alt="A fitness app builder reviewing a permission flow beside separate run and step summaries" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship a narrow history before a health-data platform
&lt;/h2&gt;

&lt;p&gt;A common trap is starting with a promise to aggregate every device and workout source. Each integration adds permission copy, data mapping, source-specific behavior, revocation handling, and support questions. Before doing that work, ship the part you can validate: a consistent activity list for the records your app creates itself.&lt;/p&gt;

&lt;p&gt;A practical sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log one type of workout and show it in a history.&lt;/li&gt;
&lt;li&gt;Add a second type with its own fields and display.&lt;/li&gt;
&lt;li&gt;Add daily steps as a separate summary, not as a fake workout.&lt;/li&gt;
&lt;li&gt;Let users filter the history by type and date.&lt;/li&gt;
&lt;li&gt;Add one external source and test imports, updates, duplicates, and revocation.&lt;/li&gt;
&lt;li&gt;Add cross-source summaries only when units and time windows are well defined.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At each step, ask whether the product's main screen answers a real question faster. If a “total activity” number cannot explain what it includes, remove it until the definition is clear. If a chart changes after a sync, tell the user which source contributed the new data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a starter for the app foundation, not for missing integrations
&lt;/h2&gt;

&lt;p&gt;The OTF Fitness Kit is aimed at builders making mobile fitness and wellness apps: its audience fits this problem more closely than a SaaS dashboard or an arcade-game starter. The latest outside-visitor funnel figures available for the last 30 days showed Fitness 6, SaaS Dashboard 5, Arcade 1, and landing templates 12 combined. These counts are small, so they are directional context rather than proof of demand; the audience match is the reason for the recommendation.&lt;/p&gt;

&lt;p&gt;The kit's live page shows workout tracking, activity rings, onboarding, sign-in, a Postgres-backed app, and Expo-based iOS, Android, and web output. It also states that health-data sync and payments are not included. That boundary matters for this article: a starter can reduce the work of building the app shell and workout experience, but you still need to implement and validate Health Connect or HealthKit access for your own product.&lt;/p&gt;

&lt;p&gt;If that is the work you are planning, &lt;a href="https://otf-kit.dev/templates/fitness-kit" rel="noopener noreferrer"&gt;review the Fitness Kit and its $99 scope&lt;/a&gt;, then &lt;a href="https://fitness-preview.otf-kit.dev" rel="noopener noreferrer"&gt;try the demo&lt;/a&gt; before deciding whether its existing screens are a useful base. For a broader build sequence, see &lt;a href="https://dev.to/blog/how-to-build-a-fitness-app-with-ai"&gt;how to build a fitness app with AI you actually own&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/health-and-fitness/health-connect/data-types" rel="noopener noreferrer"&gt;Android Health Connect data types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>reactnative</category>
      <category>kits</category>
      <category>crossplatform</category>
    </item>
    <item>
      <title>Filter TwelveLabs Jockey searches by metadata and creation time</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 08:18:53 +0000</pubDate>
      <link>https://dev.to/davekurian/filter-twelvelabs-jockey-searches-by-metadata-and-creation-time-ak2</link>
      <guid>https://dev.to/davekurian/filter-twelvelabs-jockey-searches-by-metadata-and-creation-time-ak2</guid>
      <description>&lt;h2&gt;
  
  
  A search can be relevant and still be too broad
&lt;/h2&gt;

&lt;p&gt;A video search that matches the right words can still return too much to use. If the same library holds customer interviews, tutorials, product demos, and a stream of newly uploaded clips, a single natural-language query may not answer the actual question: which relevant moments came from this project, in this date window, or in this category?&lt;/p&gt;

&lt;p&gt;TwelveLabs’ 15 October 2026 Jockey release adds knowledge-store search filters for &lt;code&gt;user_metadata&lt;/code&gt;, &lt;code&gt;enrichment&lt;/code&gt;, and &lt;code&gt;created_at&lt;/code&gt;. It also documents a hard edge: if the filter conditions match more than 10,000 items, search returns a 422 error with the code &lt;code&gt;metadata_filter_too_broad&lt;/code&gt;. The practical change is to design the search around a bounded set of items instead of relying on the text query alone. &lt;a href="https://beta.docs.twelvelabs.io/docs/get-started/release-notes" rel="noopener noreferrer"&gt;The release notes&lt;/a&gt; list the new fields and the error behavior.&lt;/p&gt;

&lt;p&gt;This is useful when you are building a product around a growing media library. A user looking for “the onboarding walkthrough” may also need to say “from the latest release” or “tagged for mobile.” The language query expresses what the clip is about; metadata and creation-time filters express which items are eligible to search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate relevance from eligibility
&lt;/h2&gt;

&lt;p&gt;Treat a search request as two decisions. First, define the eligible slice of the library: the user’s project, content type, category, or time window. Then send the natural-language query that ranks matches within that slice. These are different jobs. A better prompt can improve the meaning of a search, but it cannot replace an application’s access rules or identify which records belong to one customer.&lt;/p&gt;

&lt;p&gt;Before wiring a filter into the interface, write down what each field means in your own product. For example, a support-video library might store a project identifier and a content category as custom metadata, while &lt;code&gt;created_at&lt;/code&gt; can narrow to a period such as “added since the last release.” A training platform might use a course identifier and a lesson type. The exact metadata keys are your application’s design; the new search fields do not invent those values for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1e761ernlx0wr8u6ouy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1e761ernlx0wr8u6ouy.webp" alt="Byte and Luna use a metadata tag and a calendar-like time range to narrow video thumbnails into a small selection." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Combine custom metadata and creation-time criteria to scope the clips you want to search.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Keep the filter values in the same system of record as the item they describe. When your ingest flow adds a video, validate and store the metadata alongside the item. If a user changes a category later, decide whether that edit updates the searchable item metadata, the source asset metadata, or both. Those are separate concepts in the API. Avoid treating a display label in your frontend as proof that the backend indexed the corresponding value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design for the 10,000-match boundary
&lt;/h2&gt;

&lt;p&gt;The 422 &lt;code&gt;metadata_filter_too_broad&lt;/code&gt; response is a signal to make the eligible set more specific. Don’t respond by silently dropping the filter and searching the whole library: that can produce results that are irrelevant to the user or outside the intended workspace.&lt;/p&gt;

&lt;p&gt;Build the user experience around narrowing. Start with filters that correspond to values users understand, such as project, content type, or a date range. Show which filters are active, make them easy to remove, and keep the text query visible when the results refresh. If a request still crosses the matching-item threshold, explain that the selection is too broad and suggest one more constraint. The UI should preserve the user’s query while asking for a narrower scope.&lt;/p&gt;

&lt;p&gt;A useful fallback is a staged flow: choose a project or collection first, then search within it. If projects can still contain very large libraries, add a date window or a second metadata facet. Keep the error as a distinct application state so it does not appear as an empty result set. “No matches” and “the request matched too many items to run” call for different user actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4egxnd40j7w6v7dwdtw0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4egxnd40j7w6v7dwdtw0.webp" alt="Dex and Nova face a tall video archive with an over-broad search paused, then prepare metadata and time filters before retrying." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;More than 10,000 matching items triggers a 422 &lt;code&gt;metadata_filter_too_broad&lt;/code&gt; response; narrow filters before searching.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep pagination and filter changes predictable
&lt;/h2&gt;

&lt;p&gt;Search results can span multiple pages. When a user changes the query or any filter, treat it as a new search and clear the old page token. Reusing a token from a different query can make the interface confusing: the next page may belong to the prior search, or the token may no longer be valid. Preserve the active query and filters together, and only request another page for that exact search state.&lt;/p&gt;

&lt;p&gt;Also distinguish a result from a video item. Depending on the response grouping, results can represent matching clips or items with their matching clips. Make the UI label clear: a hit may be one moment in a longer video, not a separate uploaded file. When the user opens a hit, use the returned timing information to take them to the relevant part rather than treating every match as a full-video result.&lt;/p&gt;

&lt;p&gt;You can test this behavior without guessing at production scale. Create a small fixture library with a few known metadata values and creation times. Check that a query without filters can find the expected clips, then add one metadata constraint and a date boundary. Test the empty state, a changed filter, and a next-page action. Finally, exercise the broad-filter error path in your own integration tests or a controlled test dataset if you can reproduce the documented threshold safely. Don’t fabricate a successful example by claiming to have run a search you have not run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep tenant boundaries outside semantic search
&lt;/h2&gt;

&lt;p&gt;A user-facing search filter is not an authorization mechanism by itself. Enforce the current user’s project or tenant scope in trusted server-side code before sending a request to the provider. Do not accept an arbitrary project identifier from the browser and assume it is safe because it appears in &lt;code&gt;user_metadata&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This distinction matters when a product mixes private and shared videos. A metadata filter can help select items, while your application still needs to verify that the caller is permitted to request those items. Keep the provider credential on the server, validate the tenant and filter values, and return only results your application has authorized. Do not expose private API keys or rely on a hidden filter control as a security boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical implementation checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Decide which user decision each filter supports: project, category, owner, or creation window.&lt;/li&gt;
&lt;li&gt;Define and validate metadata at ingestion, using stable values rather than labels that may change.&lt;/li&gt;
&lt;li&gt;Build the provider request from trusted server-side scope plus the user’s allowed filters.&lt;/li&gt;
&lt;li&gt;Keep the natural-language query and the item filters separate in your application state.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;metadata_filter_too_broad&lt;/code&gt; as a request to narrow, not as an empty result or a reason to remove safeguards.&lt;/li&gt;
&lt;li&gt;Reset pagination whenever the query or filters change.&lt;/li&gt;
&lt;li&gt;Test clip-level versus item-level results and confirm the interface communicates which one it shows.&lt;/li&gt;
&lt;li&gt;Log the request shape and provider error code without logging secrets or private video content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Jockey’s new filters are most useful when the application has meaningful metadata and a clear policy for choosing it. If your ingestion process has no stable project, category, or time values, adding filter controls first will only expose that data-quality gap. Start with one workflow, decide how metadata enters and changes, then add controls that help the user narrow the search they actually need.&lt;/p&gt;

&lt;p&gt;If you are building the surrounding dashboard, OTF’s &lt;a href="https://otf-kit.dev/templates/saas-dashboard" rel="noopener noreferrer"&gt;SaaS Dashboard kit&lt;/a&gt; is an optional editable app shell with sign-in, projects, teams, tables, and analytics screens. The live page lists it at $99 one-time and describes repo access by email after checkout; the kit does not include a TwelveLabs integration. Review the live demo and &lt;a href="https://dev.to/pricing"&gt;current pricing&lt;/a&gt;, checked 15 October 2026, before deciding whether the shell fits your app. For a related example of presenting table controls, see &lt;a href="https://dev.to/blog/saas-dashboard-table-column-preferences"&gt;how to let SaaS users keep their preferred columns&lt;/a&gt;. If you want to explore an editable starting point, &lt;a href="https://dev.to/start"&gt;start here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://beta.docs.twelvelabs.io/docs/get-started/release-notes" rel="noopener noreferrer"&gt;TwelveLabs v1.3 release notes&lt;/a&gt; — Jockey availability, knowledge-store search filters, the 10,000-match threshold, and the &lt;code&gt;metadata_filter_too_broad&lt;/code&gt; response; checked 15 October 2026.&lt;/li&gt;
&lt;li&gt;OTF &lt;a href="https://dev.to/templates/saas-dashboard"&gt;SaaS Dashboard kit&lt;/a&gt; and &lt;a href="https://dev.to/pricing"&gt;pricing&lt;/a&gt; — current kit contents, $99 price, and repo-access flow; checked 15 October 2026.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aitools</category>
      <category>backend</category>
      <category>kits</category>
    </item>
    <item>
      <title>Keep Gemini API keys out of Google AI Studio Android app builds</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 06:58:34 +0000</pubDate>
      <link>https://dev.to/davekurian/keep-gemini-api-keys-out-of-google-ai-studio-android-app-builds-2ki1</link>
      <guid>https://dev.to/davekurian/keep-gemini-api-keys-out-of-google-ai-studio-android-app-builds-2ki1</guid>
      <description>&lt;p&gt;An Android API key inside an APK is not private. Anyone who can download the app can inspect the package, recover embedded strings, and try the key elsewhere. Google AI Studio’s Firebase integration gives one specific path for AI-built Android apps: route Gemini requests through Firebase AI Logic and require Firebase App Check to verify the calling app.&lt;/p&gt;

&lt;p&gt;The important qualifier is “built in Google AI Studio.” The automatic wiring described here is not a universal setup for every Android IDE, framework, or existing app. If you are using another toolchain, you still need to configure Firebase AI Logic and App Check for that project yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwec674et6t8jgt5ealm.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwec674et6t8jgt5ealm.webp" alt="Byte and Dex inspect a blank phone request and matching app card passing through an inspection gate; a separate brass provider key stays behind glass in the relay cabinet." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual view of App Check verifying the app before requests reach Firebase AI Logic; it is an added protection layer, not a guarantee against every attack.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Google AI Studio wires up
&lt;/h2&gt;

&lt;p&gt;When you enable the Firebase integration for an Android app with generative AI features, Google AI Studio can add Firebase AI Logic and enforce Firebase App Check. Firebase AI Logic sends model requests through a Firebase proxy. With the Gemini Developer API provider, that proxy keeps the provider key on the server instead of embedding it in the APK. App Check adds an attestation check so Firebase can reject requests that do not come from a verified app instance.&lt;/p&gt;

&lt;p&gt;This changes the threat model. You are no longer treating a string shipped in a downloadable binary as a secret. Instead, the app calls a service that can verify requests before forwarding them. App Check helps protect the project and quota from unauthorized clients; it does not make a client app impossible to inspect, replace authentication or authorization, or guarantee that every request is benign.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup path in Google AI Studio
&lt;/h2&gt;

&lt;p&gt;Start by asking the AI Studio Build agent to add Firebase AI Logic to the Android app, then enable Firebase in the integration card. You can choose an existing Google Cloud project that you own or use a project created for the app. If you select an existing project, check that it meets the integration’s requirements before asking the agent to provision services.&lt;/p&gt;

&lt;p&gt;For Android generative-AI apps, the integration can configure Firebase AI Logic, Firebase App Check, and the release attestation path. The generated project files and Firebase console settings are part of the implementation, so inspect them in the repository and in the console instead of treating a successful generation message as proof that the production path is correct.&lt;/p&gt;

&lt;p&gt;Use a short validation sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the app is calling Gemini through Firebase AI Logic rather than a direct provider endpoint.&lt;/li&gt;
&lt;li&gt;Check that App Check enforcement is enabled for Firebase AI Logic in the intended Firebase project.&lt;/li&gt;
&lt;li&gt;Run a debug build in the AI Studio emulator or on a test phone and make a real model request.&lt;/li&gt;
&lt;li&gt;Build a release variant and verify the production attestation path before distributing it through Google Play.&lt;/li&gt;
&lt;li&gt;Review API usage, quotas, and the generated security configuration in the Firebase console.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That sequence keeps three questions separate: did the app connect, did Firebase verify it, and did the release configuration behave as expected? A generated code diff answers only the first part until you test the other two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug tokens and Google Play releases are different
&lt;/h2&gt;

&lt;p&gt;During development and trusted testing, Google AI Studio manages an App Check debug token for the app. That token is intended for debug builds: it lets the emulator, a USB-installed test build, or trusted collaborators make requests without publishing a production attestation configuration first.&lt;/p&gt;

&lt;p&gt;Treat a debug token as a development credential. Do not ship it in a production build or paste it into a public issue. Keep debug and release variants easy to distinguish, and confirm that the release artifact is not using the debug provider.&lt;/p&gt;

&lt;p&gt;For a release build intended for Google Play, the current AI Studio integration documentation says the app switches to reCAPTCHA Enterprise using a preconfigured site key. That behavior is specific to this guided AI Studio flow. Firebase’s general Android App Check documentation also describes other provider configurations, including Play Integrity, but do not assume that the AI Studio build path and a hand-configured Android app use the same defaults. Follow the provider shown for the exact path you are shipping.&lt;/p&gt;

&lt;p&gt;Test the release build as a release build. If it only works while the debug provider is active, you have tested your development setup, not the production attestation flow. Check the Firebase console’s App Check request metrics and enforcement state, and make sure the app is registered in the same project that the generated code uses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0m5imvldvq1xkv2wqt1x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0m5imvldvq1xkv2wqt1x.webp" alt="Luna and Nova compare a test phone with a removable debug-token tag to a separate release package passing through an attestation scanner." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The guided Google AI Studio Build-mode flow separates debug-token testing from production release attestation; its official guide specifies reCAPTCHA Enterprise for the Google Play release path.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  App Check is one layer, not the whole security design
&lt;/h2&gt;

&lt;p&gt;App Check helps establish that a request comes from an attested app or environment. It does not decide which signed-in person may access a record, and it does not validate every action your app exposes. Keep Firebase Authentication and database security rules aligned with the data users should be able to read or change. Review AI-generated rules by hand and test both allowed and denied cases before release.&lt;/p&gt;

&lt;p&gt;Also plan for quota and abuse controls. App Check makes unauthorized requests harder; it is not a substitute for monitoring usage, setting project limits, or designing the feature so a user cannot trigger unlimited expensive work. Watch request metrics after enabling enforcement. For an existing released app, moving directly to enforcement can block older app versions that do not send valid App Check tokens, so stage that rollout deliberately.&lt;/p&gt;

&lt;p&gt;New AI Studio Android projects should test with enforcement from the start. If you already have a released app, review its App Check metrics and version coverage before changing enforcement so older clients do not fail unexpectedly.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this integration fits
&lt;/h2&gt;

&lt;p&gt;This route fits a narrow case: you are building an Android app in Google AI Studio Build mode, the app calls Gemini, and you want to avoid embedding the provider key in a downloadable APK. It is especially useful when the app is still early enough to validate the generated Firebase project, request path, and release configuration before users depend on it.&lt;/p&gt;

&lt;p&gt;It is not a drop-in security layer for every Android app. A project built in another environment needs its own Firebase AI Logic and App Check setup. An app that needs custom server-side policy, provider routing, or application-specific request validation may still need a backend designed for those jobs. Keep the mechanism matched to the threat model rather than treating “App Check enabled” as a complete security review.&lt;/p&gt;

&lt;p&gt;If you want a separate mobile app shell to extend, OTF’s Fitness Kit is an optional Expo and React Native starter with Android and iOS screens, sign-in, and a Postgres database. It costs $99 one-time as checked on October 15, 2026. Firebase AI Logic, Gemini, and App Check are not included; you would add and verify that integration yourself. See the &lt;a href="https://otf-kit.dev/templates/fitness-kit" rel="noopener noreferrer"&gt;Fitness Kit&lt;/a&gt; and &lt;a href="https://otf-kit.dev/pricing" rel="noopener noreferrer"&gt;current pricing&lt;/a&gt; before deciding whether that starting point fits your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  A release checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping, verify the Google AI Studio integration actually generated the expected Firebase AI Logic and App Check configuration. Confirm the provider key does not appear in the app bundle, while remembering that the Firebase project key in configuration is an identifier rather than an authorization secret. Test debug builds with the managed debug token, then test the release build using the production attestation path. Review generated Firebase Security Rules, confirm enforcement and metrics in the intended project, and keep authentication, authorization, quotas, and abuse monitoring in scope.&lt;/p&gt;

&lt;p&gt;The value of the integration is a shorter route to a safer default for one clearly defined workflow. It does not remove the need to inspect the generated app or test the exact artifact you plan to distribute. For the broader question of how to carry an AI-built app into a maintainable repository, see &lt;a href="https://otf-kit.dev/blog/the-first-prompt-in-a-new-repo" rel="noopener noreferrer"&gt;the first-prompt-in-a-new-repo guide&lt;/a&gt;, then &lt;a href="https://otf-kit.dev/start" rel="noopener noreferrer"&gt;start with an editable kit&lt;/a&gt; if you want a codebase to extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Google AI Studio integration with Firebase, checked October 15, 2026: &lt;a href="https://firebase.google.com/docs/ai-assistance/ai-studio-integration" rel="noopener noreferrer"&gt;https://firebase.google.com/docs/ai-assistance/ai-studio-integration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OTF Fitness Kit and pricing, checked October 15, 2026: &lt;a href="https://otf-kit.dev/templates/fitness-kit" rel="noopener noreferrer"&gt;https://otf-kit.dev/templates/fitness-kit&lt;/a&gt; and &lt;a href="https://otf-kit.dev/pricing" rel="noopener noreferrer"&gt;https://otf-kit.dev/pricing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aitools</category>
      <category>backend</category>
      <category>announcement</category>
    </item>
    <item>
      <title>Build a React dashboard where agents and users share actions</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 05:26:21 +0000</pubDate>
      <link>https://dev.to/davekurian/build-a-react-dashboard-where-agents-and-users-share-actions-1k31</link>
      <guid>https://dev.to/davekurian/build-a-react-dashboard-where-agents-and-users-share-actions-1k31</guid>
      <description>&lt;h2&gt;
  
  
  A dashboard can give an agent more than a chat box
&lt;/h2&gt;

&lt;p&gt;A SaaS dashboard usually gives a person a list, filters, forms, and buttons. Adding an agent often means adding a chat panel beside that interface. The panel can answer questions, but the product still has two ways to act: the human uses the application, while the agent may be handed a separate collection of tools or a browser to click through.&lt;/p&gt;

&lt;p&gt;Builder.io’s Agent-Native beta proposes a different shape for this problem. It is an open-source TypeScript framework for agent applications, and Builder says the app and agent share actions, data, and application state. A capability can be exposed across the UI, agent, HTTP API, CLI, MCP, and A2A surfaces. That makes the launch relevant to teams building a React SaaS dashboard: it gives them a concrete architecture to evaluate when they want an agent to act on the same records and workflow a user sees.&lt;/p&gt;

&lt;p&gt;The important question is not whether an agent can click a dashboard. It is whether the application has a small, deliberate set of operations that both the UI and the agent can call, with the same validation and permission checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “shared actions” changes
&lt;/h2&gt;

&lt;p&gt;Suppose a support dashboard has a case list. A user opens a case, changes its priority, and assigns it to a teammate. A separate agent integration might expose a tool called &lt;code&gt;update_case&lt;/code&gt;, while the React screen has its own form handler. If those paths have different rules, the same request can behave differently depending on who initiated it. A new validation rule may be applied in the UI and forgotten in the agent tool, or the agent may write fields the screen never allows a person to change.&lt;/p&gt;

&lt;p&gt;The Agent-Native model described by Builder puts the capability behind one action implementation. The agent calls that action as a tool; the UI calls it from application code. The launch says both paths go through the same validation, permissions, and implementation. In principle, that gives a team one place to reason about the operation instead of maintaining parallel behavior.&lt;/p&gt;

&lt;p&gt;“One action” does not mean “one unrestricted endpoint.” A well-designed action still needs a narrow input schema, identity, authorization, tenant scope, and an explicit list of fields it can change. For example, an assignment action might accept a case ID and a teammate ID, then check that the caller belongs to the workspace and that the teammate is eligible. It should not accept a raw SQL string or a client-supplied workspace ID as proof of access. The server must derive or verify the caller’s authority.&lt;/p&gt;

&lt;p&gt;This matters most for operations with consequences: changing account settings, issuing a refund, publishing content, or closing a customer case. A shared action can reduce duplicated logic, but it cannot decide which actions should require confirmation, which roles can perform them, or what should be logged. Those remain product and security decisions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftc92ezwibdrl0pcr9ei0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftc92ezwibdrl0pcr9ei0.webp" alt="Byte and Nova direct a UI action and an agent request through one shared approval checkpoint before a dashboard record changes." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual view of two callers reaching the same validation and permission boundary before a shared action changes data.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared data and selected UI state provide context
&lt;/h2&gt;

&lt;p&gt;The second part of the proposal is shared data. Builder says work performed by the agent appears in the UI, and changes made in the UI are available to the agent. That avoids building a separate “agent memory” copy of a record just to keep a chat conversation informed. The source of truth still needs normal database constraints, transaction handling, and conflict rules; sharing the same data model does not remove those responsibilities.&lt;/p&gt;

&lt;p&gt;The third part is application state. Builder’s launch gives examples such as the current page, selected record, or active view. That can make a short instruction like “summarize this” refer to the record already open on screen. Without that context, the agent may need to ask the user to paste an ID or repeat which item they mean.&lt;/p&gt;

&lt;p&gt;Treat UI context as a convenience, not authorization. A selected row tells the agent what the user is discussing; it does not prove that the user may read or modify that row. The server action should re-check access on every call, including calls made after the agent has been idle or the page selection has changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjrpjf8c8o27ed7usuokt.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjrpjf8c8o27ed7usuokt.webp" alt="Luna selects a dashboard record that remains visible while Nova receives its context; a separate locked control represents authorization." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual illustration of selected-record context reaching an agent while authorization remains a separate check.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical evaluation path for a React SaaS team
&lt;/h2&gt;

&lt;p&gt;Start with one low-risk workflow that already has a clear UI operation. A good pilot is “summarize the selected case and propose a priority change,” where the user remains the final approver. Avoid beginning with bulk deletion, financial actions, or a wide-open workspace assistant. The pilot should test the shared-action idea, not maximize autonomy.&lt;/p&gt;

&lt;p&gt;First, write down the operation as a contract. Specify the fields it reads, the fields it may change, who can call it, which records are in scope, and what a successful result looks like. Add invalid and unauthorized cases before connecting the agent. This gives the UI and agent the same behavior to exercise.&lt;/p&gt;

&lt;p&gt;Next, expose the operation to the existing React screen and confirm that the normal user path still works. Then connect the agent to that same operation. Ask it to perform a task using the selected record, and inspect the exact structured inputs it submits. Test missing context, stale selection, malformed inputs, a user from another tenant, a user without the required role, and a retry after a timeout. A fluent chat answer is not evidence that the action boundary is correct.&lt;/p&gt;

&lt;p&gt;Keep the first agent response in a proposal or preview state. Show the user the target record and proposed change, then require an explicit confirmation before committing a consequential update. Record the actor, selected workspace, action name, inputs after validation, result, and whether the user approved it. Those controls make it possible to investigate surprising behavior later.&lt;/p&gt;

&lt;p&gt;Finally, compare maintenance costs rather than only demo speed. Are the UI and agent truly invoking the same implementation? Do tests cover both entry points? Can a developer trace which rule rejected an action? Can the team revoke an agent capability without breaking the human interface? If the answers are unclear, shared abstractions may only hide two separate systems behind one label.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to verify before adopting a beta framework
&lt;/h2&gt;

&lt;p&gt;Builder announced Agent-Native as a beta on October 14, 2026. Treat the framework as something to evaluate, not a guarantee that every production concern is solved. Read the current documentation, inspect the repository and examples, and verify the parts your product depends on: framework and runtime compatibility, data migrations, session and tenant boundaries, deployment behavior, logging, upgrades, and support for your chosen model and database.&lt;/p&gt;

&lt;p&gt;The launch describes integrations across several surfaces, including MCP and A2A. Each additional surface expands the places from which an operation can be invoked. Make a capability inventory and enable only what the product needs. An action intended for the in-app agent should not automatically become available to every external client merely because the framework can expose it there.&lt;/p&gt;

&lt;p&gt;Also separate model quality from application safety. A model may choose the wrong action or produce unsuitable arguments. Schema validation catches malformed inputs; authorization checks prevent callers from exceeding their access; business rules prevent invalid transitions; and confirmation gates put a person in control of sensitive changes. No single layer replaces the others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where an OTF kit fits
&lt;/h2&gt;

&lt;p&gt;If your team wants a starting point for the surrounding SaaS interface, the &lt;a href="https://otf-kit.dev/templates/saas-dashboard" rel="noopener noreferrer"&gt;SaaS Dashboard kit&lt;/a&gt; is an optional editable app shell priced at $99. It is not an Agent-Native integration and does not include this framework setup. Treat it as a separate UI foundation to inspect, not as a shortcut that removes the integration work described above. Check the live demo and current terms before deciding whether its structure fits your app.&lt;/p&gt;

&lt;p&gt;For a related but different problem, see &lt;a href="https://otf-kit.dev/blog/make-your-website-agent-readable" rel="noopener noreferrer"&gt;how to make a website readable to AI agents&lt;/a&gt;. That article is about helping an agent understand a website; this one is about sharing application actions and state between an agent and a React product UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.builder.io/blog/introducing-agent-native" rel="noopener noreferrer"&gt;Introducing Agent-Native Beta: The Framework for Agentic Apps&lt;/a&gt; — Builder.io, October 14, 2026. Launch scope, beta status, shared actions/data/state, supported surfaces, and local development claims were checked against the vendor announcement.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>architecture</category>
      <category>templates</category>
    </item>
    <item>
      <title>Add S3 uploads to Vercel apps without storing long-lived AWS keys</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 03:51:14 +0000</pubDate>
      <link>https://dev.to/davekurian/add-s3-uploads-to-vercel-apps-without-storing-long-lived-aws-keys-5ggg</link>
      <guid>https://dev.to/davekurian/add-s3-uploads-to-vercel-apps-without-storing-long-lived-aws-keys-5ggg</guid>
      <description>&lt;p&gt;A Vercel app can accept a file upload and store the object in Amazon S3 without putting a long-lived AWS access key in source code or deployment variables. Vercel’s Amazon S3 Marketplace integration now connects an S3 resource to a Vercel project, supplies connection details as environment variables, and authenticates with short-lived OIDC credentials. The practical work remains yours: authorize the uploader, choose the object key, validate the file, and decide who can read it.&lt;/p&gt;

&lt;p&gt;This guide walks through the setup boundary, the server-side upload path, and the security decisions that still matter after credentials are handled. It is for a team adding file uploads to a Vercel-hosted application, especially one that already uses AWS and wants its storage flow near the project configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the integration for the workflow you have
&lt;/h2&gt;

&lt;p&gt;Amazon S3 and Vercel Blob solve overlapping but different setup problems. Vercel Blob is a storage service for files in a Vercel app, with Vercel-managed delivery features. The S3 Marketplace integration is a fit when the application already depends on AWS, when the team wants S3 resources visible beside its Vercel project, or when its storage workflow uses S3-specific services such as S3 Tables or S3 Vectors.&lt;/p&gt;

&lt;p&gt;The Marketplace path keeps project setup together: create or connect the resource in the Vercel dashboard, then use the values made available to the project. A linked AWS account has an important constraint: the Vercel flow supports buckets created through Vercel, rather than attaching an arbitrary pre-existing bucket. If an application already writes to a bucket that was created elsewhere, check the current setup path before planning a migration. Do not assume “connect an AWS account” means every bucket in that account is selectable.&lt;/p&gt;

&lt;p&gt;The integration also does not remove the need to understand AWS permissions. OIDC changes how the application obtains credentials; the IAM role still determines which AWS actions those credentials can perform. Treat the role policy, project environment, and upload route as separate controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect the S3 resource to the project
&lt;/h2&gt;

&lt;p&gt;Start in the Vercel Marketplace from the project that will run the upload route. Create an S3 resource through the guided flow or link the AWS account you intend to use. Review the environment variables that Vercel adds and the project environments to which the connection applies. Keep production and preview behavior deliberate; a preview deployment should not automatically inherit broader access than it needs.&lt;/p&gt;

&lt;p&gt;Vercel’s integration uses short-lived OIDC credentials instead of storing static AWS keys in the application. That means you do not paste an IAM user’s long-lived access key into an environment variable or commit it in a local file. The application still needs ordinary configuration such as the bucket name and region, and AWS still needs a role with a trust relationship for the intended Vercel project and environment.&lt;/p&gt;

&lt;p&gt;For new integrations, inspect the connection details and the resulting IAM access before sending real user files. Confirm the role can write only to the bucket and key prefix the application requires. If a function only stores user uploads, it should not receive broad permissions to list every bucket or alter bucket policy. Keep any administrative bucket operations outside the request path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a server-side upload route
&lt;/h2&gt;

&lt;p&gt;The Vercel changelog’s example uses &lt;code&gt;createS3&lt;/code&gt; from &lt;code&gt;@vercel/aws&lt;/code&gt;, then sends an AWS SDK &lt;code&gt;PutObjectCommand&lt;/code&gt; from a route handler. The outline is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/s3.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createS3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@vercel/aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createS3&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/upload/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// First authenticate the user and verify upload permission.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A file is required.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Apply application-specific size and content checks here.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`uploads/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;objectKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9b4e37bplj8w43jeesx.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa9b4e37bplj8w43jeesx.webp" alt="Dex, Byte, and Nova look into a linked cloud account where existing storage buckets remain untouched beside one new bucket receiving an image." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An existing AWS account can be linked; buckets provisioned through the Vercel connection are new buckets. The older buckets shown remain untouched.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a route shape, not a complete production policy. The example deliberately leaves authentication and validation as explicit steps because they depend on the application. A successful AWS write does not prove that the requester is allowed to upload for the account identified by the URL or form.&lt;/p&gt;

&lt;p&gt;The object key should be generated on the server. Do not accept a bucket name or unrestricted key from the browser: a user could otherwise choose another tenant’s path or overwrite an object the app treats as authoritative. Use an opaque identifier for the storage key and save the original filename as display metadata after normalizing it. Scope each upload to the authenticated user or tenant, and ensure the IAM policy and application checks agree about the allowed prefix.&lt;/p&gt;

&lt;p&gt;Set upload limits before reading an entire file into memory. Check the declared MIME type, but do not trust it as proof of file contents; inspect or decode the bytes when the file type matters. Restrict accepted formats, cap size, and rate-limit requests. For larger uploads, design a multipart or direct-to-storage flow separately, including how a client receives narrowly scoped authorization and how unfinished uploads are cleaned up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide who can read each object
&lt;/h2&gt;

&lt;p&gt;Storage access is a product decision. A profile image meant to be public and an invoice or user export should not share the same read path. Keep private objects behind application authorization, and return a short-lived download mechanism only after checking the caller’s access. If a file is intentionally public, make that choice explicit and avoid placing sensitive content in that bucket or prefix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw02hkviz16urdg4xyq7l.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw02hkviz16urdg4xyq7l.webp" alt="Byte and Luna examine a short-lived credential on an app server upload path to a new bucket while a permanent key remains sealed behind glass; Nova hovers overhead." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Marketplace S3 connection uses short-lived OIDC credentials rather than static AWS keys for the upload flow; the scene is conceptual, not a Vercel or OTF integration screen.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do not rely on an unguessable object key as the authorization rule. Random identifiers reduce accidental discovery, but the app still needs to check ownership before returning a download URL or proxying bytes. Consider whether a user can replace a file, whether each replacement should create a new version, and how deletion requests propagate to S3 and the app’s metadata store.&lt;/p&gt;

&lt;p&gt;Think through consistency as well. An upload can succeed in S3 while a database write fails, or the reverse if the application records an object before storage confirms it. Store the object key and upload state in a way that supports retries and cleanup. Make the route safe to retry where possible, and avoid returning a success response until the application has enough proof to show the file to the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the full path before opening it to users
&lt;/h2&gt;

&lt;p&gt;Use a small, non-sensitive file in a development or preview environment. Verify the route rejects an unauthenticated request, a missing file, an oversized file, and a file type the product does not accept. Then upload an allowed file and confirm that the generated key uses the intended tenant prefix and that an authorized user can retrieve it.&lt;/p&gt;

&lt;p&gt;Test the negative cases, too: a user from another tenant should not be able to overwrite or read the object; a preview project should not gain production access by accident; and a failed metadata write should not leave a misleading success state. Check the Vercel function logs and AWS-side audit trail without logging file bytes, user credentials, or temporary tokens.&lt;/p&gt;

&lt;p&gt;Finally, test the deployment environments that matter. A route that works locally can still fail if the project is not connected to the S3 resource in Preview or Production, if the bucket region is wrong, or if the AWS role trust policy does not match the project and environment. Keep a short runbook with the resource owner, bucket and region, allowed prefixes, expected environment variables, and a safe way to rotate or narrow permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where an app template fits
&lt;/h2&gt;

&lt;p&gt;Adding S3 is a storage integration task; it does not create the rest of the product around it. If the same team also needs an editable SaaS application shell, the &lt;a href="https://dev.to/templates/saas-dashboard"&gt;OTF SaaS Dashboard template&lt;/a&gt; is an optional $99 starting point with a Vite and Hono app, sign-in, projects, teams, and sample analytics screens. It does not include Amazon S3 or configure this Marketplace integration. Keep the storage connection in the application’s own Vercel and AWS setup.&lt;/p&gt;

&lt;p&gt;For an example of how a small Hono backend can be deployed on Vercel’s alternative serverless platform, see our guide to &lt;a href="https://dev.to/blog/aws-lambda-web-functions-hono"&gt;running a Hono backend on AWS Lambda&lt;/a&gt;. Use that as a separate deployment discussion; it is not a prerequisite for the Vercel Marketplace path described here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://vercel.com/changelog/amazon-s3-joins-the-vercel-marketplace" rel="noopener noreferrer"&gt;Amazon S3 joins the Vercel Marketplace&lt;/a&gt; — October 14, 2026. Marketplace setup, environment variables, OIDC credentials, upload example, linked-account limits, and S3 versus Blob use cases.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/templates/saas-dashboard"&gt;SaaS Dashboard template&lt;/a&gt; and &lt;a href="https://dev.to/pricing"&gt;pricing&lt;/a&gt; — checked October 15, 2026. The kit is $99 and does not include this S3 integration.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vercel</category>
      <category>backend</category>
      <category>kits</category>
    </item>
    <item>
      <title>Lock GitHub Actions dependencies to reviewed commits with workflow lockfiles</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Thu, 15 Oct 2026 03:28:11 +0000</pubDate>
      <link>https://dev.to/davekurian/lock-github-actions-dependencies-to-reviewed-commits-with-workflow-lockfiles-3h7g</link>
      <guid>https://dev.to/davekurian/lock-github-actions-dependencies-to-reviewed-commits-with-workflow-lockfiles-3h7g</guid>
      <description>&lt;h2&gt;
  
  
  A moving tag is not a review
&lt;/h2&gt;

&lt;p&gt;Your workflow can keep saying &lt;code&gt;uses: vendor/build-action@v3&lt;/code&gt; while the code behind &lt;code&gt;v3&lt;/code&gt; changes. That leaves a gap between the commit your team reviewed and the commit a later workflow run actually executes. Pinning every action by hand to a full commit SHA narrows that gap, but it is easy to miss transitive dependencies and reusable workflow calls.&lt;/p&gt;

&lt;p&gt;GitHub Actions workflow dependency lockfiles are now in public preview for Free, Team, and eligible Enterprise Cloud plans. The GitHub CLI extension &lt;code&gt;gh actions-lock&lt;/code&gt; generates &lt;code&gt;.github/workflows/actions.lock&lt;/code&gt;; GitHub says an onboarded workflow must have its dependencies represented there, and runs use the recorded commits even if a tag or branch moves. The file records direct actions, transitive action dependencies, and reusable-workflow dependencies. GitHub’s October 14 announcement also describes gradual onboarding, stale-lock checks, policy enforcement, and Dependabot updates. The preview is useful for teams whose review process already depends on knowing exactly what automation code will run, but a lockfile does not tell you whether the locked code is trustworthy. &lt;a href="https://github.blog/changelog/2026-10-14-github-actions-workflow-lockfiles-in-public-preview/" rel="noopener noreferrer"&gt;GitHub’s announcement&lt;/a&gt; explains the current preview scope and behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the lock changes
&lt;/h2&gt;

&lt;p&gt;A workflow reference such as &lt;code&gt;owner/action@v3&lt;/code&gt; is readable, but a tag is a name that can be moved. A full commit SHA in the workflow pins that one reference directly. The lockfile takes a different approach: your workflow can retain a human-meaningful ref while the generated lock records the exact commit to use. For an onboarded workflow, GitHub requires the needed lock entries and executes the locked commit.&lt;/p&gt;

&lt;p&gt;That is most valuable when a workflow uses more than one action or when actions themselves call other actions. The lockfile captures those transitive dependencies too, so your review can see the resolved set rather than relying on each top-level action tag. Reusable workflows need attention as well: a reusable workflow is covered by the lockfile in the repository where it is defined. Locking the caller’s repository does not make a separate reusable-workflow repository’s dependencies disappear from your inventory.&lt;/p&gt;

&lt;p&gt;The lockfile is generated by &lt;code&gt;gh actions-lock&lt;/code&gt; and should not be edited manually. Its workflow section connects onboarded workflow files to their dependencies; its dependency section records the reference, resolved commit, and repository identity. That gives reviewers a change they can inspect when a ref moves or an action version is deliberately updated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbva8xpxmgk62qx8uwdnw.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbva8xpxmgk62qx8uwdnw.webp" alt="Byte and Dex compare a moving action reference between two commit tiles with one resolved commit recorded in a generated lock ledger." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The lock records which commit a workflow dependency resolves to; it does not assess whether the action is safe.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adopt it in a small slice first
&lt;/h2&gt;

&lt;p&gt;Do not start by turning on a repository-wide requirement before you know what your workflows depend on. Begin with one workflow that runs on pull requests or deploys a non-production environment. Inventory the &lt;code&gt;uses:&lt;/code&gt; references in that file, including reusable workflows, then install the GitHub CLI extension and generate the lockfile using the official getting-started instructions.&lt;/p&gt;

&lt;p&gt;Review the generated diff alongside the workflow changes. Confirm that every resolved repository is one you intended to use and that the commit corresponds to the version you meant to adopt. Treat the lockfile as generated source: change the workflow reference or update through the tool, then review the resulting lockfile diff. Avoid hand-editing a commit field to make a check pass. A manually altered lock entry can no longer be trusted to match what the generator resolved.&lt;/p&gt;

&lt;p&gt;Onboard workflows gradually. A repository with several pipelines can start with the highest-impact path, such as release or deployment automation, and add other workflows after the team understands the update process. GitHub’s preview requires dependencies for an onboarded workflow to be present in its lockfile. This means onboarding is a meaningful boundary: once a workflow is covered, missing entries should fail visibly rather than falling back to a floating tag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftsszdght7ee6akbznb1f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftsszdght7ee6akbznb1f.webp" alt="Byte and Luna review an incoming dependency update beside one workflow represented in a generated lock record, while three other workflow folders remain outside it." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Start with one workflow, check for stale lock entries, and review dependency updates as code; lockfiles do not replace that review.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the lock current without surprise changes
&lt;/h2&gt;

&lt;p&gt;A lockfile is only useful if the team can tell when it has gone stale. The extension can check whether a lockfile is current without modifying it, which makes that check suitable for CI. Run the check in a lightweight workflow and make its failure actionable: the owner should regenerate the lock, review which references changed, and commit the update with the corresponding workflow change.&lt;/p&gt;

&lt;p&gt;Dependabot can keep an onboarded action update and its matching lock entry in the same pull request. That helps avoid a split state where the workflow asks for one version but the lock still resolves another. Still review the update as code. A Dependabot PR tells you what changed; it does not substitute for deciding whether the new action release belongs in your build pipeline.&lt;/p&gt;

&lt;p&gt;If you maintain a reusable workflow, keep its lockfile and update process in the repository that defines it. Callers can then consume the reusable workflow without pretending that their own lockfile governs the dependencies inside somebody else’s repository. Document who owns that upstream lock and how callers learn about changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locking is not a security review
&lt;/h2&gt;

&lt;p&gt;The most important limit is also the easiest to blur in a security announcement: a lock makes the executed commit predictable; it does not make that commit safe. It does not assess the action’s code, the maintainer’s practices, the permissions granted to &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;, the secrets available to a job, or the trustworthiness of a pull request. If you lock a malicious or vulnerable commit, you have made its execution repeatable, not acceptable.&lt;/p&gt;

&lt;p&gt;Keep the rest of your supply-chain controls. Review third-party actions before adoption, grant workflows the smallest permissions they need, keep secrets out of untrusted pull-request contexts, and investigate ownership or repository changes. These measures answer different questions. A lockfile answers “which commit did this workflow execute?” It does not answer “should this code be allowed to run?”&lt;/p&gt;

&lt;p&gt;Because the feature is a public preview, check the current GitHub documentation before making it a required repository policy. Start with a low-risk workflow, learn how lock updates appear in pull requests, and only then decide whether to expand onboarding or apply an organization-wide requirement. If the preview changes, your rollout should be small enough that you can adapt without blocking every delivery pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where an app template fits
&lt;/h2&gt;

&lt;p&gt;Lockfiles govern GitHub Actions dependencies; they do not create the application those workflows build or deploy. If your team is separately building a SaaS product, OTF’s SaaS Dashboard is an optional editable Vite and Hono application foundation with sign-in, projects, teams, and sample analytics screens. It costs $99 once and includes 12 months of updates. It does not include GitHub Actions workflow lockfiles or configure this feature for your repository. Use it only if an editable app shell is useful alongside your own CI setup.&lt;/p&gt;

&lt;p&gt;For another practical way to keep repository automation reviewable, see our guide to &lt;a href="https://dev.to/blog/github-app-least-privilege-repository-creation"&gt;building a GitHub App with least-privilege repository creation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.blog/changelog/2026-10-14-github-actions-workflow-lockfiles-in-public-preview/" rel="noopener noreferrer"&gt;GitHub Actions workflow lockfiles in public preview&lt;/a&gt; — October 14, 2026. Includes preview availability, lock behavior, gradual onboarding, stale-lock checks, policy enforcement, and Dependabot updates.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aitools</category>
      <category>architecture</category>
      <category>kits</category>
    </item>
    <item>
      <title>GitHub App least privilege for SaaS repo creation and access</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Wed, 14 Oct 2026 14:26:24 +0000</pubDate>
      <link>https://dev.to/davekurian/github-app-least-privilege-for-saas-repo-creation-and-access-2750</link>
      <guid>https://dev.to/davekurian/github-app-least-privilege-for-saas-repo-creation-and-access-2750</guid>
      <description>&lt;p&gt;A SaaS product may need to create a customer repository during onboarding, or add an existing repository to an already installed GitHub App. Those are legitimate product workflows, but asking a customer for a broad organization personal access token can make the integration difficult to approve and hard to constrain.&lt;/p&gt;

&lt;p&gt;GitHub announced two fine-grained permissions for both GitHub Apps and fine-grained personal access tokens: &lt;code&gt;repository_creation:write&lt;/code&gt; for creating a repository in the account where the app or token has access, and &lt;code&gt;installation_repositories:write&lt;/code&gt; for adding or removing repositories from an installation. GitHub says repository creation no longer needs the broader &lt;code&gt;repository_administration:write&lt;/code&gt;, which can also allow deletion and transfer. The announcement is the &lt;a href="https://github.blog/changelog/2026-10-13-github-app-improvements-for-least-privilege-and-management/" rel="noopener noreferrer"&gt;GitHub App least-privilege update&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a SaaS builder, the practical change is to match each operation to the smallest permission and approval path it needs. Separate “make a new repo” from “manage which repos an installation can access.” Then explain the remaining organization approval requirements plainly. Narrower permissions reduce unnecessary access; they do not remove GitHub’s installation or organization controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the customer action
&lt;/h2&gt;

&lt;p&gt;Write down the operation in customer language before selecting permissions. “Connect GitHub” is too broad to guide a request. Ask whether the user wants to create a new repository, attach an existing repository to an installed app, or both.&lt;/p&gt;

&lt;p&gt;For repository creation, the app needs the new &lt;code&gt;repository_creation:write&lt;/code&gt; permission. GitHub also makes the new permission available to fine-grained personal access tokens, though a SaaS integration should use the credential model appropriate to its product and customer workflow. GitHub’s announcement says this permission can create a repository in an account where the app or token is granted access. It also explains that the new permission avoids requesting &lt;code&gt;repository_administration:write&lt;/code&gt; just to create a repository. That distinction matters: a create-only workflow should not imply that your product needs permissions to delete or transfer repositories.&lt;/p&gt;

&lt;p&gt;For an existing repository, use &lt;code&gt;installation_repositories:write&lt;/code&gt; only when your workflow adds or removes repositories from an app installation. GitHub says those APIs previously required the broader &lt;code&gt;repo&lt;/code&gt; scope for personal access tokens; the fine-grained permission now narrows that request for supported callers. This controls the set of repositories the installation can access; it is not a substitute for the separate permission to create a repository.&lt;/p&gt;

&lt;p&gt;Some products need both operations. Request both only if the customer-facing workflow actually offers both. A user who only connects an existing repository should not be pushed through a request for repository creation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the permission request understandable
&lt;/h2&gt;

&lt;p&gt;Permission review is part of onboarding. Tell the customer what the app will do, when it will do it, and which repositories are in scope. If you need repository creation, say that clearly. If you need to update an installation’s repository selection, explain that separately.&lt;/p&gt;

&lt;p&gt;The new permission names make a narrower request possible, but they do not make approval automatic. GitHub notes that organization-level permissions, repository-administration permissions, and repository-creation permissions require organization-owner approval. Plan for that approval in the product flow. Do not describe a repository administrator as able to approve every request, or imply that a customer can avoid an organization owner when GitHub requires one.&lt;/p&gt;

&lt;p&gt;Also keep app installation distinct from selecting repositories. An organization must install the GitHub App before it can operate through that installation. The announcement says that when an app is already installed at the organization level, repository administrators can add it to a repository without a new organization-owner approval. That is useful for the existing-installation path, but it does not eliminate the initial organization-level installation decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build two explicit workflows
&lt;/h2&gt;

&lt;p&gt;A reliable integration can present two choices instead of one vague “connect” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1w5oa6ybvp5guximadfr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1w5oa6ybvp5guximadfr.webp" alt="An organization owner confirms a request at a green approval checkpoint, after which one blue repository folder appears; Dex, Luna, Byte, and Nova observe." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One repository is created after the organization-owner approval checkpoint. Conceptual illustration; no product UI is shown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a repository.&lt;/strong&gt; Explain the name and purpose that will be used, request the relevant repository-creation permission, and tell the user if an organization owner must approve it. After GitHub returns the created repository, show its name and link and let the user continue setup. Keep the operation idempotent: if the browser retries after a timeout, check whether the intended repository was created before trying to create another one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add an existing repository.&lt;/strong&gt; First establish that the app is installed for the organization. Then let an authorized user choose the specific repository and use the installation-repository permission to add it to the installation. Confirm the selected repository in the UI. Avoid presenting access to every organization repository as the default when the customer is choosing one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F13lueuhaza6if5q1yui3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F13lueuhaza6if5q1yui3.webp" alt="Byte and Dex move one blue repository folder into an existing app installation boundary while the other colored repository folders stay outside; Luna and Nova observe." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One selected repository is added to an already installed app; the other repositories remain outside its scope.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the same product supports both, make the choice visible before the permission screen. A user should understand why the app asks for a permission, and what happens if their organization requires an owner to approve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design for approval and failure
&lt;/h2&gt;

&lt;p&gt;Permission scoping does not replace operational safeguards. Use a clear audit trail for who initiated the action, the customer account, the repository, the requested operation, and the outcome. Keep enough context to diagnose a denied request or a retry without storing credentials in logs.&lt;/p&gt;

&lt;p&gt;Handle approval states as normal product states. If an organization owner needs to approve a permission, explain what the user can do next and preserve their setup progress. If the app is installed but a selected repository is not part of its installation, guide the user to the repository-access step rather than returning a generic failure.&lt;/p&gt;

&lt;p&gt;Test at least four cases before launch: a personal account that can grant the requested access; an organization where an owner must approve; an app already installed with a repository that is not yet selected; and a repeated request after a network timeout. Verify that the product asks only for the permission needed by each path and that it does not silently broaden access after a denial.&lt;/p&gt;

&lt;p&gt;For related GitHub integration risk, see our guide to &lt;a href="https://dev.to/blog/github-anthropic-key-push-protection"&gt;protecting repositories from leaked keys&lt;/a&gt;. Keep those concerns distinct: secret scanning protects credentials committed to source; least-privilege app permissions control what your integration can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the boundary before launch
&lt;/h2&gt;

&lt;p&gt;Review the permission list against product actions whenever you add a GitHub workflow. A useful review question is: if this permission were denied, which exact button or automated step would stop working? If the answer is “nothing,” remove the permission from the request. If several unrelated features depend on it, split those features into separate customer choices where possible.&lt;/p&gt;

&lt;p&gt;For repository creation, verify that the app creates only the repository the customer asked for and records the resulting repository identity. For installation repository changes, verify that adding one repository does not expand access to all repositories. Test the denied and partially approved states, not only the successful path. Make the requested permissions visible in your setup copy so a customer can compare them with the workflow they initiated.&lt;/p&gt;

&lt;p&gt;This review also helps support teams answer “why does the app need this?” with a concrete explanation. Tie each permission to a named action, show the relevant approval status, and avoid implying that a successful app installation means every repository is already accessible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for a small SaaS
&lt;/h2&gt;

&lt;p&gt;The improvement is most useful when your product automates customer setup or provisions a repository as part of a project workflow. A narrow create permission can make that request easier to explain than a broad administration permission. A separate installation-repository permission can support a deliberate “choose the repositories” step.&lt;/p&gt;

&lt;p&gt;Neither permission means your app has access without a customer granting it, nor does it bypass organization-owner approval where GitHub requires approval. Keep the product copy precise, request only the operations you ship, and provide an escape route when the customer cannot approve the request themselves.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://otf-kit.dev/templates/saas-dashboard" rel="noopener noreferrer"&gt;SaaS Dashboard kit&lt;/a&gt; can serve as an optional UI and authentication starting point for a custom review flow. It does not include a GitHub App integration; the permission request, GitHub API work, and approval handling remain implementation work for your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2026-10-13-github-app-improvements-for-least-privilege-and-management/" rel="noopener noreferrer"&gt;GitHub Changelog: GitHub App improvements for least privilege and management (October 13, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>aitools</category>
    </item>
    <item>
      <title>Add consented voice cloning to a TypeScript app with Sarvam AI</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Wed, 14 Oct 2026 13:36:46 +0000</pubDate>
      <link>https://dev.to/davekurian/add-consented-voice-cloning-to-a-typescript-app-with-sarvam-ai-2lca</link>
      <guid>https://dev.to/davekurian/add-consented-voice-cloning-to-a-typescript-app-with-sarvam-ai-2lca</guid>
      <description>&lt;p&gt;A voice sample is easy to collect. Turning it into a safe, useful feature is the harder part.&lt;/p&gt;

&lt;p&gt;If your app needs spoken replies in a user’s own voice, the integration has to do more than send audio to a model. It needs clear permission, a durable identity for the approved voice, an output path that fits the interface, and a way to stop using the voice later.&lt;/p&gt;

&lt;p&gt;Sarvam’s TypeScript SDK now exposes beta instant voice cloning methods for conversion, HTTP streaming, WebSocket streaming, and voice management. That gives a TypeScript app several ways to build the workflow. The important choice is how you organize consent, storage, and playback around those calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with permission and a narrow purpose
&lt;/h2&gt;

&lt;p&gt;Treat a voice sample as a permissioned asset. Before recording or uploading it, tell the speaker what the app will generate, where the resulting voice can be used, and how they can remove it. Collect affirmative consent before creating a cloned voice. Do not let one user submit another person’s sample simply because they can access the upload form.&lt;/p&gt;

&lt;p&gt;Keep the consent record separate from the audio. A practical record can identify the account, the purpose the person approved, the time of consent, and the current status. Avoid storing the sample longer than your product needs. Restrict access to the raw recording and make the deletion path visible in the same settings area where users manage their voice.&lt;/p&gt;

&lt;p&gt;This is a product-design baseline, not a substitute for legal review. The API capability does not decide whether you have the rights to a recording or whether a particular use is appropriate. Your app must set those boundaries and enforce them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a saved voice once
&lt;/h2&gt;

&lt;p&gt;For a user who will generate speech repeatedly, a saved voice is easier to manage than uploading the reference recording with every request. Sarvam’s voice-cloning guide describes creating a voice and receiving a &lt;code&gt;voice_id&lt;/code&gt;. Store that identifier against the consenting account and the approved purpose, then use it for later generations. Keep the provider API key on your server; the TypeScript SDK reads &lt;code&gt;SARVAM_API_KEY&lt;/code&gt; from the environment, and a browser client should never receive that secret.&lt;/p&gt;

&lt;p&gt;A minimal application record might contain an internal voice record ID, the provider’s &lt;code&gt;voice_id&lt;/code&gt;, the account owner, consent state, creation time, and deletion state. Keep the internal record ID in your product UI. The provider ID is an implementation detail, not a public authorization token. Every request should verify that the signed-in user is allowed to use the voice for the selected feature.&lt;/p&gt;

&lt;p&gt;The create-voice response can include an automatically generated reference transcript. Treat that as a useful review aid, not as proof of consent or a guarantee that the sample is suitable. Let the user confirm the voice belongs to them and that the result is acceptable before enabling it for other workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose one input path per request
&lt;/h2&gt;

&lt;p&gt;The API supports either a saved &lt;code&gt;voice_id&lt;/code&gt; or an inline &lt;code&gt;ref_audio&lt;/code&gt; for a conversion request. The guide says to provide one or the other; sending both returns a 400 response. That simple constraint should shape your server-side request builder, so a UI state cannot accidentally submit two sources.&lt;/p&gt;

&lt;p&gt;Use a saved ID when the user has created a voice and expects to return to it. Use inline reference audio for a one-time flow where you do not need a persistent voice record. If you support inline audio, make the retention behavior explicit and remove temporary files according to your policy. The voice library and the temporary upload path should not silently become the same thing.&lt;/p&gt;

&lt;p&gt;Validate file size and duration before forwarding audio. The guide documents a 50 MB maximum for voice creation, while inline reference audio is limited to 10 MB and 15 seconds. It recommends a 10–15 second recording. Reject invalid files with a useful message before making a paid generation call, and avoid logging raw audio or secrets when diagnosing failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match generation to the experience
&lt;/h2&gt;

&lt;p&gt;The beta interface offers three useful shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One-shot conversion is the simplest choice when the user submits a complete text and can wait for the finished audio. The response contains base64 audio that your server can decode and return or store.&lt;/li&gt;
&lt;li&gt;HTTP streaming sends audio as sentences become ready. It can reduce the wait before playback for a response that is already known as text.&lt;/li&gt;
&lt;li&gt;WebSocket streaming suits an interactive session where text arrives incrementally or the client needs a continuing stream. The SDK exposes a streaming interface for this path, which requires an event-driven client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose the least complex option that meets the product’s latency needs. A profile narration button usually does not need a persistent socket. A live conversational feature may benefit from one, but it also needs reconnect handling, a clear end-of-session action, and a fallback when the stream closes unexpectedly.&lt;/p&gt;

&lt;p&gt;Make the user interface distinguish “generating” from “ready.” For HTTP or WebSocket streaming, include an immediate stop control, keep the text visible while audio arrives, and allow replay only within the product’s retention policy. Do not play speech automatically in a way that surprises people or exposes private content in a shared space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj996mwf8qftxl1j370fd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj996mwf8qftxl1j370fd.webp" alt="A complete audio waveform waits for a play action on one side, while short audio segments arrive in sequence as Byte and Nova inspect streaming playback on the other." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One-shot playback waits for the complete response; streaming can make portions available as they arrive. Conceptual comparison, not a provider interface.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the TypeScript integration on the server
&lt;/h2&gt;

&lt;p&gt;Install the official &lt;code&gt;sarvamai&lt;/code&gt; package and create the client in a server-only module. Put &lt;code&gt;SARVAM_API_KEY&lt;/code&gt; in the deployment environment, not in a &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; variable, checked-in file, or client bundle. Route requests through an authenticated endpoint that checks account ownership, consent status, allowed purpose, input limits, and the user’s remaining generation budget before calling Sarvam.&lt;/p&gt;

&lt;p&gt;The SDK changelog names the TypeScript methods &lt;code&gt;voiceCloning.convert&lt;/code&gt;, &lt;code&gt;voiceCloning.convertStream&lt;/code&gt;, &lt;code&gt;voiceCloningStreaming&lt;/code&gt;, and voice lifecycle operations such as &lt;code&gt;createVoice&lt;/code&gt;, &lt;code&gt;listVoices&lt;/code&gt;, &lt;code&gt;getVoice&lt;/code&gt;, and &lt;code&gt;deleteVoice&lt;/code&gt;. The exact call and return types can change while the feature is beta, so pin the SDK version you tested and follow the current TypeScript reference for its request fields. Do not copy Python parameter names into a TypeScript call and assume they match.&lt;/p&gt;

&lt;p&gt;Handle provider errors deliberately. A malformed request should become a clear validation message; a timeout should not create a second generation automatically; and a failed stream should release its socket and temporary buffers. Log a request ID, operation, duration, and outcome, but leave out API keys, reference audio, and generated audio. This gives support enough information to investigate without turning logs into a second audio store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make deletion part of the feature
&lt;/h2&gt;

&lt;p&gt;Voice creation is only half of the lifecycle. The SDK exposes list, get, and delete operations. Build a settings view where users can see their saved voices and remove one. When deletion starts, disable new generations for that voice, call the provider deletion method from the server, and then mark the internal record as deleted after you receive confirmation. If the provider request fails, show a retryable state instead of claiming the voice is gone.&lt;/p&gt;

&lt;p&gt;Sarvam’s guide says the free plan supports up to five cloned voices, while paid plans are not capped; deleting a voice frees a slot. Generation is billed by text character. Those limits make it useful to show the user which voices are active and to place a server-side budget guard before generating long text. Consider a maximum input length, a per-account daily allowance, and a preview step before an expensive request. Surface the applicable plan limit instead of letting a provider error be the first sign that the account ran out of capacity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq85cqwye8bf2yq7kr1uf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq85cqwye8bf2yq7kr1uf.webp" alt="Dex moves a saved voice card from a library shelf toward a clearly marked removal bin; Luna, Byte, and Nova observe." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual withdrawal flow for a saved voice. The app must disable future use and confirm provider deletion before claiming removal is complete.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship a small, testable first version
&lt;/h2&gt;

&lt;p&gt;Start with one consented voice, one server endpoint, and one-shot generation. Test ownership checks with two accounts, verify the API key never appears in browser assets or logs, and confirm that an unconsented or deleted voice cannot be used. Then add HTTP streaming if the wait for complete audio is harming the experience. Add WebSocket streaming only when the product has a real incremental interaction to support.&lt;/p&gt;

&lt;p&gt;Track the time to first audio, total generation time, failure rate, and characters generated. Those measurements tell you whether streaming is helping and where to add limits. Pair them with a clear consent state and deletion test; a fast voice endpoint is not ready if it keeps working after a user has withdrawn permission.&lt;/p&gt;

&lt;p&gt;If you are building the surrounding account screens and app shell, the &lt;a href="https://otf-kit.dev/templates/saas-dashboard" rel="noopener noreferrer"&gt;SaaS Dashboard kit&lt;/a&gt; is an optional starting point. It does not include Sarvam, voice cloning, audio storage, or this integration; you would build and verify those pieces yourself. For another voice-oriented workflow, see &lt;a href="https://otf-kit.dev/blog/elevenlabs-on-openrouter-voice-app" rel="noopener noreferrer"&gt;how to add file-based voice input and spoken replies to an OpenRouter app&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.sarvam.ai/api/api-guides-tutorials/voice-cloning/overview" rel="noopener noreferrer"&gt;Sarvam AI: Instant Voice Cloning overview and API guide&lt;/a&gt; — beta endpoints, saved voice lifecycle, input limits, plan limits, and generation billing.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aitools</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Deploy a Hono backend on AWS Lambda without an adapter layer</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:55:14 +0000</pubDate>
      <link>https://dev.to/davekurian/deploy-a-hono-backend-on-aws-lambda-without-an-adapter-layer-43km</link>
      <guid>https://dev.to/davekurian/deploy-a-hono-backend-on-aws-lambda-without-an-adapter-layer-43km</guid>
      <description>&lt;p&gt;A Hono app may already have a normal HTTP server, routes, middleware, and database calls. Moving that app to Lambda used to mean adapting the request lifecycle or adding a bridge layer. AWS Lambda Web Functions changes that deployment choice: at launch, it can run a Node.js web server directly, including Hono, without a Lambda-specific handler adapter.&lt;/p&gt;

&lt;p&gt;That does not make every Lambda app a fit. Web Functions have their own runtime, concurrency, networking, endpoint, and billing model. Before moving a SaaS backend, check whether your server starts in the expected way, how the endpoint will be authenticated, which regions are available, and how your actual mix of CPU work, waiting, memory, and data transfer affects the bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Web Function changes
&lt;/h2&gt;

&lt;p&gt;A traditional Lambda Event Function is built around an invocation handler. Your code receives an event, runs the handler, and returns a response. A Web Function instead runs a standard Node.js HTTP server that listens on a port. AWS supplies the endpoint address through the &lt;code&gt;AWS_LAMBDA_HTTP_ENDPOINT&lt;/code&gt; environment variable, and the server listens on that address. The application can keep its framework's normal routing and middleware instead of translating each request into a framework-specific handler event.&lt;/p&gt;

&lt;p&gt;At launch, AWS lists Node.js 24 on Arm64 as the supported runtime. Express, Fastify, and Hono can run without adapters; Next.js in standalone mode needs a small entry file to set its host and port. The service has a Web Function, an immutable revision of code and configuration, and one or more endpoints that route traffic to revisions. That makes the endpoint and revision important parts of your deploy plan, not just implementation detail.&lt;/p&gt;

&lt;p&gt;This is useful when you want an HTTP app to behave like an HTTP app: REST or GraphQL routes, server-rendered pages, streaming responses, or a small service that scales with incoming traffic. An event-driven task such as processing a queue message is still a better match for an Event Function. The new form factor changes how you run a web server; it does not remove the need to choose the right execution model for each workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the server before you package it
&lt;/h2&gt;

&lt;p&gt;Start by looking at the app's entry point. Does it start one server process and listen on a port? Does it rely on a development-only command, a local reverse proxy, or a platform-specific adapter? Does it assume that a request will always begin at one fixed path? Check these assumptions before deploying. The server needs to use the address AWS provides at runtime rather than binding to a hard-coded port or host.&lt;/p&gt;

&lt;p&gt;Then run the production build locally and confirm the app starts with the same runtime and environment variables you plan to use. Test routes that depend on streaming, long-lived connections, large request bodies, file handling, and database access. A basic “hello world” endpoint can prove that the endpoint responds, but it does not validate the behavior of your real API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure concurrency with your own routes
&lt;/h2&gt;

&lt;p&gt;A Web Function execution environment can handle multiple concurrent requests. AWS describes up to 128 requests per environment at launch. That can let a warm process share its runtime across overlapping requests, but the number is not a target concurrency for every application. Your database pool, downstream API limits, memory use, and request duration still determine safe load.&lt;/p&gt;

&lt;p&gt;For a SaaS API, test the paths that create the most contention: a route that holds a database connection while waiting on an external API, a CPU-heavy transformation, and a streaming response. Increase load gradually and watch latency percentiles, errors, open database connections, and environment count. Make sure per-request state stays isolated. Mutable module-level state can be shared across concurrent requests in the same process, so do not use it as a substitute for user-scoped storage or transaction boundaries.&lt;/p&gt;

&lt;p&gt;If your app uses a database, review connection behavior before moving traffic. Overlapping requests may need a different pool size than a one-invocation handler. Bound the pool, account for active environments, and test burst exhaustion. Keep secrets in supported configuration and restrict the runtime role.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpbpyvf8wq1w3p4fjq2b3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpbpyvf8wq1w3p4fjq2b3.webp" alt="Byte points to a generic app endpoint as several request tokens enter the same warm application environment; Dex, Luna, and Nova observe the shared process." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual view of a web server using its runtime-provided listening address while multiple requests reach one warm environment.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimate cost from the full request path
&lt;/h2&gt;

&lt;p&gt;AWS describes Web Function charges across requests, active CPU time, memory consumed over the execution environment's lifetime, and data transfer. Waiting on a database or API call uses little CPU, but memory remains part of the environment's bill. That means “active CPU billing” alone is not a cost estimate. Long-lived or mostly idle processes, large memory settings, high request counts, and outbound traffic can all change the result.&lt;/p&gt;

&lt;p&gt;Build a small cost worksheet from a representative period. Record request volume, CPU-active work per request, memory configuration and environment lifetime, and data transfer. Include retries, background work, and streaming duration if the app uses them. Compare the total with your existing host using the same user load and the same response behavior. A short synthetic test checks a code path, but cannot predict production cost if it omits cold starts, burst concurrency, real database waits, or regional traffic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo0hw5nhqg6data4g1t86.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo0hw5nhqg6data4g1t86.webp" alt="Luna, Byte, Dex, and Nova compare CPU activity, memory duration, requests, and outbound data around a generic running web app." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual cost worksheet separating CPU-active time, memory over environment lifetime, request count, and data transfer; no price values are shown.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide how users reach the endpoint
&lt;/h2&gt;

&lt;p&gt;A Web Function endpoint has an authentication type. AWS documents ApplicationManaged, where your app handles authentication, and IamAuth, where Lambda checks AWS Signature Version 4 for single-region endpoints and Signature Version 4a for multi-region endpoints. Under IamAuth, callers also need the &lt;code&gt;lambda:InvokeWebFunctionEndpoint&lt;/code&gt; permission. Pick this based on who should call the endpoint; do not assume that an HTTPS URL is private simply because it is difficult to guess.&lt;/p&gt;

&lt;p&gt;For a customer-facing SaaS API, the application still needs its normal user authentication and authorization. If an endpoint is public and the app handles sign-in, test the path through the same middleware your users rely on. If the endpoint is for service-to-service traffic, evaluate IamAuth and the identity permissions required for the caller. Keep the endpoint authentication decision separate from user authorization inside the app.&lt;/p&gt;

&lt;p&gt;At launch, AWS lists availability in five regions: US East (N. Virginia), US East (Ohio), US West (Oregon), Asia Pacific (Tokyo), and Europe (Ireland). Multi-region deployments use latency-based routing on a shared endpoint, described by AWS as best-effort. Confirm that the regions you need are supported, and check how the app reaches its database in each region. A nearby compute region does not make a single-region database local to every request.&lt;/p&gt;

&lt;p&gt;If you need a custom domain, caching, or AWS WAF, AWS documents CloudFront in front of the Web Function endpoint. Origin Access Control can restrict the endpoint to requests from that distribution. Keep the direct endpoint from becoming an unintended path around your edge rules, and check that your app's own authentication headers still reach the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy a reversible first version
&lt;/h2&gt;

&lt;p&gt;Treat a deployment as an immutable revision and move traffic deliberately. Start with a non-production endpoint or a small internal test. Verify authentication, error handling, health checks, streaming, logs, and database connectivity against the deployed URL. Then use endpoint routing to shift traffic gradually if the rollout needs a canary. Keep the previous revision available so a rollback is a routing change rather than a scramble to rebuild the old code.&lt;/p&gt;

&lt;p&gt;For the surrounding SaaS screens, the &lt;a href="https://dev.to/templates/saas-dashboard"&gt;SaaS Dashboard kit&lt;/a&gt; is an optional editable UI and account shell. It does not include AWS deployment or Lambda integration. Teams still need to configure the Web Function, endpoint, identity, network access, and release process themselves. For another example of deciding where application logic should run, see our guide to &lt;a href="https://dev.to/blog/cloudflare-agent-search-fetch-flow"&gt;bounded web research for SaaS agents&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A launch checklist for a Hono service
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the app runs on the supported Node.js runtime and starts an ordinary HTTP server.&lt;/li&gt;
&lt;li&gt;Read the AWS-provided endpoint address and listen on that host and port.&lt;/li&gt;
&lt;li&gt;Test production routes, streaming, database calls, and error paths—not only the health endpoint.&lt;/li&gt;
&lt;li&gt;Set an authentication type and verify the endpoint is not unintentionally public.&lt;/li&gt;
&lt;li&gt;Model concurrency against database and downstream service limits; use a bounded connection pool.&lt;/li&gt;
&lt;li&gt;Estimate requests, active CPU, environment memory duration, and data transfer from representative traffic.&lt;/li&gt;
&lt;li&gt;Confirm region availability and database placement before enabling latency-based routing.&lt;/li&gt;
&lt;li&gt;Deploy a revision, validate the endpoint, then shift traffic in a reversible way.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lambda Web Functions give a Hono backend a direct HTTP-server path on Lambda. Whether that is a good fit depends on your server startup, network and identity requirements, traffic shape, and full request cost. Test one service under realistic load, inspect the result, and expand only when the measurements support the move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/blogs/compute/exploring-aws-lambda-web-functions-for-building-web-applications/" rel="noopener noreferrer"&gt;Exploring AWS Lambda Web Functions for building web applications&lt;/a&gt; — AWS Compute Blog, October 13, 2026; checked October 14, 2026.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>architecture</category>
      <category>announcement</category>
    </item>
    <item>
      <title>Cloudflare web search and bounded fetch for SaaS agent research workflows</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Wed, 14 Oct 2026 11:52:36 +0000</pubDate>
      <link>https://dev.to/davekurian/cloudflare-web-search-and-bounded-fetch-for-saas-agent-research-workflows-43mm</link>
      <guid>https://dev.to/davekurian/cloudflare-web-search-and-bounded-fetch-for-saas-agent-research-workflows-43mm</guid>
      <description>&lt;p&gt;An assistant that can search the web is useful only if it can bring back the right evidence. Give it unrestricted page fetching, though, and a simple research question can turn into a large, noisy prompt—or a request to a host your app never meant to contact.&lt;/p&gt;

&lt;p&gt;Cloudflare’s Agents SDK now documents a paired &lt;code&gt;web_search&lt;/code&gt; and &lt;code&gt;web_fetch&lt;/code&gt; workflow. Search can return a short numbered set of titles, URLs, and descriptions; fetch can read selected pages and return bounded text with response details. The implementation decision is how your application chooses results, limits each request, and presents source provenance to the user. The wrappers help with that work, but they do not guarantee that retrieved content is safe or true.&lt;/p&gt;

&lt;p&gt;For a SaaS assistant, the useful pattern is narrow: ask a focused question, search for a small number of relevant pages, select only the results worth reading, fetch them under application-defined limits, and show the resulting source URLs and statuses. Keep the fetched material separate from trusted system instructions. That gives the user a reviewable research trail instead of an opaque answer backed by a large context dump.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn search results into a deliberate reading list
&lt;/h2&gt;

&lt;p&gt;Start with the user’s actual decision. “What changed in the latest release?” needs a different query from “Which option supports our deployment constraint?” A vague query usually returns a list that is harder to assess and easier for an agent to over-trust. Ask the model to state the question it is trying to answer, then issue a search with a modest result limit appropriate to that task.&lt;/p&gt;

&lt;p&gt;The search tool returns structured result descriptions, not proof that every result is relevant or authoritative. Before fetching, have the application or agent inspect the title, host, and snippet. Prefer the vendor’s documentation for product behavior, a primary announcement for release details, and a first-party policy page for rules. A search result can point to commentary, an obsolete page, or a copy that omits important conditions. Treat it as a candidate source.&lt;/p&gt;

&lt;p&gt;Selection is a useful place to make the workflow visible. Show the chosen URLs, and let the user remove a result when the assistant’s interpretation of the question is off. For automated flows, record why each result was selected and preserve the original URL. If the source redirects, keep the final URL returned by the fetch step as well; that is the address whose content the model actually received.&lt;/p&gt;

&lt;p&gt;This creates a small evidence set. The goal is not to fetch the maximum number of pages. It is to answer the buyer’s question from enough trustworthy material that a person can check the key claims. If the initial results are weak, revise the query or say that the evidence is thin rather than broadening access without a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bound the fetch operation in application code
&lt;/h2&gt;

&lt;p&gt;Cloudflare’s &lt;code&gt;web_fetch&lt;/code&gt; tool can convert HTML, PDF, and supported Office documents into readable text, request Markdown from sites that support it, and format JSON. Long pages are returned in windows that can be continued with an offset. Results include details such as final URL, HTTP status, content type, and character counts. Those fields let your application report what it read and whether the source response was successful.&lt;/p&gt;

&lt;p&gt;Set the request policy when you create the tool. Cloudflare documents controls for allowed hosts, blocked hosts, response bytes, timeouts, redirects, and the number of characters returned in one window. Use the narrowest policy that fits the product. A research assistant that should read your own documentation and a few approved vendor domains does not need permission to fetch every public host on the internet.&lt;/p&gt;

&lt;p&gt;Allowlisting is a policy boundary, not a substitute for validating the task. Check redirect destinations as part of the effective policy, and keep private or local hosts unavailable unless your product has a specific, reviewed reason to reach them. Enforce byte and timeout limits so a slow or unusually large response cannot consume unbounded resources. If users can request arbitrary domains, decide explicitly how that access is mediated and logged rather than inheriting a permissive default by accident.&lt;/p&gt;

&lt;p&gt;The response window also matters. A long page may contain one section that answers the question and many that do not. Use the returned offset to request another portion only when the first window shows that more text is needed. Keep each continuation tied to the same source and task, and stop when you have enough evidence. This helps keep context manageable and makes it easier to explain which portion informed the answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo05c2o26v5u7zjkh4a0x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo05c2o26v5u7zjkh4a0x.webp" alt="Luna, Byte, Dex, and Nova inspect approved source cards passing a physical host gate; a long page is shown through successive narrow reading windows, with an outside source held back." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use a host allowlist and cap each read; continue to another bounded page window only when needed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If a fetch fails, preserve the structured failure details for the application and show a clear status to the user. A timeout, unsupported file, blocked host, or ordinary HTTP error should not silently become an empty “source.” The agent can retry a correctable input or use another selected source, but the UI should distinguish a failed read from a successful page with little relevant content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat page text as untrusted input
&lt;/h2&gt;

&lt;p&gt;Retrieved pages can contain instructions aimed at the model. A web-fetch wrapper can label the returned page as untrusted content, but Cloudflare explicitly does not describe that wrapper as a prompt-injection guarantee. Your application still has to preserve the distinction between trusted instructions and text gathered from the public web.&lt;/p&gt;

&lt;p&gt;Keep fetched text inside a clearly marked data field in the model’s request. Tell the assistant to use it as evidence to summarize, not to obey commands found in it. Do not let a page change tool permissions, redefine the user’s request, or authorize unrelated actions. For consequential operations, require a separate confirmation path that is independent of whatever a page says.&lt;/p&gt;

&lt;p&gt;The assistant should cite claims to the final source URL and make uncertainty visible. A source can be reachable and still be outdated, incomplete, or irrelevant. Keep the fetched status and content type alongside the citation, and avoid turning an HTTP 200 into an assertion that the source is authoritative. If two pages disagree, surface the disagreement and its sources instead of hiding it in a single confident summary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnf99p5mz680khmnri733.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnf99p5mz680khmnri733.webp" alt="Dex, Luna, Byte, and Nova review source pages kept in an amber evidence tray, physically separated from a blue trusted-instructions notebook; the source trail remains visible as they inspect the material." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Treat retrieved page text as untrusted evidence, not instructions; check sources and keep provenance separate from trusted instructions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a broader example of separating durable application state from a live browser session, see our guide to &lt;a href="https://dev.to/blog/cloudflare-agents-browser-session-recovery"&gt;recovering browser tasks across agent turns&lt;/a&gt;. The same design habit applies here: retain the evidence your app needs to explain a result, while treating remote content as something to inspect rather than trust by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the workflow reviewable in a SaaS product
&lt;/h2&gt;

&lt;p&gt;Before launch, test the path with ordinary pages, a long document that needs a second window, an unsupported content type, a redirect, a slow response, and a host outside the allowlist. Verify that every case produces a meaningful status and that the assistant can continue from an alternate selected source when appropriate. Use synthetic or public material for tests; do not place customer secrets in URLs or fetched prompts.&lt;/p&gt;

&lt;p&gt;Log the query, selected source URLs, final URLs, response status, content type, and whether the fetch completed or continued. Apply your privacy and retention policy to query text and page content. If a user asks for a report later, these records let you answer which sources were used without storing a full copy of every page indefinitely.&lt;/p&gt;

&lt;p&gt;Give users a compact evidence panel with the question, selected sources, read status, and citations. They should be able to open a source directly and see when the assistant could not read it. For teams, an owner can review the host policy, byte limit, timeout, and redirect rules in one place. That turns a capability demo into an operational feature that can be supported and audited.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/templates/saas-dashboard"&gt;SaaS Dashboard kit&lt;/a&gt; can serve as an optional editable UI and account shell for a research workspace. It does not include Cloudflare’s Agents SDK, web search, web fetch, source allowlisting, or this integration. Teams still need to implement the model tools and request policy themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical rollout checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Define the buyer task and the source domains the feature is intended to read.&lt;/li&gt;
&lt;li&gt;Search for a small set of candidate pages and inspect their hosts before fetching.&lt;/li&gt;
&lt;li&gt;Configure host restrictions, redirect checks, byte limits, timeouts, and a bounded text window in the application.&lt;/li&gt;
&lt;li&gt;Fetch only the selected sources; continue through offsets only when the answer needs more of a page.&lt;/li&gt;
&lt;li&gt;Preserve final URLs and response details, and show users which sources succeeded or failed.&lt;/li&gt;
&lt;li&gt;Keep web text in an untrusted-data boundary and require separate authorization for consequential actions.&lt;/li&gt;
&lt;li&gt;Test redirect, timeout, content-type, and pagination cases before enabling the feature for customers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This flow makes web research more controlled, but it does not make the model’s conclusions automatically correct. The application decides where the agent may read, how much content enters context, and what evidence the user can inspect. Keep those choices explicit, test the failure paths, and give the assistant permission to say when the available sources do not answer the question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developers.cloudflare.com/agents/tools/web-fetch/" rel="noopener noreferrer"&gt;Cloudflare Agents SDK: Web fetch&lt;/a&gt; — Cloudflare documentation, last updated October 12, 2026; checked October 14, 2026.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>agents</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>GitHub push protection for Anthropic keys: check repo coverage</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Wed, 14 Oct 2026 11:30:53 +0000</pubDate>
      <link>https://dev.to/davekurian/github-push-protection-for-anthropic-keys-check-repo-coverage-4m0j</link>
      <guid>https://dev.to/davekurian/github-push-protection-for-anthropic-keys-check-repo-coverage-4m0j</guid>
      <description>&lt;p&gt;When an AI coding agent commits a generated configuration file, an API key can end up in Git before anyone notices. GitHub’s new Anthropic detectors add another chance to stop that push, but the protection only works where secret scanning is enabled. Check repository coverage before relying on it.&lt;/p&gt;

&lt;p&gt;On October 13, GitHub added two Anthropic key types—service API keys and user API keys—to push protection defaults. Commits containing those patterns are blocked in repositories where secret scanning is enabled, including free public repositories. That is useful coverage for teams whose agents create or edit configuration, but it does not mean every private repository is protected automatically.&lt;/p&gt;

&lt;p&gt;The practical question is not only “Does GitHub know this key pattern?” It is “Is secret scanning enabled for the exact repository where our agent pushes?” Start by checking that boundary, then test the workflow without exposing a real credential.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the new detection changes
&lt;/h2&gt;

&lt;p&gt;The GitHub announcement names anthropic_service_api_key and anthropic_user_api_key as newly detected types and includes both in the set of patterns covered by push protection by default. When scanning is active, GitHub can reject a push that contains a matching key before the commit is accepted.&lt;/p&gt;

&lt;p&gt;That is a prevention step at the Git hosting boundary. It can catch an accidental commit from a developer, a code generator, or an AI agent using the same Git remote. It does not prevent a key from being written to a local file, copied into a prompt, printed in a build log, or sent to another service before the push. Treat it as one control in a wider secret-handling process.&lt;/p&gt;

&lt;p&gt;GitHub’s wording has an important condition: the repository must have secret scanning enabled. Public repositories get secret scanning automatically at no charge. Organization-owned private and internal repositories need GitHub Secret Protection enabled on GitHub Team or GitHub Enterprise Cloud. An organization that assumes the new default covers every private repo may have an unprotected gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the repository before you depend on the block
&lt;/h2&gt;

&lt;p&gt;Start with the repository’s owner and visibility. A personal public repository, an organization-owned public repository, and an organization-owned private repository may have different eligibility and settings. For an organization-owned private or internal repository, verify the organization’s plan and whether Secret Protection is enabled; do not infer coverage from the fact that a code host offers the feature.&lt;/p&gt;

&lt;p&gt;Then open the repository’s security settings and look for secret scanning and push protection. Confirm the feature is enabled for this repository, not merely available in the organization’s plan. If you manage many repositories, use the organization’s security overview or policy tooling to identify coverage gaps and make the check repeatable.&lt;/p&gt;

&lt;p&gt;The announcement adds these Anthropic patterns to push protection defaults, but a repository still needs the underlying scanning feature enabled. Check that the policy applies to the repositories where agents can push. If your team uses internal forks, sandbox repositories, or personal workspaces during development, include those in the review rather than checking only the production repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcq19l0hkp10qh85f7hf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcq19l0hkp10qh85f7hf.webp" alt="Luna and the team compare public, personal-owner, and organization-owned repository models; some show confirmed protection while other private/internal cases remain unverified." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visibility, owner, plan, and settings affect coverage. Verify the feature on each repository instead of assuming all private repositories are protected.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do not paste a production key into a test commit to see what happens. Use GitHub’s documented test procedure or a disposable test repository and a non-secret test value that GitHub explicitly supports. Confirm that the push is blocked and that the alert or remediation flow is understandable to the person or agent who receives it. If the expected block does not happen, stop and verify the repository settings before treating the control as active.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make agent-created changes safer
&lt;/h2&gt;

&lt;p&gt;AI coding agents often touch .env examples, deployment configuration, test fixtures, and scripts. Decide which files and environments the agent may edit, and keep actual credentials in a secret manager or deployment environment rather than in the working tree. Add safe placeholder values to examples; avoid asking an agent to copy a live key into a file just to make a demo work.&lt;/p&gt;

&lt;p&gt;Before an agent pushes, check the same repository policy that applies to human commits. A local pre-commit scanner can provide faster feedback, and CI can add another check, but neither proves that GitHub push protection is enabled. Conversely, a successful push only tells you that the host accepted that commit under its current rules; it does not validate every secret-handling path in your build and deployment system.&lt;/p&gt;

&lt;p&gt;Give the agent a clear recovery path for a blocked push: remove the secret from the proposed change, load it from the approved secret store at runtime, and retry. Do not instruct the agent to bypass protection just to clear the error. If a real key has already been committed or exposed, rotate or revoke it even if a later commit removes it. Removing a value from the latest tree does not erase it from Git history or from copies already made.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0gxy4ektoq0nsov1apq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0gxy4ektoq0nsov1apq.webp" alt="Byte removes an abstract exposed credential from a code change; Dex creates a different replacement inside a separate locked vault while Luna checks the recovery steps, then a clean patch is retried." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remove the exposed value from the proposed change, rotate or revoke it in the approved secret store, then retry with a clean patch; do not bypass protection.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://dev.to/blog/secrets-rotation-owned-backend"&gt;secret rotation guide&lt;/a&gt; covers the response after a credential has escaped. This GitHub update addresses an earlier point in the workflow: whether a matching key is stopped at push time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roll out the setting across teams
&lt;/h2&gt;

&lt;p&gt;Use a small rollout rather than assuming that one repository’s result applies everywhere:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;List the repositories where agents or automation can push, including development and internal repositories.&lt;/li&gt;
&lt;li&gt;Record repository visibility, owner type, plan eligibility, and whether secret scanning and push protection are enabled.&lt;/li&gt;
&lt;li&gt;Enable Secret Protection for eligible organization-owned private and internal repositories where the risk justifies it.&lt;/li&gt;
&lt;li&gt;Test the expected block with GitHub’s documented safe test method in a disposable repository.&lt;/li&gt;
&lt;li&gt;Tell developers and agent operators how to remove the secret and retry without bypassing the control.&lt;/li&gt;
&lt;li&gt;Re-check coverage when repositories move organizations, change visibility, or adopt a different plan.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For repositories that do not qualify for GitHub Secret Protection, do not describe the new Anthropic push rule as a control you already have. Use your approved secret-scanning workflow and enforce secret injection at runtime. Keep a written exception list for repositories that cannot be covered, with an owner and a compensating control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the boundary clear
&lt;/h2&gt;

&lt;p&gt;GitHub’s new default is valuable because it recognizes specific Anthropic key formats and blocks matching pushes where secret scanning is enabled. It is not a universal guarantee that no secret reaches Git, and it does not automatically turn on paid coverage for every private repository. The configuration check is part of adopting the feature.&lt;/p&gt;

&lt;p&gt;If your team is building an internal review or operations dashboard around repository checks, OTF’s &lt;a href="https://dev.to/templates/saas-dashboard"&gt;SaaS Dashboard kit&lt;/a&gt; is an optional editable app starting point. It includes dashboard and account UI; it does not include GitHub Secret Protection, Anthropic key detection, or a secret-scanning integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.blog/changelog/2026-10-13-secret-scanning-blocks-anthropic-keys-by-default-adds-salesforce/" rel="noopener noreferrer"&gt;Secret scanning blocks Anthropic keys by default&lt;/a&gt; — GitHub Changelog, October 13, 2026; checked October 14, 2026.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>announcement</category>
      <category>backend</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
