<?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: madebyaman</title>
    <description>The latest articles on DEV Community by madebyaman (@madebyaman).</description>
    <link>https://dev.to/madebyaman</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%2F93848%2Fd4828cfa-d9eb-40f9-9b5b-4b3c5c74e236.png</url>
      <title>DEV Community: madebyaman</title>
      <link>https://dev.to/madebyaman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/madebyaman"/>
    <language>en</language>
    <item>
      <title>Making Human Approvals Trustworthy</title>
      <dc:creator>madebyaman</dc:creator>
      <pubDate>Mon, 29 Jun 2026 18:35:58 +0000</pubDate>
      <link>https://dev.to/madebyaman/making-human-approvals-trustworthy-4855</link>
      <guid>https://dev.to/madebyaman/making-human-approvals-trustworthy-4855</guid>
      <description>&lt;p&gt;At first, approvals sound simple.&lt;/p&gt;

&lt;p&gt;Show a button. Let someone click approve. Continue the workflow.&lt;/p&gt;

&lt;p&gt;But real approvals are much harder than that.&lt;/p&gt;

&lt;p&gt;Nod has to answer important questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who requested this approval?&lt;/li&gt;
&lt;li&gt;Who approved or rejected it?&lt;/li&gt;
&lt;li&gt;Was the person allowed to decide?&lt;/li&gt;
&lt;li&gt;Did the approval expire?&lt;/li&gt;
&lt;li&gt;Was the callback delivered?&lt;/li&gt;
&lt;li&gt;Can we prove what happened later?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why Nod stores approvals as real state, not temporary UI.&lt;/p&gt;

&lt;p&gt;Each approval has a status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pending
approved
rejected
expired
canceled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only one final decision can win. If two people click at the same time, Nod must safely accept the first valid decision and reject stale attempts.&lt;/p&gt;

&lt;p&gt;Slack also needs careful handling. Nod verifies Slack signatures, checks the approval and channel, and stores an actor snapshot for the audit log. After a decision, Slack messages can be updated so old buttons are no longer useful.&lt;/p&gt;

&lt;p&gt;Webhooks also need trust. Nod signs every callback so customer apps can verify it before continuing.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&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;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secret&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;NOD_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&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;We learned that approvals are not just a product feature. They are a security system.&lt;/p&gt;

&lt;p&gt;A good approval layer needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Idempotency&lt;/li&gt;
&lt;li&gt;Expiration&lt;/li&gt;
&lt;li&gt;Webhook signing&lt;/li&gt;
&lt;li&gt;Retry logic&lt;/li&gt;
&lt;li&gt;Audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what Nod is built around.&lt;/p&gt;

</description>
      <category>h0hackathon</category>
    </item>
    <item>
      <title>Why Nod Is An Approval Layer, Not A Workflow Engine</title>
      <dc:creator>madebyaman</dc:creator>
      <pubDate>Mon, 29 Jun 2026 18:35:13 +0000</pubDate>
      <link>https://dev.to/madebyaman/why-nod-is-an-approval-layer-not-a-workflow-engine-2j0b</link>
      <guid>https://dev.to/madebyaman/why-nod-is-an-approval-layer-not-a-workflow-engine-2j0b</guid>
      <description>&lt;p&gt;One of the biggest decisions we made while building Nod was what not to build.&lt;/p&gt;

&lt;p&gt;Nod is not a workflow engine.&lt;/p&gt;

&lt;p&gt;It does not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run customer code&lt;/li&gt;
&lt;li&gt;Store workflow steps&lt;/li&gt;
&lt;li&gt;Resume execution cursors&lt;/li&gt;
&lt;li&gt;Hold customer infrastructure credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, Nod does one thing well: it handles the human decision.&lt;/p&gt;

&lt;p&gt;Your app stays in control of the workflow. Nod only answers the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did a human approve this action?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The developer experience looks like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;approval&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;nod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;approvals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`deploy:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;commitSha&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="na"&gt;policyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod_deploy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deploy to production?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;justnod/web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;commitSha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;diffUrl&lt;/span&gt;&lt;span class="p"&gt;,&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;After a human approves or rejects, Nod sends a signed webhook:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&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;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secret&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;NOD_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;approval.approved&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;await&lt;/span&gt; &lt;span class="nf"&gt;deployToProduction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;This separation keeps Nod simple and useful.&lt;/p&gt;

&lt;p&gt;The customer owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The agent&lt;/li&gt;
&lt;li&gt;The workflow&lt;/li&gt;
&lt;li&gt;The business logic&lt;/li&gt;
&lt;li&gt;The final side effect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nod owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The approval request&lt;/li&gt;
&lt;li&gt;The policy&lt;/li&gt;
&lt;li&gt;The human decision&lt;/li&gt;
&lt;li&gt;The audit trail&lt;/li&gt;
&lt;li&gt;The signed callback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That boundary made the product much cleaner. Developers do not need to move their whole system into Nod. They only add one approval checkpoint where risk begins.&lt;/p&gt;

</description>
      <category>h0hackathon</category>
    </item>
    <item>
      <title>Building Nod With Vercel And Amazon Aurora PostgreSQL</title>
      <dc:creator>madebyaman</dc:creator>
      <pubDate>Mon, 29 Jun 2026 18:34:31 +0000</pubDate>
      <link>https://dev.to/madebyaman/building-nod-with-vercel-and-amazon-aurora-postgresql-4e8g</link>
      <guid>https://dev.to/madebyaman/building-nod-with-vercel-and-amazon-aurora-postgresql-4e8g</guid>
      <description>&lt;p&gt;Nod is an approval API for AI agents, scripts, and workflows.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app wants to do something risky.&lt;/li&gt;
&lt;li&gt;Nod asks a human for approval.&lt;/li&gt;
&lt;li&gt;The human approves in Slack or web.&lt;/li&gt;
&lt;li&gt;Nod sends a signed callback.&lt;/li&gt;
&lt;li&gt;Your app continues safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built the web app on &lt;strong&gt;Vercel&lt;/strong&gt;. The dashboard lets teams manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workspaces&lt;/li&gt;
&lt;li&gt;Members and roles&lt;/li&gt;
&lt;li&gt;Approval policies&lt;/li&gt;
&lt;li&gt;Slack channels&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Callback endpoints&lt;/li&gt;
&lt;li&gt;Approval history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the database, we used &lt;strong&gt;Amazon Aurora PostgreSQL&lt;/strong&gt;. Nod needs a strong relational database because approval data must be correct. An approval is not just a UI card. It has a lifecycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pending -&amp;gt; approved
pending -&amp;gt; rejected
pending -&amp;gt; expired
pending -&amp;gt; canceled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aurora stores the source of truth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approval requests&lt;/li&gt;
&lt;li&gt;Human decisions&lt;/li&gt;
&lt;li&gt;Policy versions&lt;/li&gt;
&lt;li&gt;Webhook events&lt;/li&gt;
&lt;li&gt;Delivery attempts&lt;/li&gt;
&lt;li&gt;Audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend runs on AWS with Lambda workers. One worker sends Slack notifications. Another sends signed callbacks. Another expires old approvals.&lt;/p&gt;

&lt;p&gt;A typical flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App or agent
  -&amp;gt; Nod API
  -&amp;gt; Aurora PostgreSQL
  -&amp;gt; Slack or web approval
  -&amp;gt; Signed callback
  -&amp;gt; App continues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vercel helped us move fast on the user experience. Aurora gave us the reliable data layer needed for real approvals. Together, they helped us build Nod as infrastructure, not just a demo.&lt;/p&gt;

</description>
      <category>h0hackathon</category>
    </item>
  </channel>
</rss>
