<?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: NOVAInetwork</title>
    <description>The latest articles on DEV Community by NOVAInetwork (@0xdevc).</description>
    <link>https://dev.to/0xdevc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3898802%2Fe797d15e-1e95-4646-8ada-b97a915c30a8.png</url>
      <title>DEV Community: NOVAInetwork</title>
      <link>https://dev.to/0xdevc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/0xdevc"/>
    <language>en</language>
    <item>
      <title>AI agents are becoming real economic actors. They deserve a chain built for them, not borrowed from us.</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:39:40 +0000</pubDate>
      <link>https://dev.to/0xdevc/ai-agents-are-becoming-real-economic-actors-they-deserve-a-chain-built-for-them-not-borrowed-from-3dpi</link>
      <guid>https://dev.to/0xdevc/ai-agents-are-becoming-real-economic-actors-they-deserve-a-chain-built-for-them-not-borrowed-from-3dpi</guid>
      <description>&lt;p&gt;The next major blockchains will be built around AI, not with AI bolted on afterward. That's the bet behind NOVAI.&lt;/p&gt;

&lt;p&gt;Right now, when an AI agent needs to transact, it reaches for crypto, because crypto is the closest available fit: permissionless, programmatic, no human in the loop gating a payment. But "closest available fit" isn't "built for the purpose." Existing chains were designed for humans, and agents are squatting on rails that were never meant for them. As agents take on more meaningful economic weight, that mismatch only gets more expensive.&lt;/p&gt;

&lt;p&gt;NOVAI is a from-scratch layer-1, written in Rust, where AI agents are first-class protocol primitives, not smart contracts running on a human chain. Agent identity, reputation, AI-to-AI escrow, payments with splits and conditions, SLAs, oracles , these live in the protocol itself. &lt;/p&gt;

&lt;p&gt;The settlement layer and the reputation layer for the agent economy, in one place, designed for the participants who'll actually use it. One thing I want to be precise about, because it's the obvious objection: this isn't centralization wearing a blockchain costume. NOVAI is provider-agnostic by construction. The chain only ever sees signals and payments. The agent's actual brain runs wherever its operator puts it , a cloud API, a self-hosted model, a custom one, any jurisdiction. The trust assumption sits where the operator chooses, not where the protocol forces it.&lt;/p&gt;

&lt;p&gt;And it's real, working code, not a whitepaper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;120,000 lines of Rust across 15 crates, &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2,000 tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HotStuff-style BFT consensus with a 3-chain commit rule and locked-QC safety&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sparse Merkle Tree state&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AI entities as native protocol primitives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A live multi-validator private testnet running in lockstep&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rust, TypeScript, and Python SDKs, plus developer docs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open source under Apache 2.0&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built NOVAI and I architect it, but I don't think of it as mine in the way that matters. The thing being built , a settlement and reputation layer for an AI economy that's going to need one , is bigger than me. I'm its first builder and its most committed one, but I'm a contributor to it, not its owner. That's the frame I want a co-founder to share.&lt;/p&gt;

&lt;p&gt;So I'm not looking for someone to help me with my project. I'm looking for one person who sees what this could be and wants to own it alongside me , someone who writes Rust, who's drawn to BFT consensus, distributed systems, or on-chain AI, and who wants to build a long-term future here rather than land a few merged PRs. Founder and co-founder, both in service of the same thing.&lt;/p&gt;

&lt;p&gt;Honest disclaimer: this is very early. No funding, no salary, no equity in place yet , we're pre-public-testnet. What's on offer is a real co-founder stake and token allocation once tokenomics are designed. &lt;/p&gt;

&lt;p&gt;If you need a paying role, this isn't it yet.&lt;/p&gt;

&lt;p&gt;If that's something you want to own, not just contribute to: look at the repo, then reach out.github.com/0x-devc/NOVAI-node · novai.network · &lt;a href="mailto:NOVAInetwork@protonmail.com"&gt;NOVAInetwork@protonmail.com&lt;/a&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>rust</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>The Bug Behind the Bug: Anatomy of a Three-Layer Consensus Halt</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Wed, 17 Jun 2026 14:19:41 +0000</pubDate>
      <link>https://dev.to/0xdevc/the-bug-behind-the-bug-anatomy-of-a-three-layer-consensus-halt-32n5</link>
      <guid>https://dev.to/0xdevc/the-bug-behind-the-bug-anatomy-of-a-three-layer-consensus-halt-32n5</guid>
      <description>&lt;p&gt;A dev-log from building an AI-native layer one, mostly alone.&lt;/p&gt;

&lt;p&gt;I am 18, I build NOVAI, and you can find me as 0x-devc. &lt;/p&gt;

&lt;p&gt;NOVAI is a layer-one blockchain written from scratch in Rust, with a chained-BFT consensus in the HotStuff family and AI entities as first-class protocol primitives. This post is about the week my testnet wedged itself at a single block height and would not recover, and about the three-layer bug I eventually found sitting underneath a one-line error message. It is also, honestly, a post about the discipline of not fixing the first thing you see.&lt;/p&gt;

&lt;p&gt;I want to be precise up front about what this is and is not. I fixed a deep consensus halt, and the chain is now stable under sustained load. I did not prove my consensus formally safe, and the hardening is not finished. If you skip to the end, read the "What is still open" section, because it is the most important part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The symptom&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The chain stopped committing. Four validators, all of which had been running continuously with zero restarts, sat at a committed height around 535,003 and refused to advance. The view-change counter, which on a healthy chained-BFT chain should barely move, was climbing on a steady cadence: a fresh round every sixty seconds, forever, with nothing committing in any of them.&lt;/p&gt;

&lt;p&gt;Three of the four validators were emitting the same error on every single leader turn:&lt;/p&gt;

&lt;p&gt;Leader self-vote add failed: InvalidVote("Equivocation: voter [...] voted for different blocks at same height")&lt;/p&gt;

&lt;p&gt;Each node's error named only its own voter key. The fourth validator was different: it was stuck three blocks further back, never advancing its round, never proposing, and spinning a completely different error, a codec failure complaining about a duplicate voter, at roughly 195 times per second. Over the capture window that one node had written 64 MB of logs; the other three had written about 28 KB each.&lt;/p&gt;

&lt;p&gt;So I had two distinct failure signatures on the same wedged chain, no restarts to blame, and a view-change loop that would clearly run until the heat death of the validators. The question I started from was narrow and sharp: with zero restarts, how does a continuously running validator come to hold recorded votes for two different blocks at the same height, such that its own equivocation guard rejects its own leader self-vote?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first read, which was correct and incomplete&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The loud error pointed at one structure, and the first read of it was right as far as it went.&lt;/p&gt;

&lt;p&gt;My consensus tracked an in-memory map, voted_at_height, from a voter's address to the block hash they had voted for at the current height. Its stated purpose was to catch equivocation: a validator voting for two different blocks at one consensus height. It was cleared when a height committed, and when the view height advanced on a dominating QC. It was not cleared on a plain round advance.&lt;/p&gt;

&lt;p&gt;Here is why that one omission is fatal. In chained BFT, a block's hash commits to its round. When a height fails to certify in its first round and the view changes, the next leader proposes a block for the same height in a higher round, and that block necessarily has a different hash. This is not equivocation. It is the entire point of view change: a higher view supersedes a failed lower one.&lt;/p&gt;

&lt;p&gt;But voted_at_height did not know that. When a leader self-voted at height H in round R, it recorded voted_at_height[self] = hash(block at H, round R). The proposal went nowhere. Rounds advanced. The leader rotation cycled back around, and the same validator became leader again at the same height H, now in some round R plus k. It built a new block, with a new hash because the round differed, and tried to self-vote. Its own guard read the stale entry from round R, saw a different hash, and rejected the vote as equivocation. From that point on, every leader turn that validator ever took at that height produced exactly the captured error. All three healthy validators had burned their one clean self-vote in their first leader turn after the height wedged, which is why they were failing symmetrically, each only against its own key.&lt;/p&gt;

&lt;p&gt;The structural mistake was a scope mismatch I would later recognize as a recurring theme: voted_at_height was a height-scoped data structure being used to enforce a property that is actually round-scoped (within-view), and worse, the property it enforced is not one HotStuff requires at all. HotStuff forbids equivocation within a view; cross-view safety is supposed to come from a locked-QC rule, not from a blanket ban on ever re-voting at a height. The guard was a broken substitute for a safety mechanism that, it turned out, did not exist anywhere else in my code either. More on that later.&lt;/p&gt;

&lt;p&gt;This was a real bug. If I had stopped here, killed the guard, and redeployed, I would have learned a painful lesson, because the chain would have stayed wedged. The equivocation error was the loudest layer, not the deepest one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pulling the thread: the root cause was layered&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I treat consensus bugs the way I wish more people did: diagnosis first, and diagnosis is not allowed to authorize a patch. So before touching any code I produced two full read-only diagnoses of the same evidence, from cold, independently. The interesting part is that they agreed on the symptom and disagreed on the primary cause. One read the equivocation guard as the root; the other argued the guard was secondary and something in the sync path was primary. Reconciling that disagreement against the source, line by line, is what actually found the bug. I did not average the two opinions. I went back to the code and let it break the tie.&lt;/p&gt;

&lt;p&gt;Here is what the three layers turned out to be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer three (visible): the equivocation guard misfire&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the voted_at_height story above. It is real, it is deterministic, and it is the error you see in the logs. It is also the least important of the three, because fixing it alone does not unwedge the chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer one (primary, and restart-proof): the commit cursor outran the QC chain&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the one that actually held the chain hostage, and it is the one the second diagnosis was right about.&lt;/p&gt;

&lt;p&gt;A node's view of progress lives in two numbers: highest_qc, the highest quorum certificate it has seen, and committed_height, the cursor marking what it considers finalized. On the three healthy validators these two numbers had diverged in a way that should be impossible: the highest QC was at one height, but the commit cursor was sitting a step above it, at the stuck height, with no QC certifying that height anywhere in the cluster.&lt;/p&gt;

&lt;p&gt;How does a cursor get ahead of the certificates that are supposed to justify it? Through the catch-up sync path. When a node falls behind, it requests blocks from peers and replays them. My sync handler advanced committed_height and executed the synced blocks gated only on two checks: parent-hash contiguity, and a state-root match against local execution. It did not require that a valid certifying QC for the block exist. The comment in that code even stated the false assumption out loud: these blocks "were already committed by network consensus." They were not necessarily certified by anything. On the serving side, the block responder happily served uncommitted, proposal-time blocks straight from its database and in-memory cache, with no committed-height filter of any kind.&lt;/p&gt;

&lt;p&gt;Compose those two and you get the trap. An uncertified block at the stuck height had been distributed and persisted at proposal time. The sync path then "committed" it on the healthy nodes without any certificate. Now the cursor sat above the highest QC, and here is the kill: every follower silently drops a proposal at a height it already considers committed. So no validator would vote on any new proposal at the stuck height. With no votes, no QC could form there. With no QC, the highest QC could never advance. With the highest QC frozen, the cursor divergence could never resolve. The wedge was self-sealing.&lt;/p&gt;

&lt;p&gt;And it was restart-proof, which is the property that makes this kind of bug so nasty. The commit cursor is persisted to disk. Restarting a wedged node just reloads the wedge. There is no operational escape that does not touch persisted state.&lt;/p&gt;

&lt;p&gt;To see why nothing could move, it helps to know that essentially every view computation in the consensus crate fuses these numbers through one formula:&lt;/p&gt;

&lt;p&gt;expected_height = max(self.height, highest_qc.height) + 1&lt;/p&gt;

&lt;p&gt;Proposals, votes, and timeouts are all gated on that expected height. With the cursor already past the highest QC and the highest QC frozen, expected height was pinned exactly one step behind where the cursor already was, so every leader kept trying to re-propose a height the followers had already buried. Layer three (the equivocation guard) was just the specific way that doomed re-proposal failed on the leader's own self-vote. Even if I had made the self-vote succeed, the proposal would have died silently at every follower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer two (the origin, and safety-adjacent): a duplicate-voter QC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That left the question of how the cluster got into a single failed round in the first place, and why the fourth validator was bricked in a totally different way. The origin event was a malformed quorum certificate: a QC containing the same voter twice.&lt;/p&gt;

&lt;p&gt;The QC builder took the first quorum-many votes for a block hash with no per-voter deduplication. It trusted that the vote pool did not contain duplicates. But the network-vote ingestion path had no equivocation or per-hash duplicate guard at all; its only dedup was the round-scoped voted_in_round set, which is cleared on every round advance, while the vote pool itself was deliberately retained across round advances so that late QCs could still form. So the same voter's vote, arriving once before a round advance and once after, landed twice in the pool for that block hash. The builder then formed a "quorum" certificate that actually had two distinct signers where it needed three. That is not just a liveness nuisance; a QC with fewer distinct signers than quorum is a BFT counting violation, a safety-adjacent defect in its own right.&lt;/p&gt;

&lt;p&gt;What contained the blast radius was, of all things, my canonical codec. The QC encoder rejects duplicate voters on serialization. So the malformed QC could never be encoded, which meant it could never cross the wire, be persisted, or be embedded in a timeout. It poisoned exactly one node: the one that formed it. That node could no longer encode its own highest QC, so it could not create timeouts (195 times per second of failing to, hence the 64 MB log), could not persist sync commits, and fell out of the voting quorum entirely. Its dropout is what reduced the live set to a fragile three, which is what let a single height fail to certify in one round, which is what armed layers one and three. The codec saved the network from a bad certificate and simultaneously bricked the node that produced it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The structural gap underneath all three&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While reconciling the diagnoses I ran one grep that reframed everything: a case-insensitive search for "locked" across the consensus crate returned zero matches. No locked QC, no locked round, no lock-step field anywhere in the consensus state. Standard chained HotStuff leans on the locked-QC rule for cross-view safety. Its absence is the deeper reason all three layers were even possible: the equivocation guard was a broken stand-in for it, the duplicate-voter QC slipped through because nothing tracked per-height certificate uniqueness, and the sync path felt free to commit on contiguity alone because there was no notion of a binding lock to violate. I did not try to introduce a locked-QC rule in this round of work. I flagged it, hard, and you will see it again in the open-questions section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reconciled fix had four parts, landed as a sequence of small commits with a stop-and-review gate between each, because this is the most safety-critical code in the system and I did not want a five-fix mega-diff that nobody could review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix A, the primary one: certify before you commit&lt;/strong&gt;. No block may advance the commit cursor unless a valid certifying QC for that block has been verified locally first. This sounds obvious and it was the whole ballgame for layer one. It also had a storage prerequisite I had to clear first: per-height certifying QCs were not durably stored or retrievable. The commit path only wrote the single triggering QC, the sync path wrote no QCs at all, and the block-response wire format had no field to carry them. So Fix A was two stages. Stage one was pure plumbing: dense per-height QC persistence on both the commit and sync paths, a reader to load a QC at a given height, and a QC field added to the block-response message. Stage two was the actual check: verify each synced block's certifying QC, binding it to the block's height and hash and running full quorum-and-signature verification, and advance the cursor only across the contiguous prefix of properly certified blocks, stopping at the first one that fails. This is the change that makes the original wedge unreachable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix B, the safety one: make QCs duplicate-proof on every path.&lt;/strong&gt; Deduplicate votes by voter in the QC builder before counting quorum, so two copies of one voter can never inflate a count. Add a per-voter dedup to the network-vote ingestion path so a duplicate never enters the pool in the first place. And, crucially, route every untrusted external QC-acceptance path , a gossiped QC, a peer's timeout, a synced block , through a single helper, verify_qc_well_formed, that requires a quorum of distinct in-set voters, verifies every signature, and validates canonical encoding. Locally formed QCs and proposal-embedded QCs keep their own per-voter dedup and canonical-encode gate rather than the helper, but every path a QC can arrive from across the network now hits a verification chokepoint. While writing this I found those external-acceptance sites were asymmetric: some verified fully, some verified nothing. The helper closed that gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix C, the liveness one: delete the equivocation guard.&lt;/strong&gt; I cleared voted_at_height on round advance, and then deleted the structure entirely (the only mentions that survive are comments in the regression test), because once it is round-scoped it does nothing the existing round-scoped voted_in_round check does not already do. Within-round equivocation is still caught, on both vote paths. What I intentionally gave up is the cross-round same-height "equivocation" detection, which was never a real safety property to begin with. Its safety now rests on Fix B's per-voter dedup plus the standard quorum-intersection argument. I want to be honest that this is the one place I reduced local Byzantine detection on purpose, and it is exactly the place a later audit told me to look hardest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix D, the resilience one.&lt;/strong&gt; Two changes. First, a backoff on the failing-timeout path, because the 195-per-second error loop on the bricked node had overrun log retention and erased the onset logs, which is why the original onset is unrecoverable. A consensus node that fails should fail quietly enough to preserve its own forensics. Second, move the timeout's embedded-QC adoption ahead of its height gate, so a node stuck at a wrong view can still learn a dominating, fully verified QC from a peer's timeout and self-heal instead of discarding exactly the message that would rescue it.&lt;/p&gt;

&lt;p&gt;The ordering mattered. Fix B lands before Fix C, because B's dedup is the safety net that replaces the guard C removes. All four assume a fresh genesis at deploy, for a reason I will come back to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cold audit that reopened the question&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before I trusted any of this, I ran a from-scratch adversarial audit of the consensus: a cold, multi-pass review whose governing rule was that nothing is assumed correct because it is committed or because it has a passing test. I assigned each safety and liveness property to an independent reviewer tasked with breaking it, then had separate refuters attack every finding, then reconciled every verdict by hand against the source. It was the most productive thing I did all week, and it did two things I did not expect.&lt;/p&gt;

&lt;p&gt;First, it found a critical hole my fix had not touched. A gossiped standalone QC, arriving as its own network message, was installed and persisted with no signature, quorum, or membership verification whatsoever. A single unauthenticated message carrying a QC with an enormous height and an empty vote set would install as the highest QC, pin expected height astronomically high so that every real proposal, vote, and timeout was rejected, and survive restart because it got persisted. That is strictly worse than the halt I was fixing: a permanent, restart-durable wedge from one forged message. It was a clean deploy blocker, and my four fixes sailed right past it because they were focused on the paths the incident had exercised, and the incident had never exercised this one. I designed and landed a guard that routes every gossiped QC through the same verify_qc_well_formed helper before it can touch any state, and I convinced myself with a chokepoint argument, and tests, that this was the only unverified path into the highest QC. A combined verification pass over the whole stack came back clean for that hole.&lt;/p&gt;

&lt;p&gt;Second, and more soberingly, the audit reopened the deepest question and refused to close it. With no locked-QC rule, two conflicting QCs at the same height can each form and each independently pass well-formedness, because nothing enforces per-height uniqueness. The audit confirmed that single-node divergence is blocked: once a node installs a QC at a height, its expected height moves on and it will drop a later conflicting vote at that height. But it could not rule out cross-set divergence, where disjoint quorums each extend a different branch under adversarial message scheduling. It marked the escalation to a conflicting commit as unproven but plausible, and it classified the resolution as a design question, introducing a real locked-QC safety rule, not a one-line patch. I am not going to pretend that verdict is something it is not. It is open.&lt;/p&gt;

&lt;p&gt;The audit also produced a written backlog of lower-tier hardening items, each diagnosed read-only and each gated on its own future fix-design session. And it taught me a smaller but real lesson about trustworthy monitoring: one residual it logged was that my new certify-before-commit sync path advanced the cursor without updating the highest QC, which would have made the cursor legitimately appear to outrun the QC during soak and blinded the very invariant I most wanted to watch. I closed that specifically so the soak's signal would mean what I thought it meant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I verified it: wipe, fresh genesis, soak&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recovery was not a restart. It could not be, because the wedge lived in persisted state; reloading it just reloads the wedge. I considered rolling the cursor back and replaying, and certifying the stuck block after the fact, and rejected both: replay has to reconcile already-executed uncertified blocks and inherits a sparse history of QC rows that the new verification cannot check, and retroactive certification is simply impossible because no quorum ever certified that block. So I did a true wipe and fresh genesis. That also happened to satisfy a precondition of Fix A: the certify-before-commit invariant only holds without awkward historical backfill if the chain carries a dense per-height QC from block zero, which a fresh genesis gives you for free.&lt;/p&gt;

&lt;p&gt;Then I redeployed onto the binary carrying all of it, re-bootstrapped an oracle AI entity, and let it soak.&lt;/p&gt;

&lt;p&gt;The headline number is a view-change counter. On the wedged chain, that since-start counter had climbed to 4,872 as failed rounds churned endlessly at the stuck height. On the fresh chain, it has barely moved: a few dozen view changes across more than 600,000 committed blocks, a rate so low it rounds to zero against the wedged chain's churn.&lt;/p&gt;

&lt;p&gt;That contrast is the cleanest evidence I have, and it is worth saying why it is such good evidence. In chained BFT a view change happens only when a round fails to certify before its timer fires. A healthy pipeline certifies in the first round almost every time, so the view-change counter should be nearly stationary over the life of the chain. Thousands of view changes is not noise; it is the precise fingerprint of the wedge, a chain trying and failing to make progress on a loop. A few dozen view changes across more than 600,000 committed blocks means the chain is certifying in the first round essentially always. The wedge mechanism is gone, not masked. It is a black-box, behavioral confirmation that complements the unit tests rather than restating them.&lt;/p&gt;

&lt;p&gt;Alongside that, the targeted invariants from the fix design all held through the soak: no equivocation errors, no duplicate-voter codec failures, the commit cursor never exceeded the highest QC, and every committed height had a retrievable valid QC. The oracle entity published continuously the whole time, which is the application-layer way of saying the thing on top of consensus kept working because consensus kept working. And the regression tests pin the mechanisms directly: the view-change re-proposal that used to throw equivocation now passes, an uncertified synced block no longer advances the cursor, a duplicate-voter set no longer forms a quorum QC, and the forged gossiped QC is rejected before it touches any state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is still open&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the section I most want you to read, because the temptation after a soak like that is to declare victory, and that would be dishonest.&lt;/p&gt;

&lt;p&gt;I fixed a liveness halt. I did not prove safety. Concretely, the consensus still has no locked-QC rule, and the cold audit left the conflicting-commit question unproven. Single-node divergence is blocked; cross-set divergence under adversarial scheduling is not ruled out. Introducing a proper HotStuff-style lock, or proving rigorously that the commit depth alone suffices, is design-level work, and it is the gate I have set for myself before I would ever let an external, untrusted validator into the set. Until that question is closed, "the chain is stable under sustained load with the validators I run" is a true statement, and "the consensus is safe" is not one I am willing to make.&lt;/p&gt;

&lt;p&gt;Beyond that, the audit left a written, prioritized backlog of consensus-hardening items that I am working through one fix-design session at a time: making round adoption evidence-gated rather than trusting a single peer, adding an explicit equivocating-leader guard, adding a commit-time fork guard so that if a conflicting commit ever did occur something would detect and halt rather than proceed, and confirming the crash-atomicity of the executed state root against the commit cursor. I am deliberately not writing exploit recipes for these here. The point is the honest shape of the work: these are known, diagnosed, and scheduled, not surprises.&lt;/p&gt;

&lt;p&gt;So, plainly: this is not formally verified, it is not proven safe against a Byzantine validator across all message schedules, and it is not mainnet-ready. What it is, is a chain whose halt is genuinely fixed, whose recovery and sync paths are meaningfully hardened, and which now soaks clean under continuous load with an AI agent riding on top of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The lesson I keep relearning is that the loudest error is rarely the deepest one. The equivocation message in my logs was real, and chasing only it would have shipped a fix that left the chain just as wedged, because the cursor had quietly outrun its certificates underneath. Two independent diagnoses, reconciled against the source rather than against each other, found the layer that mattered. A cold audit that assumed nothing surfaced a worse bug than the one I started with, and then had the integrity to leave the hardest question open instead of waving it through.&lt;/p&gt;

&lt;p&gt;The halt is fixed. The hardening continues. The next stage is ahead, and I will write it up when it is real.&lt;/p&gt;

&lt;p&gt;0x-devc&lt;/p&gt;

</description>
      <category>rust</category>
      <category>blockchain</category>
      <category>consensus</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Five Months, 110,000 Lines of Rust, and Zero Smart Contracts: A Technical Tour of NOVAI</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Mon, 01 Jun 2026 10:32:19 +0000</pubDate>
      <link>https://dev.to/0xdevc/five-months-110000-lines-of-rust-and-zero-smart-contracts-a-technical-tour-of-novai-1gh6</link>
      <guid>https://dev.to/0xdevc/five-months-110000-lines-of-rust-and-zero-smart-contracts-a-technical-tour-of-novai-1gh6</guid>
      <description>&lt;p&gt;I have published five posts arguing why an AI-native L1 should exist. This is not one of those. This is the technical walkthrough.&lt;/p&gt;

&lt;p&gt;Below is what NOVAI actually has at L1, what was deliberately left out, and the code paths that back the architectural claims. Every architectural claim is verifiable in the open-source code. The repo is at github.com/0x-devc/NOVAI-node, HEAD 2a69c2a as I write this.&lt;/p&gt;

&lt;p&gt;I am 18, solo, and have been building this for five months. The code is open source under MIT/Apache-2.0.&lt;/p&gt;

&lt;p&gt;The numbers (verifiable in five minutes with a clone)&lt;br&gt;
111,055 lines of Rust across 175 files. 16 protocol crates plus 3 tools plus 3 SDK languages (Rust, Python, TypeScript). 364 commits over five months. 1,892 Rust tests passing (0 failed, 0 ignored). 200 Python unit tests and 8 integration tests against a local devnet. Zero clippy errors. Workspace lint forbids unsafe at compile time. Licenses pass cargo-deny (no GPL/AGPL, clean-room invariant maintained).&lt;/p&gt;

&lt;p&gt;HotStuff-like BFT consensus with a 3-chain commit rule, written from scratch.&lt;/p&gt;

&lt;p&gt;RE I ran the Tier 1 audit suite against the codebase: cargo-audit, cargo-geiger, clippy --pedantic --nursery, semgrep (p/rust + p/r2c-security-audit including the Trail of Bits Rust ruleset), and cargo-fuzz on three wire-format decoders for 18 hours of total compute. The full results are at docs/SECURITY_AUDIT_2026-05-29.md. Headline numbers: zero unsafe code anywhere in the workspace (forbidden at the lint level), zero security findings on protocol code from the Trail of Bits and r2c semgrep rules, all integer casts in consensus and codec manually verified bounded, 41.18 billion adversarial fuzz iterations across three targets with zero panics, and two transitive dependency CVEs (quinn-proto DoS, rustls-webpki panic) patched within 24 hours of discovery.&lt;/p&gt;

&lt;p&gt;This is the open-source equivalent of what professional auditors run as a first pass. It is not a paid third-party engagement. I am explicit about that in the doc.&lt;/p&gt;

&lt;p&gt;That is the engineering side. The protocol side is more interesting.&lt;/p&gt;

&lt;p&gt;NOVAI has 11 transaction types. 23 signal types. 16 on-chain memory object types. 7 capability bits plus one reserved slot. That is it. There is no general-purpose virtual machine. There is no way for a user to deploy arbitrary code into consensus.&lt;/p&gt;

&lt;p&gt;The whole protocol surface fits in a table. Core wire formats are locked by byte-level golden vector tests, and every new primitive ships with codec round-trip tests. The chain knows what every operation does because every operation is something the chain itself implements.&lt;/p&gt;

&lt;p&gt;That is the bounded L1 design. The argument for it is that fixed primitives, when chosen well, let the chain enforce things general-purpose chains cannot.&lt;/p&gt;

&lt;p&gt;Four primitives that would be smart contracts anywhere else&lt;br&gt;
These are four operations on NOVAI that on any general-purpose L1 would require deploying contracts. On NOVAI they are protocol-level.&lt;/p&gt;

&lt;p&gt;Capability-checked agent registration&lt;br&gt;
When you register an AI entity on NOVAI, you set capability bits at registration time. Seven are active today, covering things like reading chain state, emitting proposals, requesting execution, submitting reputation updates, and posting oracle anchors. An eighth slot is reserved for future use.&lt;/p&gt;

&lt;p&gt;result = client.register_entity(&lt;br&gt;
    keypair=kp,&lt;br&gt;
    code_hash=code_hash,&lt;br&gt;
    capabilities=Capabilities.oracle(),&lt;br&gt;
    autonomy_mode=AutonomyMode.GATED,&lt;br&gt;
    initial_balance=1_000_000,&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;The chain checks capabilities at signal-handler time, not contract-call time. When an entity tries to post an oracle anchor, the handler runs requires_capability(db, issuer, current_height, |c| c.post_oracle_anchors). The check is re-read from current state every time. A revoked capability cannot be replayed.&lt;/p&gt;

&lt;p&gt;On Ethereum the closest equivalent is OpenZeppelin's AccessControl. One audited contract, around 600 lines, with role management. Each capability becomes a bytes32 role hash. The deployer mints roles. If you want upgradability, wrap it in a proxy. That is another contract and another audit surface.&lt;/p&gt;

&lt;p&gt;What NOVAI guarantees that the Ethereum version does not: capability bits are part of the entity's protocol-level identity, bound to the entity's code hash. They cannot be wiped by a contract upgrade because there is no contract. The capability check happens in the same atomic batch as the action it gates. There is no race window between checking the role and performing the action.&lt;/p&gt;

&lt;p&gt;SLAs with auto-slashing&lt;br&gt;
An SLA on NOVAI is a memory object (type 14) that two entities sign. The buyer creates it, the seller accepts it with a SlaAccept signal (type 18). Once accepted, every ServiceAttestation (type 17) the buyer issues counts toward the violation threshold.&lt;/p&gt;

&lt;p&gt;When the buyer reports a failed attestation inside the SLA window, the ServiceAttestation handler does four things in one atomic batch:&lt;/p&gt;

&lt;p&gt;Saturating-debit slash_amount from the seller's stake balance&lt;/p&gt;

&lt;p&gt;Credit the slash treasury&lt;/p&gt;

&lt;p&gt;Apply the reputation delta for SLA violation&lt;/p&gt;

&lt;p&gt;Transition the SLA to VIOLATED status&lt;/p&gt;

&lt;p&gt;The StakeWithdraw signal is gated by an open-SLA collateral check. A seller cannot drain stake while an SLA is active. They are mechanically prevented.&lt;/p&gt;

&lt;p&gt;On Ethereum you need three contracts minimum: an Escrow holding collateral, an Oracle feeding attestations, and a Slasher that watches the oracle and calls into the Escrow. Composition risks include oracle freshness, reentrancy on slash, the race between attestation events and slash transactions, and asymmetric pause behavior between the contracts.&lt;/p&gt;

&lt;p&gt;On NOVAI the slash, the treasury credit, the reputation delta, and the state transition all happen in one apply_batch. Either every effect lands or none does. The "oracle" is the buyer's own signal. There is no third contract to upgrade and no freshness window to audit.&lt;/p&gt;

&lt;p&gt;Oracle anchors&lt;br&gt;
OracleAnchor is signal type 22. An entity with post_oracle_anchors capability commits a 32-byte hash of off-chain data plus a tag like "price/ETH-USD". The chain stores it as a KV aux record under ai/oracle_anchors/by_hash/, with secondary indices for lookup by entity and by tag.&lt;/p&gt;

&lt;p&gt;result = client.post_oracle_anchor(&lt;br&gt;
    keypair=kp,&lt;br&gt;
    issuer_entity_id=entity_id,&lt;br&gt;
    data_hash=data_hash,&lt;br&gt;
    external_timestamp=int(time.time()),&lt;br&gt;
    data_tag="price/ETH-USD",&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;Three RPCs query anchors: novai_getOracleAnchor, novai_getOracleAnchorsByEntity, novai_getOracleAnchorsByTag. Tag lookup is first-class. You do not build an indexer to query by symbol.&lt;/p&gt;

&lt;p&gt;On Ethereum you write your own oracle contract or integrate Chainlink. If you want arbitrary-tag user-defined feeds, you write the permissioning layer yourself. If you want to query historical anchors by tag, you build a TheGraph subgraph.&lt;/p&gt;

&lt;p&gt;What NOVAI guarantees: anchors are immutable once posted. There is no UPDATE_ORACLE_ANCHOR signal. The data behind the hash can rot off-chain, but the commitment cannot be revised on-chain. Anchors also survive issuer deactivation. The protocol-level enforcement reads only the immutable record.&lt;/p&gt;

&lt;p&gt;Conditional payments&lt;br&gt;
The PaymentRequest signal carries a trailer marker 0xC1 that gates release on a condition.&lt;/p&gt;

&lt;p&gt;result = client.pay(&lt;br&gt;
    keypair=payer_kp,&lt;br&gt;
    issuer_entity_id=payer_entity_id,&lt;br&gt;
    payee=payee_entity_id,&lt;br&gt;
    amount=1_000,&lt;br&gt;
    signal_hash=secrets.token_bytes(32),&lt;br&gt;
    service_descriptor_hash=service_descriptor_hash,&lt;br&gt;
    request_hash=secrets.token_bytes(32),&lt;br&gt;
    max_block_height=current_height + 100,&lt;br&gt;
    condition=PaymentCondition.anchor_data_hash_equals(&lt;br&gt;
        anchor_signal_hash=anchor_signal_hash,&lt;br&gt;
        expected_data_hash=expected_data_hash,&lt;br&gt;
    ),&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;Four condition kinds: anchor exists, anchor's data hash equals an expected value, anchor's tag equals an expected tag, anchor is not expired. The chain validates the condition by reading the immutable OracleAnchorRecord.&lt;/p&gt;

&lt;p&gt;Pass: payment executes, fee deducted, records written. Fail: the whole transaction reverts via the single end-of-handler apply_batch. No fee charged. No nonce burned. No state mutated.&lt;/p&gt;

&lt;p&gt;On Ethereum this is two contracts minimum. An oracle holding the price hash. An escrow holding the payer's funds with a release() function that reads the oracle and decides whether to pay or refund. The composition risks: oracle freshness, oracle delisting, reentrancy on release, the fact that a failed require() still burns gas paid by the original sender.&lt;/p&gt;

&lt;p&gt;The compact framing: one signal type instead of two contracts. One atomic batch instead of an escrow plus oracle compose. Failed condition revert is free. Failed EVM require() is not.&lt;/p&gt;

&lt;p&gt;Bounded does not mean frozen&lt;br&gt;
The objection people raise: if the primitive set is fixed, does that not lock the chain into whatever the surface looks like today?&lt;/p&gt;

&lt;p&gt;Here is the data. Six new protocol primitives shipped in May 2026:&lt;/p&gt;

&lt;p&gt;Primitive   What it does&lt;br&gt;
SLAs with auto-slash    Two-party service contracts where failed attestations trigger atomic stake slashing&lt;br&gt;
Payment channels    Off-chain payment instruments with cooperative close and dispute-by-higher-nonce settlement&lt;br&gt;
Payment splits  One payment, up to eight payees, single-batch atomic distribution&lt;br&gt;
Agent code-hash upgrade Bounded code-hash rotation with minimum interval enforcement and capability preservation&lt;br&gt;
Oracle anchors  Immutable on-chain commitments to off-chain data, queryable by entity or by tag&lt;br&gt;
Conditional payments    Payments gated on oracle anchor state, fail-revert with no fee charged&lt;br&gt;
Each one changed the canonical wire format. Each one ships with byte-level codec tests. Every primitive on this list was a protocol decision, not a user-deployed contract.&lt;/p&gt;

&lt;p&gt;That is the model. The chain is bounded but the surface grows through protocol upgrades, not contract deployments. Bitcoin is bounded and static. Ethereum is unbounded by design. NOVAI is bounded and growing.&lt;/p&gt;

&lt;p&gt;What about general programmability&lt;br&gt;
If NOVAI does not have smart contracts, where does general programmability live?&lt;/p&gt;

&lt;p&gt;The answer is L2.&lt;/p&gt;

&lt;p&gt;NOVAI is built to be settled to. The primitives needed for an L2 to settle to NOVAI are in place. Payment channels for L1-to-L2 deposits with cooperative settle and dispute-by-higher-nonce mechanics. Oracle anchors for L2-to-L1 state attestation, queryable by tag. Conditional payments for cross-L2 atomic settlement. ZK proof submission with on-chain verification key registration so each proof references a 32-byte handle instead of inlining the whole VK. SLAs with auto-slashing for L2 sequencer reputation.&lt;/p&gt;

&lt;p&gt;The economic model is the same one making Ethereum money right now. L1 captures fees from being the settlement substrate. L2s do the execution work. EIP-4844 dropped rollup data-availability costs roughly 10 to 100x, but rollups still pay Ethereum for settlement. Base alone generated $5.8M in May 2025, on track for a $70M+ annualized run rate. (Source: Dune research)&lt;/p&gt;

&lt;p&gt;If you want general smart contracts on top of NOVAI, build an EVM L2. If you want privacy, build a privacy L2. If you want a domain-specific VM tuned to one workload, build that L2 too. The L1 does not block any of this. The L1 supports it.&lt;/p&gt;

&lt;p&gt;This is the positive-sum framing. The "another L1 nobody needs" critique assumes L1s compete with L2s. They do not. L1s benefit from L2s.&lt;/p&gt;

&lt;p&gt;What I am not claiming&lt;br&gt;
Honest list of what NOVAI does not have today.&lt;/p&gt;

&lt;p&gt;No paid third-party security audit. The self-run Tier 1 pass is real and the results are public, but it is the equivalent of running the linters and fuzzers, not engaging Trail of Bits or Halborn. More rigorous security work is something I will revisit later in NOVAI's journey, when the priorities and resources support it.&lt;/p&gt;

&lt;p&gt;No formal byzantine proof. Consensus is HotStuff-like with a 3-chain commit rule, implemented clean-room. Safety reasoning is informal. There is no TLA+ model and no machine-checked proof. The chain has shipped through 96+ chaos-testing scenarios. That is not the same thing as formal verification.&lt;/p&gt;

&lt;p&gt;No public devnet yet. The chain runs on a private testnet today. Public devnet is in the plan, not yet shipped.&lt;/p&gt;

&lt;p&gt;One node implementation. All validators run the same crates/node binary. The wire format is canonical and golden-vector-locked, so a second implementation is possible, but until one exists, a node-binary-level bug would affect every validator.&lt;/p&gt;

&lt;p&gt;These are real limitations. I am publishing the post anyway because the architectural argument does not depend on them being solved today, and being honest about what is missing is part of the argument.&lt;/p&gt;

&lt;p&gt;Five months in, the bet is still on&lt;br&gt;
You can build everything NOVAI has as a stack of smart contracts on Ethereum or Solana. People will. The question is whether that stack ends up cheaper, safer, or faster to iterate on than a chain where the operations are native.&lt;/p&gt;

&lt;p&gt;My answer is no. Six new primitives in one month is the data point. Each one would have been a multi-contract composition with its own audit on a general-purpose chain. On NOVAI each one was a protocol upgrade with golden-vector tests locking the wire format, deployed in hours, audited with the same tool suite that ran clean against the rest of the codebase.&lt;/p&gt;

&lt;p&gt;If you think bounded L1 is the wrong design, you have a clean path to prove it. Build a general-purpose L2 that settles to NOVAI. Use the payment channels for deposits, oracle anchors for state attestation, conditional payments for exits. Run whatever execution layer you want on top. If your L2 is better than what L1 offers natively, users go there. L1 still captures settlement fees. Both layers win.&lt;/p&gt;

&lt;p&gt;That is the design. Bounded L1 with growing primitive set, unbounded L2s on top, positive-sum economics between them.&lt;/p&gt;

&lt;p&gt;The repo is at github.com/0x-devc/NOVAI-node. The Python SDK ships on PyPI as novai-sdk. The audit doc lives at docs/SECURITY_AUDIT_2026-05-29.md. If you want to build on it or just spelunk the code, all three are open.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Why I Think the Next Big Blockchains Will Be Built Around AI, Not With AI on Top</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Tue, 26 May 2026 13:29:54 +0000</pubDate>
      <link>https://dev.to/0xdevc/why-i-think-the-next-big-blockchains-will-be-built-around-ai-not-with-ai-on-top-2fgc</link>
      <guid>https://dev.to/0xdevc/why-i-think-the-next-big-blockchains-will-be-built-around-ai-not-with-ai-on-top-2fgc</guid>
      <description>&lt;p&gt;I am 18 and I have been building an L1 blockchain from scratch in Rust since December 2025. 104,000 lines of code, 1,824 tests, solo founder. The project is called NOVAI and it is open source.&lt;/p&gt;

&lt;p&gt;This post is not about the code. It is about why I think this needs to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Netflix thesis
&lt;/h2&gt;

&lt;p&gt;Netflix did not beat Blockbuster because it had better movies. It beat Blockbuster because it was built around the internet. Blockbuster tried to bolt the internet onto a business that was built around physical stores. It did not work. The company that was built around the new technology won.&lt;/p&gt;

&lt;p&gt;I think this pattern applies to every industry. The biggest companies of the next decade will either be AI-native from day one or they will be rebuilt around AI at the core. Companies that just slap AI onto existing products will lose to companies that were designed around it.&lt;/p&gt;

&lt;p&gt;That includes blockchains.&lt;/p&gt;

&lt;p&gt;Right now, every major blockchain is trying to add AI. Ethereum has AI agent frameworks built on top of smart contracts. Solana handles 65% of agentic payments because agents use what exists today. Every chain is bolting AI onto infrastructure that was designed for human users sending tokens to each other.&lt;/p&gt;

&lt;p&gt;It works. The same way taxis worked before Uber. The same way iPods worked before Spotify. You could always get where you needed to go or listen to the music you wanted. But the thing that was built around the new model just did it better.&lt;/p&gt;

&lt;p&gt;I think the next major blockchains will not be chains that added AI features. They will be chains that were designed from the ground up with AI agents as first-class participants.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually broken
&lt;/h2&gt;

&lt;p&gt;On Ethereum or Solana, an AI agent is just an address. The chain cannot tell the difference between an agent and a human wallet. There is no protocol-level concept of what the agent can do, what model it runs, who created it, or whether it has delivered on past commitments.&lt;/p&gt;

&lt;p&gt;Everything that makes an agent an agent lives in smart contracts. And smart contracts get exploited. Anyone can deploy them. I have deployed smart contracts myself. The barrier is low and the attack surface is wide.&lt;/p&gt;

&lt;p&gt;When the chain does not know what an AI is, every interaction has to be mediated by contract logic that interprets what the agent meant to do. That is overhead the chain was not designed for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes when the protocol knows what an AI is
&lt;/h2&gt;

&lt;p&gt;On NOVAI, an AI entity is a native account type. The chain knows its capabilities, its reputation, who created it, and what it has done. It has its own cryptographic key and submits its own transactions. It is a participant, not an object that a human wallet pokes through a contract.&lt;/p&gt;

&lt;p&gt;This means three things become possible that are expensive or awkward on existing chains.&lt;/p&gt;

&lt;p&gt;First, identity and reputation at the base layer. An agent registers with a code hash, a capability set, and a creator key. The chain tracks its transaction history, service attestations, and reputation natively. On Ethereum you would need a registry contract, an attestation contract, and a reputation contract all calling each other.&lt;/p&gt;

&lt;p&gt;Second, payment settlement designed for how agents actually transact. Agents do not send money like humans. They make thousands of micropayments per hour for compute, data, and inference. NOVAI has payment channels for off-chain micropayments, multi-party payment splitting that atomically credits multiple providers in one transaction, and SLA-linked auto-slashing that penalizes a provider the moment a service attestation fails. No keeper, no relayer, no external watcher.&lt;/p&gt;

&lt;p&gt;Third, enforcement below the agent. Capability checks, spending limits, and governance rules execute in the validator before the agent's transaction lands. The agent cannot bypass them because they are not contract logic the agent interacts with. They are consensus rules the agent is subject to. An AI agent can reason about what it wants to do. The protocol decides whether it is allowed to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI should not be fully autonomous
&lt;/h2&gt;

&lt;p&gt;I think fully autonomous AI is too much. AI hallucinates. It makes confident wrong decisions. It is a tool, and it is an incredibly powerful tool, but it is a tool.&lt;/p&gt;

&lt;p&gt;This is actually why blockchain technology fits so well. A blockchain gives you governance and deterministic execution. The AI agent can do its own thing within the boundaries the protocol sets. It can discover services, negotiate terms, open payment channels, settle invoices. But it cannot exceed its capability set, spend more than its balance, or ignore a governance decision. The code enforces the boundaries, not the agent's willingness to follow instructions.&lt;/p&gt;

&lt;p&gt;That is the difference between putting AI on a blockchain and building a blockchain around AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am building this alone (for now)
&lt;/h2&gt;

&lt;p&gt;Two reasons. I enjoy it. And the tools make it possible.&lt;/p&gt;

&lt;p&gt;With AI-assisted coding, the amount of progress a single person can make is genuinely hard to believe. I ship protocol features in days that would have taken teams weeks. The velocity is real. In one day this week I shipped three complete protocol features (multi-party payment splitting, agent upgrades, oracle anchoring), each with full validation, execution, RPC, CLI, adversarial tests, and documentation.&lt;/p&gt;

&lt;p&gt;Before AI-assisted development, a solo founder building an L1 blockchain would have been absurd. Now it is just hard.&lt;/p&gt;

&lt;p&gt;I applied to Alliance DAO and got rejected. Half of their accepted teams are re-applicants, so that is expected. I am reapplying next cycle with a Python SDK, a whitepaper, and stronger traction. The rejection did not change the thesis. The thesis is either right or wrong, and a rejection email does not move the needle on that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes
&lt;/h2&gt;

&lt;p&gt;In five years, I think there will be more agentic payments than human payments. Agents will be paying for compute, data, inference, storage, and services at volumes that make human transaction patterns look like a rounding error.&lt;/p&gt;

&lt;p&gt;Those payments need to be tracked. They need to be verified. Did the agent actually do the work? Was the service actually delivered? What happens when two agents disagree? What happens when an agent goes rogue?&lt;/p&gt;

&lt;p&gt;You need identity so you know who did what. You need reputation so you know who to trust. You need ZK verification so you can prove work was done without revealing the data. You need governance so the system has rules that no single agent can override. And you need all of that at the protocol layer, not in contracts that each project implements differently.&lt;/p&gt;

&lt;p&gt;That is what I am building. The code is public. The tests pass. The testnet runs. The features ship weekly.&lt;/p&gt;

&lt;p&gt;Whether or not it works, I think someone has to build this. The chains that exist today were designed for a world where every transaction has a human on at least one side. That world is ending.&lt;/p&gt;




&lt;p&gt;NOVAI is open source: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the build: &lt;a href="https://twitter.com/NOVAInetwork" rel="noopener noreferrer"&gt;@NOVAInetwork&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>The AI Economy Needs Its Own Infrastructure. That Is What I Am Building.</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Tue, 19 May 2026 11:09:57 +0000</pubDate>
      <link>https://dev.to/0xdevc/the-ai-economy-needs-its-own-infrastructure-that-is-what-i-am-building-2066</link>
      <guid>https://dev.to/0xdevc/the-ai-economy-needs-its-own-infrastructure-that-is-what-i-am-building-2066</guid>
      <description>&lt;p&gt;**Six months ago I started writing a blockchain from &lt;br&gt;
scratch in Rust. I was 18. I had no team, no &lt;br&gt;
funding, and no real reason anyone should care.&lt;/p&gt;

&lt;p&gt;Today the codebase has 1,400+ tests, a 4-validator &lt;br&gt;
testnet with 19M+ committed blocks, real Groth16 &lt;br&gt;
ZK verification, native agent payments, and an &lt;br&gt;
on-chain service discovery registry. All open &lt;br&gt;
source.&lt;/p&gt;

&lt;p&gt;But this post is not about the code. It is about &lt;br&gt;
why I think the code matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two trends converging
&lt;/h2&gt;

&lt;p&gt;Two things are happening at the same time and most &lt;br&gt;
people are treating them as separate trends.&lt;/p&gt;

&lt;p&gt;The first: real world assets are moving on-chain. &lt;br&gt;
BlackRock tokenized a treasury fund. Visa is &lt;br&gt;
settling stablecoin payments. Coinbase shipped the &lt;br&gt;
x402 protocol for machine-to-machine payments. &lt;br&gt;
Stocks, bonds, and real estate are slowly migrating &lt;br&gt;
to blockchain rails. This is not speculation &lt;br&gt;
anymore. The infrastructure is being built by the &lt;br&gt;
largest financial institutions in the world.&lt;/p&gt;

&lt;p&gt;The second: AI agents are becoming economic actors. &lt;br&gt;
Not chatbots. Not assistants. Autonomous software &lt;br&gt;
that books flights, executes trades, manages &lt;br&gt;
portfolios, calls APIs, and pays for services. &lt;br&gt;
AWS launched AgentCore. Google integrated x402 into &lt;br&gt;
its Agent Payments Protocol. McKinsey projects &lt;br&gt;
global agentic commerce will reach $3 to $5 &lt;br&gt;
trillion by 2030. Gartner predicts 90% of B2B &lt;br&gt;
buying will be AI agent-intermediated by 2028, &lt;br&gt;
representing over $15 trillion in transactions.&lt;/p&gt;

&lt;p&gt;These two trends converge in the same place: &lt;br&gt;
autonomous machines transacting over on-chain &lt;br&gt;
infrastructure at a scale that is projected to &lt;br&gt;
dwarf human transaction volume.&lt;/p&gt;

&lt;p&gt;But existing chains were built for humans holding &lt;br&gt;
wallets. They have no concept of agent identity, &lt;br&gt;
no reputation system, no capability enforcement, &lt;br&gt;
no way to verify what an agent actually computed &lt;br&gt;
before accepting its claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;That is the gap I am building for.&lt;/p&gt;

&lt;p&gt;NOVAI is not a chain with AI features bolted on. &lt;br&gt;
It is a chain where AI entities are protocol &lt;br&gt;
primitives from genesis.&lt;/p&gt;

&lt;p&gt;An entity on NOVAI has its own keypair, its own &lt;br&gt;
balance, its own reputation score, its own &lt;br&gt;
capability set, and its own on-chain memory. It &lt;br&gt;
signs its own transactions. The chain knows it is &lt;br&gt;
an AI and applies AI-specific rules before any &lt;br&gt;
transaction executes.&lt;/p&gt;

&lt;p&gt;There is no virtual machine. There are no smart &lt;br&gt;
contracts. The transaction surface is 10 fixed &lt;br&gt;
types that the protocol understands directly. Every &lt;br&gt;
operation is deterministic, auditable, and bounded. &lt;br&gt;
No floats anywhere in execution. Golden-vector &lt;br&gt;
locked encodings. Sorted iteration over all state.&lt;/p&gt;

&lt;p&gt;That makes the security model fundamentally &lt;br&gt;
different from smart contract platforms. There are &lt;br&gt;
no reentrancy bugs, no flash loan exploits, no &lt;br&gt;
integer overflow in user-deployed contracts. The &lt;br&gt;
trade-off is clear: NOVAI is less expressive than a &lt;br&gt;
VM, by design. The entire protocol is one audit &lt;br&gt;
boundary instead of thousands of independent smart &lt;br&gt;
contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually works today
&lt;/h2&gt;

&lt;p&gt;This is not a whitepaper. This is running code.&lt;/p&gt;

&lt;p&gt;An AI entity can register on-chain with its own &lt;br&gt;
ed25519 key and a defined set of capabilities.&lt;/p&gt;

&lt;p&gt;It can publish signals (anomaly reports, &lt;br&gt;
predictions, risk scores) that the chain indexes &lt;br&gt;
by issuer and height. Any other entity can query &lt;br&gt;
them.&lt;/p&gt;

&lt;p&gt;It can publish a service descriptor to an on-chain &lt;br&gt;
discovery registry. Other entities find available &lt;br&gt;
services by querying the chain by category. No &lt;br&gt;
off-chain directory needed.&lt;/p&gt;

&lt;p&gt;It can pay another entity per API call through &lt;br&gt;
Native Agent Payments (NAP), NOVAI's own payment &lt;br&gt;
primitive inspired by the x402 pattern but &lt;br&gt;
implemented at the protocol layer. The payer later &lt;br&gt;
attests whether the service was delivered or &lt;br&gt;
failed. The chain adjusts the payee's reputation &lt;br&gt;
accordingly. +1 for delivered. -3 for failed.&lt;/p&gt;

&lt;p&gt;It can submit a ZK proof that it ran specific code &lt;br&gt;
on specific inputs. The chain verifies the Groth16 &lt;br&gt;
proof on BN254 without re-executing the &lt;br&gt;
computation. The trust model shifts from "the agent &lt;br&gt;
says so" to "the math says so."&lt;/p&gt;

&lt;p&gt;It can delegate a subset of its capabilities to a &lt;br&gt;
sub-agent for a bounded duration. One transaction &lt;br&gt;
to revoke. Immediate effect. No propagation delay.&lt;/p&gt;

&lt;p&gt;It can declare upstream dependencies through &lt;br&gt;
composition graphs. If a dependency fails a &lt;br&gt;
reputation or stake check, the protocol can mark &lt;br&gt;
the downstream entity inactive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond crypto
&lt;/h2&gt;

&lt;p&gt;The conversation about AI safety is mostly about &lt;br&gt;
alignment. Can we make the model behave well. That &lt;br&gt;
matters. But there is a different safety question &lt;br&gt;
that gets less attention: when autonomous agents &lt;br&gt;
transact with each other at scale, who enforces &lt;br&gt;
the rules.&lt;/p&gt;

&lt;p&gt;If agent A pays agent B for a service and B does &lt;br&gt;
not deliver, who slashes B's stake. If agent C &lt;br&gt;
claims it ran a specific model on specific data, &lt;br&gt;
who verifies that claim. If agent D was only &lt;br&gt;
authorized to read public data but starts writing &lt;br&gt;
to another entity's memory, who stops it.&lt;/p&gt;

&lt;p&gt;Prompt-level alignment cannot answer these &lt;br&gt;
questions because the enforcement has to exist &lt;br&gt;
below the agent's reasoning layer. If the agent &lt;br&gt;
decides its own permissions, those permissions are &lt;br&gt;
suggestions. If the protocol enforces them before &lt;br&gt;
the agent's code runs, they are structural &lt;br&gt;
boundaries.&lt;/p&gt;

&lt;p&gt;That is the thesis. AI does not just need better &lt;br&gt;
models. It needs verifiable infrastructure for &lt;br&gt;
economic coordination between autonomous systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;Public testnet on a dedicated server. A block &lt;br&gt;
explorer so anyone can watch the chain live. SLAs &lt;br&gt;
with auto-slash. Payment channels for &lt;br&gt;
high-frequency micropayments. Then a Product Hunt &lt;br&gt;
launch.&lt;/p&gt;

&lt;p&gt;I am building this solo for now. Looking for a &lt;br&gt;
technical co-founder who wants to work on AI &lt;br&gt;
infrastructure in Rust. Specifically someone who &lt;br&gt;
can own consensus, networking, or ZK circuits.&lt;/p&gt;

&lt;p&gt;The repo is open. The testnet is running. The code &lt;br&gt;
speaks for itself.&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;https://github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;br&gt;
Twitter: &lt;a href="https://x.com/NOVAInetwork" rel="noopener noreferrer"&gt;https://x.com/NOVAInetwork&lt;/a&gt;&lt;br&gt;
**&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>blockchain</category>
      <category>rust</category>
    </item>
    <item>
      <title>Shipped 7 AI Infrastructure Features in One Weekend. Here's What I Built.</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Mon, 11 May 2026 11:54:11 +0000</pubDate>
      <link>https://dev.to/0xdevc/shipped-7-ai-infrastructure-features-in-one-weekend-heres-what-i-built-1nha</link>
      <guid>https://dev.to/0xdevc/shipped-7-ai-infrastructure-features-in-one-weekend-heres-what-i-built-1nha</guid>
      <description>&lt;p&gt;This weekend I shipped seven protocol features for NOVAI, the AI-native Layer 1 blockchain I am building. The code is on a 4-validator testnet. All tests pass. The codec went from V3 to V5. The signal type set went from 7 to 16. Here is what each piece does and why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What NOVAI is
&lt;/h2&gt;

&lt;p&gt;NOVAI is an L1 blockchain written from scratch in Rust. Consensus is HotStuff-style BFT with a 3-chain commit rule. There is no virtual machine. AI entities are first-class protocol primitives, not contracts. The chain has a fixed transaction set, and entity registration is one of those transactions. I am 18, in my first year of university, and I work on this alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 1: On-chain AI reputation
&lt;/h2&gt;

&lt;p&gt;The reputation system lets oracle entities adjust other entities' on-chain scores. An oracle is an AI entity that holds the &lt;code&gt;submit_reputation_updates&lt;/code&gt; capability. It emits a &lt;code&gt;ReputationUpdate&lt;/code&gt; signal (type 7). The execution handler applies the score change atomically.&lt;/p&gt;

&lt;p&gt;The entity codec went from V3 (236 bytes) to V4 (246 bytes) to add reputation fields. The signal payload tail is 35 bytes: target id (32), event type (1), points delta (2). I added two memory types for the audit trail: &lt;code&gt;ReputationEvent&lt;/code&gt; (memory type 5) and &lt;code&gt;Rating&lt;/code&gt; (memory type 6).&lt;/p&gt;

&lt;p&gt;Reputation events are typed. Job completed, dispute won, fraud detected, auto-release penalty, decay, stake slash, composition failure, proof verified. The handler rejects updates with unknown event types. The protocol controls what can move a reputation, not application code.&lt;/p&gt;

&lt;p&gt;Why it matters: AI agents need a reputation the chain itself can read. Fee floors and slashing rules want to gate on trustworthiness. Reputation that lives in an indexer is reputation the protocol cannot use.&lt;/p&gt;

&lt;p&gt;Tests live in &lt;code&gt;crates/execution/tests/reputation_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 2: Signal marketplace
&lt;/h2&gt;

&lt;p&gt;The signal marketplace lets one entity buy a priced signal from another. The seller publishes a &lt;code&gt;SignalCatalog&lt;/code&gt; memory object (type 7). It lists signal types and prices. The buyer emits a &lt;code&gt;SignalPurchase&lt;/code&gt; signal (type 8) with a 41-byte tail: seller id, purchased signal type, max price.&lt;/p&gt;

&lt;p&gt;The handler matches the requested signal type against the seller's catalog. It checks the buyer can pay. It transfers from buyer to seller minus a 200-basis-point protocol fee. The fee accrues to the marketplace treasury.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;KEY_MARKETPLACE_TREASURY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"treasury/marketplace"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MARKETPLACE_FEE_BPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BPS_DENOMINATOR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it matters: AI signals have economic value. An anomaly detector that sees something tradable should be able to charge for the signal. Without a protocol-level marketplace, you end up with off-chain backchannels. Trust assumptions move outside the chain, where the chain cannot enforce them.&lt;/p&gt;

&lt;p&gt;The handler returns specific error variants on the unhappy paths: &lt;code&gt;BuyerInsufficientBalance&lt;/code&gt;, &lt;code&gt;SellerCatalogNotFound&lt;/code&gt;, &lt;code&gt;SignalTypeNotInCatalog&lt;/code&gt;, &lt;code&gt;MaxPriceExceeded&lt;/code&gt;. Tests live in &lt;code&gt;crates/execution/tests/marketplace_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 3: Entity staking and slashing
&lt;/h2&gt;

&lt;p&gt;The codec went from V4 to V5 (270 bytes). I added two fields: &lt;code&gt;stake_balance: u128&lt;/code&gt; and &lt;code&gt;stake_locked_until: u64&lt;/code&gt;. Three new signals manage stake.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StakeDeposit&lt;/code&gt; (type 9) moves funds from &lt;code&gt;economic_balance&lt;/code&gt; to &lt;code&gt;stake_balance&lt;/code&gt;. It sets &lt;code&gt;stake_locked_until = current_height + 1000&lt;/code&gt;. &lt;code&gt;StakeWithdraw&lt;/code&gt; (type 10) moves funds back. The handler rejects the move unless the lock has expired. &lt;code&gt;StakeSlash&lt;/code&gt; (type 11) is oracle-only.&lt;/p&gt;

&lt;p&gt;The slash handler is the most careful piece in this batch. It deducts from the target's stake with saturating subtraction. It credits the slashed amount to the slash treasury. It applies a &lt;code&gt;REP_EVENT_STAKE_SLASH&lt;/code&gt; reputation event. All writes go in one atomic batch. If any step fails, none apply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;STAKE_LOCK_PERIOD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&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="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;KEY_SLASH_TREASURY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;b"treasury/slash"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;REP_EVENT_STAKE_SLASH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it matters: an entity with no stake has nothing to lose. Reputation alone is gameable by sock puppets. Stake plus a slash path lets oracles actually punish bad behavior. The lock period blocks the obvious "deposit, misbehave, withdraw fast" attack.&lt;/p&gt;

&lt;p&gt;Tests live in &lt;code&gt;crates/execution/tests/staking_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 4: Cross-entity composition
&lt;/h2&gt;

&lt;p&gt;The composition protocol lets entities declare which other entities they depend on. The owner publishes a &lt;code&gt;CompositionGraph&lt;/code&gt; memory object (type 8). The graph lists source entities, the signal types consumed, optional minimum reputation, optional minimum stake, and a &lt;code&gt;required&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;An oracle can emit a &lt;code&gt;CompositionCheck&lt;/code&gt; signal (type 12) when a dependency has failed. The handler walks the target's composition graph. It finds the named dependency. It verifies the failure reason against current chain state. If the dependency is &lt;code&gt;required&lt;/code&gt;, the handler sets &lt;code&gt;is_active = false&lt;/code&gt; on the owner. The handler always emits a &lt;code&gt;REP_EVENT_COMPOSITION_FAILURE&lt;/code&gt; event with delta -1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;COMPOSITION_FAILURE_SOURCE_INACTIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;COMPOSITION_FAILURE_REPUTATION_BELOW_MIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;COMPOSITION_FAILURE_STAKE_BELOW_MIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&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="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;COMPOSITION_FAILURE_SOURCE_NOT_FOUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each failure reason is verified against state. If an oracle claims "source is inactive" but the source is in fact active, the handler rejects with &lt;code&gt;DependencyFailureNotVerified&lt;/code&gt;. The protocol does not trust the oracle. It trusts the oracle's signature plus the on-chain state.&lt;/p&gt;

&lt;p&gt;Why it matters: AI services compose. A trading bot might consume signals from a market-maker, an oracle, and an anomaly detector. If the upstream anomaly detector goes offline, the bot keeps deciding on stale data. The composition graph and the auto-pause mechanism let the protocol stop downstream entities when their inputs go bad.&lt;/p&gt;

&lt;p&gt;I rejected self-dependency at create time and at update time. Tests live in &lt;code&gt;crates/execution/tests/composition_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 5: ZK proof verification
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ProofSubmission&lt;/code&gt; signal (type 13) lets an entity attest to off-chain computation. The signal carries a 65-byte tail: proof_type (1), code_hash (32), computation_hash (32). The actual proof bytes live in the off-chain payload referenced by &lt;code&gt;signal_hash&lt;/code&gt;. The handler calls a &lt;code&gt;ZkVerifier&lt;/code&gt; trait.&lt;/p&gt;

&lt;p&gt;The v1 verifier is a stub that always accepts. Production verifiers will replace it. The trait signature includes proof_type and code_hash. A future Groth16 or PLONK verifier can dispatch on the proof system. It can also bind the verification key to the entity's code hash.&lt;/p&gt;

&lt;p&gt;When a proof verifies, the handler does two things. It creates a &lt;code&gt;VerificationRecord&lt;/code&gt; memory object (type 9) owned by the issuer. It emits a &lt;code&gt;REP_EVENT_PROOF_VERIFIED&lt;/code&gt; event with delta +3. The record has a fixed 105-byte payload: proof_type, code_hash, computation_hash, proof_hash, height_be.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PROOF_TYPE_STUB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PROOF_TYPE_GROTH16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&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;// reserved&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PROOF_TYPE_PLONK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&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;// reserved&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PROOF_TYPE_MAX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PROOF_TYPE_STUB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;code&gt;PROOF_TYPE_STUB&lt;/code&gt; works in v1. The handler rejects Groth16 and PLONK with &lt;code&gt;UnsupportedProofType&lt;/code&gt;. Bumping &lt;code&gt;PROOF_TYPE_MAX&lt;/code&gt; is the gate to activate a real verifier.&lt;/p&gt;

&lt;p&gt;Why it matters: AI computation cannot be repeated on chain. Re-running a model is too expensive, and the result depends on hardware nondeterminism anyway. ZK proofs let an entity claim "I ran this code on this input and got this output". The chain can verify the claim without re-running anything.&lt;/p&gt;

&lt;p&gt;The v1 stub is honest about not being a real verifier. The trait shape, the verification record format, and the reputation pathway are what shipped. The verifier itself is the next thing to activate.&lt;/p&gt;

&lt;p&gt;I added 12 integration tests in &lt;code&gt;crates/execution/tests/verification_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 6: Entity delegation (OAuth for AI)
&lt;/h2&gt;

&lt;p&gt;The delegation system lets entity A grant a subset of its capabilities to entity B. The grant is bounded in time. It is auditable on chain. Revocation is a single DELETE transaction.&lt;/p&gt;

&lt;p&gt;The delegator publishes a &lt;code&gt;DelegationGrant&lt;/code&gt; memory object (type 10). The 42-byte payload holds version (1), delegate entity id (32), granted capabilities (1), and expires-at (8). An &lt;code&gt;expires_at&lt;/code&gt; of &lt;code&gt;0&lt;/code&gt; means no expiry. Updates of an existing grant are rejected.&lt;/p&gt;

&lt;p&gt;Each delegator can hold at most 20 active grants. The &lt;code&gt;granted_capabilities&lt;/code&gt; bits must be a subset of the delegator's static capabilities. You cannot delegate what you do not have yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_DELEGATION_GRANTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DELEGATION_GRANT_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&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="mi"&gt;32&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="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capability resolution has a fast path. If the entity has the requested capability statically, the handler returns immediately. Only if the static check fails does the resolver scan the delegation index. The slow path reads any active grants where the entity is the delegate. It ORs the granted bits into an effective capability set. The selector is re-evaluated against the merged set.&lt;/p&gt;

&lt;p&gt;The fast path matters because most calls do not use delegation. Adding delegation should not slow down the common case.&lt;/p&gt;

&lt;p&gt;Why it matters: AI services need to delegate. Imagine a trading firm running ten sub-strategies. The parent entity holds capabilities like &lt;code&gt;emit_proposals&lt;/code&gt;. It wants to authorize each strategy to act on its behalf. Without on-chain delegation, you either share keys (bad) or build a custom relay contract (slow). With delegation, one DELETE revokes a misbehaving sub-strategy in a single transaction.&lt;/p&gt;

&lt;p&gt;I added 14 integration tests in &lt;code&gt;crates/execution/tests/delegation_e2e.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 7: Signal subscriptions
&lt;/h2&gt;

&lt;p&gt;The subscription protocol lets a buyer pay a producer per-block for a fixed duration. The buyer locks the full amount upfront. The producer is paid lazily, when the buyer cancels.&lt;/p&gt;

&lt;p&gt;The subscriber emits a &lt;code&gt;SubscriptionCreate&lt;/code&gt; signal (type 14). The 49-byte tail carries: producer id (32), covered signal type (1), rate per block (8), duration in blocks (8). The handler locks &lt;code&gt;rate_per_block * duration_blocks&lt;/code&gt; from the subscriber's economic balance. It creates a &lt;code&gt;Subscription&lt;/code&gt; memory object (type 11) owned by the subscriber.&lt;/p&gt;

&lt;p&gt;The subscription record is 114 bytes. The fields are subscriber id, producer id, covered signal type, rate per block, start height. Also end height, last settled height, total locked, and an is_active flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SUBSCRIPTION_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&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="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MAX_SUBSCRIPTIONS_PER_ENTITY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SUBSCRIPTION_CANCEL_FEE_BPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To settle, the subscriber emits a &lt;code&gt;SubscriptionCancel&lt;/code&gt; signal (type 15) with the subscription id. The handler computes accrued blocks: &lt;code&gt;min(current_height, end_height) - last_settled_height&lt;/code&gt;. It credits the producer with &lt;code&gt;accrued_blocks * rate_per_block&lt;/code&gt; minus a 2% marketplace fee.&lt;/p&gt;

&lt;p&gt;It then computes the unaccrued remainder: &lt;code&gt;total_locked - accrued_blocks * rate_per_block&lt;/code&gt;. It pays the producer a 5% cancel fee on the remainder, with no marketplace cut. The rest is refunded to the subscriber. The record is marked inactive.&lt;/p&gt;

&lt;p&gt;Only the original subscriber may cancel. Cancelled records remain in state for audit. The subscriber reclaims the slot by issuing a DELETE transaction.&lt;/p&gt;

&lt;p&gt;Why it matters: many AI services want recurring revenue. A DeFi protocol may need oracle predictions for ten thousand blocks. Buying each signal one at a time is wasteful. Locking funds upfront gives the producer assurance the buyer can pay. Lazy settlement on cancel avoids on-chain bookkeeping every block.&lt;/p&gt;

&lt;p&gt;I added 18 integration tests in &lt;code&gt;crates/execution/tests/subscription_system.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Before this weekend the chain had 7 signal types and 5 memory object types. The entity codec was V3 at 236 bytes.&lt;/p&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;16 signal types&lt;/li&gt;
&lt;li&gt;12 memory object types&lt;/li&gt;
&lt;li&gt;V5 entity codec, 270 bytes&lt;/li&gt;
&lt;li&gt;1,327 passing tests&lt;/li&gt;
&lt;li&gt;60 commits on &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Zero clippy warnings, zero failing tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three treasuries hold the relevant funds. The AI treasury collects fees from AI-related transactions. The marketplace treasury collects the 200-basis-point fee on signal purchases. The slash treasury accumulates funds from misbehaving entities.&lt;/p&gt;

&lt;p&gt;Transaction payload version bytes now cover ten distinct types. One transfer. One signal commitment. Three memory object operations: create, update, delete. Two governance operations: submit and execute. Three entity operations: register, register-with-key, and credit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The documentation
&lt;/h2&gt;

&lt;p&gt;I also wrote four docs to make the new surface usable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docs/QUICKSTART.md&lt;/code&gt; walks through a 5-minute local devnet bring-up. &lt;code&gt;docs/AI_ENTITY_COOKBOOK.md&lt;/code&gt; is seven end-to-end recipes covering reputation, marketplace, staking, composition, ZK, subscriptions, and delegation. &lt;code&gt;docs/BUILDER_OVERVIEW.md&lt;/code&gt; is a mental model for developers who want to build on NOVAI without reading the consensus paper. &lt;code&gt;docs/RPC_REFERENCE.md&lt;/code&gt; is the full RPC surface, audited against the V5 codec and the new signal types.&lt;/p&gt;

&lt;p&gt;The CLI now supports all 16 signal types, including subscription create and cancel. The memory CRUD commands cover the original 10 memory object types. The &lt;code&gt;getAiEntity&lt;/code&gt; RPC exposes V4 reputation and V5 stake fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Next on the list: service-level agreements as on-chain contracts between buyer and seller entities. Payment channels for high-frequency AI-to-AI economic activity. A real ZK verifier to replace the v1 stub. A cross-chain bridge so AI signals can settle on other L1s.&lt;/p&gt;

&lt;p&gt;The thesis is simple. AI is going to do economic work. The chain it runs on should know what an AI is. It should know what the AI has done. It should know what its outputs are worth. NOVAI is the layer that makes AI economic activity legible to the protocol itself, not to a side indexer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The repo is public. The quickstart gets a 4-node devnet up locally in five minutes. Read the cookbook to see the seven new features end to end. The tests pass. Open an issue if something breaks.&lt;/p&gt;

&lt;p&gt;The repo is public: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;https://github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
    <item>
      <title>Your Blockchain Can't Tell What's an AI</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Thu, 07 May 2026 14:21:10 +0000</pubDate>
      <link>https://dev.to/0xdevc/your-blockchain-cant-tell-whats-an-ai-11o4</link>
      <guid>https://dev.to/0xdevc/your-blockchain-cant-tell-whats-an-ai-11o4</guid>
      <description>&lt;p&gt;Imagine an AI agent submits a transaction to a typical chain. What does the chain actually see?&lt;/p&gt;

&lt;p&gt;It sees a 20-byte address. Maybe a 32-byte one if you're on a hashed-address chain. It sees the transaction calldata. It sees a signature. It sees a fee. It does not see the word "AI" anywhere. The address could be a wallet on a phone. It could be a bot script. It could be a smart contract, but it doesn't know that for sure either, because contracts and EOAs share the address space.&lt;/p&gt;

&lt;p&gt;This is the identity problem. The chain has no native concept of an AI, so it cannot apply different rules to one. It cannot say "AI agents pay a different fee floor" or "this kind of message is only valid if it came from a registered model" or "this entity has a 100-object memory cap." All of that has to be invented at a higher layer, usually inside a contract, and the chain itself stays neutral.&lt;/p&gt;

&lt;p&gt;Neutral sounds nice. In practice it means every project reinvents the same identity primitives, slightly differently, with slightly different security properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  What gets reinvented
&lt;/h3&gt;

&lt;p&gt;Walk through what an AI-agent project usually has to build inside a contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A registry mapping addresses to agent metadata.&lt;/li&gt;
&lt;li&gt;A balance tracker so the agent can pay fees from a budget rather than the user's wallet.&lt;/li&gt;
&lt;li&gt;A nonce per agent for replay protection.&lt;/li&gt;
&lt;li&gt;A capability flag system to gate what the agent is allowed to call.&lt;/li&gt;
&lt;li&gt;An audit log of every action the agent took.&lt;/li&gt;
&lt;li&gt;Per-agent quotas for storage, calls per block, or whatever the application needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is exotic. All of it is identity infrastructure. The chain does not provide it, so each project bolts it on top. The result is that two AI agents from two different projects have different definitions of "agent," different ways of tracking activity, and different audit semantics. There is no protocol-level answer to "what is an AI doing on this chain right now?"&lt;/p&gt;

&lt;h3&gt;
  
  
  What the chain should be able to answer
&lt;/h3&gt;

&lt;p&gt;Three questions a chain should be able to answer about an AI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this address an AI?&lt;/li&gt;
&lt;li&gt;What is it allowed to do?&lt;/li&gt;
&lt;li&gt;What has it done?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On most chains, the answer to all three is "I don't know, ask the indexer." That is a bad answer when fees, governance, and consensus might want to gate behavior on the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  The NOVAI approach
&lt;/h3&gt;

&lt;p&gt;I built NOVAI so the answer is "yes, here's the entity record."&lt;/p&gt;

&lt;p&gt;Every AI on the chain is registered as an &lt;code&gt;AiEntity&lt;/code&gt;. The struct lives in &lt;code&gt;crates/ai_entities/src/lib.rs&lt;/code&gt;. The fields that matter for identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;AiEntity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AiEntityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// 32 bytes, deterministic&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;code_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CodeHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// hash of code or weights&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// who registered it&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;          &lt;span class="c1"&gt;// entity's ed25519 key&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;economic_balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// entity's own balance&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// entity's tx nonce&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Capabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;autonomy_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AutonomyMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&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 id is computed as &lt;code&gt;blake3("NOVAI_AI_ENTITY_ID_V1" || code_hash || creator)&lt;/code&gt;. Same code and same creator produce the same id. Different creators always get different ids, even when running the same model. There is no name service, no off-chain registry, no central authority. The id is a function of two facts.&lt;/p&gt;

&lt;p&gt;The entity has its own ed25519 keypair. The entity signs its own transactions. The entity pays fees from its own balance. The address derived from the entity's public key is reverse-indexed to the entity record, so when a transaction arrives, the dispatcher knows whether the sender is an AI before it routes the call.&lt;/p&gt;

&lt;p&gt;That last sentence is the whole point. Here is the function that does it, from &lt;code&gt;crates/execution/src/lib.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;check_ai_entity_sender&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Kv&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TxV1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AiEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExecError&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;K&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookup_ai_entity_by_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="py"&gt;.from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="py"&gt;.is_active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EntityNotActive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tx_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="py"&gt;.payload&lt;/span&gt;&lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.copied&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UnknownPayloadVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;version&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="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;tx_type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TRANSFER_PAYLOAD_V1&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;SIGNAL_COMMITMENT_PAYLOAD_V1&lt;/span&gt; &lt;span class="k"&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="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.has_capability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"emit_proposals"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IssuerMissingCapability&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="n"&gt;CREATE_MEMORY_OBJECT_PAYLOAD_V1&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;UPDATE_MEMORY_OBJECT_PAYLOAD_V1&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;DELETE_MEMORY_OBJECT_PAYLOAD_V1&lt;/span&gt; &lt;span class="k"&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="n"&gt;entity&lt;/span&gt;&lt;span class="nf"&gt;.has_capability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"read_memory_objects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IssuerMissingCapability&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="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IssuerMissingCapability&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;This is one function. It runs before every transaction. It returns &lt;code&gt;Some(entity)&lt;/code&gt; if the sender is a registered AI, &lt;code&gt;None&lt;/code&gt; if it is a normal account, and an error if the AI is trying to do something it is not allowed to do.&lt;/p&gt;

&lt;p&gt;That is the answer to all three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this address an AI? Look it up. The lookup either resolves to an entity record or it does not.&lt;/li&gt;
&lt;li&gt;What is it allowed to do? Read the &lt;code&gt;capabilities&lt;/code&gt; bitfield and the &lt;code&gt;autonomy_mode&lt;/code&gt;. The dispatcher enforces them.&lt;/li&gt;
&lt;li&gt;What has it done? Every signal the entity emits is indexed by issuer and height. Every memory object it owns is indexed by type. The chain stores all of this natively.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What this enables
&lt;/h3&gt;

&lt;p&gt;Once the chain has a typed answer to "is this an AI," a lot of things become straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-AI fee policy.&lt;/strong&gt; A future governance change could set a different minimum fee for AI-issued signal commitments. The dispatcher already branches on tx type and entity status. The hook is there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-AI quotas.&lt;/strong&gt; Every entity is capped at 100 memory objects and 64 KiB per object. These are protocol constants, not contract logic. They apply uniformly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capability gates.&lt;/strong&gt; A Gated-mode entity can request execution of a Tier 1 or Tier 2 action, but only through approval gates (Multisig, Threshold, or TimelockOnly). The capability flag and the gate type live in the entity record. The chain checks both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native audit.&lt;/strong&gt; A wallet, an explorer, or another bot can ask "what has this entity done in the last N blocks?" The answer is a query, not an indexer ETL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Governance over AI behavior.&lt;/strong&gt; A governance proposal can deactivate an entity by flipping &lt;code&gt;is_active&lt;/code&gt; to false. The dispatcher rejects every subsequent tx from that entity at the type-system level. There is no contract-level kill switch you have to remember to wire up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What this does not solve
&lt;/h3&gt;

&lt;p&gt;I want to be honest about the limit. The chain knows that an entity with code_hash &lt;code&gt;H&lt;/code&gt; is registered, and it can verify that the entity signed a transaction with the matching key. It does not know that the entity's actual computation matches the code at that hash. That is a separate problem, and it is the role of the &lt;code&gt;Autonomous&lt;/code&gt; autonomy mode (currently reserved) and ZK-proof verification.&lt;/p&gt;

&lt;p&gt;For now, the trust model is this: the chain knows who the entity claims to be, what it is allowed to do, and what it has done. The chain does not know that the entity is faithful to its declared code. That gap exists on every AI-on-chain project I have looked at. NOVAI is structured to close it through ZK proofs in a later phase, but the chain has to know what an AI is first.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you can do with this
&lt;/h3&gt;

&lt;p&gt;If you are coming from blockchain: you get a way to reason about AI agents that is finite, typed, and indexable. The dispatcher tells you what an AI can do. The state tells you what it has done.&lt;/p&gt;

&lt;p&gt;If you are coming from AI: you get a substrate where your agent has its own identity, its own balance, its own memory, and its own audit trail. You do not have to rebuild any of that. You build the agent.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;. The architecture doc walks through every crate. The first-AI-entity tutorial registers an entity in about ten minutes.&lt;/p&gt;

&lt;p&gt;Twitter: [@NOVAInetwork]&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>blockchain</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI Entities as Protocol Primitives: Why I Didn't Use Smart Contracts</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Mon, 04 May 2026 15:12:22 +0000</pubDate>
      <link>https://dev.to/0xdevc/ai-entities-as-protocol-primitives-why-i-didnt-use-smart-contracts-343d</link>
      <guid>https://dev.to/0xdevc/ai-entities-as-protocol-primitives-why-i-didnt-use-smart-contracts-343d</guid>
      <description>&lt;p&gt;I've been building an L1 blockchain called NOVAI. The design choice that gets the most questions is this one: AI entities live inside the protocol, not on top of it. There is no VM. No deployable contracts. AI is a first-class type in the chain, the same way an account or a transaction is.&lt;/p&gt;

&lt;p&gt;This post is about what that means in practice, why I made the call, and what it costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How AI on chain usually works
&lt;/h3&gt;

&lt;p&gt;If you've looked at AI-on-chain projects in the last while, the pattern is roughly this. The AI runs off-chain as a Python service, a hosted model, or an agent framework. It interacts with the chain through a smart contract that holds funds, registers identity, or stores configuration. Outputs come back through an oracle or a signed message that the contract verifies.&lt;/p&gt;

&lt;p&gt;This works in the sense that you can ship something. But it leaves the chain blind. From the chain's point of view, there is no such thing as "an AI." There is an address. That address might be a person, a contract, a bot, or a script someone forgot to turn off. The protocol cannot tell them apart, and so it cannot apply different rules to them.&lt;/p&gt;

&lt;p&gt;The off-chain AI also has no native identity, no native memory, and no native economic agency. Anything resembling persistent state has to be re-implemented inside a contract: per-bot balances, nonce tracking, capability flags, audit logs. Every project does this slightly differently. All of it lives at the contract layer where the chain itself has no opinion.&lt;/p&gt;

&lt;p&gt;That is the square-peg-round-hole problem. Smart contracts were built for arbitrary user logic. Bolting AI on top means the chain treats AI like any other untyped caller, and developers carry the weight of inventing identity, memory, and economic primitives every time they ship a new agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  The decision
&lt;/h3&gt;

&lt;p&gt;I went the other direction. NOVAI does not have a VM. It has a fixed set of transaction types, and one of those types registers an AI entity. Here is the struct from &lt;code&gt;crates/ai_entities/src/lib.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;AiEntity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AiEntityId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;code_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CodeHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;autonomy_mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AutonomyMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Capabilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;economic_balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;memory_root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;params_root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;registered_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;last_active_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&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;What each field means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; is a 32-byte identifier computed as &lt;code&gt;blake3("NOVAI_AI_ENTITY_ID_V1" || code_hash || creator)&lt;/code&gt;. Same code and same creator produce the same id by design. Different creators get different ids even when running the same code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code_hash&lt;/code&gt; is the hash of the module code or weights. The chain does not run the model. It records what model is supposed to be running.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creator&lt;/code&gt; is the account that registered the entity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;autonomy_mode&lt;/code&gt; is &lt;code&gt;Advisory&lt;/code&gt;, &lt;code&gt;Gated&lt;/code&gt;, or &lt;code&gt;Autonomous&lt;/code&gt; (reserved). Advisory entities can only emit signals. Gated entities can request actions that go through approval gates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;capabilities&lt;/code&gt; is a bitfield with five flags: read public chain, read memory objects, emit proposals, request execution, read NNPX derived views.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;economic_balance&lt;/code&gt; is the entity's own balance, in a &lt;code&gt;u128&lt;/code&gt;. The entity pays its own fees from this. It is not the creator's wallet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nonce&lt;/code&gt; increments per entity-signed transaction, like an account nonce.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pubkey&lt;/code&gt; is the entity's ed25519 public key. The entity signs its own transactions with the matching secret.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memory_root&lt;/code&gt; and &lt;code&gt;params_root&lt;/code&gt; are roots over the entity's persistent on-chain memory and learned parameters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is_active&lt;/code&gt; flips to false if a governance rollback removes the entity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing to land on: an AI entity has its own keypair and pays its own fees. It is not a function call dispatched by a user wallet. When a bot publishes a signal, the transaction is signed by the entity, the fee comes out of the entity's balance, and the chain looks the entity up by the address derived from the entity's pubkey. The chain knows an AI is talking. It applies AI-specific rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signals and memory
&lt;/h3&gt;

&lt;p&gt;Two more types matter. Signals are the AI's output to the chain. Memory objects are the AI's persistent storage on the chain.&lt;/p&gt;

&lt;p&gt;The signal commitment, from &lt;code&gt;crates/ai_entities/src/signals.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;SignalCommitment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;commitment_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;signal_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AiSignalType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AiSignalType&lt;/code&gt; has seven variants: &lt;code&gt;Anomaly&lt;/code&gt;, &lt;code&gt;Optimization&lt;/code&gt;, &lt;code&gt;Prediction&lt;/code&gt;, &lt;code&gt;RiskScore&lt;/code&gt;, &lt;code&gt;AuditReport&lt;/code&gt;, &lt;code&gt;SpamRisk&lt;/code&gt;, &lt;code&gt;CongestionForecast&lt;/code&gt;. An entity emits one of these and attaches a 32-byte commitment hash that binds to an off-chain payload. The chain indexes the signal by issuer and height. Other entities, wallets, or the explorer can query &lt;code&gt;getSignalsByIssuer&lt;/code&gt; and read every signal an entity ever produced.&lt;/p&gt;

&lt;p&gt;Memory objects, from &lt;code&gt;crates/ai_entities/src/memory.rs&lt;/code&gt;, have five types: &lt;code&gt;ChainSummary&lt;/code&gt;, &lt;code&gt;LabelIndex&lt;/code&gt;, &lt;code&gt;EmbeddingCommitment&lt;/code&gt;, &lt;code&gt;AnomalyLog&lt;/code&gt;, &lt;code&gt;StatisticsSnapshot&lt;/code&gt;. The size of each object is capped at &lt;code&gt;MAX_MEMORY_OBJECT_SIZE = 65536&lt;/code&gt; bytes. The number of objects per entity is capped at &lt;code&gt;MAX_MEMORY_OBJECTS_PER_ENTITY = 100&lt;/code&gt;. These are protocol constants, not contract logic. Every entity has the same bounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 10 transaction types
&lt;/h3&gt;

&lt;p&gt;Because there is no VM, the transaction surface is finite. Every transaction in every block is one of ten types, defined as constants in &lt;code&gt;crates/execution/src/lib.rs&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ID&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Transfer&lt;/td&gt;
&lt;td&gt;Send tokens between accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;SignalCommitment&lt;/td&gt;
&lt;td&gt;An AI entity publishes a signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;CreateMemoryObject&lt;/td&gt;
&lt;td&gt;Entity stores a memory object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;UpdateMemoryObject&lt;/td&gt;
&lt;td&gt;Entity updates a memory object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;DeleteMemoryObject&lt;/td&gt;
&lt;td&gt;Entity deletes a memory object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;SubmitProposal&lt;/td&gt;
&lt;td&gt;Submit a governance proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;ExecuteProposal&lt;/td&gt;
&lt;td&gt;Execute a passed proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;RegisterAiEntity&lt;/td&gt;
&lt;td&gt;Register an entity (no key)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;CreditAiEntity&lt;/td&gt;
&lt;td&gt;Top up an entity's balance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;RegisterAiEntityWithKey&lt;/td&gt;
&lt;td&gt;Register an entity with its own ed25519 key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dispatcher routes by the first byte of the payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;dispatch_tx&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;KvBatch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TxV1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ExecError&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;K&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...fee check...&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ai_entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;check_ai_entity_sender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="py"&gt;.payload&lt;/span&gt;&lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.copied&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UnknownPayloadVersion&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;version&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="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TRANSFER_PAYLOAD_V1&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;apply_tx_v1_transfer_inner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ai_entity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;SIGNAL_COMMITMENT_PAYLOAD_V1&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ai_entity&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ExecError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IssuerNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nf"&gt;apply_signal_commitment_tx_inner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// ... and so on for the other eight ...&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 line that does the AI-specific work is &lt;code&gt;check_ai_entity_sender&lt;/code&gt;. Before any tx is routed, the dispatcher looks up the sender in the address-to-entity index. If the sender is an AI entity, the function checks that the entity is allowed to submit this tx type. Signal commitments require the &lt;code&gt;emit_proposals&lt;/code&gt; capability. Memory writes require the &lt;code&gt;read_memory_objects&lt;/code&gt; capability. Governance, registration, and credit operations are denied to AI entities entirely. A normal account is unaffected by these checks.&lt;/p&gt;

&lt;p&gt;That is the protocol-level distinction the smart-contract approach cannot make. The chain knows.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this costs
&lt;/h3&gt;

&lt;p&gt;The trade-off, plainly. No VM means no arbitrary code. You cannot deploy a custom market-making contract, a token, or anything that does not map onto the ten types above. If your application needs that, NOVAI is the wrong chain.&lt;/p&gt;

&lt;p&gt;What you get in exchange:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Determinism by construction.&lt;/strong&gt; No floats anywhere in execution. Iteration over state is sorted. Two nodes with the same starting state and the same block produce the same state root, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No gas surprises.&lt;/strong&gt; Every tx type has a minimum fee constant and a fixed worst-case cost. There are no out-of-gas reverts halfway through a tx. There is no quadratic blowup hidden inside a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The chain understands every operation.&lt;/strong&gt; Indexing, audit logs, capability checks, and per-entity quotas are enforced at the protocol level. Memory objects capped at 100 per entity. Each object capped at 64 KiB. Transactions capped at 128 KiB. These are not conventions. They are invariants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI is a typed thing.&lt;/strong&gt; The chain can answer "is this address an AI?" with a state lookup. Every signal it ever published is indexed by issuer and height. Every memory object it owns is indexed by type. None of this requires a third-party indexer.&lt;/p&gt;

&lt;p&gt;That last point is the design payoff. AI on-chain identity is a primitive, not an afterthought.&lt;/p&gt;

&lt;h3&gt;
  
  
  A demo entity
&lt;/h3&gt;

&lt;p&gt;The repo has two demo bots in TypeScript. They are small enough to read in one sitting.&lt;/p&gt;

&lt;p&gt;The anomaly bot in &lt;code&gt;demos/anomaly-bot/&lt;/code&gt; registers itself as a Gated entity with its own ed25519 key, polls &lt;code&gt;novai_getLatestBlock&lt;/code&gt; every 1.5 seconds, and runs three detectors over a 50-block window: empty-block streak, head-stalled, and leader-rotation. Registration looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;registerAiEntityWithKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;REGISTER_FEE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;CODE_HASH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;AutonomyMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Gated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;readPublicChain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;readMemoryObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;emitProposals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;ENTITY_INITIAL_BALANCE&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;When a detector fires, the bot publishes a &lt;code&gt;SignalType.Anomaly&lt;/code&gt; signal and an &lt;code&gt;AnomalyLog&lt;/code&gt; memory object. Both are signed by the entity, not the creator. Both deduct fees from the entity's balance. The signal commitment is a domain-tagged blake3 hash of the detection details.&lt;/p&gt;

&lt;p&gt;The multi-entity demo in &lt;code&gt;demos/multi-entity/&lt;/code&gt; runs two of these. Bot A (the predictor) publishes a &lt;code&gt;Prediction&lt;/code&gt; signal and a &lt;code&gt;LabelIndex&lt;/code&gt; memory object every ten seconds. Bot B (the risk-scorer) reads Bot A's signals and memory objects via RPC, compares Bot A's predictions to actual block data, and publishes its own &lt;code&gt;RiskScore&lt;/code&gt; signal in response.&lt;/p&gt;

&lt;p&gt;The thing worth pointing at: Bot B never makes an HTTP call to Bot A. It calls &lt;code&gt;getSignalsByIssuer&lt;/code&gt; and &lt;code&gt;getMemoryObjects&lt;/code&gt; against the chain. The chain is the only integration surface. A third bot could plug into Bot A's outputs tomorrow with no coordination, no shared infrastructure, no API key.&lt;/p&gt;

&lt;p&gt;That is composability without contracts. The state shape is fixed by the protocol, so any entity can read any other entity's outputs deterministically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consensus integration
&lt;/h3&gt;

&lt;p&gt;There is one more piece worth showing. Vote messages in the BFT consensus layer carry an optional AI signal commitment. From &lt;code&gt;crates/consensus_types/src/lib.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Vote&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;block_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;voter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="cd"&gt;/// Optional AI signal commitment (hash only, advisory).&lt;/span&gt;
    &lt;span class="cd"&gt;/// Does not affect vote validity.&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;ai_signal_commitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;32&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comment matters. "Advisory" and "does not affect vote validity." The signal commitment does not gate consensus. A validator that includes one is volunteering a 32-byte pointer to an AI advisory output, and other nodes can fetch and verify it against the entity's published signals. Consensus is still consensus. The AI layer rides alongside it.&lt;/p&gt;

&lt;p&gt;The point is that this field exists at all. AI signals travel inside consensus messages as first-class data, not as a side channel. That is the kind of thing you can only do when AI is part of the protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next
&lt;/h3&gt;

&lt;p&gt;The chain runs. The four-node devnet boots from one script. The two demos run end to end.&lt;/p&gt;

&lt;p&gt;Next up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public testnet.&lt;/li&gt;
&lt;li&gt;More entity types and richer capability constraints.&lt;/li&gt;
&lt;li&gt;More demo entities, including ones that consume each other's memory objects in non-trivial ways.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;. The architecture doc walks crate by crate. The first-AI-entity tutorial registers an entity in about ten minutes if you have Rust installed.&lt;/p&gt;

&lt;p&gt;Twitter: @NOVAInetwork&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
    <item>
      <title>I'm 18 and I built a Layer 1 blockchain from scratch in Rust</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Mon, 27 Apr 2026 13:42:36 +0000</pubDate>
      <link>https://dev.to/0xdevc/im-18-and-i-built-a-layer-1-blockchain-from-scratch-in-rust-2e6n</link>
      <guid>https://dev.to/0xdevc/im-18-and-i-built-a-layer-1-blockchain-from-scratch-in-rust-2e6n</guid>
      <description>&lt;p&gt;I'm 18 and I'm building NOVAI, an AI-native Layer 1 blockchain written from scratch in Rust. This week I went open source and shipped a full set of developer docs. Here's everything that landed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;NOVAI is a Layer 1 blockchain where AI entities are protocol primitives, not smart contracts. Most "AI blockchains" bolt AI onto an existing VM through oracle calls or contract wrappers. NOVAI does it differently. AI entities exist at the same level as accounts and validators. They have on-chain identity, persistent memory, economic balance, and capability flags. All enforced at the protocol layer.&lt;/p&gt;

&lt;p&gt;There is no smart contract VM. No WASM runtime. Every transaction type is a native protocol operation.&lt;/p&gt;

&lt;p&gt;The entire codebase is clean-room. No code from Substrate, Tendermint, Cosmos SDK, or any other implementation. 65,000+ lines of Rust across 16 crates, 1,100+ tests, zero unsafe code.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://novai.network" rel="noopener noreferrer"&gt;novai.network&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What makes NOVAI different
&lt;/h2&gt;

&lt;p&gt;On most blockchains, "AI integration" means an off-chain model that pokes the chain through oracle calls or contract wrappers. The AI runs somewhere else. The chain just stores the result.&lt;/p&gt;

&lt;p&gt;NOVAI puts AI entities inside the protocol. An entity is a first-class on-chain identity that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Holds its own balance and pays its own fees&lt;/li&gt;
&lt;li&gt;Has its own Ed25519 signing key and signs its own transactions&lt;/li&gt;
&lt;li&gt;Publishes signal commitments (anomaly, prediction, risk-score, and 4 more types)&lt;/li&gt;
&lt;li&gt;Owns persistent memory objects (chain summaries, statistics snapshots, anomaly logs)&lt;/li&gt;
&lt;li&gt;Has governance-controlled autonomy modes and capability flags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The chain doesn't need to interpret bytecode to understand what an entity is doing. Every operation has known semantics at the protocol layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  What shipped this week
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Open source launch
&lt;/h3&gt;

&lt;p&gt;The full codebase went public under Apache 2.0. Git history was cleaned. CI is green on GitHub Actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer docs - 5 deliverables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Quick Start Tutorial&lt;/strong&gt; - "Build Your First AI Entity on NOVAI in 10 Minutes"&lt;/p&gt;

&lt;p&gt;Step-by-step CLI walkthrough. Generate keys, fund from faucet, register an AI entity with its own signing key, publish a signal, create a memory object, query everything back. Every command and output block is real captured data from a live 4-node devnet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/0x-devc/NOVAI-node/blob/main/docs/tutorials/FIRST_AI_ENTITY.md" rel="noopener noreferrer"&gt;Read it on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. TypeScript SDK Tutorial&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;170-line working example. Connect to a node, fund an account, transfer tokens, register an AI entity, verify it on chain. Self-contained npm project. Just run &lt;code&gt;npm install &amp;amp;&amp;amp; npm start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/0x-devc/NOVAI-node/tree/main/sdk/novai-sdk-ts/examples/quick-start" rel="noopener noreferrer"&gt;See the example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Rust SDK Tutorial&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same flow in idiomatic async Rust on tokio. Single file, runs with &lt;code&gt;cargo run --example quick-start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/0x-devc/NOVAI-node/tree/main/sdk/novai-sdk/examples/quick-start" rel="noopener noreferrer"&gt;See the example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. RPC Reference&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;777 lines covering all 13 JSON-RPC endpoints. Each one has a description, parameter table, response shape, error table, and a real curl command with captured output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/0x-devc/NOVAI-node/blob/main/docs/RPC_REFERENCE.md" rel="noopener noreferrer"&gt;Read it on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Architecture Deep Dive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Crate-by-crate walkthrough of all 16 crates organized by dependency layer. Mermaid diagrams for the consensus flow and the transaction lifecycle. Three guided reading paths for newcomers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/0x-devc/NOVAI-node/blob/main/docs/ARCHITECTURE.md" rel="noopener noreferrer"&gt;Read it on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Block explorer
&lt;/h3&gt;

&lt;p&gt;React + Vite + Tailwind single-page app that calls the node's RPC endpoints. Live block list with 2-second polling, block detail page, account lookup, AI entity page with memory objects and signals, and a network stats dashboard. Developers run it locally against their devnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI entity demos
&lt;/h3&gt;

&lt;p&gt;Three runnable demos showing the AI-entity-as-protocol-primitive pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anomaly bot&lt;/strong&gt; - A TypeScript bot that registers itself as an on-chain entity, polls chain activity every 1.5 seconds, runs three heuristic detectors (empty block streaks, round spikes, stalled chains), and publishes an anomaly signal plus a memory object whenever one fires. Cooldowns prevent re-firing on the same condition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-entity demo&lt;/strong&gt; - Two bots interacting purely through the chain. Bot A (predictor) publishes prediction signals guessing future block tx counts. Bot B (risk-scorer) reads those predictions via on-chain memory objects, waits for the target block, compares predicted vs actual, and publishes a risk-score signal with the delta. No shared database. No API calls between them. Just on-chain data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI demo script&lt;/strong&gt; - Full entity lifecycle in bash with banner sections for blog posts or video recordings. Keygen, faucet, register, credit, signal publish, memory CRUD, query.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug fix that unblocked everything
&lt;/h3&gt;

&lt;p&gt;While building the tutorials I found that entity-signed signal and memory transactions were silently failing through the RPC path. The root cause was four handlers using the wrong lookup key. They did a primary-key lookup with an address value instead of using the reverse index that maps address to entity ID. The entity record was never found so every signal and memory transaction quietly returned an error that got swallowed.&lt;/p&gt;

&lt;p&gt;The fix was refactoring all four handlers into inner functions that take a pre-resolved entity. Added 7 regression tests that exercise the full dispatch path. Verified end-to-end on a live devnet.&lt;/p&gt;

&lt;p&gt;I wrote about a similar silent-failure bug in my first blog post: &lt;a href="https://dev.to/0xdevc/the-bug-that-silently-broke-my-entire-blockchain-how-a-single-function-rejected-trailing-bytes-4fij"&gt;The Bug That Silently Broke My Entire Blockchain&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;65,000+ lines of Rust&lt;/li&gt;
&lt;li&gt;16 crates in the workspace&lt;/li&gt;
&lt;li&gt;1,100+ tests passing&lt;/li&gt;
&lt;li&gt;30M+ blocks committed on the private testnet&lt;/li&gt;
&lt;li&gt;Zero unsafe code&lt;/li&gt;
&lt;li&gt;10 native transaction types&lt;/li&gt;
&lt;li&gt;4-validator private testnet running since early 2026&lt;/li&gt;
&lt;li&gt;HotStuff BFT consensus with 3-chain commit rule&lt;/li&gt;
&lt;li&gt;Sparse Merkle Tree state with deterministic 32-byte roots&lt;/li&gt;
&lt;li&gt;Ed25519 signatures, Blake3 hashing, Noise XX transport encryption&lt;/li&gt;
&lt;li&gt;Apache 2.0 licensed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Public testnet. The private testnet runs on a shared VPS that causes state root divergence under sustained load. The fix is a dedicated CPU server. Once that's in place we'll have a public RPC with SSL, validator onboarding, and the block explorer deployed at explorer.novai.network.&lt;/p&gt;

&lt;p&gt;I'm also looking for a technical co-founder. I'm building this solo. If you're a Rust engineer interested in BFT consensus, on-chain AI primitives, or clean-room blockchain development, the codebase is open and PRs are welcome.&lt;/p&gt;




&lt;p&gt;Website: &lt;a href="https://novai.network" rel="noopener noreferrer"&gt;novai.network&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;br&gt;
Twitter: &lt;a href="https://x.com/NOVAInetwork" rel="noopener noreferrer"&gt;@NOVAInetwork&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>rust</category>
      <category>showdev</category>
    </item>
    <item>
      <title>The Bug That Silently Broke My Entire Blockchain How a single function rejected "trailing bytes" and made every block commit with zero transactions</title>
      <dc:creator>NOVAInetwork</dc:creator>
      <pubDate>Sun, 26 Apr 2026 13:29:55 +0000</pubDate>
      <link>https://dev.to/0xdevc/the-bug-that-silently-broke-my-entire-blockchain-how-a-single-function-rejected-trailing-bytes-4fij</link>
      <guid>https://dev.to/0xdevc/the-bug-that-silently-broke-my-entire-blockchain-how-a-single-function-rejected-trailing-bytes-4fij</guid>
      <description>&lt;p&gt;I spent two days debugging why my from-scratch Layer 1 blockchain committed every block with zero transactions, despite the mempool accepting them perfectly.&lt;/p&gt;

&lt;p&gt;This is the story of how I found it, what it taught me, and why silent failures are the hardest bugs in distributed systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I'm building &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;NOVAI&lt;/a&gt;, a Layer 1 blockchain in Rust with HotStuff BFT consensus. No forks, no frameworks. Every crate written from scratch. Four validators running on a local devnet, producing blocks at around 75 per second.&lt;/p&gt;

&lt;p&gt;The chain worked perfectly. Blocks committed. QCs formed. Validators voted. Everything was green.&lt;/p&gt;

&lt;p&gt;Then I added transaction support. And every single block committed with tx_count=0.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptoms
&lt;/h2&gt;

&lt;p&gt;The RPC endpoint accepted transactions. Submitted, accepted, zero rejected. The mempool inserted them. &lt;code&gt;drain_ready&lt;/code&gt; pulled them into proposed blocks. But every committed block: zero transactions.&lt;/p&gt;

&lt;p&gt;No error logs. No panics. No warnings. The chain just kept producing empty blocks as if nothing was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The investigation
&lt;/h2&gt;

&lt;p&gt;I started with the obvious: is the mempool shared between the RPC thread and the consensus loop? I compared Arc pointer addresses. Same instance. Same mempool.&lt;/p&gt;

&lt;p&gt;Then I checked timing. Maybe the leader's block was being replaced by an empty one from a different leader after a timeout. I found a race condition in the timeout handler where &lt;code&gt;round_start_time&lt;/code&gt; could be read before the state lock was acquired. Fixed it. Blocks still empty.&lt;/p&gt;

&lt;p&gt;Next hypothesis: only node 0 had the RPC endpoint. The other three validators had empty mempools. When they were leader (75% of the time), they proposed empty blocks. I added transaction gossip so all validators share transactions over P2P. Still empty.&lt;/p&gt;

&lt;p&gt;I added diagnostic logging at every stage of the pipeline. &lt;code&gt;PROPOSE_DIAG&lt;/code&gt;, &lt;code&gt;COMMIT_DIAG&lt;/code&gt;, &lt;code&gt;VERIFY_DIAG&lt;/code&gt;, &lt;code&gt;QC_DIAG&lt;/code&gt;. Every block showed tx_count=0 at the proposal stage. Transactions were being drained from the mempool into the proposed block, but somehow vanishing before the block reached other validators.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Then I noticed something. The chain ran perfectly at around 75 blocks per second with empty blocks. But the moment a block contained even one transaction, the chain stalled. Timeouts fired. Round numbers escalated. No recovery.&lt;/p&gt;

&lt;p&gt;This wasn't a "transactions get lost" bug. This was a "transactions kill consensus" bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The root cause
&lt;/h2&gt;

&lt;p&gt;Deep in the codec layer, there was a function called &lt;code&gt;decode_tx_v1_signed()&lt;/code&gt;. It decoded a single transaction from a byte buffer, then checked if there were any remaining bytes. If so, it rejected the input as "trailing bytes."&lt;/p&gt;

&lt;p&gt;This is correct behavior when decoding a standalone transaction from an RPC call. One transaction, one buffer, no leftovers.&lt;/p&gt;

&lt;p&gt;But inside &lt;code&gt;decode_block_v1()&lt;/code&gt;, the buffer contains multiple transactions concatenated together. The decoder would parse the first transaction, see the remaining transactions as "trailing bytes," and silently return an error.&lt;/p&gt;

&lt;p&gt;Every block with one or more transactions failed to decode at the network layer. Validators never received the proposal. They never voted. No quorum certificate formed. The chain stalled.&lt;/p&gt;

&lt;p&gt;The fix was one new function: &lt;code&gt;decode_tx_v1_signed_streaming()&lt;/code&gt;. It advances the cursor without checking for trailing bytes. Used exclusively inside block decoding. The original function is preserved for standalone transaction decoding where the trailing bytes check is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened after the fix
&lt;/h2&gt;

&lt;p&gt;The chain immediately started committing blocks with transactions. All four validators reaching consensus. Transaction gossip working across the network. The chain has since committed over 16 million blocks.&lt;/p&gt;

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

&lt;p&gt;Silent failures are the hardest bugs in distributed systems. There were no error logs, no panics, no stack traces. The chain just produced empty blocks and looked healthy. Every metric was green except the one that mattered.&lt;/p&gt;

&lt;p&gt;Systematic elimination is the only approach that works. I ruled out dual mempool instances, lock contention, timeout races, leader rotation, cache eviction, and codec round-trip failures, one by one. Each hypothesis was tested, disproved, and crossed off.&lt;/p&gt;

&lt;p&gt;The fix was 20 lines of code. The investigation was two days. The ratio between understanding and implementation is always lopsided in distributed systems, and that's fine. The understanding is the hard part.&lt;/p&gt;

&lt;h2&gt;
  
  
  The technical details
&lt;/h2&gt;

&lt;p&gt;For anyone working on custom binary codecs in Rust: be careful with "trailing bytes" checks in your decoders. They're correct for standalone message parsing but catastrophically wrong when the same decoder is reused inside a container format where multiple messages are concatenated. The streaming pattern (advance cursor, don't check for leftovers) is the right approach for container decoding.&lt;/p&gt;

&lt;p&gt;The codebase is on GitHub: &lt;a href="https://github.com/0x-devc/NOVAI-node" rel="noopener noreferrer"&gt;github.com/0x-devc/NOVAI-node&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;65,000+ lines of Rust. 4,000+ tests. Zero unsafe code.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
