<?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: Manikant Kella</title>
    <description>The latest articles on DEV Community by Manikant Kella (@manikant92).</description>
    <link>https://dev.to/manikant92</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2F594e40e2-f0df-4d2d-bb0a-5f64c3533563.jpeg</url>
      <title>DEV Community: Manikant Kella</title>
      <link>https://dev.to/manikant92</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manikant92"/>
    <language>en</language>
    <item>
      <title>Hermes Agent Has Four Memories — And That's Why It Doesn't Forget You</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Sat, 23 May 2026 17:37:26 +0000</pubDate>
      <link>https://dev.to/manikant92/hermes-agent-has-four-memories-and-thats-why-it-doesnt-forget-you-2e55</link>
      <guid>https://dev.to/manikant92/hermes-agent-has-four-memories-and-thats-why-it-doesnt-forget-you-2e55</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;: Write About Hermes Agent&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;A cognitive-science tour of the only open-source agent that gets smarter the longer you run it. We map Hermes' file layout onto the four classical memory systems of the human brain — and find an architecture that quietly solves the problem every other agent fakes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I want to argue something that sounds like marketing but isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hermes Agent isn't impressive because it's "self-improving."&lt;/strong&gt; That phrase has been on a thousand landing pages. It's impressive because — possibly by accident, possibly on purpose — the architecture matches how a human brain stores memory. Once you see the mapping, the whole project stops looking like a clever Python wrapper and starts looking like a thesis: &lt;em&gt;if you want an agent that compounds, you have to give it the same memory systems we use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm an ML engineer who builds recommendation and intent systems for a living, and I spend a lot of time thinking about the gap between an agent that's &lt;em&gt;interesting&lt;/em&gt; and an agent that &lt;em&gt;holds up after week three&lt;/em&gt;. Almost few article write-up of Hermes I've read covers the surface — "it remembers things, it has skills, it has crons." All true. None of them ask the more useful question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why this particular decomposition? Why these specific files? Why does a system designed in 2026 look so much like a 1970s cognitive-psychology paper?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the post. Grab a coffee.&lt;/p&gt;




&lt;h2&gt;
  
  
  The goldfish problem (and why "context window" isn't the answer)
&lt;/h2&gt;

&lt;p&gt;Every agent in production today has the same failure mode, and we've all felt it.&lt;/p&gt;

&lt;p&gt;Monday: you carefully explain your project, your stack, the three constraints that always trip up the model. The agent crushes the task. You're delighted.&lt;/p&gt;

&lt;p&gt;Tuesday: you open a fresh session. The agent has no idea who you are. You re-explain. You re-correct. You re-paste the same context blob. This is what people mean when they say agents "feel like demos." They aren't building anything that lasts; they're paying the same context tax every single morning.&lt;/p&gt;

&lt;p&gt;The standard responses to this problem are bad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bigger context windows&lt;/strong&gt; — expensive, doesn't scale with how long you actually want the agent to live (months, not turns), and still amnesiac the moment the conversation ends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector databases&lt;/strong&gt; — useful for retrieval, terrible for identity. A RAG store doesn't know who you are; it knows which 8 chunks resemble your last question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-tuning&lt;/strong&gt; — slow, expensive, and updates an agent's beliefs at the speed of GPU procurement, not the speed of Tuesday.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is &lt;em&gt;memory.&lt;/em&gt; They're all variations on "search more text faster." The reason your brain doesn't work this way is that &lt;strong&gt;your brain has at least four distinct memory systems, and each one solves a different problem.&lt;/strong&gt; When cognitive psychologists separated them in the 1970s and 80s — names like Tulving, Squire, Cohen — they did it because patients with brain damage would lose one and keep the others. The systems are genuinely independent.&lt;/p&gt;

&lt;p&gt;So here's the simple claim: &lt;strong&gt;the reason Hermes Agent works is that it has all four, and they're separate, and they're stored on disk where you can read them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me show you.&lt;/p&gt;




&lt;h2&gt;
  
  
  A 90-second cognitive-science primer
&lt;/h2&gt;

&lt;p&gt;You don't need a degree to follow this. Four words, four jobs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Holds&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Procedural memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How to do things&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Riding a bike. Touch-typing. Driving a stick shift.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semantic memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Facts about the world&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Paris is the capital of France. Mitochondria are the powerhouse of the cell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Episodic memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Things that happened to you&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;What you had for breakfast yesterday. The argument you had in 2019.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Working memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What you're holding right now&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;The phone number someone just said out loud.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There's a fifth thing that isn't a memory system but is the &lt;em&gt;substrate&lt;/em&gt; the others run inside: your &lt;strong&gt;identity&lt;/strong&gt; — the sense of who you are that makes all four useful instead of just being a pile of disconnected data. Without a self, your "memories" are just other people's footage.&lt;/p&gt;

&lt;p&gt;Now look at this directory listing from a fresh Hermes Agent install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.hermes/
├── SOUL.md           # identity (the substrate)
├── memories/
│   ├── USER.md       # who you are (the agent's model of me)
│   └── MEMORY.md     # what's true in my world right now
├── skills/           # how to do things  (one folder per SKILL.md)
├── sessions/         # SQLite + FTS5: every conversation, searchable
└── cron/             # scheduled jobs that fire in fresh sessions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm going to take you through these one at a time and show you which cognitive system each one is doing, why the boundaries are drawn where they are, and what each one prevents from going wrong. We'll end with the part nobody on dev.to has written about yet: &lt;strong&gt;how a feature called the Curator (added in v0.12) functions as the agent's equivalent of sleep.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  SOUL.md — the substrate (identity)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SOUL.md&lt;/code&gt; is the first file loaded into the system prompt at every turn. The Hermes architecture docs call it "slot #1." It contains the agent's personality, communication style, role, and constraints. It's about 1,000–3,000 characters in most setups.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You are a senior research analyst. You write in short paragraphs. You don't apologize. You never use the word 'leverage.'"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you stop reading here you'll think this is just a system prompt. It isn't. The trick is what &lt;code&gt;SOUL.md&lt;/code&gt; &lt;em&gt;doesn't&lt;/em&gt; contain: facts about your projects, your name, anything that changes week-to-week. Those go in different files. &lt;code&gt;SOUL.md&lt;/code&gt; is the slow-moving part — the part that makes a Telegram bot reading your code reviews behave consistently different from one that drafts your customer support replies.&lt;/p&gt;

&lt;p&gt;You can run multiple Hermes profiles on the same machine, each with its own &lt;code&gt;SOUL.md&lt;/code&gt;. A finance-ops agent and a research agent share the same underlying model but read as different colleagues. This is exactly how human personality works: same neural hardware, different self-concepts shaping the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this prevents:&lt;/strong&gt; &lt;em&gt;tone drift.&lt;/em&gt; The slow, frustrating phenomenon where an agent that was crisp on day 1 has become a verbose, hedging, em-dash-spamming mess by day 30 because nothing was holding its character in place.&lt;/p&gt;




&lt;h2&gt;
  
  
  USER.md and MEMORY.md — semantic memory (facts about your world)
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;~/.hermes/memories/USER.md&lt;/code&gt; after a few weeks of use and you'll find something like this (this is illustrative, not from any specific install):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Manikant&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ML/AI engineer&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Communication&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prefers terse responses, no apology preambles&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Working on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompt-recommendation pipelines, intent taxonomy&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Avoid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;marketing language, "leverage", em-dashes in code blocks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;MEMORY.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Active project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CCx personalization pipeline&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Stack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Databricks, FastAPI on AKS, Redis, OneLLM&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;p95 target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sub-1s on warm path&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Recent issue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kong/Okta 403 — fixed by adding personalization_services scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two files. One is &lt;em&gt;who you are&lt;/em&gt; (slow-changing). One is &lt;em&gt;what's currently true in your world&lt;/em&gt; (medium-changing). Both get injected into the system prompt at session start as a frozen snapshot.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;semantic memory&lt;/strong&gt; — declarative facts that the agent can state without re-deriving them. The reason it's split into two files instead of one is the same reason your brain separates "I am a software engineer" from "the build is currently broken." One is identity-level. One is operational. Mixing them is how you end up with an agent that introduces itself in every reply.&lt;/p&gt;

&lt;p&gt;The character limits matter. The published numbers I've seen are roughly &lt;strong&gt;USER.md ≤ 1,375 chars, MEMORY.md ≤ 2,200 chars.&lt;/strong&gt; That's tiny on purpose. Memory in Hermes is a &lt;em&gt;pointer index&lt;/em&gt;, not a data lake. If something needs more space, it doesn't belong here — it belongs in a skill, or in the episodic store (the session DB). This is the single most important design choice in the system and the one most newcomers fight against.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this prevents:&lt;/strong&gt; &lt;em&gt;the context-amnesia tax.&lt;/em&gt; You stop re-explaining yourself every Monday. The agent walks into the session already knowing the constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The non-obvious failure mode:&lt;/strong&gt; stale memory. If &lt;code&gt;MEMORY.md&lt;/code&gt; still says "the build is broken" three weeks after you fixed it, the agent will keep working around a phantom. Treat these files like sticky notes on your monitor, not like a database. If your agent starts behaving oddly, this is almost always the first place to look.&lt;/p&gt;




&lt;h2&gt;
  
  
  SKILL.md — procedural memory (how to do things)
&lt;/h2&gt;

&lt;p&gt;This is the part of Hermes that most reviewers fixate on, but they usually describe &lt;em&gt;what&lt;/em&gt; it is without explaining &lt;em&gt;why this particular shape&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A skill is a markdown file with YAML front matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy-fastapi-aks&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy a FastAPI service to Azure Kubernetes Service with our standard manifests&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.2.0&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hermes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;devops&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;azure&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;aks&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Deploy FastAPI to AKS&lt;/span&gt;

&lt;span class="gu"&gt;## When to use&lt;/span&gt;
The user wants to ship a FastAPI service to our AKS cluster.

&lt;span class="gu"&gt;## Procedure&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Confirm the image tag exists in ACR
&lt;span class="p"&gt;2.&lt;/span&gt; Run &lt;span class="sb"&gt;`kubectl apply -f manifests/`&lt;/span&gt;
&lt;span class="p"&gt;3.&lt;/span&gt; Watch the rollout with &lt;span class="sb"&gt;`kubectl rollout status`&lt;/span&gt;
&lt;span class="p"&gt;4.&lt;/span&gt; Hit /healthz from a pod in the cluster to confirm

&lt;span class="gu"&gt;## Pitfalls&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; If the rollout stalls &amp;gt; 60s, check the readiness probe path
&lt;span class="p"&gt;-&lt;/span&gt; Token expiry: rotate via &lt;span class="sb"&gt;`az acr login`&lt;/span&gt; before applying
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is procedural memory. It's not facts about your stack; it's a &lt;em&gt;recipe&lt;/em&gt; for executing a workflow that survives the agent forgetting your conversation. You can install skills from URLs (&lt;code&gt;hermes skills install https://example.com/SKILL.md&lt;/code&gt;), browse a community hub at &lt;code&gt;agentskills.io&lt;/code&gt;, or write your own — but the most interesting case is the one where the agent writes them itself.&lt;/p&gt;

&lt;p&gt;After Hermes solves a complex task, it can call a tool called &lt;code&gt;skill_manage&lt;/code&gt; and propose saving the solution as a new skill. You confirm, it writes the file, and from then on the workflow is one slash command away. The next time you ask for the same kind of thing, the agent doesn't re-derive the solution from first principles — it loads the recipe.&lt;/p&gt;

&lt;p&gt;Here's the part that took me three readings of the Hermes architecture page to appreciate. &lt;strong&gt;Skills don't cost tokens until they're used.&lt;/strong&gt; The mechanism is called &lt;strong&gt;progressive disclosure&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At session start: &lt;code&gt;skills_list()&lt;/code&gt; loads a compact list of names and descriptions only. Roughly 3k tokens for ~100 skills.&lt;/li&gt;
&lt;li&gt;When the agent decides one is relevant: &lt;code&gt;skill_view(name)&lt;/code&gt; reads the full SKILL.md.&lt;/li&gt;
&lt;li&gt;If the skill references a deep file: &lt;code&gt;skill_view(name, "references/api-docs.md")&lt;/code&gt; reads that one file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's a three-tier lazy-load. The agent never pays the full cost of all its procedural knowledge — only the tip of the iceberg you're using &lt;em&gt;right now&lt;/em&gt;. This is the unlock that makes the "more skills makes the agent better, not slower" property actually hold. Without progressive disclosure, every skill you add is a tax on every conversation. With it, the cost is bounded by what you actually invoke.&lt;/p&gt;

&lt;p&gt;You don't have to take my word for it. The official skills docs (&lt;a href="https://hermes-agent.nousresearch.com/docs/guides/work-with-skills" rel="noopener noreferrer"&gt;here&lt;/a&gt;) lay out the three-tier load explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this prevents:&lt;/strong&gt; &lt;em&gt;workflow drift.&lt;/em&gt; The same task done differently every time because the agent is improvising rather than following a known-good procedure. Skills are the difference between "the agent figured it out again" and "the agent did it the way we always do it."&lt;/p&gt;




&lt;h2&gt;
  
  
  sessions/ + FTS5 — episodic memory (things that happened)
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;~/.hermes/sessions/&lt;/code&gt; and you'll find a SQLite database. Inside, FTS5 — SQLite's full-text search — indexes every turn, every tool call, every result the agent has ever produced.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;episodic memory.&lt;/strong&gt; Not facts ("I prefer dark mode"), not skills ("how to deploy"), but the actual diary of what happened. When does this matter? When you say something like: &lt;em&gt;"that bug we hit two weeks ago with the Redis TTL — what did we end up doing?"&lt;/em&gt; Hermes runs a search across the session history, summarizes the relevant turns with the LLM, and brings the answer back.&lt;/p&gt;

&lt;p&gt;This is the layer that most people who shrug off "the agent has memory" never actually use. They put facts in MEMORY.md and call it done. The episodic layer is what makes the agent feel like a colleague who was &lt;em&gt;there&lt;/em&gt; with you, not just a search engine that remembers your settings.&lt;/p&gt;

&lt;p&gt;The reason this is implemented as SQLite + FTS5 instead of a vector database is worth pausing on. Embeddings are great for "find me something semantically similar." But episodic memory is dominated by &lt;em&gt;literal recall&lt;/em&gt;: specific error messages, file paths, dates, function names. FTS5 is faster, cheaper, has no extra service to run, and handles literal queries better than embeddings. The trade-off is that fuzzy queries work less well — but that's what the LLM-on-top-of-search layer compensates for. The agent reads the raw matches and synthesizes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this prevents:&lt;/strong&gt; &lt;em&gt;the "did we already try that?" tax.&lt;/em&gt; You stop re-running experiments you've already run. The agent can tell you what was tried, what worked, and what was abandoned without you remembering the original session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Working memory — the running conversation
&lt;/h2&gt;

&lt;p&gt;The current session — your active conversation, the in-flight tool calls, the scratchpad of what the agent has done in the last 20 turns — is working memory. It's bounded by the model's context window, and Hermes has a dedicated component called &lt;code&gt;context_compressor.py&lt;/code&gt; that summarizes older turns once the context crosses about 50% of the available window, preserving recent messages and grouping related tool calls together.&lt;/p&gt;

&lt;p&gt;This isn't fancy. But it's the unglamorous plumbing that makes long sessions possible without the agent suddenly forgetting what it was just doing. Working memory in humans has the same property: about 7±2 items, and the brain dumps older items into longer-term stores constantly. Hermes does it explicitly because LLMs don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part nobody writes about: the Curator (v0.12+)
&lt;/h2&gt;

&lt;p&gt;If you've read other Hermes write-ups, you've seen the four-pillar story (memory, skills, soul, crons) and the five-pillar story (add the self-improving loop). Almost nobody is talking about what shipped in version 0.12: &lt;strong&gt;the autonomous Curator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Curator is a cron job — running on a 7-day cycle by default — whose only job is to &lt;strong&gt;grade, consolidate, and prune the agent's own skill library.&lt;/strong&gt; It walks through every skill in &lt;code&gt;~/.hermes/skills/&lt;/code&gt;, scores it, merges near-duplicates, archives skills that haven't been invoked in N weeks, and rewrites descriptions for clarity. The agent does this on itself, while you sleep.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because every learning system has the same problem: &lt;strong&gt;growth alone isn't intelligence.&lt;/strong&gt; If your agent adds a skill every time you do something complex, in six months you have 800 skills, half of them redundant, a third of them stale. The skill index gets noisy. The agent picks the wrong skill for a task because the descriptions overlap. The compounding starts to compound backwards.&lt;/p&gt;

&lt;p&gt;The Curator is consolidation. In humans, the analogous process is &lt;strong&gt;sleep&lt;/strong&gt; — specifically slow-wave sleep, when the hippocampus replays the day's episodic memories and the cortex selectively keeps, merges, and discards them. You wake up and the things that didn't matter are gone, the things that did are integrated. Memory researchers have been writing about this since the 1990s. Hermes is, as far as I can tell, the first widely-used open-source agent to ship it as a first-class feature.&lt;/p&gt;

&lt;p&gt;When I realized this is what the Curator was doing, the architecture story clicked all the way: &lt;strong&gt;Hermes isn't a smart loop on top of an LLM. It's an attempt to give a stateless model the same memory-management apparatus a biological system uses.&lt;/strong&gt; Identity, semantic, procedural, episodic, working — plus consolidation. That's the whole stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  A diagram, because diagrams help
&lt;/h2&gt;

&lt;p&gt;Here is the mental model I've been building toward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌───────────────────────────────────────────┐
                    │            SOUL.md  (identity)            │
                    │  the substrate that makes the rest useful │
                    └────────────────────┬──────────────────────┘
                                         │
        ┌────────────────────┬───────────┼────────────────────────┐
        │                    │           │                        │
   ┌────▼─────┐    ┌─────────▼────┐  ┌───▼───────┐    ┌──────────▼─────────┐
   │ USER.md  │    │  MEMORY.md   │  │ SKILL.md  │    │  sessions/ (FTS5)  │
   │ + USER MODEL    │              │  │           │    │                    │
   │ semantic  │   │  semantic    │  │ procedural│    │      episodic      │
   │ (who I am)│    │ (my world)   │  │ (how-to)  │    │  (what happened)   │
   └───────────┘    └──────────────┘  └─────┬─────┘    └──────────┬─────────┘
                                            │                     │
                                       progressive            FTS5 + LLM
                                        disclosure              recall
                                            │                     │
                                       ┌────▼─────────────────────▼────┐
                                       │   working memory (the turn)   │
                                       │   + context_compressor.py     │
                                       └────────────────┬──────────────┘
                                                        │
                                                ┌───────▼────────┐
                                                │  the Curator   │
                                                │ (weekly cron)  │
                                                │  = "sleep"     │
                                                └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever stared at a Tulving diagram of memory systems and then stared at this layout, the resemblance is uncanny.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bonus layer most people miss: trajectory export
&lt;/h2&gt;

&lt;p&gt;There's one more thing I want to flag, because it's the part that makes Hermes interesting if you're an ML engineer rather than just an end user.&lt;/p&gt;

&lt;p&gt;Every conversation Hermes has is, by design, exportable in &lt;strong&gt;ShareGPT format&lt;/strong&gt; — the de-facto schema for instruction-tuning data. The repo includes an Atropos integration for reinforcement-learning environments. The agent's own sessions become &lt;em&gt;training data for the next version of the model that runs the agent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This closes a flywheel that almost no other open-source agent has closed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You use Hermes. It logs what worked.&lt;/li&gt;
&lt;li&gt;The Curator consolidates the useful skills.&lt;/li&gt;
&lt;li&gt;The trajectories get exported.&lt;/li&gt;
&lt;li&gt;Someone (Nous Research, your team, you) fine-tunes a model on those trajectories.&lt;/li&gt;
&lt;li&gt;The fine-tuned model becomes a better Hermes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is also why Hermes is described as "built by model trainers." Nous Research isn't a UX shop; they ship the Hermes, Nomos, and Psyche model families. The agent is &lt;em&gt;also&lt;/em&gt; their data-collection apparatus. Once you know this, the design choices stop looking arbitrary. Of course they separated procedural from semantic from episodic — they need clean labels to train on.&lt;/p&gt;

&lt;p&gt;I'm not making a claim about what fine-tuning runs they're doing internally. The mechanism is there in the codebase; what they do with it is their business. What I'm pointing out is that &lt;strong&gt;the architecture is shaped by the data it generates&lt;/strong&gt;, which is a property almost no other open-source agent has.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means if you're building something
&lt;/h2&gt;

&lt;p&gt;If you've read this far you probably want practical takeaways. Here are five.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Don't treat MEMORY.md as a dumping ground.&lt;/strong&gt; It's a sticky note, not a database. If you put everything in it, the agent will get confused about what's currently true. Aggressively prune.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Let the agent write skills, then edit them.&lt;/strong&gt; Hand-written skills from scratch tend to be too generic. Agent-written skills are concrete because they were extracted from a real workflow you just completed. Your job is editorial — tighten the description, add the pitfall you noticed, ship it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "when to use" section in a SKILL.md is the most important sentence in the file.&lt;/strong&gt; That's what the agent searches against when deciding to invoke the skill. Treat it like ad copy: specific, keyword-rich, unambiguous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Multiple &lt;code&gt;SOUL.md&lt;/code&gt; profiles beat one mega-agent.&lt;/strong&gt; A finance agent and a research agent with different &lt;code&gt;SOUL.md&lt;/code&gt; files and different credentials are cleaner, safer, and easier to debug than one agent with all the skills and all the API keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The cron + skill combo is the actual moat.&lt;/strong&gt; A single sentence — &lt;em&gt;"every night at 1am, pull the latest commits and summarize them on Slack"&lt;/em&gt; — produces a skill, a scheduled job, and an output destination, all at once. This is what shifts an agent from reactive to proactive. Most "agent" demos can't do this without 200 lines of glue code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest caveats
&lt;/h2&gt;

&lt;p&gt;I'd rather not write a love letter, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stale memory is the #1 reason Hermes "starts acting weird."&lt;/strong&gt; If your agent is misbehaving, open &lt;code&gt;MEMORY.md&lt;/code&gt; before anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill name collisions are real.&lt;/strong&gt; A skill in your home directory shadows the same name in an external repo, which means "it works on my machine" is a known failure pattern when a team shares skills.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The cost story depends on your model choice.&lt;/strong&gt; Progressive disclosure keeps the &lt;em&gt;agent&lt;/em&gt; lean. The model behind it can still be expensive if you point it at a frontier API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Self-improving" doesn't mean automatic.&lt;/strong&gt; The official docs say it themselves: the loop works best when you actively correct mistakes, save things to memory, and update skills. Passive use produces some improvement; active use compounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 40% research-task speedup number that floats around&lt;/strong&gt; is from a third-party benchmark I haven't personally re-run. Treat it as directional.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I started this post with a claim: that Hermes Agent works because its architecture matches how a brain stores memory. I think the mapping is real, and I think it explains why this particular agent has 164,000+ GitHub stars while a dozen frameworks with louder marketing are stuck at 5k.&lt;/p&gt;

&lt;p&gt;Identity that doesn't drift. Facts that update. Procedures that get reused. Episodes that can be searched. Consolidation that happens while you sleep.&lt;/p&gt;

&lt;p&gt;None of those features is novel on its own. Vector DBs do semantic-ish search. Prompt frameworks do identity-ish anchoring. RAG does episodic-ish retrieval. What's new is putting them all in one place, with clean boundaries, in plain markdown files you can &lt;code&gt;cat&lt;/code&gt;. That's the architecture. And once you see it through this lens, the next time you build an agent, you'll find yourself reaching for the same five buckets — because they're the buckets that work.&lt;/p&gt;

&lt;p&gt;If you've made it this far, I'd love to hear which of the five layers you'd want to extend first. The Curator is the one I'm most curious about — there's a whole research direction in "how should an agent forget" that nobody's seriously explored yet, and it's sitting right there in &lt;code&gt;~/.hermes/cron/&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful, the conversation is open — drop your own mental model in the comments. And if you spot something I got wrong about the internals, please correct me. Hermes is moving fast and I want to keep this honest.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>Gemma 4 26B Is Not the MoE You Think It Is</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Sat, 23 May 2026 17:02:16 +0000</pubDate>
      <link>https://dev.to/manikant92/gemma-4-26b-is-not-the-moe-you-think-it-is-3ocb</link>
      <guid>https://dev.to/manikant92/gemma-4-26b-is-not-the-moe-you-think-it-is-3ocb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Write About Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;"Everyone describes Gemma 4 26B as a standard MoE. The architecture says otherwise. Here's the design choice nobody's unpacking."&lt;/p&gt;

&lt;p&gt;When Google released Gemma 4 on April 2, 2026, the headlines about the 26B model were predictable: &lt;em&gt;"It's an MoE!"&lt;/em&gt; — &lt;em&gt;"Activates only 3.8B parameters!"&lt;/em&gt; — &lt;em&gt;"26B quality at 4B cost!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;All of those are technically true. None of them describe what Google actually built.&lt;/p&gt;

&lt;p&gt;Read any of the great write-ups on this topic — and there are a lot of them — and you'll find the same explanation of Gemma 4's MoE: &lt;em&gt;"A router picks a small subset of experts for each token."&lt;/em&gt; That's the description of Mixtral. Of DeepSeek. Of Qwen. It's a fine description of Mixture-of-Experts in general.&lt;/p&gt;

&lt;p&gt;It is &lt;strong&gt;not what Gemma 4 26B does.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google built something quietly different. If you're planning to fine-tune Gemma 4, serve it at scale, or just want to know what's inside the binary you're downloading, this is the architecture detail that's worth your time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The standard MoE pattern (Mixtral, DeepSeek, Qwen)
&lt;/h2&gt;

&lt;p&gt;Pretend you've never seen MoE before. A transformer is a stack of blocks. Each block has two main components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Attention&lt;/strong&gt; — figures out which other tokens matter for the current one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feed-forward network (FFN)&lt;/strong&gt;, also called the MLP — applies a learned non-linear transformation. This is where most of the parameters live.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The standard MoE trick is: &lt;strong&gt;replace the FFN with multiple FFNs (called "experts") and add a small "router" that picks which ones to run for each token.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Standard MoE block (Mixtral / DeepSeek / Qwen style):

         Input
           │
           ▼
      [Attention]
           │
           ▼
       [Router]  ── decides top-K of N experts
           │
           ▼
  ┌────────┼─────────┬─────────┐
  ▼        ▼         ▼         ▼
[Exp 1] [Exp 2]  [Exp 3] ... [Exp N]
  │        │         │         │
  └────────┴─────────┴─────────┘
           │ (only top-K fire)
           ▼
         Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mixtral 8x7B: 8 experts, 2 active per token. DeepSeek V3: 256 experts, 8 active. The router gives you sparse compute — all parameters are stored in VRAM, but only a small fraction does math for any given token.&lt;/p&gt;

&lt;p&gt;When people say "MoE," this is what they mean. &lt;strong&gt;The dense FFN is replaced by sparse experts.&lt;/strong&gt; That word matters: &lt;em&gt;replaced&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Gemma 4 26B actually does
&lt;/h2&gt;

&lt;p&gt;Gemma 4's MoE block keeps the dense FFN. It doesn't replace it. The sparse experts run as a &lt;strong&gt;parallel path&lt;/strong&gt; alongside the dense one, and the outputs are summed. There's also a &lt;em&gt;shared expert&lt;/em&gt; that fires on every token regardless of what the router decides.&lt;/p&gt;

&lt;p&gt;Three pathways. Two of them always on. One sparse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gemma 4 26B MoE block (the actual design):

                 Input
                   │
                   ▼
              [Attention]
                   │
       ┌───────────┼───────────┐
       ▼           ▼           ▼
  [Dense FFN] [Shared Exp]  [Router]
  (always on)  (always on)     │
                               ▼
                    ┌──────────┼──────────┐
                    ▼          ▼          ▼
                 [Exp 1]   [Exp 2]  ...  [Exp 128]
                    │          │           │
                    └──────────┴───────────┘
                       │ (only 8 fire)
                       ▼
       ┌───────────┴───────────┐
       ▼
   [Sum all three pathways]
       │
       ▼
     Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbers, with the caveat that Google hasn't released a full technical report at time of writing: roughly 128 routed experts, 8 active per token, one always-on shared expert, plus the always-on dense FFN. Total parameters ≈ 25.2B, active per token ≈ 3.8B.&lt;/p&gt;

&lt;p&gt;That third always-on pathway is what makes this design unusual. Standard MoE is a substitution. Gemma 4's MoE is an addition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Google built it this way
&lt;/h2&gt;

&lt;p&gt;Here's the part nobody is unpacking. Why bother keeping a dense path when you've already built an expert system?&lt;/p&gt;

&lt;p&gt;The cleanest answer is &lt;strong&gt;robustness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a standard MoE, the output quality of each block depends entirely on the router making the right choice. If the router picks the wrong experts for a token, the representation degrades, and there's no fallback. Routers are small networks, easy to break during training, and prone to collapse — a failure mode where the router gets stuck picking the same few experts and the rest go to waste.&lt;/p&gt;

&lt;p&gt;In Gemma 4, the dense FFN provides a stable, always-on representational backbone. The shared expert adds a second always-on signal. The routed experts add specialization on top. If the router whiffs on a token, two coherent signals still carry it forward.&lt;/p&gt;

&lt;p&gt;That buys you several real things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Easier training.&lt;/strong&gt; Router collapse is less catastrophic when the dense path is doing useful work in parallel — your loss still goes down even when the routing is bad.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner distillation from a dense teacher.&lt;/strong&gt; Gemma 4 was trained alongside a 31B dense sibling. A student that already has a dense path can absorb a dense teacher's signal naturally; a pure MoE student has to learn to route at the same time it's learning to imitate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation under quantization.&lt;/strong&gt; When you Q3 or Q2 the routed experts to save memory, the dense path stays at full precision and keeps the model coherent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tradeoff is efficiency. A pure MoE wastes nothing — every FLOP serves the network. Gemma 4 spends FLOPs on a dense path that runs even when the experts could have handled a token alone. Google deliberately traded peak MoE efficiency for training robustness and distillation friendliness.&lt;/p&gt;

&lt;p&gt;That choice has downstream consequences. Let's get into them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The catch: memory ≠ compute
&lt;/h2&gt;

&lt;p&gt;Here is the most expensive mistake you can make with Gemma 4 26B: &lt;strong&gt;assuming "3.8B active" means it'll run like a 4B model on your hardware.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It computes like a 4B model. It does not fit like one.&lt;/p&gt;

&lt;p&gt;The router doesn't know in advance which experts will be needed for the next token, so all 128 of them must be loaded in VRAM simultaneously. You save nothing on memory.&lt;/p&gt;

&lt;p&gt;Real numbers (a recent controlled benchmark on Gemma 4, Phi-4, and Qwen3 measured peak VRAM under realistic inference loads):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Active params / token&lt;/th&gt;
&lt;th&gt;Total in VRAM&lt;/th&gt;
&lt;th&gt;Measured peak VRAM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 E4B (dense)&lt;/td&gt;
&lt;td&gt;4.5B&lt;/td&gt;
&lt;td&gt;4.5B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.9 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 26B A4B&lt;/td&gt;
&lt;td&gt;3.8B&lt;/td&gt;
&lt;td&gt;25.2B&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48.1 GB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same active parameter count. Three-times the memory footprint. People running the 26B MoE on Macs with 24 GB have reported swap thrashing and &lt;code&gt;~2 tok/s&lt;/code&gt;. The model is fast in terms of compute per token; it's slow at fitting on consumer hardware.&lt;/p&gt;

&lt;p&gt;The 31B Dense, by comparison, is more honest about its appetite. You see 31B, you allocate for 31B. The MoE label hides the fact that you still need workstation- or server-class memory to use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; if you can't comfortably hold ≥48 GB of model + KV cache + activation buffers, skip the 26B MoE. Use E4B for an edge build, or 31B with quantization-and-offloading for a quality build.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means for production serving
&lt;/h2&gt;

&lt;p&gt;Standard playbook for serving a 26B-ish model: one beefy GPU, one request at a time, cap concurrency to control latency.&lt;/p&gt;

&lt;p&gt;That's exactly the wrong setup for Gemma 4 26B.&lt;/p&gt;

&lt;p&gt;The parallel-dense-plus-experts design rewards concurrency. Different tokens from different users activate different sets of experts. With a sane batcher (vLLM, SGLang, TGI), you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send token A from user 1 through experts &lt;code&gt;{3, 17, 42, 88, 91, 102, 110, 124}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Send token B from user 2 through experts &lt;code&gt;{1, 5, 9, 26, 33, 57, 71, 119}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;…in the same forward pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;expert parallelism&lt;/strong&gt;, and the flag that turns it on is the one that matters in vLLM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; vllm.entrypoints.openai.api_server &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--model&lt;/span&gt; google/gemma-4-26B-A4B-it &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--dtype&lt;/span&gt; bfloat16 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--max-model-len&lt;/span&gt; 32768 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--enable-expert-parallel&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--served-model-name&lt;/span&gt; gemma4-moe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;--enable-expert-parallel&lt;/code&gt;, you're paying the memory cost of an MoE and getting dense-model throughput. With it on, the MoE earns its keep.&lt;/p&gt;

&lt;p&gt;The decision tree, in one line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single user, laptop, predictable latency&lt;/strong&gt; → Gemma 4 &lt;strong&gt;31B Dense&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-user API with 8+ concurrent requests&lt;/strong&gt; → Gemma 4 &lt;strong&gt;26B MoE&lt;/strong&gt; with expert parallelism enabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick one based on your traffic shape, not just the parameter counts on the model card.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this means for fine-tuning
&lt;/h2&gt;

&lt;p&gt;This is where the parallel-dense design becomes really interesting, and where most tooling hasn't caught up yet.&lt;/p&gt;

&lt;p&gt;In Mixtral, DeepSeek, or Qwen, fine-tuning an MoE means choosing what to update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update the router&lt;/strong&gt; → risks expert collapse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update the experts&lt;/strong&gt; → safe but slow and gradient-sparse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LoRA on the experts&lt;/strong&gt; → the standard pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Gemma 4 26B you have a &lt;strong&gt;third surface&lt;/strong&gt; to fine-tune: the dense FFN that runs on every token. That changes the strategy.&lt;/p&gt;

&lt;p&gt;A LoRA on the dense FFN gives you a steady, broadly-applied signal that doesn't require any router decision to take effect. The gradients are clean — they fire on every single token in your dataset, no sparse activation problem. Your domain knowledge "lifts the floor" across the whole model.&lt;/p&gt;

&lt;p&gt;This suggests a layered approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First pass:&lt;/strong&gt; LoRA on the dense FFN paths only. Cheap, fast, broadly effective. Best for general domain adaptation (legal tone, medical terminology, your company's writing style).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Second pass:&lt;/strong&gt; LoRA on the shared expert. Similar broad coverage, smaller capacity, slightly more specialized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third pass (only if needed):&lt;/strong&gt; LoRA on selected routed experts. Targeted, expensive, useful when you need specialization the dense path can't absorb (a rare schema, an unusual task format).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I haven't seen this layered strategy spelled out anywhere yet for Gemma 4 — it falls naturally out of how the architecture works, but standard MoE fine-tuning guides don't anticipate having a dense path to lean on.&lt;/p&gt;

&lt;p&gt;Practical heads-up: as of launch, full QLoRA tooling for Gemma 4 26B was not ready in the major libraries. Unsloth and the TRL team are working on it. If you're planning a serious fine-tune, watch their repos before committing to a stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this tells us about Google's research direction
&lt;/h2&gt;

&lt;p&gt;Gemma 4 26B's hybrid design isn't an accident. It's a deliberate bet on a few principles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Distillation matters more than peak MoE efficiency.&lt;/strong&gt; A model that distills cleanly from a dense teacher is more useful than one that wrings every FLOP out of routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robustness beats benchmark peaks.&lt;/strong&gt; Quality you can count on across diverse inputs is worth more than a leaderboard score that depends on the router being perfectly trained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open weights have to fine-tune well.&lt;/strong&gt; A model the community can't reliably fine-tune won't grow the ecosystem Google needs to compete with Llama.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The other open-model labs are running a different play. DeepSeek and Qwen are pushing pure MoE further — more experts, finer sparsity, more peak efficiency. Google is going the opposite direction: more pathways in parallel, more redundancy, more graceful failure.&lt;/p&gt;

&lt;p&gt;It's too early to say which approach wins long-term. But the next time someone tells you "Gemma 4 26B is just Google's MoE," you can correct them. It's something else — a hybrid that quietly rewrites the rules of how a Mixture-of-Experts model should look.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to do with this
&lt;/h2&gt;

&lt;p&gt;Three concrete takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read architecture diagrams before benchmarks.&lt;/strong&gt; "MoE" is not one design. The label hides differences that show up in production — in your memory bill, your throughput, your fine-tuning recipe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Size your hardware for total parameters, not active parameters.&lt;/strong&gt; Active is compute. Total is memory. The 26B MoE eats ~48 GB at BF16; if you can't hold that, use E4B or 31B with offloading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're fine-tuning, target the dense FFN first.&lt;/strong&gt; It's the most stable surface in Gemma 4 26B, and most MoE models don't even have one. Use that.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Architecture details aren't trivia. They're the gap between a model that fits your workload and one that quietly disappoints.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Sources: &lt;a href="https://ai.google.dev/gemma/docs/core/model_card_4" rel="noopener noreferrer"&gt;Gemma 4 model card&lt;/a&gt;, &lt;a href="https://huggingface.co/blog/gemma4" rel="noopener noreferrer"&gt;Welcome Gemma 4 on Hugging Face&lt;/a&gt;, Louis Wang's &lt;a href="https://louiswang524.github.io/blog/gemma-family/" rel="noopener noreferrer"&gt;Gemma 4 architecture analysis&lt;/a&gt; (cited for the parallel-dense observation), and a controlled multi-model benchmark on Gemma 4 / Phi-4 / Qwen3 (cited for VRAM numbers). Exact expert counts (8 of 128) come from third-party architecture write-ups; Google hasn't released a full Gemma 4 technical report at time of writing. If a paper drops and numbers shift, this post will be updated.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built on a workstation; nothing here required cloud access. If you want to verify the memory figures, run &lt;code&gt;nvidia-smi&lt;/code&gt; during a vLLM serve of &lt;code&gt;google/gemma-4-26B-A4B-it&lt;/code&gt; at BF16. The numbers are reproducible.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>Google I/O 2026 quietly ended a 20-year-old web problem — meet the HTML-in-Canvas API</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Sat, 23 May 2026 16:40:23 +0000</pubDate>
      <link>https://dev.to/manikant92/google-io-2026-quietly-ended-a-20-year-old-web-problem-meet-the-html-in-canvas-api-4h9d</link>
      <guid>https://dev.to/manikant92/google-io-2026-quietly-ended-a-20-year-old-web-problem-meet-the-html-in-canvas-api-4h9d</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-io-writing-2026-05-19"&gt;Google I/O Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Among the Gemini 3.5, Antigravity 2.0, and Android XR headlines from Google I/O 2026, Chrome's engineering team quietly shipped an origin trial for an API that resolves a tradeoff every web developer has lived with for two decades: &lt;strong&gt;DOM for rich, semantic, accessible UI, or Canvas for fast 3D and pixel-level graphics — pick one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The new &lt;strong&gt;HTML-in-Canvas API&lt;/strong&gt; lets you draw live DOM elements directly into a 2D canvas, a WebGL texture, or a WebGPU texture, while preserving accessibility, find-in-page, translation, dark mode, autofill, browser extensions, and DevTools inspection. The DOM elements remain real DOM elements — they just &lt;em&gt;render&lt;/em&gt; inside the canvas.&lt;/p&gt;

&lt;p&gt;That sentence sounds simple. It isn't. This is one of the most consequential changes to the web platform in years, and it shipped behind a one-paragraph bullet point on the dev keynote recap.&lt;/p&gt;

&lt;p&gt;This post is the deep-dive that bullet point deserved.&lt;/p&gt;




&lt;h2&gt;
  
  
  The choice nobody wanted to make
&lt;/h2&gt;

&lt;p&gt;If you've ever built anything ambitious in the browser, you've hit this wall.&lt;/p&gt;

&lt;p&gt;Want a beautiful 3D product configurator with real text labels users can copy-paste? Either you live in the DOM and give up on 3D, or you live in WebGL and re-implement font layout, text selection, and accessibility from scratch.&lt;/p&gt;

&lt;p&gt;Want a Figma-like canvas with rich form controls inside? You either go full DOM (slow once you have thousands of nodes) or you go canvas and accept that screen readers see a black hole where your UI used to be.&lt;/p&gt;

&lt;p&gt;Want a WebXR scene that includes a tooltip the browser can actually translate to the user's language? Tough luck. Canvas is a pixel grid. The browser's translation engine doesn't speak in pixels.&lt;/p&gt;

&lt;p&gt;This is the choice the Chrome team's &lt;a href="https://developer.chrome.com/blog/html-in-canvas-origin-trial" rel="noopener noreferrer"&gt;official I/O 2026 blog post&lt;/a&gt; names directly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"For years, web developers have had to make a tough architectural choice when building complex, highly-interactive visual applications on the web: do you lean on the DOM for its rich semantic features, or do you render directly to the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element for low-level graphics performance?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The honest answer until I/O 2026 was: &lt;strong&gt;both choices were wrong, you picked the one that was less wrong for your app, and you spent the rest of the project apologizing for it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look at how the industry coped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;html2canvas / dom-to-image:&lt;/strong&gt; thousands of GitHub stars, built around taking &lt;em&gt;screenshots&lt;/em&gt; of the DOM and dumping them as static images. CORS-restricted. Video and SVG render blank. Slow on complex pages. The output is dead — no interactivity, no accessibility, no text selection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Figma, Google Docs, Miro:&lt;/strong&gt; all built bespoke text-layout engines in JavaScript on top of canvas because the DOM couldn't keep up at scale. Beautiful products. Multi-megabyte bundle sizes. Accessibility implementations that had to mirror everything into hidden DOM trees for screen readers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebGL frameworks like Three.js and PlayCanvas:&lt;/strong&gt; an entire cottage industry of HUD libraries, in-scene UI systems, billboard text renderers — none of which inherited any of the DOM's accessibility, find-in-page, translation, or autofill behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every "solution" was a workaround for the same architectural sin: the browser had two rendering pipelines that didn't speak to each other.&lt;/p&gt;




&lt;h2&gt;
  
  
  What HTML-in-Canvas actually does
&lt;/h2&gt;

&lt;p&gt;The pitch in one sentence: &lt;strong&gt;the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element can now have HTML children, and the canvas can paint them anywhere it wants, while the browser keeps treating them as real DOM elements.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don't snapshot the DOM. You don't proxy events. The element is &lt;em&gt;the&lt;/em&gt; element — selectable, focusable, accessible, find-in-page-able, extension-friendly, autofilled-by-Chrome — and your canvas is just drawing it at the coordinates you want, with the transform you want, in the texture you want.&lt;/p&gt;

&lt;p&gt;Three rendering paths ship at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;2D Canvas&lt;/strong&gt; — &lt;code&gt;ctx.drawElementImage(element, x, y)&lt;/code&gt; returns a CSS transform you apply back to the DOM element so events line up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebGL&lt;/strong&gt; — &lt;code&gt;gl.texElementImage2D(target, level, internalFormat, format, type, element)&lt;/code&gt; works like &lt;code&gt;texImage2D&lt;/code&gt; but accepts a DOM element as the source. The element ends up as a real WebGL texture you can map onto any geometry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebGPU&lt;/strong&gt; — &lt;code&gt;device.queue.copyElementImageToTexture(element, { texture })&lt;/code&gt; does the equivalent for WebGPU.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three keep the DOM element &lt;em&gt;alive&lt;/em&gt; underneath the rendered output. Click in the right spot and the real &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; gets the click. Press Cmd+F and your text gets highlighted. Switch Chrome to translate the page and the labels in your 3D scene change language. Enable a dark-mode extension and your in-canvas form follows along.&lt;/p&gt;




&lt;h2&gt;
  
  
  The smallest possible demo
&lt;/h2&gt;

&lt;p&gt;Set the &lt;code&gt;layoutsubtree&lt;/code&gt; attribute on a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and put real HTML inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 200px; height: 200px;"&lt;/span&gt; &lt;span class="na"&gt;layoutsubtree&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"form_element"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Name:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then on every paint, draw the element and re-sync its transform so click/focus targets stay correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&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;form_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form_element&lt;/span&gt;&lt;span class="dl"&gt;"&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onpaint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Draw the form element at (0, 0) inside the canvas&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawElementImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form_element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Keep the underlying DOM hit-test region aligned with the painted output&lt;/span&gt;
  &lt;span class="nx"&gt;form_element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;That's the entire API surface for 2D mode. A real &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; lives inside a real &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;. Autofill works. Tab order works. Screen readers find it. Find-in-page finds it. None of those sentences were true on the web a month ago.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;layoutsubtree&lt;/code&gt; attribute is the key signal — it tells the browser "this canvas has DOM children that are alive; lay them out, expose them to the a11y tree, but let me decide where their pixels go."&lt;/p&gt;




&lt;h2&gt;
  
  
  The WebGL path — where it gets fun
&lt;/h2&gt;

&lt;p&gt;The really interesting use case isn't 2D — it's mapping live DOM onto 3D meshes. Here's the WebGL primitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onpaint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texElementImage2D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;texElementImage2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNSIGNED_BYTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;form_element&lt;/span&gt;  &lt;span class="c1"&gt;// &amp;lt;-- this is a DOM element, not an HTMLImageElement&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;If you've written WebGL before, look at that line again. The texture source is a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. You can put it on a cube, a sphere, a 3D book page, a curved billboard. The text stays selectable. Find-in-page still highlights it on the 3D mesh. The browser's translation engine can localize it in place.&lt;/p&gt;

&lt;p&gt;There's a price: getting events to line up in 3D space is harder than in 2D. The on-screen position of a textured element depends on your shader's model-view-projection matrix, and the browser can't deduce it from your draw call. So when you need hit-testing to follow a 3D-transformed element, you compute a DOM matrix that maps clip-space back to pixel-space and hand it to &lt;code&gt;canvas.getElementTransform(element, screenSpaceTransform)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementTransform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. WebGL MVP matrix → DOM matrix&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mvpDOM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMMatrix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&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="nx"&gt;htmlElementMVP&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Normalize the HTML element (pixel size → 1x1 unit square)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetHTMLElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&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;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetHTMLElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&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;cssToUnitSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMMatrix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;// shrink + flip Y&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// center&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Map clip space back to the actual canvas viewport in pixels&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clipToCanvasViewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DOMMatrix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Compose: viewport · MVP · normalize&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenSpaceTransform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;clipToCanvasViewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mvpDOM&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssToUnitSpace&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 5. Tell the browser where the element actually sits on screen&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;computedTransform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;targetHTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;screenSpaceTransform&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;computedTransform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;targetHTMLElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computedTransform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;The first time I read that snippet I thought "that's a lot of matrix math for one element." Then I realized — this is the math you were doing implicitly &lt;em&gt;anyway&lt;/em&gt; to position your fake HUD over your WebGL scene. The difference is now it's blessed by the browser, so the DOM element actually &lt;em&gt;lives&lt;/em&gt; at that 3D location and hit-testing works.&lt;/p&gt;

&lt;p&gt;If you don't want to write the math yourself, Three.js and PlayCanvas have already shipped wrappers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three.js in one line
&lt;/h2&gt;

&lt;p&gt;The Three.js team merged experimental support with a new &lt;code&gt;HTMLTexture&lt;/code&gt; class. Mapping a DOM element onto a cube becomes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THREE&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;three&lt;/span&gt;&lt;span class="dl"&gt;"&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;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MeshBasicMaterial&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTMLTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uiElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// pass any DOM element&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BoxGeometry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;mesh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mesh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've used Three.js, you know how striking that is. A texture that's a live, accessible, find-in-page-able, browser-translatable DOM element. PlayCanvas has the equivalent. The framework boilerplate is gone.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this unlocks that wasn't possible
&lt;/h2&gt;

&lt;p&gt;Let me try to be concrete instead of hand-wavy. Here are five things you genuinely could not build before this API, broken down by what specifically changes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. WebXR / 3D product configurators with real, accessible text.&lt;/strong&gt;&lt;br&gt;
The 3D Pottery Barn sofa demo on a phone could finally have a label that screen readers can read, that Chrome's auto-translate localizes for German users, and that find-in-page highlights when someone searches "leather." Today, all three of those features silently fail inside WebGL textures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Figma-class apps that don't ship a 4MB text engine.&lt;/strong&gt;&lt;br&gt;
Anything in the canvas-app category — Figma, Miro, Whimsical, Lucid, Photopea — built its own text layout, IME handling, font fallback, copy-paste, and accessibility shadow tree on top of canvas. With HTML-in-Canvas, the layout engine is the browser's. Bundle size goes down. CJK and bidirectional text become free. Accessibility stops being a parallel mirror you have to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. WebGL games where the in-world terminal is a real &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;
Every time I've seen a "computer terminal" inside a 3D game on the web, it's been faked with canvas text drawing and a hidden DOM element absorbing keypresses. Now the terminal can &lt;em&gt;be&lt;/em&gt; a &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;, mapped onto the in-game CRT, with autofill, undo/redo, IME, and clipboard all just working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. AI-agent-ready 3D scenes.&lt;/strong&gt;&lt;br&gt;
This is the part most coverage misses. The Chrome team explicitly calls out &lt;a href="https://developer.chrome.com/blog/html-in-canvas-origin-trial" rel="noopener noreferrer"&gt;indexability and AI agents&lt;/a&gt; as a use case: web crawlers and AI agents can now read the text rendered into 2D and 3D scenes because it's still in the DOM. When you combine HTML-in-Canvas with the &lt;em&gt;other&lt;/em&gt; underrated I/O 2026 announcement (WebMCP), suddenly canvas-driven web apps become first-class agent surfaces. They were second-class for 20 years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Translatable, dark-mode-aware, extension-friendly 3D experiences.&lt;/strong&gt;&lt;br&gt;
A user with a Chrome dictionary extension installed gets word definitions when they highlight text &lt;em&gt;inside your 3D scene&lt;/em&gt;. A user with &lt;code&gt;prefers-color-scheme: dark&lt;/code&gt; gets a dark UI inside your WebXR app. Nobody had to do anything to enable these. They just inherit them now, because the DOM was always responsible for them and the DOM never left.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd build first
&lt;/h2&gt;

&lt;p&gt;Two ideas I can't stop thinking about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The translatable 3D book.&lt;/strong&gt; Chrome has &lt;a href="https://chrome.dev/html-in-canvas/demos/webgl-book-curl.html" rel="noopener noreferrer"&gt;a demo&lt;/a&gt; where a WebGL-rendered book has pages that are real DOM. Users can change the font with CSS. The browser's translation feature works on the actual page content. This isn't a tech demo — this is the future of edtech, immersive journalism, and museum web experiences. Build one for a specific use case (kids' anatomy book, historical document, recipe book) and you have a portfolio piece that didn't exist a week ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A "real form" inside a WebGPU jelly slider.&lt;/strong&gt; Chrome's &lt;a href="https://wicg.github.io/html-in-canvas/Examples/webgpu-jelly-slider/" rel="noopener noreferrer"&gt;WebGPU jelly slider demo&lt;/a&gt; shows an &lt;code&gt;&amp;lt;input type="range"&amp;gt;&lt;/code&gt; refracting through a 3D jelly material while still responding to &lt;code&gt;step&lt;/code&gt; and keyboard arrows. That's the killer pattern: take a piece of HTML you'd find on any boring form and put it &lt;em&gt;inside&lt;/em&gt; a WebGPU effect that previously would have required you to give up form semantics entirely. Replace "jelly" with "frosted glass," "paper," "liquid metal" — every brand-marketing site shipping with WebGL right now becomes a candidate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm skeptical about
&lt;/h2&gt;

&lt;p&gt;This is genuinely exciting. I still have three concerns I'd want to see addressed before I ship anything load-bearing on this API:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Chrome-only, behind a flag in Canary, and the origin trial spans only M148–M151.&lt;/strong&gt;&lt;br&gt;
The &lt;a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/t_nGEmJ_v4s" rel="noopener noreferrer"&gt;Intent to Experiment&lt;/a&gt; trail confirms a four-version window. That's three months. After that the API either becomes stable, gets pushed for another OT, or — historically — gets reshaped enough that early adopters have rewrites coming. There's no Firefox or Safari signal yet. The WICG explainer is healthy, but "WICG" doesn't mean "standardized." Plan for change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Main-thread scrolling inside the canvas isn't a free win.&lt;/strong&gt;&lt;br&gt;
This is buried in the docs, but it matters: HTML-in-Canvas content is drawn from JavaScript, so scrolling and animations &lt;em&gt;inside&lt;/em&gt; the canvas can't be off-the-main-thread the way ordinary DOM scrolling can. If you put a long scrollable list inside a canvas, every scroll event walks through your paint handler. Sometimes that's fine. Sometimes it's a 60fps → 24fps cliff you didn't see coming. Profile early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cross-origin content is blocked.&lt;/strong&gt;&lt;br&gt;
For security reasons, the API doesn't work with cross-origin iframes. That's the right call — letting one origin paint another origin's content into its own canvas would be an obvious info-leak vector. But it does mean every "embed a third-party widget into my WebXR scene" idea is dead on arrival. Plan to ship same-origin or proxy through your own server.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to actually try it this weekend
&lt;/h2&gt;

&lt;p&gt;If you want to play with this today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://www.google.com/chrome/canary/" rel="noopener noreferrer"&gt;Chrome Canary&lt;/a&gt; (you want at least 149).&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;chrome://flags/#canvas-draw-element&lt;/code&gt; and enable it. Relaunch.&lt;/li&gt;
&lt;li&gt;Clone the &lt;a href="https://github.com/WICG/html-in-canvas/tree/main/Examples" rel="noopener noreferrer"&gt;WICG html-in-canvas examples&lt;/a&gt; and open one of them.&lt;/li&gt;
&lt;li&gt;Open &lt;a href="https://chrome.dev/html-in-canvas/" rel="noopener noreferrer"&gt;the Chrome demos page&lt;/a&gt; to see what's possible end-to-end — the 3D book, the animated billboard, the refractive fluid prism text.&lt;/li&gt;
&lt;li&gt;Sign up for the &lt;a href="https://developer.chrome.com/origintrials#/view_trial/3478467762190286849" rel="noopener noreferrer"&gt;origin trial&lt;/a&gt; if you want to expose it to real users on your origin during M148–M151.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're a Three.js or PlayCanvas dev: jump straight to &lt;code&gt;THREE.HTMLTexture&lt;/code&gt; or PlayCanvas's HTML texture support. The framework wrappers turn a 50-line matrix exercise into one line of code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;

&lt;p&gt;Almost every I/O 2026 recap I've read leads with Gemini 3.5 and Antigravity 2.0. Those are the right headlines for an AI conference. But the announcements that &lt;em&gt;quietly change what the web is capable of&lt;/em&gt; tend to come from the platform team, not the model team. WebMCP got coverage because it has "MCP" in the name. HTML-in-Canvas got one bullet because "DOM in canvas, but better" doesn't fit on a slide.&lt;/p&gt;

&lt;p&gt;Here's what's actually happening, in one sentence: &lt;strong&gt;the browser is no longer forcing you to choose between semantic web and graphical web.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a 20-year-old constraint dissolving in real time. The first generation of apps built on this stack — the WebGL stores that read like text, the 3D book your kid can actually use a screen reader on, the canvas dashboards that finally have native accessibility — won't ship this month. They'll ship over the next 18 months as the origin trial matures and Three.js / PlayCanvas integrations stabilize.&lt;/p&gt;

&lt;p&gt;But the work to be one of the people who shipped them starts now. Today. With a Chrome Canary flag and one weird &lt;code&gt;layoutsubtree&lt;/code&gt; attribute on a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's the announcement I think actually matters from Google I/O 2026.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you build anything with HTML-in-Canvas — especially in WebGL or WebGPU — I'd love to see it. Drop a link in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Find me on &lt;a href="https://github.com/Manikant92" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://linkedin.com/in/manikantkella" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleiochallenge</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Firewall and governance for your LLM usage</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Wed, 17 Dec 2025 06:37:46 +0000</pubDate>
      <link>https://dev.to/manikant92/firewall-and-governance-for-your-llm-usage-2c21</link>
      <guid>https://dev.to/manikant92/firewall-and-governance-for-your-llm-usage-2c21</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__hidden-navigation-link"&gt;PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Xano AI-Powered Backend Challenge: Full-Stack Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/manikant92" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" alt="manikant92 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/manikant92" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Manikant Kella
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Manikant Kella
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3105098" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/manikant92" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Manikant Kella&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 14 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" id="article-link-3105098"&gt;
          PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/xanochallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;xanochallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;23&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              8&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>devchallenge</category>
      <category>xanochallenge</category>
      <category>ai</category>
      <category>backend</category>
    </item>
    <item>
      <title>Guardrail your LLMs</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Mon, 15 Dec 2025 18:06:39 +0000</pubDate>
      <link>https://dev.to/manikant92/guardrail-your-llms-1d9a</link>
      <guid>https://dev.to/manikant92/guardrail-your-llms-1d9a</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__hidden-navigation-link"&gt;PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Xano AI-Powered Backend Challenge: Full-Stack Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/manikant92" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" alt="manikant92 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/manikant92" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Manikant Kella
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Manikant Kella
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3105098" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/manikant92" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Manikant Kella&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 14 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" id="article-link-3105098"&gt;
          PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/xanochallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;xanochallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;23&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              8&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>devchallenge</category>
      <category>xanochallenge</category>
      <category>ai</category>
      <category>backend</category>
    </item>
    <item>
      <title>As teams build AI-powered apps, one issue shows up fast 👉 runaway LLM costs with no guardrails.

Thats where PromptShield AI comes into picture</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Mon, 15 Dec 2025 05:59:38 +0000</pubDate>
      <link>https://dev.to/manikant92/as-teams-build-ai-powered-apps-one-issue-shows-up-fast-runaway-llm-costs-with-no-guardrails-5gmo</link>
      <guid>https://dev.to/manikant92/as-teams-build-ai-powered-apps-one-issue-shows-up-fast-runaway-llm-costs-with-no-guardrails-5gmo</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__hidden-navigation-link"&gt;PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Xano AI-Powered Backend Challenge: Full-Stack Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/manikant92" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" alt="manikant92 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/manikant92" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Manikant Kella
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Manikant Kella
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3105098" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/manikant92" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F828858%2Fedaac384-7af4-4fff-aa75-38b6a6f5d996.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Manikant Kella&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Dec 14 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" id="article-link-3105098"&gt;
          PromptShield AI – An AI Cost &amp;amp; Risk Firewall Built with Xano
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/xanochallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;xanochallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;23&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              8&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>devchallenge</category>
      <category>xanochallenge</category>
      <category>ai</category>
      <category>backend</category>
    </item>
    <item>
      <title>PromptShield AI – An AI Cost &amp; Risk Firewall Built with Xano</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Sun, 14 Dec 2025 16:51:17 +0000</pubDate>
      <link>https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e</link>
      <guid>https://dev.to/manikant92/promptshield-ai-an-ai-cost-risk-firewall-built-with-xano-346e</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/xano-2025-11-20"&gt;Xano AI-Powered Backend Challenge&lt;/a&gt;: Full-Stack, AI-First Application&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;PromptShield AI – An AI Cost &amp;amp; Risk Firewall for LLM Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As teams rapidly build agentic apps and AI-powered features, one problem shows up almost immediately:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;LLM costs explode, usage becomes opaque, and there are no guardrails.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers lack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Per-user and per-feature budgets
&lt;/li&gt;
&lt;li&gt;Visibility into token and cost usage
&lt;/li&gt;
&lt;li&gt;Protection against risky prompts (PII, secrets)
&lt;/li&gt;
&lt;li&gt;Smart routing to cheaper models when budgets are exceeded
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PromptShield AI&lt;/strong&gt; solves this by acting as an &lt;strong&gt;intelligent backend control plane&lt;/strong&gt; that sits between applications and LLM providers.&lt;/p&gt;

&lt;p&gt;It enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost budgets (tenant / user / feature)&lt;/li&gt;
&lt;li&gt;Usage analytics and spend visibility&lt;/li&gt;
&lt;li&gt;Safety and routing policies&lt;/li&gt;
&lt;li&gt;Multi-provider cost control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a &lt;strong&gt;production-ready AI infrastructure backend&lt;/strong&gt;, not just a wrapper around LLM APIs.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧱 Architecture Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Xano (Postgres, APIs, background jobs, AI workflows)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Lovable.dev (low-code SaaS dashboard)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-first approach:&lt;/strong&gt; Backend generated with AI, refined by hand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public API + Admin UI:&lt;/strong&gt; Production-ready by design&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎬 Demo
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;strong&gt;Live Application:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://promptshield.lovable.app/" rel="noopener noreferrer"&gt;https://promptshield.lovable.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;Source Code (GitHub):&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Manikant92/promptshield_ai" rel="noopener noreferrer"&gt;https://github.com/Manikant92/promptshield_ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Demo Walkthrough Video:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;


  &lt;iframe src="https://www.youtube.com/embed/TkOK88Ty3Jk"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;📸 &lt;strong&gt;Product Screenshots&lt;/strong&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fzjod758wnbcgg63l2ior.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.amazonaws.com%2Fuploads%2Farticles%2Fzjod758wnbcgg63l2ior.png" alt="Xano Dev"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F9w1teqzs3ss726agzhzl.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.amazonaws.com%2Fuploads%2Farticles%2F9w1teqzs3ss726agzhzl.png" alt="Xano Dev"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F2ycnwx01wyrlp1v3uy1i.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.amazonaws.com%2Fuploads%2Farticles%2F2ycnwx01wyrlp1v3uy1i.png" alt="Xano Dev"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fm6z7tky6a99l4t2yfatf.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.amazonaws.com%2Fuploads%2Farticles%2Fm6z7tky6a99l4t2yfatf.png" alt="Xano Dev"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fvhavslv1lo7sbgzosu2y.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.amazonaws.com%2Fuploads%2Farticles%2Fvhavslv1lo7sbgzosu2y.png" alt="Xano Dev"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F11256167j6r9de5cnf2a.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.amazonaws.com%2Fuploads%2Farticles%2F11256167j6r9de5cnf2a.png" alt="Dashboard Overview"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F9oc0l8x67xtftqn2c56u.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.amazonaws.com%2Fuploads%2Farticles%2F9oc0l8x67xtftqn2c56u.png" alt="API Key Management"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F8pxnalw9plf5s6f3oxqs.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.amazonaws.com%2Fuploads%2Farticles%2F8pxnalw9plf5s6f3oxqs.png" alt="Tenant Management"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fpftzgii1k14xs8mdr98g.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.amazonaws.com%2Fuploads%2Farticles%2Fpftzgii1k14xs8mdr98g.png" alt="Budget Management"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fh2o52na22rw8v3fw17xr.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.amazonaws.com%2Fuploads%2Farticles%2Fh2o52na22rw8v3fw17xr.png" alt="Provider Management"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Foj0hbn66rxsfz7nea2um.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.amazonaws.com%2Fuploads%2Farticles%2Foj0hbn66rxsfz7nea2um.png" alt="Usage and Analytics"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fneku2cxh9fodj3tjqx71.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.amazonaws.com%2Fuploads%2Farticles%2Fneku2cxh9fodj3tjqx71.png" alt="Policy Management"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔎 &lt;strong&gt;Swagger / Public API:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://x8ki-letl-twmt.n7.xano.io/api:q5xLch4v" rel="noopener noreferrer"&gt;https://x8ki-letl-twmt.n7.xano.io/api:q5xLch4v&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The dashboard shows real API keys, budgets, policies, providers, and usage analytics powered entirely by Xano.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🧠 The AI Prompt I Used (Backend Generation)
&lt;/h2&gt;

&lt;p&gt;All backend workflows, API definitions, and schema refinements are tracked in the GitHub repository below for transparency and reproducibility:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/Manikant92/promptshield_ai" rel="noopener noreferrer"&gt;https://github.com/Manikant92/promptshield_ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used XanoScript with an AI-first workflow to generate the initial backend.&lt;br&gt;&lt;br&gt;
Below is the &lt;strong&gt;original prompt&lt;/strong&gt; used to bootstrap the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert backend architect building a production-ready, multi-tenant AI infrastructure backend using Xano.

Build a backend called "PromptShield AI" — an AI Cost &amp;amp; Risk Firewall that sits between applications and multiple LLM providers (OpenAI, Anthropic, etc.) to enforce budgets, rate limits, and safety policies before requests reach the LLM.

The backend must be secure, scalable, and suitable for public API consumption.


Create the initial backend for PromptShield AI with the following requirements:

1. Core Concept
PromptShield AI acts as a proxy API for LLM calls. Applications send standard chat/completion payloads to PromptShield, which enforces usage policies, budgets, and risk checks before forwarding requests to LLM providers.

2. Database Schema (Postgres)
Design tables for:
- tenants (org_id, name, plan, created_at)
- api_keys (key, tenant_id, status, last_used_at)
- users (user_id, tenant_id, role)
- llm_providers (provider, model, cost_per_1k_tokens)
- usage_logs (tenant_id, user_id, feature, provider, model, tokens_in, tokens_out, cost, timestamp)
- budgets (tenant_id, scope_type [tenant/user/feature], scope_id, daily_limit, monthly_limit)
- policies (tenant_id, preferred_models, fallback_model, blocked_categories)

3. API Endpoints
Create the following APIs:

POST /llm/proxy
- Accepts OpenAI-compatible chat/completion payload
- Authenticates using API key
- Identifies tenant, user, and feature
- Performs budget checks and policy enforcement
- Routes request to the selected LLM provider
- Logs token usage and cost

POST /limits/configure
- Allows tenants to define per-user, per-feature, or per-tenant budgets
- Supports daily and monthly limits

GET /usage/summary
- Returns aggregated usage by tenant, user, feature, and model
- Optimized for dashboards

4. AI Logic
Use AI workflows to:
- Classify prompts for risky categories (PII, secrets, unsafe content)
- Block or redact requests that violate policy
- Automatically downgrade to cheaper models when nearing budget limits
- Detect anomalous usage spikes (e.g., sudden 10x increase)

5. Background Jobs
- Aggregate daily and monthly usage
- Recalculate remaining budgets
- Run anomaly detection periodically

6. Security &amp;amp; Scalability
- Multi-tenant isolation
- Rate limiting per API key
- Clean error responses
- Extensible provider abstraction

7. Output
Generate:
- Database tables
- API endpoint logic
- AI workflows
- Background jobs
Use clean, maintainable naming and comments suitable for a production backend.

Do NOT generate frontend code.
Focus entirely on the backend implementation in Xano.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt allowed AI to quickly generate a &lt;strong&gt;solid baseline backend&lt;/strong&gt;, which I then refined heavily inside Xano.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## 🛠️ How I Refined the AI-Generated Backend in Xano

AI gave me a starting point — **human refinement made it production-ready**.

### Key Improvements I Made in Xano

#### 🔐 Security &amp;amp; Multi-Tenancy
- Introduced tenant isolation across all tables
- Added API key lifecycle management (create, revoke, rotate)
- Hardened error handling and rate limits

#### 💰 Cost &amp;amp; Budget Enforcement
- Added scoped budgets (tenant / user / feature)
- Implemented background aggregation jobs for daily &amp;amp; monthly usage
- Enabled budget thresholds and warning states

#### 🧠 AI Logic Enhancements
- Added prompt classification for risky categories (PII, secrets)
- Implemented policy-based model fallback when budgets are exceeded
- Designed provider abstraction for future expansion

#### 📊 Observability &amp;amp; Analytics
- Normalized usage logs for dashboards
- Enabled cost-by-model and cost-by-feature views
- Optimized APIs for frontend consumption

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; AI-generated CRUD-style endpoints&lt;br&gt;&lt;br&gt;
&lt;strong&gt;After:&lt;/strong&gt; A scalable, secure AI infrastructure backend suitable for real-world use&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎨 Frontend: Turning APIs into a Product
&lt;/h2&gt;

&lt;p&gt;I connected the Xano backend to &lt;strong&gt;Lovable.dev&lt;/strong&gt; to build a clean, enterprise-style dashboard.&lt;/p&gt;

&lt;p&gt;The UI allows users to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manage API keys securely&lt;/li&gt;
&lt;li&gt;Define and monitor budgets&lt;/li&gt;
&lt;li&gt;Configure routing and safety policies&lt;/li&gt;
&lt;li&gt;Analyze token and cost usage with filters and charts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step demonstrated how Xano’s backend capabilities translate directly into &lt;strong&gt;product value&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 My Experience with Xano
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I Loved
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI + Human workflow:&lt;/strong&gt; AI for speed, Xano for control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background jobs:&lt;/strong&gt; Perfect for cost aggregation and analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean API design:&lt;/strong&gt; Easy to connect to any frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production mindset:&lt;/strong&gt; Xano encourages scalable patterns by default&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Thinking through multi-tenant isolation correctly (worth the effort)&lt;/li&gt;
&lt;li&gt;Designing APIs that balance flexibility and simplicity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, Xano made it incredibly easy to go from &lt;strong&gt;idea → AI-generated backend → production-grade system&lt;/strong&gt; in a very short time.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;PromptShield AI is not just a demo — it’s a realistic example of how &lt;strong&gt;AI-assisted backend development&lt;/strong&gt;, combined with &lt;strong&gt;thoughtful human refinement&lt;/strong&gt;, can produce scalable, secure, and maintainable systems.&lt;/p&gt;

&lt;p&gt;Xano was the perfect platform to bring this idea to life.&lt;/p&gt;




&lt;p&gt;Thanks for checking it out! 🚀&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>xanochallenge</category>
      <category>ai</category>
      <category>backend</category>
    </item>
    <item>
      <title>How I Built an AI-Powered Website Modernizer in 3 Days with Kiro</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Fri, 05 Dec 2025 18:38:32 +0000</pubDate>
      <link>https://dev.to/manikant92/how-i-built-an-ai-powered-website-modernizer-in-3-days-with-kiro-1e3h</link>
      <guid>https://dev.to/manikant92/how-i-built-an-ai-powered-website-modernizer-in-3-days-with-kiro-1e3h</guid>
      <description>&lt;p&gt;I built &lt;strong&gt;ReVitalize&lt;/strong&gt; - an AI-powered platform that transforms outdated websites into modern, accessible designs - in just 3 days using Kiro. What would typically take 2-3 weeks became a weekend project thanks to Kiro's context-aware vibe coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Result:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;60+ files of production-ready code&lt;/li&gt;
&lt;li&gt;Complete AI transformation pipeline&lt;/li&gt;
&lt;li&gt;Beautiful UI with animations&lt;/li&gt;
&lt;li&gt;Side-by-side preview system&lt;/li&gt;
&lt;li&gt;Chat refinement, component extraction, accessibility auditing&lt;/li&gt;
&lt;li&gt;Export to ZIP and GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Secret:&lt;/strong&gt; Natural conversations with an AI that actually remembers context and evolves with you.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Outdated Websites Everywhere
&lt;/h2&gt;

&lt;p&gt;Have you ever visited a website that looks like it's from 1995? Plain HTML, no styling, broken layouts? Companies like Berkshire Hathaway still run websites that look ancient!&lt;/p&gt;

&lt;p&gt;I thought: &lt;strong&gt;"What if AI could instantly modernize any outdated website?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's how ReVitalize was born - an AI-powered tool that transforms any website in seconds, not weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Kiro Difference: Context-Aware Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before Kiro: The Traditional Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Plan everything upfront (2-3 days)
2. Write detailed specs (1 day)
3. Set up project structure (1 day)
4. Build features one by one (1-2 weeks)
5. Debug and refine (3-4 days)
6. Write documentation (1-2 days)

Total: 2-3 weeks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  With Kiro: Iterative Conversations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Day 1: "Build a full production-grade application called ReVitalize"
       → 60+ files generated with complete architecture

Day 2: "Make UI much better, add example sites"
       → Beautiful landing page with animations

Day 3: "Preview isn't showing" → Fixed with debugging
       "Token limit exceeded" → Switched to better model
       "Secure API keys" → Complete security audit

Total: 3 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What Makes Kiro Special?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Context Awareness&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Traditional AI coding tools forget context. You have to re-explain everything.&lt;/p&gt;

&lt;p&gt;Kiro remembers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Previous conversations&lt;/li&gt;
&lt;li&gt;Project structure&lt;/li&gt;
&lt;li&gt;Design decisions&lt;/li&gt;
&lt;li&gt;Code patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Add example sites with auto-fill"
Kiro: *Remembers the URLInputForm component*
      *Updates LandingPage to pass URL*
      *Modifies TransformationWorkspace*
      *Updates all connected components*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to specify which files to modify. Kiro knows.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Intelligent Problem-Solving&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When we hit a token limit error with GPT-4 Turbo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Token limit exceeded"
Kiro: "Let me analyze the OpenAI pricing table..."
      "GPT-4o Mini has 200K TPM vs 30K for GPT-4 Turbo"
      "It's also 98% cheaper!"
      *Updates all model references*
      *Updates UI branding*
      *Creates documentation*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiro didn't just fix the error - it optimized our entire approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;Vibe Coding&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of writing detailed specs, I had natural conversations:&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;// Traditional approach:&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create a React component called LandingPage with:
- Animated gradient background using Framer Motion
- Three floating orbs with different animation delays
- Hero section with gradient text
- Feature grid with 9 cards
- Hover effects on cards
- Stats section with 4 metrics
- CTA section
- Footer with links&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;// With Kiro:&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Make UI much better than this reference website&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;// Result: 500+ lines of beautiful code in 5 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Build Process: A Day-by-Day Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Day 1: Architecture &amp;amp; Core Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Initial Prompt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a full production-grade application called ReVitalize,
an AI-powered platform that modernizes any outdated website.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What Kiro Generated:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete Next.js 14 project structure&lt;/li&gt;
&lt;li&gt;TypeScript configuration&lt;/li&gt;
&lt;li&gt;API routes for scraping, transformation, refinement&lt;/li&gt;
&lt;li&gt;React components (URLInput, Preview, Chat, etc.)&lt;/li&gt;
&lt;li&gt;AI integrations (OpenAI, Google Gemini)&lt;/li&gt;
&lt;li&gt;Puppeteer scraping system&lt;/li&gt;
&lt;li&gt;Type definitions&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Files Created:&lt;/strong&gt; 60+&lt;br&gt;
&lt;strong&gt;Time:&lt;/strong&gt; ~2 hours of conversation&lt;/p&gt;
&lt;h3&gt;
  
  
  Day 2: UI/UX &amp;amp; Refinement
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Conversations:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Make UI much better than reference website"
Kiro: *Creates stunning landing page*
      - Animated gradient background
      - Floating orbs animation
      - Feature cards with hover effects
      - Professional typography

Me: "Add example sites that auto-populate"
Kiro: *Adds 4 example cards*
      *Implements click-to-fill*
      *Updates all connected components*

Me: "Text size too large"
Kiro: *Adjusts all typography*
      *Maintains hierarchy*
      *Updates responsive breakpoints*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; ~4 hours of iterative refinement&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 3: Debugging &amp;amp; Polish
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Preview Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Preview isn't showing"
Kiro: *Adds comprehensive logging*
      *Identifies issue: React imports instead of HTML*
      *Creates React-to-HTML converter*
      *Implements three-level fallback system*
      *Generates beautiful fallback HTML*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Token Limit Issue:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Token limit exceeded"
Kiro: *Analyzes OpenAI pricing*
      *Suggests GPT-4o Mini*
      *Updates all references*
      *Documents the change*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security Audit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Me: "Check for hardcoded API keys"
Kiro: *Scans entire codebase*
      *Finds exposed keys*
      *Updates .gitignore*
      *Clears sensitive data*
      *Creates SECURITY.md*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; ~6 hours of problem-solving&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Impressive Moments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Complete AI Transformation System
&lt;/h3&gt;

&lt;p&gt;One conversation generated:&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;// src/lib/ai/openai.ts&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Smart&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="nx"&gt;engineering&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Structured&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;HTML&lt;/span&gt; &lt;span class="nx"&gt;conversion&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Three&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="nx"&gt;system&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="nx"&gt;handling&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Comprehensive&lt;/span&gt; &lt;span class="nx"&gt;logging&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;specific&lt;/span&gt; &lt;span class="nx"&gt;guidelines&lt;/span&gt;

&lt;span class="c1"&gt;// 500+ lines of production-ready code&lt;/span&gt;
&lt;span class="c1"&gt;// Time: 5 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Beautiful Landing Page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/components/LandingPage.tsx&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Animated&lt;/span&gt; &lt;span class="nx"&gt;gradient&lt;/span&gt; &lt;span class="nx"&gt;background&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Three&lt;/span&gt; &lt;span class="nx"&gt;floating&lt;/span&gt; &lt;span class="nx"&gt;orbs&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;physics&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;triple&lt;/span&gt; &lt;span class="nx"&gt;gradient&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;hover&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;expand&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Stats&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;animated&lt;/span&gt; &lt;span class="nx"&gt;counters&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Premium&lt;/span&gt; &lt;span class="nx"&gt;CTA&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Professional&lt;/span&gt; &lt;span class="nx"&gt;footer&lt;/span&gt;

&lt;span class="c1"&gt;// 800+ lines with Framer Motion&lt;/span&gt;
&lt;span class="c1"&gt;// Time: 10 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Security Audit &amp;amp; Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One prompt: "Check for hardcoded API keys"&lt;/span&gt;

&lt;span class="c"&gt;# Kiro:&lt;/span&gt;
1. Scanned entire codebase
2. Found exposed keys &lt;span class="k"&gt;in&lt;/span&gt; .env.local
3. Updated .gitignore with comprehensive rules
4. Cleared all sensitive data
5. Created SECURITY.md with guidelines
6. Updated .env.example with documentation

&lt;span class="c"&gt;# Files modified: 4&lt;/span&gt;
&lt;span class="c"&gt;# Time: 3 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Features That Made the Difference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Steering Documents
&lt;/h3&gt;

&lt;p&gt;Created &lt;code&gt;transformation-rules.md&lt;/code&gt; in &lt;code&gt;.kiro/steering/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## LLM Output Format&lt;/span&gt;

Always return JSON matching this exact structure:
{
  "summary": "...",
  "components": [...],
  "final_build": { "files": [...] }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; Consistent AI outputs, no parsing errors, predictable structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Spec-Driven Development
&lt;/h3&gt;

&lt;p&gt;Created &lt;code&gt;.kiro/spec.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;url_input&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;scraping&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ai_transformation&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;preview&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chat_refinement&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export&lt;/span&gt;

&lt;span class="na"&gt;api_endpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POST /api/scrape&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POST /api/transform&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POST /api/refine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; Clear architecture, consistent APIs, complete features.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Iterative Conversations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Broad → Specific → Refine → Polish

"Build ReVitalize" 
  → "Add example sites"
    → "Make text smaller"
      → "Fix preview display"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each conversation built on the previous context.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Technical Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 14 (App Router)&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;Framer Motion&lt;/li&gt;
&lt;li&gt;React Syntax Highlighter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js API Routes&lt;/li&gt;
&lt;li&gt;Puppeteer (scraping)&lt;/li&gt;
&lt;li&gt;Cheerio (HTML parsing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI GPT-4o Mini (primary)&lt;/li&gt;
&lt;li&gt;Google Gemini 1.5 Flash (fallback)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;All generated and integrated by Kiro through conversations.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges &amp;amp; How Kiro Helped
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: Model Selection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Started with GPT-5.1 (doesn't exist), then GPT-4 Turbo (token limits)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Let me analyze the OpenAI pricing table...
GPT-4o Mini has:
- 200K TPM (vs 30K)
- 98% cheaper
- Optimized for code generation
- Perfect for your use case"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; No more token errors, 98% cost reduction&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Empty Preview
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Preview showed "No preview available"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Added comprehensive logging
2. Identified issue: AI generating React imports
3. Created React-to-HTML converter
4. Implemented three-level fallback
5. Generated beautiful fallback HTML
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Preview always works, even if AI fails&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Security
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; API keys potentially exposed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kiro's Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Scanned entire codebase
2. Found all sensitive data
3. Updated .gitignore
4. Created security guidelines
5. Documented best practices
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Production-ready security&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Context is Everything
&lt;/h3&gt;

&lt;p&gt;Traditional AI tools lose context. Kiro maintains it across conversations, making iterative development natural.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Hybrid Approach Works Best
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec-driven&lt;/strong&gt; for initial architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vibe coding&lt;/strong&gt; for refinement and debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering docs&lt;/strong&gt; for consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. AI as Partner, Not Tool
&lt;/h3&gt;

&lt;p&gt;Kiro doesn't just generate code - it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugs with you&lt;/li&gt;
&lt;li&gt;Suggests optimizations&lt;/li&gt;
&lt;li&gt;Maintains consistency&lt;/li&gt;
&lt;li&gt;Writes documentation&lt;/li&gt;
&lt;li&gt;Solves problems&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Speed Without Sacrificing Quality
&lt;/h3&gt;

&lt;p&gt;3 days of development with Kiro produced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production-ready code&lt;/li&gt;
&lt;li&gt;Comprehensive error handling&lt;/li&gt;
&lt;li&gt;Security best practices&lt;/li&gt;
&lt;li&gt;Complete documentation&lt;/li&gt;
&lt;li&gt;Beautiful UI/UX&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ReVitalize Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Instant website transformation&lt;/li&gt;
&lt;li&gt;✅ Side-by-side comparison&lt;/li&gt;
&lt;li&gt;✅ 5 theme options&lt;/li&gt;
&lt;li&gt;✅ Chat refinement&lt;/li&gt;
&lt;li&gt;✅ Component extraction&lt;/li&gt;
&lt;li&gt;✅ Accessibility audit&lt;/li&gt;
&lt;li&gt;✅ Export to ZIP/GitHub&lt;/li&gt;
&lt;li&gt;✅ Beautiful animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development Stats:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 3 days (vs 2-3 weeks)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Files:&lt;/strong&gt; 60+ production-ready&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lines of Code:&lt;/strong&gt; ~5,000+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost per transformation:&lt;/strong&gt; $0.003&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; 15-20 seconds per transformation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Want to experience Kiro's power?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ReVitalize Demo:&lt;/strong&gt; [&lt;a href="https://youtu.be/-qBKbOdIa7U" rel="noopener noreferrer"&gt;https://youtu.be/-qBKbOdIa7U&lt;/a&gt;]&lt;br&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; [&lt;a href="https://github.com/Manikant92/ReVitalize" rel="noopener noreferrer"&gt;https://github.com/Manikant92/ReVitalize&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get Kiro:&lt;/strong&gt; [&lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;https://kiro.dev/&lt;/a&gt;]&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Kiro changed how I approach development. Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Planning everything upfront&lt;/li&gt;
&lt;li&gt;Writing detailed specs&lt;/li&gt;
&lt;li&gt;Building in isolation&lt;/li&gt;
&lt;li&gt;Debugging alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start building immediately&lt;/li&gt;
&lt;li&gt;Refine through conversation&lt;/li&gt;
&lt;li&gt;Iterate with an AI partner&lt;/li&gt;
&lt;li&gt;Solve problems together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The future of development isn't about replacing developers - it's about augmenting our capabilities with AI that truly understands context.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're still writing boilerplate by hand or spending hours on repetitive tasks, you're missing out on what's possible with context-aware AI development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Questions?
&lt;/h2&gt;

&lt;p&gt;Drop a comment below! I'd love to hear about your experiences with AI-assisted development.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Built with ❤️ using Kiro for the Kiroween Hackathon&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>openai</category>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AICHA: AI-Powered Healthcare Assistant with Permit.io Authorization</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Sun, 04 May 2025 10:40:42 +0000</pubDate>
      <link>https://dev.to/manikant92/aicha-ai-powered-healthcare-assistant-with-permitio-authorization-2kpo</link>
      <guid>https://dev.to/manikant92/aicha-ai-powered-healthcare-assistant-with-permitio-authorization-2kpo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/permit_io"&gt;Permit.io Authorization Challenge&lt;/a&gt;: AI Access Control&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is AICHA?
&lt;/h2&gt;

&lt;p&gt;AICHA (AI-powered Healthcare Assistant) is a modern healthcare management platform that uses artificial intelligence to help doctors, nurses, and administrators deliver better care. With features like AI-driven medical analysis, treatment suggestions, and secure patient data management, AICHA is designed to make healthcare smarter and safer.&lt;/p&gt;

&lt;p&gt;But with great power comes great responsibility—especially when it comes to sensitive medical data and AI features. That's why AICHA uses &lt;strong&gt;Permit.io&lt;/strong&gt; for fine-grained, externalized authorization, ensuring that only the right people can access the right features at the right time.&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.amazonaws.com%2Fuploads%2Farticles%2Fzvmnmvf8jd4xavhe1mm1.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.amazonaws.com%2Fuploads%2Farticles%2Fzvmnmvf8jd4xavhe1mm1.png" alt="Image description" width="800" height="314"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F1q1jgmp3ywfm5355vde0.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.amazonaws.com%2Fuploads%2Farticles%2F1q1jgmp3ywfm5355vde0.png" alt="Image description" width="800" height="258"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fwlbcuxq5fl41lfb7c7ss.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.amazonaws.com%2Fuploads%2Farticles%2Fwlbcuxq5fl41lfb7c7ss.png" alt="Image description" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How AICHA Leverages Permit.io, Gemini AI, and Supabase.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Permit.io Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permit.io is the backbone of AICHA's access control and security.&lt;/li&gt;
&lt;li&gt;Every time a user logs in or tries to use a feature (like running Gemini AI analysis or viewing patient data), AICHA checks with Permit.io to see if they're allowed.&lt;/li&gt;
&lt;li&gt;All access policies (who can do what, when, and where) are managed centrally in Permit.io, not scattered in the code.&lt;/li&gt;
&lt;li&gt;User roles and permissions are synced from Supabase to Permit.io, so changes are reflected instantly.&lt;/li&gt;
&lt;li&gt;Permit.io logs every permission check and action for auditing and compliance.&lt;/li&gt;
&lt;li&gt;This means:

&lt;ul&gt;
&lt;li&gt;Only doctors in the right department can run Gemini AI analysis on their patients&lt;/li&gt;
&lt;li&gt;Nurses can view results but not run or approve AI features&lt;/li&gt;
&lt;li&gt;Admins can manage everything, but all actions are tracked&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Permit.io makes it easy to update policies (like restricting AI use to working hours) without changing any code—just update the policy in the dashboard.&lt;/li&gt;

&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fgoysch0lvtisl1cejvqi.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.amazonaws.com%2Fuploads%2Farticles%2Fgoysch0lvtisl1cejvqi.png" alt="Image description" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini AI Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AICHA uses Google Gemini AI to power its medical analysis and treatment suggestion features.&lt;/li&gt;
&lt;li&gt;Doctors can submit patient data for analysis, and Gemini provides:

&lt;ul&gt;
&lt;li&gt;Disease risk predictions&lt;/li&gt;
&lt;li&gt;AI-generated treatment recommendations&lt;/li&gt;
&lt;li&gt;Natural language explanations for results&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Gemini's advanced language and reasoning capabilities help make AI suggestions more accurate and explainable for medical staff.&lt;/li&gt;

&lt;li&gt;All AI-powered actions are protected by Permit.io policies, so only authorized users can access Gemini features.&lt;/li&gt;

&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2F8bhpiq5sr6x0d3q6klsz.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.amazonaws.com%2Fuploads%2Farticles%2F8bhpiq5sr6x0d3q6klsz.png" alt="Image description" width="800" height="548"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fakqzc398tgd2d5w9ltjm.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.amazonaws.com%2Fuploads%2Farticles%2Fakqzc398tgd2d5w9ltjm.png" alt="Image description" width="800" height="661"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Flfp2kqmgryg39ljznxya.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.amazonaws.com%2Fuploads%2Farticles%2Flfp2kqmgryg39ljznxya.png" alt="Image description" width="800" height="566"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fkubg0c9r1joqyycfzpql.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.amazonaws.com%2Fuploads%2Farticles%2Fkubg0c9r1joqyycfzpql.png" alt="Image description" width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supabase Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supabase is used as the backend database for AICHA.&lt;/li&gt;
&lt;li&gt;It stores:

&lt;ul&gt;
&lt;li&gt;User accounts and roles&lt;/li&gt;
&lt;li&gt;Patient records&lt;/li&gt;
&lt;li&gt;Audit logs of all actions (including Permit.io permission checks)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Supabase's real-time features help keep user data and permissions in sync with Permit.io.&lt;/li&gt;

&lt;li&gt;When users are created or updated in Supabase, they are automatically synced to Permit.io for up-to-date access control.&lt;/li&gt;

&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fujlqf049rxpi329xav6m.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.amazonaws.com%2Fuploads%2Farticles%2Fujlqf049rxpi329xav6m.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Features of AICHA (and Who Gets to Use Them)
&lt;/h2&gt;

&lt;p&gt;AICHA is built for different types of users, each with their own set of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Administrators&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Full access to all patient records and system settings&lt;/li&gt;
&lt;li&gt;Manage users and roles&lt;/li&gt;
&lt;li&gt;Approve or audit AI analysis and treatments&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Doctors&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Run AI-powered medical analysis on patient data&lt;/li&gt;
&lt;li&gt;Suggest and approve treatments&lt;/li&gt;
&lt;li&gt;View and update patient records&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Nurses&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;View patient records and AI analysis results&lt;/li&gt;
&lt;li&gt;Monitor patient vitals&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Patients&lt;/strong&gt; (optional/future)

&lt;ul&gt;
&lt;li&gt;View their own health records and AI-generated reports&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How does AICHA make sure only the right people can do these things?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's where Permit.io comes in! Every action—like running an AI analysis or viewing a patient record—is checked against Permit.io's policies before it happens. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doctors can't approve treatments for patients outside their department&lt;/li&gt;
&lt;li&gt;Nurses can't run AI analysis, but can view results&lt;/li&gt;
&lt;li&gt;Admins can do everything, but every action is logged for compliance&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Faibxq5xbpm1htlgljzrv.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.amazonaws.com%2Fuploads%2Farticles%2Faibxq5xbpm1htlgljzrv.png" alt="Image description" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Permit.io? (vs. Traditional Authorization)
&lt;/h2&gt;

&lt;p&gt;Traditionally, access control is hard-coded into the app. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization logic is scattered everywhere&lt;/li&gt;
&lt;li&gt;Changing permissions means changing code (and redeploying)&lt;/li&gt;
&lt;li&gt;It's easy to miss a check, leading to security holes&lt;/li&gt;
&lt;li&gt;Auditing who did what is a pain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;With Permit.io (externalized authorization):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All policies are managed in one place (the Permit.io dashboard or config)&lt;/li&gt;
&lt;li&gt;You can update permissions instantly—no code changes needed&lt;/li&gt;
&lt;li&gt;Every access check is logged automatically&lt;/li&gt;
&lt;li&gt;It's easy to create complex, context-aware rules (like department-based or time-based access)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to let doctors run AI analysis only during working hours? Just update the policy in Permit.io—no code changes required!&lt;/p&gt;
&lt;/blockquote&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.amazonaws.com%2Fuploads%2Farticles%2Flbpdoon1djagj56kpst2.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.amazonaws.com%2Fuploads%2Farticles%2Flbpdoon1djagj56kpst2.png" alt="Image description" width="800" height="628"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fu4jpn1xm63gp5iucxn2p.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.amazonaws.com%2Fuploads%2Farticles%2Fu4jpn1xm63gp5iucxn2p.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How AICHA Leverages Permit.io (In Simple Terms)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role-Based Access:&lt;/strong&gt; Each user is assigned a role (admin, doctor, nurse) and only sees features they're allowed to use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-Aware Policies:&lt;/strong&gt; Access can depend on department, time of day, or even the type of AI analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Management:&lt;/strong&gt; Users are synced from our database to Permit.io, so roles and permissions are always up to date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Checks:&lt;/strong&gt; Every time a user tries to do something important, AICHA checks with Permit.io to see if it's allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Logging:&lt;/strong&gt; Every action is logged for compliance and security.&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Ftsw0oax46vc3sldxlyoy.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.amazonaws.com%2Fuploads%2Farticles%2Ftsw0oax46vc3sldxlyoy.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Permit.io for AICHA (For First-Time Users)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Manual Setup via Permit.io Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a Permit.io Account:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://permit.io" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt; and sign up&lt;/li&gt;
&lt;li&gt;Create a new project called "AICHA"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure Resources&lt;/strong&gt;:&lt;br&gt;
Navigate to the Resources tab and create three key resources:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. &lt;strong&gt;Patient Records&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: patients
   Name: Patient Records
   Description: Medical patient information
   Actions:
     - create (Create new patient records)
     - read (View patient information)
     - update (Modify patient details)
     - delete (Remove patient records)
   Attributes:
     - department_id: string
     - record_type: string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. &lt;strong&gt;AI Analysis&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: ai_analysis
   Name: AI Analysis
   Description: AI-powered medical analysis
   Actions:
     - run (Execute AI analysis)
     - approve (Approve analysis results)
     - view (View analysis reports)
   Attributes:
     - analysis_type: string
     - department_id: string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c. &lt;strong&gt;Treatment Plans&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: treatment
   Name: Treatment Plans
   Description: Patient treatment recommendations
   Actions:
     - suggest (Create treatment suggestions)
     - approve (Approve treatment plans)
     - view (View treatment details)
   Attributes:
     - department_id: string
     - treatment_type: string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set Up Roles and Permissions&lt;/strong&gt;:
Create the following roles with specific permissions:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. &lt;strong&gt;Administrator&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: admin
   Permissions:
     - create:patients
     - read:patients
     - update:patients
     - delete:patients
     - run:ai_analysis
     - approve:ai_analysis
     - view:ai_analysis
     - suggest:treatment
     - approve:treatment
     - view:treatment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;b. &lt;strong&gt;Doctor&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: doctor
   Permissions:
     - read:patients
     - update:patients
     - run:ai_analysis
     - view:ai_analysis
     - suggest:treatment
     - approve:treatment
     - view:treatment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;c. &lt;strong&gt;Nurse&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Key: nurse
   Permissions:
     - read:patients
     - view:ai_analysis
     - view:treatment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get API Credentials:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Settings &amp;gt; API Keys&lt;/li&gt;
&lt;li&gt;Copy your API key, project ID, and environment&lt;/li&gt;
&lt;li&gt;Add them to your &lt;code&gt;.env&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nv"&gt;PERMIT_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_api_key
 &lt;span class="nv"&gt;PERMIT_PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_project_id
 &lt;span class="nv"&gt;PERMIT_ENVIRONMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fbsovrf5uc86god6ovnch.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.amazonaws.com%2Fuploads%2Farticles%2Fbsovrf5uc86god6ovnch.png" alt="Image description" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Automated Setup (Using Our Scripts)
&lt;/h3&gt;

&lt;p&gt;If you want to set up everything automatically, just run our setup script!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure your &lt;code&gt;.env&lt;/code&gt; file&lt;/strong&gt; (see above)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the setup script:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd &lt;/span&gt;backend
   npm &lt;span class="nb"&gt;install
   &lt;/span&gt;node scripts/permit-setup.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create all resources and roles in Permit.io&lt;/li&gt;
&lt;li&gt;Set up permissions and policies&lt;/li&gt;
&lt;li&gt;Sync all users from your database&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Permissions Are Checked in AICHA
&lt;/h2&gt;

&lt;p&gt;Whenever a user logs in or tries to use a feature, AICHA:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks their role and department&lt;/li&gt;
&lt;li&gt;Asks Permit.io if they're allowed to do the action (like run AI analysis)&lt;/li&gt;
&lt;li&gt;If Permit.io says yes, the action happens; if not, it's blocked&lt;/li&gt;
&lt;li&gt;Every check and action is logged for auditing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check if a doctor can run AI analysis&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canRunAI&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;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkAIAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;diagnosis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;patientDepartment&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="nx"&gt;canRunAI&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&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="s1"&gt;Not allowed to run AI analysis&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Core Implementation Files (How We Use Permit.io)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. permit.js (Core Permit.io Integration)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Permit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@permit.io/permit-node&lt;/span&gt;&lt;span class="dl"&gt;'&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;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PermitService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Permit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;token&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;PERMIT_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;pdp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cloudpdp.api.permit.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;projectId&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;PERMIT_PROJECT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;environment&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;PERMIT_ENVIRONMENT&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializeSyncService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;initializeSyncService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;syncUsers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./permit-sync-users&lt;/span&gt;&lt;span class="dl"&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;syncUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;syncUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;checkPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;checkContextualPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;baseContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;time&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&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;hour12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&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;span class="nx"&gt;baseContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;checkAIAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;analysisType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;patientDepartment&lt;/span&gt;&lt;span class="p"&gt;)&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;analysis_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;analysisType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;patientDepartment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&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;hour12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkContextualPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;checkTreatmentAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;treatmentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;patientDepartment&lt;/span&gt;&lt;span class="p"&gt;)&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;treatment_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;treatmentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;patientDepartment&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkContextualPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suggest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;treatment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;logAudit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;details&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;details&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;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PermitService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does this do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connects to Permit.io&lt;/li&gt;
&lt;li&gt;Syncs users&lt;/li&gt;
&lt;li&gt;Checks permissions (including context-aware checks)&lt;/li&gt;
&lt;li&gt;Logs every important action&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. permit-utils.js (Permission Helpers)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&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;permitted&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;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&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="nx"&gt;permitted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department_id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department_id&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkWithContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checkAccess&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does this do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks if a user can do something, with extra context (like department)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. permit-sync-users.js (User Sync)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&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;permit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./permit&lt;/span&gt;&lt;span class="dl"&gt;'&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;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;user&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;DB_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;host&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;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&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;DB_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&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;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;port&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;DB_PORT&lt;/span&gt;
&lt;span class="p"&gt;});&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;syncUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;result&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      SELECT u.id, u.email, u.role, u.department_id, u.first_name, u.last_name
      FROM users u
      WHERE u.active = true
    `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &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;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&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="nx"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;department_id&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✅ Successfully synced &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; users with Permit.io`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Error syncing users:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&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;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;syncUsers&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does this do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syncs all active users from our database to Permit.io, so permissions are always up to date&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. permit.json (Resource &amp;amp; Role Config)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"patients"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Patient Records"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AI Analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"treatment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Treatment Plans"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"suggest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approve"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"create:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"read:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delete:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approve:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"suggest:treatment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approve:treatment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:treatment"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"doctor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"run:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"suggest:treatment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approve:treatment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:treatment"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nurse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"read:patients"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:ai_analysis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"view:treatment"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does this do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines all resources, actions, and roles for Permit.io&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. permit-setup.js (Automated Setup Script)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Permit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@permit.io/permit-node&lt;/span&gt;&lt;span class="dl"&gt;'&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;dotenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;syncUsers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/permit-sync-users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;permit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Permit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;token&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;PERMIT_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pdp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cloudpdp.api.permit.io&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;projectId&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;PERMIT_PROJECT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&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;PERMIT_ENVIRONMENT&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;resources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;patients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Patient Records&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Medical patient information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;record_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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;ai_analysis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI Analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AI-powered medical analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;analysis_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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;treatment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Treatment Plans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Patient treatment recommendations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suggest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;treatment_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Administrator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Full system access&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suggest:treatment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve:treatment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:treatment&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;doctor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Doctor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Medical staff with AI access&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suggest:treatment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve:treatment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:treatment&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;nurse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nurse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Basic medical staff access&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:patients&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:ai_analysis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view:treatment&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="p"&gt;};&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;setupPermit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🚀 Starting Permit.io setup...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Creating resource: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resources&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="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Creating role: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;permit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&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="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Syncing users...&lt;/span&gt;&lt;span class="dl"&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;syncUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Setup completed successfully!&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Setup failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;span class="nf"&gt;setupPermit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What does this do?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates all resources and roles in Permit.io&lt;/li&gt;
&lt;li&gt;Sets up permissions and attributes&lt;/li&gt;
&lt;li&gt;Syncs users from the database&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fwsyzdyjmx68ktm8mihr7.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.amazonaws.com%2Fuploads%2Farticles%2Fwsyzdyjmx68ktm8mihr7.png" alt="Image description" width="800" height="540"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fe303lbz5dsi6tgj65ibz.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.amazonaws.com%2Fuploads%2Farticles%2Fe303lbz5dsi6tgj65ibz.png" alt="Image description" width="800" height="561"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Forwvdtvc242oidc1bn58.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.amazonaws.com%2Fuploads%2Farticles%2Forwvdtvc242oidc1bn58.png" alt="Image description" width="800" height="598"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F9xmc3md0ofedeouwvsfu.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.amazonaws.com%2Fuploads%2Farticles%2F9xmc3md0ofedeouwvsfu.png" alt="Image description" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Validating Permit.io Functionality at Runtime
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every time a user logs in or tries to use a feature, AICHA checks with Permit.io to see if they're allowed&lt;/li&gt;
&lt;li&gt;All permission checks are context-aware (role, department, time, etc.)&lt;/li&gt;
&lt;li&gt;All actions are logged for auditing&lt;/li&gt;
&lt;li&gt;User roles and permissions are kept in sync automatically&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AICHA shows how externalized authorization with Permit.io makes it easy to build secure, flexible, and auditable healthcare apps. With Permit.io, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control who can access AI features and sensitive data&lt;/li&gt;
&lt;li&gt;Update policies instantly without code changes&lt;/li&gt;
&lt;li&gt;Keep a full audit trail for compliance&lt;/li&gt;
&lt;li&gt;Make your app safer and easier to manage&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fsrt7rl2w5yyk22ctwfon.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.amazonaws.com%2Fuploads%2Farticles%2Fsrt7rl2w5yyk22ctwfon.png" alt="Image description" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Manikant92/AICHA" rel="noopener noreferrer"&gt;https://github.com/Manikant92/AICHA&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2Fzvmnmvf8jd4xavhe1mm1.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.amazonaws.com%2Fuploads%2Farticles%2Fzvmnmvf8jd4xavhe1mm1.png" alt="Image description" width="800" height="314"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F1q1jgmp3ywfm5355vde0.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.amazonaws.com%2Fuploads%2Farticles%2F1q1jgmp3ywfm5355vde0.png" alt="Image description" width="800" height="258"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fwlbcuxq5fl41lfb7c7ss.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.amazonaws.com%2Fuploads%2Farticles%2Fwlbcuxq5fl41lfb7c7ss.png" alt="Image description" width="800" height="418"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fgoysch0lvtisl1cejvqi.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.amazonaws.com%2Fuploads%2Farticles%2Fgoysch0lvtisl1cejvqi.png" alt="Image description" width="800" height="383"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F8bhpiq5sr6x0d3q6klsz.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.amazonaws.com%2Fuploads%2Farticles%2F8bhpiq5sr6x0d3q6klsz.png" alt="Image description" width="800" height="548"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fakqzc398tgd2d5w9ltjm.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.amazonaws.com%2Fuploads%2Farticles%2Fakqzc398tgd2d5w9ltjm.png" alt="Image description" width="800" height="661"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Flfp2kqmgryg39ljznxya.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.amazonaws.com%2Fuploads%2Farticles%2Flfp2kqmgryg39ljznxya.png" alt="Image description" width="800" height="566"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fkubg0c9r1joqyycfzpql.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.amazonaws.com%2Fuploads%2Farticles%2Fkubg0c9r1joqyycfzpql.png" alt="Image description" width="800" height="575"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fujlqf049rxpi329xav6m.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.amazonaws.com%2Fuploads%2Farticles%2Fujlqf049rxpi329xav6m.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Faibxq5xbpm1htlgljzrv.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.amazonaws.com%2Fuploads%2Farticles%2Faibxq5xbpm1htlgljzrv.png" alt="Image description" width="800" height="524"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Flbpdoon1djagj56kpst2.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.amazonaws.com%2Fuploads%2Farticles%2Flbpdoon1djagj56kpst2.png" alt="Image description" width="800" height="628"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fu4jpn1xm63gp5iucxn2p.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.amazonaws.com%2Fuploads%2Farticles%2Fu4jpn1xm63gp5iucxn2p.png" alt="Image description" width="800" height="342"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Ftsw0oax46vc3sldxlyoy.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.amazonaws.com%2Fuploads%2Farticles%2Ftsw0oax46vc3sldxlyoy.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fbsovrf5uc86god6ovnch.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.amazonaws.com%2Fuploads%2Farticles%2Fbsovrf5uc86god6ovnch.png" alt="Image description" width="800" height="424"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fwsyzdyjmx68ktm8mihr7.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.amazonaws.com%2Fuploads%2Farticles%2Fwsyzdyjmx68ktm8mihr7.png" alt="Image description" width="800" height="540"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fe303lbz5dsi6tgj65ibz.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.amazonaws.com%2Fuploads%2Farticles%2Fe303lbz5dsi6tgj65ibz.png" alt="Image description" width="800" height="561"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Forwvdtvc242oidc1bn58.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.amazonaws.com%2Fuploads%2Farticles%2Forwvdtvc242oidc1bn58.png" alt="Image description" width="800" height="598"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F9xmc3md0ofedeouwvsfu.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.amazonaws.com%2Fuploads%2Farticles%2F9xmc3md0ofedeouwvsfu.png" alt="Image description" width="800" height="326"&gt;&lt;/a&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fsrt7rl2w5yyk22ctwfon.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.amazonaws.com%2Fuploads%2Farticles%2Fsrt7rl2w5yyk22ctwfon.png" alt="Image description" width="800" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>permitchallenge</category>
      <category>devchallenge</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Subtitles.ai - There is an ability in every disability</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Mon, 11 Apr 2022 17:22:36 +0000</pubDate>
      <link>https://dev.to/manikant92/subtitlesai-there-is-an-ability-in-every-disability-4gb7</link>
      <guid>https://dev.to/manikant92/subtitlesai-there-is-an-ability-in-every-disability-4gb7</guid>
      <description>&lt;h3&gt;
  
  
  Innovative Ideas Challenge
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The Innovative Ideas category is an ideal choice where I want to leverage &lt;strong&gt;&lt;em&gt;Deepgram STT&lt;/em&gt;&lt;/strong&gt; to solve the problem for &lt;strong&gt;Hard of Hearing Community(Deaf)&lt;/strong&gt;. I had experience with Deepgram as part of this hackathon where I have submitted one project leveraging Deepgram STT. Due to time constraints, I couldn't be able to submit the solution as a project, but want to bring to notice that with DG STT, real world problems can be solved.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Deepgram Use-Case
&lt;/h3&gt;

&lt;p&gt;We all know we are in a Sound tech era and there is always a problem for deaf and people with some or other hearing problems (Hard of Hearing Community) where they face many challenges what the others are speaking it can be during phone call interactions, Zoom/Teams/Slack meetings, any YouTube video, any movie watching channel/video or even to listening to audio channels like Spotify etc.,.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;&lt;em&gt;Deepgram STT&lt;/em&gt;&lt;/strong&gt;, there comes the power where subtitles can be generated as generic way without integration with any apps like mentioned above.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;&lt;em&gt;Windows App&lt;/em&gt;&lt;/strong&gt; can be built using &lt;em&gt;&lt;strong&gt;Python Tkinter GUI and Deepgram STT and via websocket programming&lt;/strong&gt;&lt;/em&gt;, everything that is getting played/listened can be heard via Windows mic and generate the subtitles so that Hard of Hearing Community can enjoy keeping aside all their disabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dive into Details
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In order to solve the problem for Hard of Hearing people, we can build an Windows (since its the OS majority of the users use) application which converts all sound apps with Subtitles under the bar which makes an effective solution for Hearing problem users.&lt;/li&gt;
&lt;li&gt;It converts any application sound that is getting played or listened (Youtube, Netflix, Prime, Spotify, Zoom/Teams call, any Video, any Audio etc.,.) to on Windows OS into Subtitles of any language preference the user have.&lt;/li&gt;
&lt;li&gt;Now using the  app, without disturbing their other apps, it displays Subtitles (leveraging Deepgram STT) near taskbar of Windows for any sound getting listened to or played to.&lt;/li&gt;
&lt;li&gt;It can also have the capability of displaying subtitles in any language the user prefers to.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;With Deepgrams STT tech, there is an ability for every disability&lt;/em&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Impacting the &lt;em&gt;Hard of Hearing community with a generic solution&lt;/em&gt; where they can leverage the app to full extent with DG STT.&lt;/li&gt;
&lt;li&gt;Expand the same solution for Mac, Linux, Android users.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hackwithdg</category>
      <category>python</category>
      <category>deepgram</category>
      <category>tkinter</category>
    </item>
    <item>
      <title>VProfressor.ai - A 24x7 Virtual Professor for Students</title>
      <dc:creator>Manikant Kella</dc:creator>
      <pubDate>Mon, 11 Apr 2022 16:56:41 +0000</pubDate>
      <link>https://dev.to/manikant92/vprofressorai-a-24x7-virtual-professor-for-students-1g8f</link>
      <guid>https://dev.to/manikant92/vprofressorai-a-24x7-virtual-professor-for-students-1g8f</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;Education is everything. Knowledge empowers anything.&lt;/p&gt;

&lt;p&gt;That said, Covid-19 forced students to virtual learning where students are struggling to interact and get timely feedback, new learnings, clarifications with the professor or friends due to various reasons.&lt;/p&gt;

&lt;p&gt;To solve the problem, we came up with web application where it helps the students and increase their speed, efficiency of learning and also get their doubts clarified with additional learnings.&lt;/p&gt;

&lt;p&gt;VProfessor.ai contains different features designed to facilitate online learning. Users can upload a wav/mp3/ text files. Then, a Deepgram STT transcript of the audio is returned, along with a summary of the data. This includes key words and main topics, Wikipedia page links, current events from NewsAPI, and recommended YouTube videos. From here, users can either read the summary report on the website or download it as a pdf for their personal studying. Additionally, students may "ask the professor" a question, and get a quick short answer for themselves which leverages wolframalpha api's.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category
&lt;/h3&gt;

&lt;p&gt;Wacky Wildcards&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code on GitHub
&lt;/h3&gt;

&lt;p&gt;GitHub Link: &lt;a href="https://github.com/Manikant92/DG_VProfressor.ai"&gt;https://github.com/Manikant92/DG_VProfressor.ai&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Demo Video: &lt;a href="https://youtu.be/hPWR2ED0NK4"&gt;https://youtu.be/hPWR2ED0NK4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With just this piece of  code comes the entire power for application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  async function transcribe(file){
    const streamSource = {
      stream: fs.createReadStream(file),
      mimetype: mime.getType(file),
    };

    const response = await deepgram.transcription.preRecorded(streamSource, {
      punctuate: false,
      utterances: true,
    });
    console.log("DG Response" + response);

    var srtTranscript = response.toSRT(); // toWebVTT() //toSRT()
    srtTranscript = srtTranscript.replace(/\d+/g, '');
    srtTranscript = srtTranscript.replace(/:/g,'');
    srtTranscript = srtTranscript.replace(/-/g,'');
    srtTranscript = srtTranscript.replace(/&amp;gt;/g,'');
    srtTranscript = srtTranscript.replace(/,/g,'');
    console.log("DG SRT Transcript" + srtTranscript);
    return srtTranscript;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YUD-V5wV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xplrpbl77lnct79iync6.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YUD-V5wV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xplrpbl77lnct79iync6.PNG" alt="App Image" width="880" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_KJjN3Yv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cur2eonspdiwdp4ggh57.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_KJjN3Yv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cur2eonspdiwdp4ggh57.PNG" alt="Deepgram Transcript" width="880" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Q6MyXM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f157jqrqzszkgk0f2shj.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Q6MyXM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f157jqrqzszkgk0f2shj.PNG" alt="Power of ML" width="880" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--98IkiHNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82i53s9n5zfd3dgvy5qr.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--98IkiHNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82i53s9n5zfd3dgvy5qr.PNG" alt="Power of everything at one place" width="880" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dive into Details
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We are leveraging Deepgram Speech-to-Text API and use it to convert audio/video files to a written transcript.&lt;/li&gt;
&lt;li&gt;With Azure Text Analytics, we will generate an analysis report containing transcript, summary, and keywords.&lt;/li&gt;
&lt;li&gt;With that keywords output, we will use the Wikipedia API, NewsAPI to generate links based on the keywords. To provide more information, we will also create a system to search recommended YouTube videos based on a search query, which used the YouTube-Data API.&lt;/li&gt;
&lt;li&gt;We are using Azure Blob Storage to store video/audio files.&lt;/li&gt;
&lt;li&gt;An additional feature of "ask the professor" is provided to users, where users can search any questions or doubts, it fetches the answer from wolframalpha api and displays to user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Impact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It creates a wide impact and provides huge benefits to all Students due to Virtual mode of learning during Covid-19.&lt;/li&gt;
&lt;li&gt;It saves students time and effort where they can get all information at one place.&lt;/li&gt;
&lt;li&gt;It increases efficiency of students/users.&lt;/li&gt;
&lt;li&gt;It improves speed of learning.&lt;/li&gt;
&lt;li&gt;A one stop app which integrates with multiples api's and makes it easy for students for learning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deepgram STT is so accurate and fast which makes it more reliable for students education. &lt;/li&gt;
&lt;li&gt;With Deepgram STT comes the whole power for entire application.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hackwithdg</category>
      <category>node</category>
      <category>deepgram</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
