<?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: Ravi Arnan Irianto</title>
    <description>The latest articles on DEV Community by Ravi Arnan Irianto (@raviarnan).</description>
    <link>https://dev.to/raviarnan</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%2F4076187%2F990c6bfd-e62d-4994-9f0a-5519b2035b50.jpg</url>
      <title>DEV Community: Ravi Arnan Irianto</title>
      <link>https://dev.to/raviarnan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/raviarnan"/>
    <language>en</language>
    <item>
      <title>Eight Things I Wish I'd Known Before Building on Tencent EdgeOne Makers</title>
      <dc:creator>Ravi Arnan Irianto</dc:creator>
      <pubDate>Thu, 13 Aug 2026 11:28:36 +0000</pubDate>
      <link>https://dev.to/raviarnan/eight-things-i-wish-id-known-before-building-on-tencent-edgeone-makers-2jii</link>
      <guid>https://dev.to/raviarnan/eight-things-i-wish-id-known-before-building-on-tencent-edgeone-makers-2jii</guid>
      <description>&lt;p&gt;I spent a weekend shipping a small tool on Tencent EdgeOne Makers, and the interesting part wasn't the code. It was the gap between what the documentation says and what the runtime actually does. Every one of the eight things below cost me a deploy cycle to discover, and none of them are written down anywhere I could find.&lt;/p&gt;

&lt;p&gt;If you are about to build your first agent on this platform, this article is the afternoon I already lost, handed to you. None of these are reasons not to use EdgeOne Makers. I would pick it again. They are just the things nobody tells you on day one.&lt;/p&gt;

&lt;p&gt;The tool is &lt;a href="https://edgeone-site-auditor.edgeone.dev" rel="noopener noreferrer"&gt;Rubric&lt;/a&gt;. You paste a URL, and it grades the page. Thirteen checks in two categories: six on the response headers, seven on the rendered document. &lt;code&gt;example.com&lt;/code&gt; scores 45 D, because it sends no security headers at all. The code is &lt;a href="https://github.com/ravi-arnan/rubric" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I picked it deliberately. EdgeOne Makers gives agents a &lt;strong&gt;sandbox with a real browser in it&lt;/strong&gt;, and I wanted to build something that would be impossible without that, not another wrapper around a chat completion. Reading response headers is something any serverless function can do. Counting the images that are missing &lt;code&gt;alt&lt;/code&gt; text requires a browser that has actually built the document. That difference turned out to be the first and most important thing I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;context.sandbox&lt;/code&gt; is injected into &lt;code&gt;agents/&lt;/code&gt;, not &lt;code&gt;cloud-functions/&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;EdgeOne Makers gives you two folders. &lt;code&gt;cloud-functions/&lt;/code&gt; is an ordinary stateless request handler. &lt;code&gt;agents/&lt;/code&gt; is the session-oriented one. The docs describe the injected &lt;code&gt;context&lt;/code&gt; object (&lt;code&gt;request&lt;/code&gt;, &lt;code&gt;env&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, &lt;code&gt;sandbox&lt;/code&gt;, &lt;code&gt;tracer&lt;/code&gt;) as though it were the same context everywhere.&lt;/p&gt;

&lt;p&gt;It is not. A handler in &lt;code&gt;cloud-functions/&lt;/code&gt; never receives &lt;code&gt;sandbox&lt;/code&gt;. If you were planning a lightweight function that opens a browser, that plan does not exist. This single fact decided the entire layout of my repo before I wrote a line of audit logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Every request needs a &lt;code&gt;makers-conversation-id&lt;/code&gt; header
&lt;/h2&gt;

&lt;p&gt;This is the one that will waste your afternoon. Call your deployed agent without that header and the platform answers &lt;strong&gt;400 before your handler ever runs&lt;/strong&gt;. Your logs stay empty, because nothing of yours executed. A perfectly healthy endpoint looks stone dead.&lt;/p&gt;

&lt;p&gt;The value just has to be 6-36 characters of &lt;code&gt;[0-9a-zA-Z-_.]&lt;/code&gt;. Any session string will do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://edgeone-site-auditor.edgeone.dev/audit?url=https://example.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'makers-conversation-id: your-session-id'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It makes sense once you know it: agents are session-scoped, and &lt;code&gt;context.store&lt;/code&gt; is keyed by that conversation id. But nothing tells you it is mandatory, and the failure looks like a deployment problem rather than a missing header.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The POST body does not reach your handler
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;await request.json()&lt;/code&gt; came back empty on every deployed attempt. The platform appears to consume the body before the handler sees it. &lt;code&gt;?url=&lt;/code&gt; worked on the first try.&lt;/p&gt;

&lt;p&gt;If you are debugging this: a &lt;code&gt;Request&lt;/code&gt; body is a stream, so you cannot try &lt;code&gt;json()&lt;/code&gt; and then fall back to &lt;code&gt;text()&lt;/code&gt;. The second read fails no matter what the platform did. Read once, and design your endpoint so the query string is the path you trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;browser.evaluate()&lt;/code&gt; returns a wrapped value
&lt;/h2&gt;

&lt;p&gt;My first live call returned an object with no &lt;code&gt;title&lt;/code&gt; in it, and the code fell over with &lt;code&gt;Cannot read properties of undefined&lt;/code&gt;. The browser had worked fine; the return value simply is not the raw result of your script. It arrives inside a wrapper.&lt;/p&gt;

&lt;p&gt;Rather than guess the wrapper's key name from a crash, I made the failure teach me: unwrap the likely candidates, and if none fit, throw an error containing the keys that actually arrived. One failed request then tells you the real shape instead of ten.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Two domains, and only one of them is your site
&lt;/h2&gt;

&lt;p&gt;A deployment gets &lt;code&gt;&amp;lt;project&amp;gt;-&amp;lt;deploymentId&amp;gt;.edgeone.dev&lt;/code&gt;. Your project also has &lt;code&gt;&amp;lt;project&amp;gt;.edgeone.dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first is pinned to one specific deployment forever. If you copy it out of the console and keep refreshing it after a push, you will watch a build succeed and conclude that your new code never landed. The production alias is the second one. Test there.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Node 24 is accepted, and routing is automatic
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;{"nodeVersion": "24"}&lt;/code&gt; in &lt;code&gt;edgeone.json&lt;/code&gt; works, and no full version string is needed. Builds took about 73 seconds consistently. And a folder at &lt;code&gt;agents/audit/&lt;/code&gt; is served at &lt;code&gt;/audit&lt;/code&gt; with no routing configuration at all, which is genuinely pleasant.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Export both handler names
&lt;/h2&gt;

&lt;p&gt;The agents documentation shows &lt;code&gt;export default async function(context)&lt;/code&gt;. The Pages Functions convention is &lt;code&gt;export async function onRequest(context)&lt;/code&gt;. I could not determine from the docs which one the agent runtime actually calls, so I exported both. It costs one line and removes the guess entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Your free subdomain has a reputation you did not earn
&lt;/h2&gt;

&lt;p&gt;This one surprised me most, and it has nothing to do with code.&lt;/p&gt;

&lt;p&gt;Every project gets a &lt;code&gt;*.edgeone.dev&lt;/code&gt; subdomain. It works, it has SSL, it is free. But it is also shared with thousands of other deployments, and platforms like this are a favourite of phishing campaigns, so the whole namespace carries their history.&lt;/p&gt;

&lt;p&gt;When I tried to post my project link on X, the post was refused outright: &lt;em&gt;"this link has been identified by X or our partners as being potentially harmful."&lt;/em&gt; My site was a day old and had never done anything. It didn't matter. I was borrowing a neighbourhood's reputation, not building my own.&lt;/p&gt;

&lt;p&gt;If you plan to share what you build, and for a hackathon or a portfolio that is the whole point, attach a custom domain early, under a name you already own. Every platform that hands you a free shared subdomain has this same property; it is simply not something anyone mentions while you are enjoying how fast the deploy was.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more, from moving the repo
&lt;/h2&gt;

&lt;p&gt;After launch I moved the project to a clean repository. &lt;strong&gt;Project Settings → Git Management → Disconnect, then connect the new repo.&lt;/strong&gt; Changing the source repository does not rename the project, so the production URL survives, which matters if you have already published the link anywhere. Connecting also does not trigger a build; you have to deploy. Mine landed in about 70 seconds and the agent came back up with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone starting
&lt;/h2&gt;

&lt;p&gt;The browser sandbox is the reason to be here. It is not a gimmick. It lets an agent observe things about a page that no amount of &lt;code&gt;fetch&lt;/code&gt; will reveal, and it is available on the free tier.&lt;/p&gt;

&lt;p&gt;The rough edges are all discovery problems, not capability problems. Every one of the eight above is a five-minute fix once you know it exists, and a lost afternoon before that. Write your first deploy off as reconnaissance, log aggressively, and make your error messages report what actually arrived rather than what you expected. The &lt;code&gt;evaluate()&lt;/code&gt; wrapper cost me one request to solve instead of ten purely because the error printed the keys it actually received.&lt;/p&gt;

&lt;p&gt;If you are early in your career and wondering whether a platform like this is worth the trouble: the trouble &lt;em&gt;is&lt;/em&gt; the material. I got a working tool out of this weekend, but I also got an article, and the article came entirely from the parts that went wrong.&lt;/p&gt;

&lt;p&gt;One last thing I did not expect from the data. Both &lt;code&gt;github.com&lt;/code&gt; and &lt;code&gt;developer.mozilla.org&lt;/code&gt; score 90 on security headers rather than 100, and both lose those points to the same missing header: &lt;code&gt;Permissions-Policy&lt;/code&gt;. When two of the most carefully operated sites on the web have the same gap, it is probably not negligence. It is a header the industry has not made a habit of yet.&lt;/p&gt;

&lt;p&gt;Written for DevHandal 2026 Batch 2. #TencentEdgeOne #EdgeOneMakers #CODEPOLITAN #EdgeOne&lt;/p&gt;

</description>
      <category>tencentedgeone</category>
      <category>edgeonemakers</category>
      <category>codepolitan</category>
      <category>edgeone</category>
    </item>
  </channel>
</rss>
