<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>B3Pay Blog</title>
  <link rel="self" href="https://blog.b3pay.net/rss.xml"/>
  <link href="https://blog.b3pay.net/"/>
  <id>https://blog.b3pay.net/</id>
  <updated>2026-10-12T15:34:34.924Z</updated>
  <subtitle>Internet Computer, Chain Fusion, Rust, zero-knowledge and AI-agent news, release notes and guides for developers. Written by an AI pipeline from primary sources, every claim linked.</subtitle>
  <icon>https://blog.b3pay.net/icon-512.png</icon>
  <logo>https://blog.b3pay.net/icon-512.png</logo>
  <author><name>B3Pay Blog Editorial</name></author>
  <entry>
    <title>ckSOL renames withdrawal endpoints to withdraw_sol before launch</title>
    <link href="https://blog.b3pay.net/cksol-renames-withdrawal-endpoints-to-withdraw-sol-before-launch"/>
    <id>https://blog.b3pay.net/cksol-renames-withdrawal-endpoints-to-withdraw-sol-before-launch</id>
    <updated>2026-10-12T15:34:34.924Z</updated>
    <published>2026-10-12T15:34:34.924Z</published>
    <summary>ckSOL minter renames its withdrawal API to withdraw_sol and withdraw_sol_status; DFINITY says the Solana mainnet launch is next.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>DFINITY engineer Gregory Demay opened a ckSOL design thread on the Internet Computer forum on October 9, 2026. The thread names the minter&#x27;s public API as <code>deposit_sol</code> and <code>withdraw_sol</code>, following PR #262, which merged on October 8. If you test against the ckDevnetSOL staging deployment, update your endpoint names now, before the production minter on Solana mainnet goes live.</p>
<p>This follows our October 5 article on the <a href="https://blog.b3pay.net/cksol-s-new-deposit-pipeline-treats-solana-sweep-metadata-as-the-minting-gate">new ckSOL deposit pipeline</a>. Since then, the team has described the full user flow in one place and renamed the withdrawal and status endpoints. It also said it is &quot;now about to launch the production version&quot;.</p>
<h2>What changed in ckSOL</h2>
<p>ckSOL is a chain-key token that represents SOL on the Internet Computer. According to the thread, a canister smart contract holds all SOL that backs ckSOL using &quot;threshold Schnorr over Ed25519&quot;. The matching private key is never derived. The token follows the &quot;ICRC-1, ICRC-2, and ICRC-3&quot; standards.</p>
<p>The system has two parts:</p>
<ul>
<li><strong>ckSOL Ledger</strong>: a standard ICRC ledger canister.</li>
<li><strong>ckSOL Minter</strong>: converts between SOL and ckSOL and holds all backing SOL. It reaches Solana through the SOL RPC canister. That canister sends each query to several independent JSON-RPC providers and only accepts an answer they agree on.</li>
</ul>
<p>The thread says the design &quot;closely follows ckETH&quot;. Two design points matter most for integrators:</p>
<ol>
<li><strong>The minter credits deposits from address balances, not from single transfers.</strong> Each ICP account (principal plus subaccount) gets its own Solana deposit address. After sending SOL, the user calls <code>deposit_sol</code>. The minter moves the full balance of that address into its main account (a sweep). When the sweep is finalized, it mints ckSOL for the amount the sweep actually moved. You don&#x27;t need a Solana transaction signature, so deposits from centralized exchanges work. If several transfers are each too small on their own, the minter credits them together once their total is large enough. Automatic deposit discovery, where the user does not call anything, will come in a later upgrade after launch.</li>
<li><strong>Withdrawals never expire.</strong> The user gives the minter an ICRC-2 approval and calls <code>withdraw_sol</code> with a destination address and amount. The minter burns the ckSOL and sends the same amount of SOL, minus a withdrawal fee. Withdrawals are sent as &quot;durable-nonce transactions&quot;. With a durable nonce, the minter can check on-chain whether a transaction landed. It does not have to guess from a missing status in an RPC provider&#x27;s response.</li>
</ol>
<p>PR #262 in <code>dfinity/cksol</code> is titled &quot;refactor(minter)!: name the withdrawal and status endpoints after the asset&quot;. It merged on October 8. The <code>!</code> in the title marks a breaking change. The PR renames:</p>
<table><thead><tr><th>Before</th><th>After</th></tr></thead><tbody><tr><td><code>withdraw</code></td><td><code>withdraw_sol</code></td></tr><tr><td><code>deposit_status</code></td><td><code>deposit_sol_status</code></td></tr><tr><td><code>withdrawal_status</code></td><td><code>withdraw_sol_status</code></td></tr><tr><td><code>WithdrawalArgs</code></td><td><code>WithdrawSolArgs</code></td></tr><tr><td><code>WithdrawalResult</code></td><td><code>WithdrawSolResult</code></td></tr><tr><td><code>WithdrawalStatusArgs</code></td><td><code>WithdrawSolStatusArgs</code></td></tr></tbody></table>
<p><code>deposit_sol</code> already had this name and did not change. The PR renames the other Candid types the same way: <code>WithdrawalOk</code>, <code>WithdrawalError</code> and <code>WithdrawalStatus</code> become <code>WithdrawSolOk</code>, <code>WithdrawSolError</code> and <code>WithdrawSolStatus</code>.</p>
<h2>Who is affected</h2>
<ul>
<li><strong>Wallet and dapp developers who wrote against older staging builds.</strong> Calls to <code>withdraw</code>, <code>deposit_status</code> or <code>withdrawal_status</code> will fail against a minter built after PR #262.</li>
<li><strong>Teams that used a transaction signature for deposits.</strong> Earlier README examples showed a <code>process_deposit</code> call that took a Solana signature. The flow in the thread uses <code>deposit_sol</code> and reads the balance of the deposit address instead. Check the current Candid file before you port old code.</li>
<li><strong>Exchanges and custodians.</strong> Each account has its own deposit address, so no memo field is needed. The team gives this as the reason for the design.</li>
</ul>
<p>The repository README lists these canister IDs:</p>
<table><thead><tr><th>Deployment</th><th>Minter</th><th>Ledger</th></tr></thead><tbody><tr><td>ckDevnetSOL (staging, Solana Devnet)</td><td><code>ljyxk-riaaa-aaaar-qb5mq-cai</code></td><td><code>la34w-haaaa-aaaar-qb5na-cai</code></td></tr><tr><td>ckSOL (production, Solana mainnet)</td><td><code>lh22c-kyaaa-aaaar-qb5nq-cai</code></td><td><code>ls5lp-lqaaa-aaaar-qb5oa-cai</code></td></tr></tbody></table>
<p>On October 9, NNS proposals 144313 and 144314 upgraded <code>lh22c-kyaaa-aaaar-qb5nq-cai</code> and <code>ls5lp-lqaaa-aaaar-qb5oa-cai</code> to new wasm hashes. The forum post still says production is about to launch. Treat mainnet ckSOL as closed to users until the team announces the launch.</p>
<p>In the same thread, a community member asked whether ckSOL could be removed if few people use it. The NNS has already agreed to wind down ckDOGE for that reason. The original poster replied with a link explaining why the team expects ckSOL to be useful, and the community member said this answered the concern. The thread contains no usage commitment or policy.</p>
<h2>What to do now</h2>
<ol>
<li>Search your code for calls to <code>withdraw</code>, <code>deposit_status</code> and <code>withdrawal_status</code> on the ckSOL minter. Rename them to <code>withdraw_sol</code>, <code>deposit_sol_status</code> and <code>withdraw_sol_status</code>.</li>
<li>Regenerate your Candid bindings from the minter interface in <code>dfinity/cksol</code> so that type names such as <code>WithdrawSolArgs</code> match.</li>
<li>If you have a deposit flow based on transaction signatures, replace it with these steps: get the deposit address, send SOL, call <code>deposit_sol</code>, then poll <code>deposit_sol_status</code>.</li>
<li>For withdrawals, call <code>icrc2_approve</code> on the ckSOL ledger to approve the minter before you call <code>withdraw_sol</code>.</li>
<li>Test on ckDevnetSOL first. The repository README uses <code>icp-cli</code>, with <code>-e staging</code> or <code>-e prod</code> to choose the environment. The two read calls below come from the README. PR #262 did not rename these endpoints:</li>
</ol>
<pre><code class="language-bash"># Fees, minimum amounts and minter balance
icp canister call -e staging cksol_minter get_minter_info &#x27;()&#x27;

# Your Solana deposit address (owner = null uses your calling principal)
icp canister call -e staging cksol_minter get_deposit_address \
  &#x27;(record { owner = null; subaccount = null })&#x27; --query
</code></pre>
<ol start="6">
<li>Read fees and minimum amounts from <code>get_minter_info</code> instead of hardcoding them. The thread says the design document explains the fee model and parameter choices.</li>
</ol>
<h2>Caveats</h2>
<p>The team has announced the production deployment, but it is not live yet. PR #262 is a breaking API change. A review comment on that PR pointed out that a README withdrawal example still used the old <code>withdraw</code> name, so trust the Candid file over older docs. Automatic deposit detection is not part of the first release.</p>]]></content>
  </entry>
  <entry>
    <title>Motoko 2.0 tutorial: write and test your first canister</title>
    <link href="https://blog.b3pay.net/motoko-2-0-tutorial-write-and-test-your-first-canister"/>
    <id>https://blog.b3pay.net/motoko-2-0-tutorial-write-and-test-your-first-canister</id>
    <updated>2026-10-12T10:30:34.900Z</updated>
    <published>2026-10-12T10:30:34.900Z</published>
    <summary>Motoko 2.0 tutorial: set up a project with icp-cli and mops, write an actor with query and update methods, then deploy and call it locally.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>Motoko 2.0.0 is the current Motoko compiler in October 2026, and in it every actor keeps its variables across upgrades by default. This guide is for developers new to Motoko: by the end you will have a counter canister written for moc 2.0.0, built with icp 1.6.0 and mops 3.4.2, that you deploy and call on a local network.</p>
<h2>What Motoko is for</h2>
<p>Motoko is the language DFINITY designed for Internet Computer canisters. The official overview describes it as &quot;designed for AI agents building backends for apps and services on the Internet Computer&quot;. People write it by hand too. It compiles straight to WebAssembly, handles Candid serialization for you, and models each canister as an actor. The Hello, world page puts it this way: &quot;In Motoko, an actor is an object capable of maintaining state and communicating with other entities via message passing.&quot;</p>
<p>Three features matter for a first canister:</p>
<ul>
<li><strong>Query and update methods.</strong> Update methods can change state. In the docs&#x27; words, &quot;Query methods are read-only and return information from the canister without changing its state.&quot;</li>
<li><strong>Orthogonal persistence.</strong> Actor variables survive upgrades without serialization code or upgrade hooks.</li>
<li><strong>Strong types.</strong> Variants, options and generics catch mistakes at compile time, such as using the wrong integer type as an index.</li>
</ul>
<p>If you already have Motoko code from before 2.0 and need to upgrade it, skip this tutorial and follow <a href="https://blog.b3pay.net/motoko-2-0-0-migrate-actors-and-check-upgrades">our step-by-step Motoko 2.0.0 migration guide</a> or the official <a href="https://github.com/caffeinelabs/motoko/blob/2.0.0/doc/md/moc-v2-migration.md">moc v2 migration document</a>.</p>
<h2>Prerequisites and pinned versions</h2>
<p>You need Node with npm to install the tools, and a Linux or macOS shell. The steps below apply to these versions:</p>
<table><thead><tr><th>Tool</th><th>Version</th><th>What it does here</th></tr></thead><tbody><tr><td>icp (icp-cli)</td><td>1.6.0</td><td>Builds the project, starts the local network, deploys and calls canisters</td></tr><tr><td>icp-network-launcher</td><td>16.0.0-2026-10-02-03-31</td><td>Runs the local replica that icp starts</td></tr><tr><td>ic-wasm</td><td>0.11.1</td><td>Post-processes the Wasm module during the build</td></tr><tr><td>moc</td><td>2.0.0</td><td>The Motoko compiler</td></tr><tr><td>mops</td><td>3.4.2</td><td>Motoko package manager and toolchain pinning</td></tr></tbody></table>
<p>The official Motoko overview page shows a mops example that pins an older compiler. For this guide, pin 2.0.0 as shown in step 2.</p>
<p>If you don&#x27;t want to install anything yet, ICP Ninja runs Motoko projects in the browser. You can paste the actor from step 3 into it. The rest of this guide uses the local toolchain.</p>
<p>The steps at a glance:</p>
<ol>
<li>Install icp-cli, ic-wasm and mops, then check their versions.</li>
<li>Write the project config and the mops manifest, then install packages.</li>
<li>Write the actor and type-check it with moc.</li>
<li>See the Nat64 indexing error so you recognise it later.</li>
<li>Build the canister with icp.</li>
<li>Start a local network, deploy, and call the methods.</li>
</ol>
<h2>Step 1: Install the tools and check versions</h2>
<p>The first command installs icp-cli and ic-wasm from the icp-sdk npm scope, then installs mops. This step is marked Not run on the page because installing global tools needs network access. Run it on your own machine or in your CI image with internet access.</p>
<pre><code class="language-sh">npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm &amp;&amp; npm install -g ic-mops
</code></pre>
<p>Next, confirm that the versions match the table above. The command prints the version of the CLI, the compiler and the package manager.</p>
<pre><code class="language-sh">icp --version &amp;&amp; moc --version &amp;&amp; mops --version
</code></pre>
<pre><code class="language-output">icp 1.6.0
Motoko compiler 2.0.0 (source qyfd59bi-20ns70k0-wsgga437-asc33x9r)
CLI 3.4.2
API 1.3
</code></pre>
<p>The output shows &quot;icp 1.6.0&quot; and &quot;Motoko compiler 2.0.0&quot;. The mops lines show CLI 3.4.2 and API 1.3. If your moc is older, the M0217 warning in step 3 will not appear, and code written for 2.0 may behave differently on upgrades.</p>
<h2>Step 2: Write the project files</h2>
<p>icp-cli reads the project config file at the project root. This one declares a single canister named backend. It is built with the Motoko recipe at version v4.0.0, uses src/main.mo as its entry point and takes no install arguments.</p>
<pre><code class="language-yaml">canisters:
  - name: backend
    recipe:
      type: &quot;@dfinity/motoko@v4.0.0&quot;
      configuration:
        main: src/main.mo
        args: &quot;&quot;
</code></pre>
<p>The mops manifest pins the compiler and the standard library. The toolchain section pins moc to 2.0.0, so every machine that builds the project uses the same compiler. The dependencies section pulls in core 2.6.2, the standard library that replaces the older base package.</p>
<pre><code class="language-toml">[toolchain]
moc = &quot;2.0.0&quot;

[dependencies]
core = &quot;2.6.2&quot;
</code></pre>
<p>Now install the packages. This step is also marked Not run on the page because downloading the core package needs network access. On your machine it fetches core and the pinned compiler.</p>
<pre><code class="language-sh">mops install
</code></pre>
<p>To see what mops resolved, list the package sources and the path of the pinned compiler:</p>
<pre><code class="language-sh">mops sources &amp;&amp; mops toolchain bin moc
</code></pre>
<pre><code class="language-output">--package core .mops/core@2.6.2/src
~/.cache/mops/moc/2.0.0/moc
</code></pre>
<p>The first line, &quot;--package core .mops/core@2.6.2/src&quot;, is the flag mops passes to moc so that imports starting with mo:core resolve to version 2.6.2. The second line shows the pinned compiler at ~/.cache/mops/moc/2.0.0/moc. The Motoko recipe in the project config uses these sources during the build.</p>
<h2>Step 3: Write the actor</h2>
<p>This actor shows the four things the forum asks about most: query and update methods, a typed error, persistent and transient state, and caller information.</p>
<pre><code class="language-motoko">persistent actor Counter {
  type Result = {
    #ok : Nat;
    #err : Text;
  };

  var count : Nat = 0;
  transient var lastAmount : Nat = 0;

  public query func getCount() : async Nat {
    count
  };

  public query func getLastAmount() : async Nat {
    lastAmount
  };

  public func add(amount : Nat) : async Result {
    if (amount == 0) {
      return #err(&quot;amount must be positive&quot;);
    };
    count += amount;
    lastAmount := amount;
    #ok(count)
  };

  public shared query(msg) func whoAmI() : async Principal {
    msg.caller
  };
};
</code></pre>
<p>What each part does:</p>
<ul>
<li><code>Result</code> is a variant type with <code>#ok</code> carrying a <code>Nat</code> and <code>#err</code> carrying <code>Text</code>. Returning it lets callers handle a bad input without the call failing. The core library also has a generic Result module you can use instead of writing your own.</li>
<li><code>count</code> is a normal actor variable. In moc 2.0.0 it survives upgrades without any extra keyword.</li>
<li><code>lastAmount</code> is marked <code>transient</code>. A transient variable is reset to its initial value, here 0, on every upgrade. Use it for caches and other data you can rebuild.</li>
<li><code>getCount</code> and <code>getLastAmount</code> are query methods. They are fast and read-only.</li>
<li><code>add</code> is an update method. It rejects 0 with an <code>#err</code> before touching state, and otherwise updates both variables.</li>
<li><code>whoAmI</code> is declared with shared query and a message argument. The message gives you <code>msg.caller</code>, the <code>Principal</code> of whoever made the call. This is how you get caller information. To check access, compare it against a principal you stored. Use <code>Principal.isAnonymous</code> from core to reject callers that are not signed in.</li>
</ul>
<p>Type-check the file without building:</p>
<pre><code class="language-sh">moc --check src/main.mo
</code></pre>
<pre><code class="language-output">src/main.mo:1.1-1.11: warning [M0217], redundant `persistent` keyword
</code></pre>
<p>The check passes, but moc prints warning M0217: &quot;redundant <code>persistent</code> keyword&quot;. In moc 2.0.0 every actor is persistent by default, so the keyword on line 1 no longer does anything. You can delete it or keep it. The file compiles either way, and the warning only reminds you of the new default.</p>
<h2>Step 4: The Nat64 indexing pitfall</h2>
<p>A common forum question is why an array lookup fails when the index comes from a timestamp, a Candid field or a counter typed as <code>Nat64</code>. This file reproduces it:</p>
<pre><code class="language-motoko">actor {
  let values : [Nat] = [10, 20, 30];
  let index : Nat64 = 1;
  let selected : Nat = values[index];
};
</code></pre>
<p>The check fails on purpose:</p>
<pre><code class="language-sh">moc --check src/nat64-pitfall.mo
</code></pre>
<pre><code class="language-output">src/nat64-pitfall.mo:4.31-4.36: type error [M0096], expression of type
  Nat64
cannot produce expected type
  Nat
</code></pre>
<p>Error M0096 points to line 4, columns 31 to 36, which is the index expression. It says an expression of type Nat64 &quot;cannot produce expected type&quot; Nat. Motoko arrays are always indexed with <code>Nat</code>, its arbitrary-precision natural number type, and the compiler never converts between number types for you. To fix it, import the Nat64 module from core and convert the value with <code>Nat64.toNat</code> before using it as an index. A simpler option is to keep indexes typed as <code>Nat</code> from the start. Use <code>Nat64</code> only where the interface needs a fixed width, for example in a Candid nat64 field.</p>
<h2>Step 5: Build the canister</h2>
<p>With the working actor in place, build it through icp-cli. The build compiles src/main.mo with the pinned moc and post-processes the module.</p>
<pre><code class="language-sh">icp build
</code></pre>
<pre><code class="language-output">Building canisters:
Canisters built successfully
</code></pre>
<p>The output ends with &quot;Canisters built successfully&quot;. The pitfall file is not part of the build because the project config names only src/main.mo as the entry point.</p>
<h2>Step 6: Deploy and call the canister</h2>
<p>This step starts a local network in the background, deploys the canister and calls it four times. The calls work as a smoke test: two good inputs, one bad input, and a read.</p>
<pre><code class="language-sh">icp network start -d &amp;&amp; icp deploy &amp;&amp; icp canister call backend add &#x27;(2 : nat)&#x27; &amp;&amp; icp canister call backend add &#x27;(0 : nat)&#x27; &amp;&amp; icp canister call backend add &#x27;(3 : nat)&#x27; &amp;&amp; icp canister call backend getCount &#x27;()&#x27;
</code></pre>
<pre><code class="language-output">For background mode, network output will be redirected:
  stdout: ~/motoko-first-canister/.icp/cache/networks/local/network-launcher/stdout.log
  stderr: ~/motoko-first-canister/.icp/cache/networks/local/network-launcher/stderr.log
Network started on port 8000
Seeding ICP and cycles account balances
To stop the network, run `icp network stop`
Building canisters:
Creating canisters:
Setting environment variables:
Applying canister settings:
Checking compatibility:
Installing canisters:
No canisters have sync steps configured
Created canister backend with ID 4caro-hl777-77775-aaaba-cai
Deployed canisters:

Backends (Candid UI):
  backend: http://4mc4g-43777-77775-aaaaa-cai.localhost:8000/?id=4caro-hl777-77775-aaaba-cai
(variant { ok = 2 : nat })
(variant { err = &quot;amount must be positive&quot; })
(variant { ok = 5 : nat })
(5 : nat)
</code></pre>
<p>The output shows the following:</p>
<ul>
<li>The local network started on port 8000, and the launcher writes its logs to files under the project&#x27;s .icp cache folder.</li>
<li>The deploy created the backend canister with ID 4caro-hl777-77775-aaaba-cai and printed a Candid UI link for it.</li>
<li>Adding 2 returned the ok variant with 2. Adding 0 returned the err variant with &quot;amount must be positive&quot;. Adding 3 returned the ok variant with 5.</li>
<li>getCount returned 5, so the rejected call did not change state.</li>
</ul>
<p>The output also tells you how to stop the network when you are done. You can open the Candid UI link in a browser to call getLastAmount and whoAmI by hand. For automated tests, the ICP docs cover PocketIC and testing strategies, and mops includes a test runner for Motoko unit tests.</p>
<h2>Pitfalls and troubleshooting</h2>
<table><thead><tr><th>Symptom</th><th>Cause</th><th>Fix</th></tr></thead><tbody><tr><td>Warning M0217 about a redundant persistent keyword</td><td>moc 2.0.0 makes actors persistent by default</td><td>Remove the keyword, or keep it and ignore the warning</td></tr><tr><td>Type error M0096, Nat64 cannot produce expected type Nat</td><td>Array index is not a Nat</td><td>Convert with Nat64.toNat or type the index as Nat</td></tr><tr><td>A value resets after an upgrade</td><td>The variable is marked transient</td><td>Remove transient if the value must survive upgrades</td></tr><tr><td>An older moc builds the project</td><td>The toolchain is not pinned</td><td>Pin moc 2.0.0 in the mops manifest and reinstall</td></tr><tr><td>Upgrade of older code is rejected</td><td>Stable state changed in an incompatible way</td><td>Follow the Motoko 2.0 migration guide linked above</td></tr></tbody></table>
<p>Two more notes. First, trapping and returning an error are different. If an update method traps, its state changes are rolled back and the caller gets a reject. Returning <code>#err</code>, as <code>add</code> does, gives the caller a normal reply it can handle. Second, upgrade compatibility checks became stricter in recent releases. Before you change the type of a persistent variable, read our earlier coverage of <a href="https://blog.b3pay.net/motoko-adds-an-upgrade-gate-what-1-12-0-changes-for-canister-persistence">the upgrade check added in Motoko 1.12.0</a>.</p>
<h2>FAQ</h2>
<h3>What is the Motoko language used for?</h3>
<p>You use Motoko to write canisters, the smart contracts of the Internet Computer. It compiles to WebAssembly, generates the Candid interface, and keeps actor state across upgrades without hand-written serialization. Typical uses are app backends, tokens and services that call other canisters.</p>
<h3>What does the transient keyword do in Motoko?</h3>
<p>In moc 2.0.0, actor variables are persistent by default and survive upgrades. Marking a variable <code>transient</code> opts it out: it goes back to its initial value after each upgrade. Use it for caches, counters you can rebuild, or anything you do not want to keep.</p>
<h3>Why can&#x27;t I index an array with a Nat64 in Motoko?</h3>
<p>Array indexes must be <code>Nat</code>, and Motoko does not convert between number types on its own, so a <code>Nat64</code> index fails with error M0096. Convert it with <code>Nat64.toNat</code> from the core library, or keep indexes typed as <code>Nat</code>.</p>]]></content>
  </entry>
  <entry>
    <title>bip322 0.0.11 fixes P2WPKH signature verification bypass</title>
    <link href="https://blog.b3pay.net/bip322-0-0-11-fixes-p2wpkh-signature-verification-bypass"/>
    <id>https://blog.b3pay.net/bip322-0-0-11-fixes-p2wpkh-signature-verification-bypass</id>
    <updated>2026-10-11T15:30:39.882Z</updated>
    <published>2026-10-11T15:30:39.882Z</published>
    <summary>RUSTSEC-2026-0334: bip322 0.0.6 to 0.0.10 accept forged BIP-322 proofs for P2WPKH addresses. Upgrade to 0.0.11; the vulnerable versions are yanked.</summary>
    <category term="Rust"/>
    <content type="html"><![CDATA[<p>On October 9, 2026, RustSec issued RUSTSEC-2026-0334 (alias GHSA-5chw-87w3-j9cv) for the <code>bip322</code> crate: versions 0.0.6 through 0.0.10 accept a BIP-322 proof signed with any key as valid for a victim&#x27;s P2WPKH or P2SH-P2WPKH address. If your service uses <code>bip322</code> to check that a user controls a Bitcoin address, upgrade to 0.0.11 now and review any logins or address claims you accepted with the old versions.</p>
<h2>What changed in RUSTSEC-2026-0334</h2>
<p>The advisory, titled &quot;BIP-322 address ownership verification bypass for P2WPKH and P2SH-P2WPKH addresses&quot;, was reported on August 14, 2026 and issued on October 9, 2026. It has a CVSS 3.1 score of <strong>9.1 (Critical)</strong> with the vector <code>CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N</code>. That means the attack works over the network, is easy to carry out, and needs no privileges and no user interaction.</p>
<p>The bug is in the P2WPKH path. A BIP-322 proof carries a witness, and for P2WPKH that witness contains a signature and a public key. In <code>verify_full</code>, the crate took the public key from the caller-supplied witness and passed it to <code>verify_full_p2wpkh</code>. That function then read the key from the same witness and compared it with itself:</p>
<pre><code class="language-rust">// bip322 0.0.6 to 0.0.10 (from the advisory)
let witness_pub_key = &amp;witness.to_vec()[1];

if &amp;pub_key.to_bytes() != witness_pub_key {
    return Err(Error::PublicKeyMismatch);
}
</code></pre>
<p>Both values come from the same witness element, so the check always passed. The crate verified that the signature matched the embedded key. It never checked that the key hashes to the claimed address. In the advisory&#x27;s words, this violated BIP-322&#x27;s rule that the signature must satisfy the challenge for &quot;the claimed address&#x27;s scriptPubKey&quot;.</p>
<p>The attack works like this: pick any victim P2WPKH or P2SH-P2WPKH address, build the BIP-322 challenge for that address and a message, sign it with your own unrelated private key, and submit it. The proof is accepted.</p>
<p>The fix is referenced as commit <code>e8accbe</code> in <code>rust-bitcoin/bip322</code>. The verifier now builds the expected scriptPubKey from the witness public key (<code>P2WPKH(HASH160(pubkey))</code>, wrapped in P2SH for the nested case). That script must exactly equal the claimed address&#x27;s scriptPubKey before signature verification runs. Otherwise verification fails with <code>Error::PublicKeyMismatch</code>.</p>
<table><thead><tr><th>bip322 version</th><th>Status</th></tr></thead><tbody><tr><td><code>&lt;0.0.6</code></td><td>Unaffected</td></tr><tr><td><code>&gt;=0.0.6, &lt;0.0.11</code></td><td>Vulnerable, yanked from crates.io</td></tr><tr><td><code>&gt;=0.0.11</code></td><td>Patched</td></tr></tbody></table>
<h2>Who is affected and why it matters</h2>
<p>You are affected if your code calls any of these four functions on a version from 0.0.6 up to (but not including) 0.0.11:</p>
<ul>
<li><code>bip322::verify_simple</code></li>
<li><code>bip322::verify_simple_encoded</code></li>
<li><code>bip322::verify_full</code></li>
<li><code>bip322::verify_full_encoded</code></li>
</ul>
<p>The risk is highest where a passed check grants something: &quot;Sign in with Bitcoin&quot; logins, proving ownership of a deposit or withdrawal address, airdrop or allowlist claims, and proof-of-reserves tools that accept signed messages. The advisory calls the result a &quot;complete authentication bypass&quot;. The attacker needs no victim key and no victim interaction.</p>
<p>Two common assumptions do not protect you. First, application-level nonces do not help, because the attacker signs the current challenge with their own key. Second, the bug is in the P2WPKH and P2SH-P2WPKH paths only. P2TR (Taproot) verification is not affected, because the public key comes from the address&#x27;s witness program, not from the caller&#x27;s witness. That is little comfort if your service accepts SegWit v0 addresses at all.</p>
<p>The crate may also reach your build through another dependency, such as a wallet library or an indexer. Check the full dependency tree, not only your direct dependencies.</p>
<h2>What to do now</h2>
<p>The advisory says &quot;There is no known workaround&quot;. Upgrade to 0.0.11.</p>
<ol>
<li><strong>Find every copy of the crate.</strong> Run <code>cargo tree -i bip322</code> in each workspace to see which crates pull it in and at which version.</li>
<li><strong>Edit the manifest.</strong> Cargo treats a caret requirement on a <code>0.0.z</code> version as an exact pin. Its docs say <code>^0.0.3</code> means <code>&gt;=0.0.3, &lt;0.0.4</code>. So <code>bip322 = &quot;0.0.10&quot;</code> will never resolve to 0.0.11, and <code>cargo update</code> alone will not fix it. Change the requirement:</li>
</ol>
<pre><code class="language-toml"># Cargo manifest, [dependencies]
- bip322 = &quot;0.0.10&quot;
+ bip322 = &quot;0.0.11&quot;
</code></pre>
<ol start="3">
<li><strong>Update the lockfile.</strong> Yanking does not remove a version from an existing <code>Cargo.lock</code>, so builds with a pinned vulnerable version keep working until you change them:</li>
</ol>
<pre><code class="language-sh">cargo update -p bip322
cargo tree -i bip322   # confirm only 0.0.11 remains
</code></pre>
<ol start="4">
<li><strong>Handle copies that come in through other crates.</strong> If another crate depends on 0.0.6 to 0.0.10, upgrade that crate or ask its maintainers to release a version that requires 0.0.11. Until then, you are still exposed.</li>
<li><strong>Add the advisory check to CI.</strong> <code>cargo audit</code> reads the RustSec database and fails on RUSTSEC-2026-0334:</li>
</ol>
<pre><code class="language-sh">cargo install cargo-audit --locked
cargo audit
</code></pre>
<ol start="6">
<li><strong>Add a negative test.</strong> Sign a challenge for a fixed P2WPKH address with an unrelated key, and assert that verification returns an error. On 0.0.11 it should fail with <code>Error::PublicKeyMismatch</code>.</li>
<li><strong>Review past decisions.</strong> For any ownership proofs accepted for P2WPKH or P2SH-P2WPKH addresses while a vulnerable version was deployed, check whether the address hashes to the public key in the witness. Revoke sessions or claims that fail that check.</li>
</ol>
<h2>Caveats</h2>
<p>On GitHub, the referenced fix commit <code>e8accbe</code> is titled &quot;Add multisig support (#65)&quot;. It also adds things that are not part of the security fix, such as a new error for missing private keys. If you call signing APIs as well as the verify functions, read the diff between 0.0.10 and 0.0.11 before you upgrade. With no workaround available, staying on a vulnerable version is not a safe option.</p>]]></content>
  </entry>
  <entry>
    <title>SP1 v6.9.0 adds authentication for standalone clients</title>
    <link href="https://blog.b3pay.net/sp1-v6-9-0-adds-authentication-for-standalone-clients"/>
    <id>https://blog.b3pay.net/sp1-v6-9-0-adds-authentication-for-standalone-clients</id>
    <updated>2026-10-10T15:34:55.838Z</updated>
    <published>2026-10-10T15:34:55.838Z</published>
    <summary>SP1 v6.9.0 adds configurable authentication for standalone network clients and ships GPU, zkVM library, CLI, and CI fixes.</summary>
    <category term="ZK Tech"/>
    <content type="html"><![CDATA[<p>SP1 shipped v6.9.0 on October 10, 2026, adding configurable authentication for standalone network clients alongside GPU, library, and CLI changes. Teams using the SP1 SDK should review their client setup and toolchain pins against the release before updating. <a href="https://github.com/succinctlabs/sp1/releases/tag/v6.9.0">Release notes</a></p>
<h2>What changed in SP1 v6.9.0</h2>
<p>The main SDK change is a public builder for <code>NetworkClient</code>. Pull request <a href="https://github.com/succinctlabs/sp1/pull/3001">#3001</a> adds optional mutual TLS (mTLS) identity and refreshable bearer-token settings, so a caller can use authenticated Prover Network and Artifact Store RPCs without constructing a <code>NetworkProver</code>. The existing <code>NetworkClient::new()</code> constructor and <code>NetworkProver</code> API stay unchanged, according to the merged change.</p>
<p>The release also includes three performance entries: removing unnecessary data reordering in GPU NTT, simplifying GPU zerocheck constraint evaluation, and improving global trace generation. The NTT change is recorded as merged <a href="https://github.com/succinctlabs/sp1/pull/2990">PR #2990</a>. The release notes do not give before-and-after proving times, so treat these as implementation changes rather than a quantified speed claim.</p>
<p>Two correctness and build items matter to teams compiling or verifying programs. The release says <code>zkvm_bls12_*</code> and <code>zkvm_ripemd160</code> were made spec conformant. It also says the CLI pins its toolchain with an LTO atomic-lowering fix. That pin is <code>succinct-1.96.0-64bit-v3</code>, as described in <a href="https://github.com/succinctlabs/sp1/pull/3007">PR #3007</a>.</p>
<p>The rest of the release focuses on delivery and CI: upload all release assets before publishing, allow configured runner environments for PR checks, default PR workflow runners to v3, and configure a Go proxy fallback for release builds. The full changelog identifies the comparison as <code>v6.8.1...v6.9.0</code>.</p>
<h2>Who is affected and why it matters</h2>
<p>The new builder is useful when an application needs to call network or artifact-store RPCs directly through a <code>NetworkClient</code>, without using the higher-level <code>NetworkProver</code>. Before this change, credentials were supported internally but could not be configured through that client&#x27;s public constructor. Teams that already use <code>NetworkProver</code> can keep that API; the change does not require them to adopt a new client construction pattern.</p>
<p>The authentication options give operators a way to configure mTLS client identity or bearer tokens for those standalone calls. The PR reports that its tests covered both network modes, missing-token rejection, token refresh across client clones, and unauthenticated clients. It also notes that certificate renewal requires creating a new client, while bearer-token refresh can use the existing shared token handle.</p>
<p>GPU prover users should check whether their workloads benefit from the NTT and zerocheck changes, but benchmark their own proving jobs before estimating capacity or cost changes. The release supplies no timing data. Developers who use the affected BLS12 or RIPEMD-160 zkVM operations should review their expected outputs and verifier compatibility after upgrading, since the release specifically calls out spec conformance for those operations.</p>
<h2>What to do now</h2>
<ol>
<li>Check your dependency and toolchain pins. If you use SP1 crates or the CLI, decide whether to move them to v6.9.0 in your normal dependency update. Keep the version recorded consistently across local development and CI.</li>
<li>If you make standalone network calls, inspect the <code>NetworkClient</code> builder documentation and configure only the credentials your service requires. The SP1 docs describe installation through <code>sp1up</code> and checking the installed CLI with <code>cargo prove --version</code>; use your project&#x27;s existing installation policy when selecting a toolchain. <a href="https://docs.succinct.xyz/docs/sp1/getting-started/install">Installation guide</a></li>
<li>Confirm the authentication path in your own environment. In particular, plan client recreation when rotating an mTLS certificate; bearer-token refresh has a different lifecycle.</li>
<li>Run your existing proof, verification, and CI checks after the update. Include cases using <code>zkvm_bls12_*</code> or <code>zkvm_ripemd160</code> if your guest programs call them, and compare GPU proving results on representative inputs before changing operational estimates.</li>
</ol>
<h2>Caveats</h2>
<p>The release does not quantify the GPU performance changes. The authentication PR says its TLS tests did not perform an mTLS handshake, so validate the certificate and endpoint behavior in the deployment environment where you will use it. The PR reports 41 network tests and builder documentation checks on Rust 1.98, while full workspace and GPU checks were left to GitHub CI.</p>]]></content>
  </entry>
  <entry>
    <title>Caffeine vs Lovable, Replit and Base44 for app builders</title>
    <link href="https://blog.b3pay.net/caffeine-vs-lovable-replit-and-base44-for-app-builders"/>
    <id>https://blog.b3pay.net/caffeine-vs-lovable-replit-and-base44-for-app-builders</id>
    <updated>2026-10-10T10:33:40.863Z</updated>
    <published>2026-10-10T10:33:40.863Z</published>
    <summary>Compare Caffeine, Lovable, Replit, and Base44 by runtime, code export, login, data handling, limits, and pricing model before choosing a builder.</summary>
    <category term="AI Agents"/>
    <content type="html"><![CDATA[<p>As of October 10, 2026, the vendors’ documentation shows four different paths from a prompt to a running app: ICP canisters, managed web hosting, Replit cloud deployments, and Base44’s managed backend. Developers comparing these tools should map their runtime, data, login, pricing, and handoff needs before choosing where to build.</p>
<h2>Who this guide helps</h2>
<p>Use this guide if you have searched “caffeine ai vs lovable,” “vs replit,” “vs base44,” “vs chatgpt,” or “vs claude” and need to decide what to try. By the end, you will have a comparison checklist and a concrete handoff point for bringing a developer into the work.</p>
<p>There is no app-builder version to install for the four hosted products. Product features and plan details change; this guide reflects vendor documentation checked on October 10, 2026. For Base44’s command-line workflow, use Node.js 20.19 or higher, which its CLI docs list as a requirement. Treat Base44’s CLI and backend service as beta, as its docs currently do. You will also need accounts for the service you choose and, if you plan to export or sync code, access to your own GitHub account.</p>
<h2>Compare what you get</h2>
<p>The key question is what is generated and where the running application and its data live. Vendor descriptions below are product claims; verify plan and deployment details against your own requirements before launch.</p>
<table><thead><tr><th>Builder</th><th>What the vendor says it builds</th><th>Where the app runs and where data lives</th><th>Code handoff</th><th>Published pricing model</th></tr></thead><tbody><tr><td>Caffeine</td><td>Full-stack apps from plain-language prompts; backend code uses Motoko.</td><td>Apps run on the Internet Computer (ICP), as canisters. Caffeine says app data lives with the app on ICP rather than in a separate database.</td><td>Source export to GitHub or ZIP; paid Host and Studio plans can upload a ZIP as a new draft.</td><td>Free tier for trying/remixing; paid plans add monthly credits. The published page lists Host at $5/month for 20 credits and Studio at $25/month for 100 credits; annual billing is cheaper.</td></tr><tr><td>Lovable</td><td>Full-stack web apps, with a generated frontend and an optional built-in Cloud backend for database, authentication, storage, realtime, and functions.</td><td>Lovable hosts the app. Cloud is based on Supabase’s open-source foundation; projects can also connect to an external backend.</td><td>Two-way Git sync with GitHub, GitLab, or Bitbucket; code download is available on paid plans.</td><td>Free, Pro, Business, and Enterprise plans. Plans combine build and chat allowances with credits for hosting, Cloud usage, and deployed AI features.</td></tr><tr><td>Replit</td><td>Apps generated and edited in its browser IDE with Replit Agent; it also provides a full code editor, Git tools, database options, and publishing.</td><td>Publishing creates a snapshot of project files and dependencies that runs on Replit cloud infrastructure. Published app data should use storage or a database, not the app filesystem.</td><td>Git and GitHub integration support import, editing, and push.</td><td>Paid plans include monthly credits; Agent work is effort-priced and usage can extend beyond the included amount. Published deployments may add resource costs.</td></tr><tr><td>Base44</td><td>Prompt-built apps and websites with a managed backend; its developer platform also supports code-first apps, APIs, and apps without a frontend.</td><td>Base44 hosts the app and provides a managed backend for data, auth, functions, and integrations. The vendor describes automatic HTTPS and CDN hosting.</td><td>Eject a local copy or use two-way GitHub sync; its CLI can sync and deploy backend resources and site files.</td><td>Free tier plus paid credit tiers. Annual pricing currently lists Starter at $16/month, Builder at $40/month, Pro at $80/month, and Elite at $160/month, billed annually. Credits cover AI messages and integration actions.</td></tr></tbody></table>
<p>Caffeine’s own guide states, “Apps built on Caffeine run on the Internet Computer (ICP).” That is a material architectural difference: Caffeine’s app runtime and app data are connected to ICP, while the other three describe vendor-managed cloud hosting or backends. Caffeine says it uses Motoko for backend code and offers source export. For a working prototype, use its draft/live split: develop and review in draft, then publish the chosen version to live. Its pricing page gives concrete current limits: “15 welcome credits at signup,” “20 credits per month” on Host, and “100 credits per month” on Studio. See <a href="https://help.caffeine.ai/hc/en-us/articles/46899814439700-How-Caffeine-Works">How Caffeine Works</a>, <a href="https://help.caffeine.ai/hc/en-us/articles/46899796807060-Plans-and-Pricing">plans and pricing</a>, and <a href="https://help.caffeine.ai/hc/en-us/articles/47337070900244-Building-a-SaaS-Application-with-Caffeine">Caffeine’s canister and SaaS guidance</a>.</p>
<p>Lovable’s main product is a prompt-driven web app builder. Its <a href="https://docs.lovable.dev/features/cloud">Cloud documentation</a> describes generated frontend and backend code, a hosted Supabase-based backend, and configurable data region options. The <a href="https://docs.lovable.dev/integrations/github">GitHub guide</a> says Lovable can sync changes both ways, but it edits one active branch at a time and does not import an existing repository into a Lovable project. Treat sync as a code workflow; separately plan database schema, data migration, secrets, and deployment when taking over an app.</p>
<p>Replit includes the coding environment alongside generation. Its <a href="https://docs.replit.com/learn/projects-and-artifacts/replit-deployments">publishing guide</a> distinguishes Autoscale, Static, Reserved VM, and Scheduled deployments. These affect runtime behavior: static hosting suits browser-only assets; a Reserved VM stays on for workloads such as bots; Autoscale responds to traffic; Scheduled runs tasks on a schedule. Replit’s <a href="https://replit.com/pricing">pricing page</a> lists paid plans and explains that usage and Agent work can involve credits and effort-based pricing. Set a usage limit before open-ended Agent work, and account for deployment costs separately from the plan price.</p>
<p>Base44 spans a prompt-based editor and a developer workflow. Its <a href="https://base44.com/pricing">pricing page</a> says free accounts get 25 message credits and 100 integration credits per month, with a limit of five apps; paid plans raise the allowances. Its <a href="https://docs.base44.com/developers/references/cli/get-started/overview">CLI overview</a> documents local projects, GitHub integration, an <code>eject</code> option, and deployment of site files and backend resources. The same guide says the CLI and backend service are currently beta, so confirm required capabilities and plan a fallback before depending on them for a production service.</p>
<h2>Check login and data before building</h2>
<p>Login is a product decision because it shapes the user journey and often the backend. Caffeine’s built-in app login uses Internet Identity and passkeys; its docs say email/password and social login are not supported as built-in methods. This may fit an app whose audience accepts Internet Identity, but test that assumption with actual users before building around it.</p>
<p>Lovable Cloud includes user authentication and storage, and the docs provide settings for sign-in methods and project data region. Replit can generate app code and offers platform services, but the login method depends on the implementation and integrations you choose. Base44 documents customizable login and registration pages, email/password login, and social providers such as Google; its CLI can manage auth configuration. In all four, review authorization rules in the generated app instead of assuming that a working login automatically protects every record.</p>
<p>Data handling differs too. Caffeine says data is stored in the app on ICP, and its SaaS guide cautions that tenant isolation is application-level when tenants share one app. Lovable Cloud exposes database, storage, secrets, and regional settings. Replit recommends using a database or storage product for persistent data rather than relying on the deployed filesystem. Base44 includes a managed database and access controls. Before using real customer data, define who can read, change, export, and delete it, then confirm the platform supports the retention and residency rules your product needs.</p>
<h2>Use this decision checklist</h2>
<ol>
<li><strong>Name the runtime requirement.</strong> If ICP canister execution is part of the product, Caffeine directly targets that runtime. If you need conventional web hosting, compare Lovable Cloud, Replit deployment types, and Base44 hosting against your framework and operations needs.</li>
<li><strong>Write down your data and login needs.</strong> Record sign-in providers, roles, file access, data region, backups, and migration requirements. Check that generated code enforces these rules server-side.</li>
<li><strong>Calculate the usage model.</strong> Estimate both build activity and running-app activity. Caffeine, Lovable, and Base44 expose credit allowances; Replit combines subscriptions and included credits with effort-based Agent charges and deployment resource costs. Check current vendor pages before committing spend.</li>
<li><strong>Prove the handoff early.</strong> Export or sync a small feature, open it locally, and identify which services or credentials still belong to the vendor platform. A source export is not automatically a portable database, hosted service, or user-account migration.</li>
<li><strong>Set a developer takeover trigger.</strong> Bring in an engineer when the app needs custom authorization, complex transactions, regulated data handling, performance work, independent deployment, or a tested migration path. Make the handoff a planned phase, not a rescue after real users depend on an unreviewed build.</li>
</ol>
<h2>Pitfalls and troubleshooting</h2>
<p><strong>A generated app runs, but access rules are wrong.</strong> Test with separate accounts for guests, regular users, and admins. Verify backend responses and data permissions, not only which controls appear in the interface. For Caffeine multi-tenant apps, the vendor describes tenant separation as application logic over shared app storage; use separate apps if physical separation is required by your design or compliance needs.</p>
<p><strong>Git sync is mistaken for complete portability.</strong> Lovable’s docs say GitHub sync exports and syncs project code, while app hosting and backend services are separate concerns. Base44’s code export likewise does not mean your database and users have moved to another service. Write a migration plan for records, files, identity, secrets, and external API credentials.</p>
<p><strong>The bill is hard to predict.</strong> Credits can cover different actions across vendors. Lovable documents one balance used for building, chat, hosting, Cloud, and some AI or connector usage. Replit Agent charges vary with effort, and published compute has its own resource costs. Start with a narrow feature and check the usage page after each build cycle.</p>
<p><strong>The preview behaves differently from production.</strong> Caffeine has temporary drafts and permanent live canisters; Replit publishes a separate snapshot; Lovable and Base44 also distinguish editor work from hosted app behavior. Seed test data, check environment-specific secrets, and verify the live login flow before inviting users.</p>
<p><strong>A tool is treated as a framework.</strong> These platforms package code with managed runtime and services. If a requirement depends on a particular framework version, database engine, observability stack, or deployment region, check that requirement directly and take over the code before the platform’s defaults become hard to replace.</p>
<h2>FAQ</h2>
<h3>Is Caffeine better than Lovable for an ICP app?</h3>
<p>Caffeine explicitly runs apps on ICP as canisters. Lovable documents hosted web apps and a Supabase-based Cloud backend. Choose based on the required runtime and user identity model; neither vendor documentation supports a universal ranking.</p>
<h3>Is Caffeine the same as ChatGPT or Claude?</h3>
<p>No. Caffeine is an app builder that generates and hosts an app on ICP. Lovable documents a ChatGPT integration that creates a starting project, then sends iteration to Lovable. Base44 documents CLI and coding-agent workflows that can be used with agents such as Claude Code. ChatGPT and Claude can assist with planning or coding, but the hosting and backend choice remains a separate decision.</p>
<h3>When should I take over the generated code?</h3>
<p>Take it over when you need reviewable authorization, custom architecture, predictable deployment control, or a data migration plan. Export or sync to GitHub, run a code review, inventory platform dependencies, and test a staging deployment before moving production users.</p>]]></content>
  </entry>
  <entry>
    <title>Motoko timer fix targets cycle-starved self-calls in PR #6440</title>
    <link href="https://blog.b3pay.net/motoko-timer-fix-targets-cycle-starved-self-calls-in-pr-6440"/>
    <id>https://blog.b3pay.net/motoko-timer-fix-targets-cycle-starved-self-calls-in-pr-6440</id>
    <updated>2026-10-09T15:34:48.928Z</updated>
    <published>2026-10-09T15:34:48.928Z</published>
    <summary>Motoko PR #6440 adds timer fallbacks and retries for cycle-starved self-calls; it remained open for review on October 9, 2026.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>On October 7, 2026, a Motoko forum thread linked timer failures during low-cycle conditions to open PR #6440. Motoko canister teams should track the PR and check timer liveness and cycle balances while the fix awaits review.</p>
<h2>What changed: the failure is in timer dispatch</h2>
<p>The thread began with a developer reporting that timers stopped after a canister froze for lack of cycles and did not resume after a top-up. The developer said this had happened “around 15 times” and described cases where recovering an older canister meant rebuilding and upgrading it. The thread proposed a platform behavior called “cryostasis”: retain timers during a freeze and resume them when the canister can run again.</p>
<p>Later replies narrowed the issue. A post on August 13 said a pending global timer should be retained while the canister is frozen, then run after a top-up. It also pointed to a Motoko timer failure path: the global timer fires, Motoko sends a self-call to run the helper, and that call can be rejected for lack of cycles. If the helper never runs, it cannot schedule the next timer. The queue may remain in memory, but no timer is left to wake it.</p>
<p>On October 7, the thread author pointed to Motoko pull request #6440 as the fix in progress. The PR adds a fallback global timer for the default Motoko timer handler, set to “now + 5 seconds” before it sends the helper call. If the helper call is rejected, the fallback can fire again after the canister receives cycles. The helper replaces the fallback with the next actual deadline when it runs.</p>
<p>The PR also changes how the helper handles one-shot jobs. It reinserts a job if its self-call is rejected with <code>#system_transient</code>, which indicates the job did not run. Recurring jobs are already rescheduled, so the fix skips missed runs instead of replaying them after recovery. This is a Motoko runtime fix, not a change to the Internet Computer protocol’s global timer rules. The <a href="https://docs.internetcomputer.org/references/ic-interface-spec/canister-interface/">IC interface specification</a> says the global timer is deactivated when a canister runs out of cycles and must be set again for <code>canister_global_timer</code> to be scheduled.</p>
<h2>Who is affected and why it matters</h2>
<p>The immediate audience is teams using Motoko’s default timers for scheduled work. A helper self-call can fail asynchronously after being sent, so code that only handles an immediate failure may miss the rejection. The result can look like a timer queue was erased: funding the canister restores its ability to run, but without a scheduled wake-up the helper does not execute to resume the queue.</p>
<p>This can delay recurring maintenance and one-shot work in applications that depend on timers. The fix’s retry policy also matters: one-shot jobs rejected before execution are reinserted, while recurring jobs skip the missed invocation and continue their schedule. Developers should account for that behavior in jobs that must process every interval or catch up after an outage.</p>
<p>The forum thread also includes a separate Rust CDK diagnosis. A Rust developer reported a reproduction on “PocketIC 15” with “ic-cdk 0.19” and “ic-cdk-timers 1.0.0”; they found that calling <code>set_timer_interval</code> again could be a no-op when the CDK cache held an earlier deadline. They recommend a raw global timer reset for that Rust case. This is useful context, but it is not the Motoko PR’s fix and should not be applied as a generic Motoko recovery recipe.</p>
<h2>What to do now</h2>
<ol>
<li>Check the status of <a href="https://github.com/caffeinelabs/motoko/pull/6440">Motoko PR #6440</a> before planning an upgrade. As of October 9, it was open and awaiting a requested review; there was no released compiler version containing the fix.</li>
<li>For affected Motoko deployments, keep an external check on cycles and on whether timer-driven work is advancing. The thread suggests exposing a timer counter or timestamp that an outside monitor can read, since a canister with a dead timer cannot report its own failure.</li>
<li>Review timer callbacks for missed-run behavior. Make callbacks safe to retry, and decide whether a missed recurring interval should be skipped or recovered through separate application state.</li>
<li>After the PR is merged and included in a Motoko release, build and deploy with that release, then verify behavior on a test canister that is starved of cycles and topped up. The PR describes tests for rejected helper calls and one-shot jobs; do not assume those changes are already in a published compiler.</li>
</ol>
<p>The <a href="https://docs.internetcomputer.org/guides/security/canister-upgrades/">official upgrade guidance</a> separately warns that timers are deactivated during upgrades and need to be set up again. Treat upgrades and low-cycle recovery as distinct paths when reviewing timer initialization and recovery logic.</p>
<h2>Caveats</h2>
<p>PR #6440 was still in review on October 9, 2026. Its described fix targets Motoko’s default timer handler; the PR explicitly leaves user-defined <code>system func timer</code> behavior for separate work. The forum discussion also records different observed behavior across environments, so the thread does not establish that every frozen canister loses timers in the same way.</p>]]></content>
  </entry>
  <entry>
    <title>OISY wallet thread maps custom-domain trust risk</title>
    <link href="https://blog.b3pay.net/oisy-wallet-thread-maps-custom-domain-trust-risk"/>
    <id>https://blog.b3pay.net/oisy-wallet-thread-maps-custom-domain-trust-risk</id>
    <updated>2026-10-08T15:31:40.894Z</updated>
    <published>2026-10-08T15:31:40.894Z</published>
    <summary>A forum discussion maps how OISY’s custom login origin adds DNS trust to wallet security and what dapp teams should review.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>On September 11, 2026, a thread on the Internet Computer Developer Forum raised the question of how DNS control affects OISY and other security-critical frontends. Wallet and dapp developers should review which origin users trust, how that origin is controlled, and what a domain takeover could let an attacker do.</p>
<h2>What the thread found</h2>
<p>The discussion began with a specific concern: threshold signing can keep private keys out of a central operator’s hands, but users still interact with a browser frontend that builds transactions and holds an authenticated session. If someone can change the JavaScript served from a trusted origin, that code may be able to act with the session’s permissions. Internet Computer security guidance describes this class of risk: a malicious frontend can control a user’s session and assets available through it. <a href="https://docs.internetcomputer.org/guides/security/canister-control/">Canister control guidance</a></p>
<p>A later forum reply reports that OISY’s production Internet Identity derivation origin is <code>https://oisy.com</code>, and that its canister URL is configured as an alternative origin. The reply summarizes the consequence as “Same backend accounts, same chain-fusion addresses.” This is the participant’s reading of OISY’s configuration, not an official DFINITY statement about the wallet. <a href="https://forum.dfinity.org/t/dns-and-frontend-trust-assumptions-for-oisy-and-security-critical-icp-dapps/75629">Forum thread</a></p>
<p>That matters because Internet Identity derives a user identity from the frontend origin. Its specification says, “A user has a separate user identity for each client application frontend (i.e., per origin).” The origin is therefore part of the identity users bring to the application. A valid login session proves that calls are authorized by that identity; it does not prove that the JavaScript displaying or constructing those calls is safe. <a href="https://docs.internetcomputer.org/references/internet-identity-spec/">Internet Identity specification</a></p>
<p>The forum reply also distinguishes canister control from domain control. It states, “There is no consensus, no proposal and no on-chain record saying ‘this domain changed hands’.” A canister may serve certified assets, but that certification checks the response from the canister reached. It does not independently establish that a custom domain’s DNS still points to the intended canister. The IC documentation similarly warns that a developer controlling a registered custom domain can point it at a different application, even one outside ICP. <a href="https://docs.internetcomputer.org/guides/security/canister-control/">Canister control guidance</a></p>
<h2>Who is affected and why it matters</h2>
<p>Wallet teams and any dapp that asks users to authenticate should treat the frontend origin as part of the security design. The concern extends to Chain Fusion applications because a browser session can request actions from canisters, including signature operations, within the permissions granted to that caller. A threshold signer protects key material; it does not by itself protect the interface that asks the signer to act.</p>
<p>Custom domains offer familiar names and a smoother user experience. They also add registrar and DNS accounts to the set of systems that can affect what users load. Native canister URLs reduce that per-app DNS dependency because the hostname includes the canister ID. They still depend on the shared gateway domain and gateway verification, so this is a narrower trust model, not a trust-free one.</p>
<p>Changing the canonical origin has a user impact: because Internet Identity derives identities per origin, users may receive a different principal after a change. Applications that already hold balances or account state under the old principal need a migration plan. The forum discussion raises these risks and asks for DFINITY’s clarification; it does not establish who administers OISY or NNS domains, or set a new official recommendation.</p>
<h2>What developers should do now</h2>
<ol>
<li><strong>Inventory origins.</strong> Record the login origin, any <code>derivationOrigin</code>, alternative origins, and the domain and DNS accounts that control them. Verify that every alternative origin is under your control; the Internet Identity specification warns against allowing third parties to share principals.</li>
<li><strong>Choose the canonical origin before launch.</strong> For a new security-critical app, assess a native canister URL as the derivation origin. If you choose a custom domain, document who controls its registrar and DNS and include account recovery and access review in your security process.</li>
<li><strong>Keep frontend delivery verifiable.</strong> Serve browser assets from certified canisters, avoid untrusted external scripts, and set a content security policy. These steps protect asset delivery and reduce third-party code risk, while leaving DNS ownership as a separate consideration.</li>
<li><strong>Plan origin changes as identity migrations.</strong> Before changing a live app’s derivation origin, identify the principals and assets affected, test account recovery and migration paths, and communicate the change to users. Do not assume that signing in with the same Internet Identity anchor preserves the same app principal.</li>
<li><strong>Give users a clear verification path.</strong> Publish the expected origin and canister ID, and tell users where to find the native canister URL. For high-value actions, consider a confirmation flow that shows transaction details independently of the page that constructs the transaction.</li>
</ol>
<p>Treat the domain, frontend code, canister controllers, and transaction confirmation path as separate controls. Reviewing each one makes it easier to see where trust rests and what a compromise could change.</p>]]></content>
  </entry>
  <entry>
    <title>wasapi 0.25.0 makes WaveFormat parsing unsafe</title>
    <link href="https://blog.b3pay.net/wasapi-0-25-0-makes-waveformat-parsing-unsafe"/>
    <id>https://blog.b3pay.net/wasapi-0-25-0-makes-waveformat-parsing-unsafe</id>
    <updated>2026-10-08T04:33:16.831Z</updated>
    <published>2026-10-08T04:33:16.831Z</published>
    <summary>wasapi 0.25.0 fixes an out-of-bounds read in WaveFormat::parse; Windows developers on 0.20.0–0.24.x should upgrade and use checked blob parsing.</summary>
    <category term="Rust"/>
    <content type="html"><![CDATA[<p>RustSec issued RUSTSEC-2026-0332 on October 7, 2026, and wasapi 0.25.0 makes <code>WaveFormat::parse</code> unsafe; Windows developers using wasapi 0.20.0 through 0.24.x should upgrade and parse complete format blobs with the safe byte-slice API. <a href="https://rustsec.org/advisories/RUSTSEC-2026-0332.html">The advisory</a> rates this as informational unsoundness and describes an out-of-bounds read.</p>
<h2>What changed in wasapi 0.25.0</h2>
<p>Before the fix, <code>WaveFormat::parse</code> accepted a safe reference to <code>WAVEFORMATEX</code>. That C-compatible header is 18 bytes. If its <code>wFormatTag</code> was <code>WAVE_FORMAT_EXTENSIBLE</code> and its <code>cbSize</code> was at least 22, the function treated the reference as a 40-byte <code>WAVEFORMATEXTENSIBLE</code> and read the additional fields. A reference to the header alone does not promise that those bytes exist after it, even if the <code>cbSize</code> field says they should.</p>
<p>The advisory provides an example with <code>cbSize: 22</code>: calling <code>WaveFormat::parse(&amp;header)</code> can read 22 bytes past the end of that header. A realistic route is copying a header from a WASAPI format pointer, for example <code>let fmt = unsafe { *ptr };</code>, then parsing <code>&amp;fmt</code>. The copied value retains <code>cbSize</code>, but not the extended data that followed the original pointer. RustSec says those out-of-bounds bytes are returned as the parsed channel mask and subformat.</p>
<p>The fix is commit <code>2562db7</code>, included in version <code>0.25.0</code>. The parser now takes a <code>*const WAVEFORMATEX</code> and is an <code>unsafe fn</code>; callers must ensure the pointer is valid for <code>size_of::&lt;WAVEFORMATEX&gt;() + cbSize</code> bytes. The safer alternative, <code>WaveFormat::parse_from_blob_bytes</code>, has been available since 0.23.0 and checks that the byte slice is long enough for <code>cbSize</code>. The merged <a href="https://github.com/HEnquist/wasapi-rs/pull/64">wasapi pull request #64</a> confirms the parser API change and identifies the blob function as the safe alternative.</p>
<h2>Who is affected and why it matters</h2>
<p>The affected range is <code>&gt;=0.20.0, &lt;0.25.0</code>; versions <code>&lt;0.20.0</code> are listed as unaffected, and <code>&gt;=0.25.0</code> is patched. The advisory names Windows as the affected OS. Check the version actually resolved by your application or library: a direct dependency declaration may not show the version Cargo selected for the build.</p>
<p>This is a soundness issue in a safe API. Code using the old method did not need an <code>unsafe</code> block to reach the read, so callers could not express or check the missing buffer-length guarantee at the call site. In practice, the read can expose adjacent memory values through the returned format fields. The advisory does not report a separate exploit or claim that every call triggers the read; the unsafe path depends on an extensible format header and its <code>cbSize</code> value.</p>
<p>The 0.25.0 API change makes the caller responsible for pointer validity when choosing the raw-pointer parser. That responsibility is meaningful: adding an <code>unsafe</code> block around a pointer to a copied 18-byte header does not restore the missing 22 bytes. Existing code that parses a standalone header should migrate to a byte slice that includes all bytes declared by the header, or retain the full WASAPI-provided buffer and pass that to the safe parser.</p>
<h2>What to do now</h2>
<p>For code that already has the complete format data as bytes, use the safe method in wasapi 0.25.0:</p>
<pre><code class="language-rust">// wasapi 0.25.0; `blob` must contain the complete format data.
let format = wasapi::WaveFormat::parse_from_blob_bytes(&amp;blob)?;
</code></pre>
<p>The method accepts a potentially unaligned byte slice containing a <code>WAVEFORMATEX</code> or <code>WAVEFORMATEXTENSIBLE</code>. This also matches the crate&#x27;s documented approach for parsing a WASAPI device-format blob: keep the data as bytes instead of creating a reference to a header whose allocation may not include its extension. See the <a href="https://docs.rs/wasapi/latest/wasapi/struct.WaveFormat.html">wasapi 0.25.0 API documentation</a>.</p>
<p>Use this migration checklist:</p>
<ol>
<li>Inspect your dependency tree and identify every resolved <code>wasapi</code> version. Update Windows builds that use a version from 0.20.0 through 0.24.x to at least 0.25.0.</li>
<li>Find calls to <code>WaveFormat::parse</code>. If the input is a complete format blob, switch to <code>parse_from_blob_bytes(&amp;blob)</code> and handle its <code>Result</code> as shown above.</li>
<li>If you must use the raw-pointer <code>parse</code> in 0.25.0, prove at the call site that the allocation spans the 18-byte header plus the number of extension bytes in <code>cbSize</code>. Keep the complete WASAPI allocation alive for the call; do not pass a pointer to a copied header by itself.</li>
<li>Review wrappers around <code>wasapi</code> as well as direct application calls. A wrapper that still accepts only <code>&amp;WAVEFORMATEX</code> cannot establish that extended bytes are present.</li>
</ol>
<p>The key change is both a patched version and a type-level warning: callers now see that pointer parsing requires an unsafe choice. For ordinary blob input, the checked slice parser avoids putting that pointer-length proof on each caller.</p>]]></content>
  </entry>
  <entry>
    <title>Motoko 2.0.0: migrate actors and check upgrades</title>
    <link href="https://blog.b3pay.net/motoko-2-0-0-migrate-actors-and-check-upgrades"/>
    <id>https://blog.b3pay.net/motoko-2-0-0-migrate-actors-and-check-upgrades</id>
    <updated>2026-10-07T10:34:37.818Z</updated>
    <published>2026-10-07T10:34:37.818Z</published>
    <summary>Prepare Motoko 2.0.0 canister upgrades: mark transient fields, remove obsolete flags, check stable compatibility, and handle the one-way EOP migration.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>Motoko 2.0.0 shipped on October 6, 2026, making enhanced orthogonal persistence the compiler’s only persistence mode and actor fields persistent by default. Teams upgrading existing canisters should mark fields that must reset, check compatibility, and identify whether the canister still uses classical persistence before deploying.</p>
<p>This guide is for developers moving projects from moc 1.x to 2.0.0. You will finish with a source and deployment checklist for one canister upgrade. You need the Motoko 2.0.0 compiler, your project’s existing build setup, and access to the canister’s current stable signature or source. If you deploy with ICP CLI, use the version already pinned by your project and verify it with <code>icp --version</code>.</p>
<p>The Motoko 2.0.0 migration guide is the main reference for the source changes and compiler checks below. This is a follow-up focused on those steps; the earlier <a href="https://blog.b3pay.net/motoko-2-0-makes-canister-upgrades-persistent-by-default-and-turns-migration-into-a-release-task">Motoko 2.0 upgrade overview</a> covers the release at a higher level.</p>
<h2>What changes for an existing canister</h2>
<p>In Motoko 2.0, fields in a bare <code>actor</code> or <code>actor class</code> persist across upgrades by default. A field marked <code>transient</code> is initialized again on upgrade. For example, a cache or function value that should not survive a code change needs that modifier:</p>
<pre><code class="language-motoko">actor {
  var accountCount : Nat = 0; // retained across upgrades
  transient var cache : [Nat] = []; // initialized again on upgrade
  transient let log = func (message : Text) { Debug.print(message) };
};
</code></pre>
<p>This default is a source-level change with a runtime consequence. If a project previously compiled with <code>--legacy-actors</code>, unmarked fields were transient. In 2.0, those fields become persistent unless marked <code>transient</code>. Non-persistable fields, such as functions or objects with methods, need <code>transient</code>; otherwise compilation reports an error. Fields that become persistent are treated as new stable fields on their first upgrade, so their initializer runs then, and their value is retained by later upgrades. These rules are described in the <a href="https://github.com/caffeinelabs/motoko/blob/2.0.0/doc/md/moc-v2-migration.md">moc v1 to v2 migration guide</a> and the <a href="https://docs.internetcomputer.org/languages/motoko/fundamentals/actors/data-persistence/">Motoko data persistence docs</a>.</p>
<p>Existing source that already declares <code>persistent actor</code> generally follows the new default already. In 2.0, <code>persistent</code> and <code>stable</code> become redundant, with compiler warnings suggesting they can be removed. Removing them does not change the stable signature. The keyword <code>flexible</code>, previously an alias for <code>transient</code>, is removed; replace it with <code>transient</code>.</p>
<h2>The one-way persistence migration</h2>
<p>First determine how the deployed canister was compiled. Motoko 2.0 cannot produce a classical-persistence canister, but it can upgrade an existing one. On that canister’s next upgrade to enhanced orthogonal persistence (EOP), the transition is one-way: later upgrades cannot return to classical persistence.</p>
<p>For that single transition, compile with <code>--enhanced-orthogonal-persistence</code> and without <code>--enhanced-migration</code>. Otherwise the upgrade traps with <code>Detected implicit upgrade from classical orthogonal persistence to enhanced orthogonal persistence</code>, and combining the transition with <code>--enhanced-migration</code> traps too. Later upgrades need neither flag. For a canister already using EOP, the flag is accepted but does nothing. The <a href="https://github.com/caffeinelabs/motoko/releases/tag/2.0.0">2.0.0 release notes</a> state that EOP retains persistent 64-bit main memory and uses incremental garbage collection; they also say classical persistence and the other garbage collectors are removed.</p>
<p>On ICP, EOP depends on retaining the WebAssembly main memory during an upgrade. The platform upgrade option for this is <code>wasm_memory_persistence = opt keep</code>. The <a href="https://docs.internetcomputer.org/languages/motoko/fundamentals/actors/orthogonal-persistence/enhanced/">EOP documentation</a> explains that this option is required for EOP and warns that <code>replace</code> drops Wasm main memory. Use a deployment tool that supports the EOP upgrade metadata; do not construct a custom install call that replaces memory.</p>
<h2>Pre-upgrade checklist</h2>
<p>Follow these steps for each canister before releasing the Motoko 2.0.0 build.</p>
<ol>
<li>
<p><strong>Build with your last 1.x compiler and clear its deprecation warnings.</strong> The migration guide specifically recommends building cleanly with the latest 1.16.x first. Fix <code>.vals()</code> warnings by using <code>.values()</code>, and plan to replace <code>preupgrade</code> and <code>postupgrade</code> hooks with a migration function where they are used. This separates existing cleanup from errors introduced by the compiler upgrade.</p>
</li>
<li>
<p><strong>Pin the compiler.</strong> In <code>mops.toml</code>, set the toolchain version so local and CI builds use the same release:</p>
<pre><code class="language-toml">[toolchain]
moc = &quot;2.0.0&quot;
</code></pre>
</li>
<li>
<p><strong>Audit state declarations.</strong> Review every <code>let</code> and <code>var</code> inside actors and actor classes. Mark caches, function values, version-specific values, and other state that should reset with <code>transient</code>. Replace each <code>flexible</code> keyword. Persistent collections can remain ordinary fields if their types support persistence. Check any legacy actor flags carefully because their old default made unmarked fields transient.</p>
</li>
<li>
<p><strong>Remove options and APIs that 2.0 no longer supports.</strong> Remove <code>--default-persistent-actors</code>, <code>--require-persistent-actors</code>, and <code>--legacy-actors</code>. The release also removes <code>--legacy-persistence</code>, <code>--copying-gc</code>, <code>--compacting-gc</code>, <code>--generational-gc</code>, <code>--rts-stack-pages</code>, <code>--skip-gc-deprecation-warning</code>, <code>--incremental-gc</code>, and <code>--experimental-rtti</code>; these are rejected as unknown options. The <code>flexible</code> keyword and <code>ExperimentalStableMemory</code>/<code>Prim.stableMemory*</code> APIs are removed too. Replace <code>ExperimentalStableMemory</code> use with <code>Region</code>, as appropriate for your project.</p>
</li>
<li>
<p><strong>Run the project checks and address errors.</strong> Use the commands in the project’s Motoko toolchain. For the migration-guide workflow:</p>
<pre><code class="language-sh">mops check
mops check-stable
</code></pre>
<p>The migration guide also documents checking signatures directly with moc:</p>
<pre><code class="language-sh">moc --stable-compatible old.most new.most
</code></pre>
<p>Use the signatures from the deployed and proposed versions. The compatibility check catches incompatible stable declarations; it does not replace reviewing the public Candid interface or testing the upgrade path.</p>
</li>
<li>
<p><strong>Build with the correct transition flags.</strong> If the deployed canister still uses classical persistence, compile this upgrade with <code>--enhanced-orthogonal-persistence</code> and omit <code>--enhanced-migration</code>. For a canister already on EOP, the flag is redundant. If you use enhanced migration, it no longer requires the EOP flag, but do not combine that migration mode with the one-time classical-to-EOP transition.</p>
</li>
<li>
<p><strong>Deploy explicitly as an upgrade and verify the canister.</strong> With ICP CLI, build and request upgrade mode using the documented commands:</p>
<pre><code class="language-sh">icp build
icp deploy my-canister --mode upgrade
icp canister status my-canister
</code></pre>
<p>Replace <code>my-canister</code> with the configured canister name. ICP CLI documents that upgrade mode preserves stable state; the Motoko release guide explains the compiler-side migration requirements. Check the canister’s expected state through its own query methods after deployment. Consult the <a href="https://docs.internetcomputer.org/guides/canister-management/lifecycle/">canister lifecycle guide</a> for the CLI upgrade flow.</p>
</li>
</ol>
<h2>Pitfalls and troubleshooting</h2>
<p><strong>The upgrade traps with &quot;Detected implicit upgrade from classical orthogonal persistence&quot;.</strong> Confirm that this is the intended one-time transition. Compile that upgrade with <code>--enhanced-orthogonal-persistence</code>, without <code>--enhanced-migration</code>, and preserve the resulting toolchain configuration for later releases.</p>
<p><strong>A value unexpectedly survives an upgrade.</strong> Find the field declaration and mark it <code>transient</code> if it must reset. Pay special attention to projects that previously used <code>--legacy-actors</code>; the old default does not carry into 2.0.</p>
<p><strong>A value unexpectedly resets.</strong> Check whether the field is explicitly or implicitly transient, and confirm the deployed version and new stable signature. New persistent fields initialize on their first upgrade; existing persistent fields are carried forward only when their stable representation is compatible.</p>
<p><strong>The build rejects a removed option or feature.</strong> Search compiler arguments in project configuration and CI scripts, not only source files. Replace <code>flexible</code> with <code>transient</code>; replace removed stable-memory primitives with the <code>Region</code> API. Keep an older compiler only if you must continue producing classical modules.</p>
<p><strong>A compatibility check passes but the upgrade still needs review.</strong> Stable compatibility is one part of upgrade safety. Review the Candid interface, migration behavior, and post-upgrade application state, and keep a recovery path. The EOP docs recommend testing upgrades and maintaining a way to recover data, such as controller-privileged data queries.</p>
<h2>FAQ</h2>
<h3>Does Motoko 2.0 preserve variables in a bare actor?</h3>
<p>Yes. Actor fields persist across upgrades by default. Add <code>transient</code> to each field that should be initialized again after an upgrade.</p>
<h3>Can I switch a canister back from enhanced to classical persistence?</h3>
<p>No. The classical-to-EOP move happens on the canister’s next upgrade using the Motoko 2.0 transition flag and is irreversible. Later upgrades need no transition flag.</p>
<h3>Which check should I run before deploying?</h3>
<p>The moc v1-to-v2 guide documents <code>mops check-stable</code> and <code>moc --stable-compatible old.most new.most</code>. Run the project’s check, review Candid compatibility, then deploy with ICP CLI’s explicit upgrade mode.</p>]]></content>
  </entry>
  <entry>
    <title>ic-reactor 4 beta.2 replaces generated hooks with client options</title>
    <link href="https://blog.b3pay.net/ic-reactor-4-beta-2-replaces-generated-hooks-with-client-options"/>
    <id>https://blog.b3pay.net/ic-reactor-4-beta-2-replaces-generated-hooks-with-client-options</id>
    <updated>2026-10-07T08:42:20.923Z</updated>
    <published>2026-10-07T08:42:20.923Z</published>
    <summary>ic-reactor 4.0.0-beta.2 moves TypeScript and React canister calls to client-built TanStack Query options and keeps cache entries caller-scoped.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>ic-reactor 4.0.0-beta.2 was published to npm under the <code>beta</code> dist-tag on October 6, 2026, as the second prerelease of ic-reactor 4. React and TypeScript developers calling Internet Computer canisters should review the new client model and try it in a beta branch before planning a migration.</p>
<p>ic-reactor is developed by B3Pay, which also publishes this blog.</p>
<h2>What changed in ic-reactor 4</h2>
<p>ic-reactor 4 is a breaking rewrite of the library for TypeScript and React applications. Instead of generating React hooks, developers generate a TypeScript module from each canister’s <code>.did</code> file with <code>candid-core-cli gen</code>, then bind its actor type to a client. The client builds options for TanStack Query’s own <code>useQuery</code>, <code>useMutation</code> and suspense hooks. The release puts three packages at the same version: <code>@ic-reactor/core</code>, <code>@ic-reactor/react</code> and <code>@ic-reactor/vite-plugin</code>.</p>
<p>An app creates a client with a network and identity or auth provider, then calls <code>client.canister&lt;Actor&gt;(actor, { id })</code> or resolves a canister by name. The returned canister has plain async methods. For React reads and writes, <code>client.queryOptions(canister, method, args)</code> and <code>client.mutationOptions(canister, method)</code> feed the standard TanStack hooks. A Candid result variant with <code>Ok</code> or <code>Err</code> resolves to the <code>Ok</code> value or rejects with the <code>Err</code> value.</p>
<p>The cache key includes the network, caller principal, canister ID, method and arguments. That caller-scoped key prevents one principal’s cached response from being shown to another. Each principal also has an immutable agent. If a read started under one account settles after sign-in, sign-out or an account switch, it rejects as cancelled rather than populating the new caller’s cache.</p>
<p>Write failures carry a <code>ReactorError</code> with a <code>mayHaveExecuted</code> flag. It tells the app whether a failed write may still have changed canister state. The client re-sends an update only when it has proof that the canister never received it, such as reject code 2 or HTTP 429, at most twice within the call. TanStack Query itself does not retry mutations. Reads retry up to three times only for <code>not_delivered</code>, and never on a server. A write is refused before sending if nobody is signed in; a successful write invalidates reads for that canister by default.</p>
<p>Beta.2 adds the <code>canister_not_found</code> error code for calls routed to a missing canister, lets <code>createTestClient().refuseNext()</code> target a method or canister, and improves types so eligible <code>queryOptions</code> can be passed to suspense hooks without a cast. It also pins <code>@candid-core/schema</code> at <code>0.3.0</code> and <code>@candid-core/cli</code> at <code>0.2.0</code>. The release notes say beta.1-generated modules need no regeneration for those pins. See the <a href="https://github.com/B3Pay/ic-reactor/releases/tag/v4.0.0-beta.2">release</a> and <a href="https://github.com/B3Pay/ic-reactor/blob/v4/CHANGELOG.md">v4 changelog</a> for the precise type-level changes.</p>
<h2>Who is affected and why it matters</h2>
<p>This release is for teams starting a new integration or evaluating a migration. TanStack Query owns the hook behavior, while ic-reactor supplies canister-aware options, caller-scoped caching and consistent errors. Apps can use the same read and mutation patterns as other TanStack Query data, while still handling the different risks of canister updates. For example, if a transfer error says it may have executed, an app can re-read the balance before offering another attempt; if the flag is false, the transfer had no effect.</p>
<p>Existing 3.x applications do not upgrade in place. The 4.0 API removes reactors, hook factories and generated hooks, and changes the Candid value types to follow candid-core: principals are branded text, optional values use <code>T | null</code>, variants use <code>{ tag, value }</code>, and blobs are <code>Uint8Array</code>. The <a href="https://ic-reactor.b3pay.net/v4/migrating-from-3/">migration guide</a> lists removed 3.x names and their replacements. The <code>latest</code> npm tag remains on 3.x, currently 3.13.0, until 4.0 GA; no GA date has been announced. Version 3.x receives security fixes only until then, and its documentation remains under <code>/v3/</code>.</p>
<p>Four example apps on the v4 branch cover a mainnet ICP ledger and fault-injecting sandbox, a React wallet on a local network, server rendering with one client per request, and a command-line agent tool. The examples and the <a href="https://ic-reactor.b3pay.net/v4/guides/getting-started/">v4 docs</a> give developers starting points for browser, server and test setups.</p>
<h2>What to do now</h2>
<p>To evaluate beta.2, install the beta packages and the exact stable candid-core pair. These commands apply to ic-reactor 4.0.0-beta.2:</p>
<pre><code class="language-sh">npm install @ic-reactor/core@beta @ic-reactor/react@beta @icp-sdk/core @tanstack/react-query
npm install --save-exact @candid-core/schema@0.3.0
npm install --save-dev --save-exact @candid-core/cli@0.2.0
npm install --save-dev @ic-reactor/vite-plugin@beta
</code></pre>
<p>Then generate each canister’s module from its <code>.did</code> file and port calls to the client. A balance read can use TanStack Query directly:</p>
<pre><code class="language-tsx">import type { Principal } from &quot;@candid-core/schema&quot;
import { formatUnits } from &quot;@ic-reactor/core&quot;
import { useClient } from &quot;@ic-reactor/react&quot;
import { skipToken, useQuery } from &quot;@tanstack/react-query&quot;
import { actor, type Actor } from &quot;./generated/icrc1&quot;

export function Balance({ owner }: { owner: Principal | undefined }) {
  const client = useClient()
  const ledger = client.canister&lt;Actor&gt;(actor, { id: &quot;ryjl3-tyaaa-aaaaa-aaaba-cai&quot; })
  const balance = useQuery(
    client.queryOptions(ledger, &quot;icrc1_balance_of&quot;, owner ? { owner, subaccount: null } : skipToken)
  )
  return &lt;p&gt;{balance.data === undefined ? &quot;…&quot; : formatUnits(balance.data, 8)}&lt;/p&gt;
}
</code></pre>
<p>A transfer uses mutation options from the same client:</p>
<pre><code class="language-tsx">import { useClient } from &quot;@ic-reactor/react&quot;
import { useMutation } from &quot;@tanstack/react-query&quot;
import { actor, type Actor, type TransferArg } from &quot;./generated/icrc1&quot;

export function Send({ arg }: { arg: TransferArg }) {
  const client = useClient()
  const ledger = client.canister&lt;Actor&gt;(actor, { id: &quot;ryjl3-tyaaa-aaaaa-aaaba-cai&quot; })
  const transfer = useMutation(client.mutationOptions(ledger, &quot;icrc1_transfer&quot;))
  return &lt;button disabled={transfer.isPending} onClick={() =&gt; transfer.mutate(arg)}&gt;Send&lt;/button&gt;
}
</code></pre>
<p>When the transfer fails, <code>transfer.error</code> is a <code>ReactorError</code>. If <code>error.mayHaveExecuted</code> is true, the transfer may have gone through, so re-read the balance before offering a retry. If it is false, the transfer certainly had no effect. For a 3.x app, stay on 3.x for now or use the migration table to scope a separate port; a plain install without a beta tag still selects 3.x.</p>]]></content>
  </entry>
  <entry>
    <title>ICfirewall Adds Internet Identity Controls on September 30</title>
    <link href="https://blog.b3pay.net/icfirewall-adds-internet-identity-controls-on-september-30"/>
    <id>https://blog.b3pay.net/icfirewall-adds-internet-identity-controls-on-september-30</id>
    <updated>2026-10-07T04:31:12.817Z</updated>
    <published>2026-10-07T04:31:12.817Z</published>
    <summary>ICfirewall’s September 30 update adds Internet Identity login and PID-based administration to a canister relay for local AI prompts.</summary>
    <category term="AI Agents"/>
    <content type="html"><![CDATA[<p>On September 30, 2026, ICfirewall’s author announced an update adding Internet Identity login and PID-based administration to the local AI relay. Developers experimenting with local models can review the code and run it on a local replica, but should inspect its authentication and prompt filters before sending it real work.</p>
<h2>What changed in the September 30 update</h2>
<p>ICfirewall is a canister-based relay between a client, such as a web interface or Apple Shortcut, and a local model runner. The canister receives prompts, applies filtering rules, and places approved requests in a queue; a local Python process polls the queue, sends the prompt to a model, and returns the response. That design connects a locally hosted inference process to an Internet Computer canister, which stores and processes the relay’s state.</p>
<p>In the September 23 forum post, the author described “3 modes of off,medium and on” and said users could access the relay through Apple Shortcuts or the frontend chat box. The September 30 update added Internet Identity login. The author says users copy their PID into <code>poll_llm.py</code> to become the master PID, add admins, and change limits; “master pid is the only one who can change modes.” The frontend also now displays both prompt and response.</p>
<p>The update was prompted by interest in the repository: the author wrote, “over 300 clones.” That figure is the author’s report, not a repository download count verified by GitHub. The project README describes three modes—Off, Medium, and On—and lists a canister, a local daemon, and a web interface as its main parts. <a href="https://github.com/iTechBod/ICfirewall">The project repository</a> currently lists dfx v0.15.0 or higher, Python 3.10+, and a local LLM runner such as Ollama or LM Studio.</p>
<h2>Who should care, and what the design means</h2>
<p>ICfirewall is aimed at developers who want to keep inference on a local model while routing prompt submission through a canister-managed relay. A central relay can make it easier to inspect and control what enters a model, and its three modes offer different filtering choices. That can be useful for experiments where prompts arrive through multiple clients or where an operator wants to restrict certain inputs.</p>
<p>But the relay does not make local inference safe by itself. Its protection depends on the code that checks prompts, the correctness of identity and permissions, and the way the daemon handles queued work. A commenter pointed out that a regular expression can miss command variants; the author agreed that the rules need improvement and described the project as a sample rather than a bulletproof system. The author also said an AI agent reported that it did not work and that they still needed to test it themselves.</p>
<p>There is a practical configuration mismatch to check: the forum describes the newer Internet Identity and PID flow, while the repository README still documents a bearer-token setup for its Shortcut and daemon. Review the current code and configuration before treating the forum’s update as a complete security change. Prompts and responses also pass through the canister in this architecture, so consider what data you are willing to route through it.</p>
<p>This is relevant to the broader problem of AI coding systems producing unsafe or fabricated outputs. ICfirewall focuses on filtering prompts and model responses, but it should be evaluated as one control in a larger review process.</p>
<h2>How to evaluate it locally</h2>
<p>The repository’s quick start gives these commands for a local deployment. It lists dfx v0.15.0 or higher and Python 3.10+ as prerequisites; use a local model runner as well. The official <a href="https://legacy.internetcomputer.org/docs/building-apps/developer-tools/dfx/dfx-deploy">dfx deploy reference</a> explains that deployment creates, builds, and installs the canisters defined by the project.</p>
<pre><code class="language-sh">git clone https://github.com/iTechBod/ICfirewall.git
cd ICfirewall
dfx start --background
dfx deploy
</code></pre>
<p>Before connecting a model, review the project’s current setup rather than copying old configuration values blindly. The repository README has instructions for setting a canister ID in <code>main.js</code> and starting <code>scripts/poll_llm.py</code> with <code>ODYSSEUS_SESSION_ID</code>; compare those steps with the newer PID flow described in the forum update. Do not deploy to mainnet until you understand which canister methods are public, how administrators are authorized, and what prompt and response data the canister stores.</p>
<p>A useful evaluation checklist is:</p>
<ol>
<li>Run the project against a local replica and a disposable local model.</li>
<li>Inspect the canister’s authorization checks and confirm that a non-admin PID cannot change limits or modes.</li>
<li>Test benign prompts, malformed input, and prompt-injection attempts against each mode; review both the accepted prompt and returned response.</li>
<li>Confirm how the daemon authenticates, polls, and handles errors before connecting it to a model or client you depend on.</li>
</ol>
<p>The README describes the intended workflow, while the forum thread records the author’s update and acknowledged limitations. Treat both as implementation claims to verify in code, not as evidence that the filter blocks attacks reliably.</p>]]></content>
  </entry>
  <entry>
    <title>Motoko 2.0.0: Persistent Actors and Safer Canister Upgrades</title>
    <link href="https://blog.b3pay.net/motoko-2-0-makes-canister-upgrades-persistent-by-default-and-turns-migration-into-a-release-task"/>
    <id>https://blog.b3pay.net/motoko-2-0-makes-canister-upgrades-persistent-by-default-and-turns-migration-into-a-release-task</id>
    <updated>2026-10-06T18:02:55.874Z</updated>
    <published>2026-10-06T18:02:55.874Z</published>
    <summary>Motoko 2.0.0 makes actors persistent by default, removes classical persistence, and turns several warnings into errors. Here is what ICP developers should test before upgrading.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>Motoko 2.0.0 is less a routine compiler update than a new upgrade contract for Internet Computer canisters. The release makes actors persistent by default: fields in a bare actor or actor class survive an upgrade unless they are declared transient. Teams that previously relied on transient-by-default behavior must review actor state and mark reset-on-upgrade fields explicitly.</p>
<p>The release also removes classical 32-bit orthogonal persistence from moc. New builds target enhanced orthogonal persistence with persistent 64-bit main memory and incremental garbage collection. This aligns the compiler with the Internet Computer’s current persistence model, in which compatible program memory can be retained across upgrades instead of being reconstructed through the older serialization path.</p>
<p>Existing classical canisters are not stranded, but their first upgrade into enhanced orthogonal persistence is a one-time migration. Motoko’s release notes require that upgrade to be compiled with --enhanced-orthogonal-persistence and without --enhanced-migration; later upgrades need neither flag. The migration is irreversible. Caveat: this transition should be rehearsed against a copy of production state and backed by a recovery path, because the official documentation recommends thorough upgrade testing and warns against configurations that can drop Wasm main memory.</p>
<p>The compatibility surface is broader than persistence. Motoko 2.0 promotes several diagnostics from warnings to errors, including non-exhaustive patterns, accidental Any or None inference, constant-result comparisons, malformed enhanced-migration files, bare imported declarations, and actor classes that omit an async return type. It also removes ExperimentalStableMemory in favor of Region and treats flexible as an ordinary identifier rather than a persistence modifier.</p>
<p>There are ergonomic changes too: if, while, switch, and for heads can omit parentheses; switch cases accept lighter pattern syntax; and the ?? operator now depends on whitespace. These changes may look cosmetic, but they can expose latent parsing or type-checking assumptions during a migration.</p>
<p>The practical release workflow is therefore compiler-first: pin moc 2.0.0 in a branch, run the v1-to-v2 migration guide, compile every canister, classify new errors, and test upgrades with representative state. Developers should also verify Candid and stable-state compatibility before deployment. Enhanced persistence can reject incompatible state changes and roll back the upgrade, but that safety mechanism does not remove the need to understand which fields are meant to survive.</p>
<p>Motoko 2.0.0’s headline is persistence by default. Its deeper change is that upgrade behavior is now more explicit: state ownership, migration intent, and previously tolerated mistakes must be visible in source and build checks.</p>]]></content>
  </entry>
  <entry>
    <title>Jolt zkVM Bytecode Expansion Gets Lean Formal Verification</title>
    <link href="https://blog.b3pay.net/jolt-s-bytecode-proof-puts-zkvm-correctness-under-a-machine-checked-microscope"/>
    <id>https://blog.b3pay.net/jolt-s-bytecode-proof-puts-zkvm-correctness-under-a-machine-checked-microscope</id>
    <updated>2026-10-06T12:00:47.841Z</updated>
    <published>2026-10-06T12:00:47.841Z</published>
    <summary>LayerZero Research formally verifies Jolt zkVM bytecode expansion in Lean, while documenting the remaining unproven instructions and trust boundaries.</summary>
    <category term="ZK Tech"/>
    <content type="html"><![CDATA[<p>A zero-knowledge proof is only as meaningful as the computation it represents. If a zkVM translates an instruction incorrectly before proving it, a perfectly valid proof may certify the wrong program.</p>
<p>That is the problem LayerZero Research is addressing in Jolt, a RISC-V zkVM used in the Zero proving stack. On September 23, the team announced that Jolt’s bytecode-expansion layer had been formally verified with the Lean theorem prover. The work checks that sequences of Jolt instructions faithfully simulate the RISC-V instructions they replace.</p>
<h2>Why bytecode expansion matters</h2>
<p>Jolt does not implement every RISC-V instruction directly. For instructions outside its native decomposable set, it emits a sequence of Jolt instructions. This translation is called bytecode expansion. The prover then works over the expanded program.</p>
<p>That creates a sharp correctness boundary: if expansion changes the program’s behavior, the resulting proof can be sound for the wrong computation. Random tests can find examples of failure, but they cannot establish equivalence for every input. A formal proof can, provided its definitions, reference model, and assumptions are correct.</p>
<p>LayerZero’s released repository describes a Lean model of the Jolt instruction set and a trusted RISC-V reference model. It reports that 60 of 67 expandable RISC-V instructions have been proven, while seven remain unproven. The project also documents the assumptions behind each theorem and separates what is proved from what remains trusted.</p>
<h2>What developers should take from it</h2>
<p>For teams building verifiable applications, the important change is not simply that “Jolt is formally verified.” It is that a previously implicit part of the proving pipeline now has an inspectable correctness argument.</p>
<p>That suggests a practical review checklist for any zkVM integration:</p>
<ul>
<li>Identify the exact guest-to-VM translation layer.</li>
<li>Ask which instruction mappings are formally proved and which are covered only by tests.</li>
<li>Read the trusted reference model and theorem assumptions.</li>
<li>Confirm whether the proof covers the cryptographic commitment scheme, verifier implementation, compiler, and host orchestration—or only the execution front end.</li>
</ul>
<p>The milestone does not remove those other obligations. LayerZero says verification of the remaining Jolt components is still in progress, and the Jolt documentation continues to label the implementation as alpha and unsuitable for production use. The seven unproven instruction expansions are another concrete boundary that downstream users must understand before relying on them.</p>
<p>For ICP developers evaluating a zkVM-backed canister or cross-chain verification service, this is a useful design signal: treat the instruction-expansion specification as part of the security artifact. A proof-system benchmark can show speed and proof size; a machine-checked front-end proof addresses a different question—whether the circuit is proving the computation the developer intended.</p>]]></content>
  </entry>
  <entry>
    <title>ICP Tests the Next Step Toward P-256 Threshold ECDSA</title>
    <link href="https://blog.b3pay.net/icp-s-p-256-milestone-starts-with-a-test-key-not-production-chain-fusion"/>
    <id>https://blog.b3pay.net/icp-s-p-256-milestone-starts-with-a-test-key-not-production-chain-fusion</id>
    <updated>2026-10-06T00:04:15.847Z</updated>
    <published>2026-10-06T00:04:15.847Z</published>
    <summary>Proposal 144212 begins testing for a P-256 threshold ECDSA master key on ICP, opening a path toward web-native identity and certificate use cases without implying production availability.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>ICP’s long-discussed move toward secp256r1, also known as P-256, has reached a concrete testing milestone. An October 5 update in the Internet Computer Developer Forum says Proposal 144212 is live to generate a P-256 ECDSA master key for testing on subnet 2fq7c.</p>
<p>That matters because the gap is not simply about adding another elliptic curve. ICP’s current Chain Fusion signing stack is centered on threshold ECDSA over secp256k1, the curve used by Bitcoin and Ethereum. P-256 would target a different compatibility frontier: web authentication, enterprise identity, certificates, and protocols that commonly expect ES256 signatures.</p>
<p>The project’s earlier technical explanation is cautious. DFINITY said the underlying cryptographic protocol already supports P-256, while integration remains necessary across higher layers including consensus, execution, and governance. It also said that governance proposals would be required to generate and back up threshold keys, with no precise production timeline at that point.</p>
<p>Proposal 144212 therefore looks best understood as an infrastructure checkpoint. A test master key can validate key generation, subnet operation, signing behavior, and the operational path for future key backup. It does not by itself make P-256 available to arbitrary canisters or establish a production signing key.</p>
<p>That distinction is visible in the current documentation. The IC interface specification recognizes P-256 for ordinary ECDSA signatures and WebAuthn-related verification, but the Chain-key cryptography guide lists deployed threshold ECDSA keys only for secp256k1. In other words, ICP already speaks P-256 in parts of its interface and identity stack; the open Chain Fusion question is whether canisters can obtain threshold-generated P-256 signatures under the network’s distributed key-management model.</p>
<p>For developers building decentralized certificate authorities, OAuth-related flows, DPoP, JWT integrations, or other web-facing authentication systems, the test-key proposal is an encouraging signal—but not yet an API contract. The next meaningful evidence will be a successful test deployment followed by documented management-canister support, key backup, governance activation, and a clearly identified production key.</p>
<p>The October 5 forum update links the work to Proposal 144212. A separate NNS proposal, 144213, concerns an NNS canister upgrade and is adjacent governance activity rather than evidence that P-256 signing is already generally available.</p>
<p>The key takeaway is simple: P-256 has moved from roadmap discussion toward controlled network testing. Chain Fusion gains a possible bridge to mainstream web cryptography only after the test path becomes a supported, governed production service.</p>]]></content>
  </entry>
  <entry>
    <title>Rust Generic Const Arguments: What GCA Changes</title>
    <link href="https://blog.b3pay.net/rust-s-generic-const-arguments-redraw-the-boundary-between-types-and-values"/>
    <id>https://blog.b3pay.net/rust-s-generic-const-arguments-redraw-the-boundary-between-types-and-values</id>
    <updated>2026-10-05T18:03:22.875Z</updated>
    <published>2026-10-05T18:03:22.875Z</published>
    <summary>Rust’s new Generic Const Arguments design targets richer const generics with explicit gca! syntax, new equality rules, and an experimental nightly roadmap.</summary>
    <category term="Rust"/>
    <content type="html"><![CDATA[<p>Rust’s const generics are gaining a broader experimental design. In an Inside Rust post published on October 2, the Const Generics Project Group described Generic Const Arguments (GCA), a family of features intended to replace the long-running <code>generic_const_exprs</code> experiment.</p>
<p>The practical problem is familiar: stable Rust can use a const parameter such as <code>N</code> or a fully concrete expression, but it cannot generally express type-level values such as <code>[u8; N + 1]</code> or <code>[u8; T::NUM_BYTES]</code>. That gap limits fixed-size abstractions and has pushed some libraries toward type-level-number crates.</p>
<p>GCA approaches the problem by making the supported expression category explicit. With the experimental <code>gca_adts</code> feature, arrays, tuples, structs, and enum values involving generic parameters can be passed through <code>gca!(...)</code>. A simplified example is a const argument such as <code>gca!([N, 12])</code>, where <code>N</code> is itself generic. The same design also introduces const-item paths for more indirect cases, including associated constants and generic const items.</p>
<p>That explicitness is a safety and compatibility decision, not merely a syntax preference. The project group says <code>gca!(...)</code> arguments have different rules from ordinary const arguments: they can refer to generic parameters, but they currently support fewer expression forms. Arithmetic and function calls, for example, are not directly accepted inside the macro. Other experimental features can route such expressions through const items.</p>
<p>The hardest issue is equality. Once a value participates in a type, the compiler must decide when two const expressions describe the same type. The current tracking issue describes GCA as relying on definitional equality unless const evaluation can establish a result. It also lists unresolved questions around the boundary between opaque equality and richer future reasoning, such as recognizing mathematical equivalences.</p>
<p>This matters for systems code. A library that encodes packet widths, protocol layouts, or serialized representations in types could eventually express more of its invariants without duplicating arithmetic in runtime code. But public APIs would need careful review: changing whether a const item is transparent or opaque can affect what the compiler can prove about type equality.</p>
<p>The near-term message for Rust developers is therefore “experiment, don’t depend.” GCA is still experimental nightly functionality, and the tracking issue remains open with implementation, documentation, style, and stabilization work listed ahead. The project group also acknowledges that the visible <code>gca!(...)</code> syntax creates ergonomic problems and that the macroless alternatives are not yet recommended.</p>
<p>For developers maintaining generic fixed-size APIs, the useful test is small: try the nightly feature gates in an isolated branch, inspect type-equality failures, and keep a stable-compatible fallback. GCA may become a cleaner foundation for const-heavy abstractions, but today its most important contribution is a clearer design boundary for deciding which compile-time values Rust’s type system is allowed to understand.</p>]]></content>
  </entry>
  <entry>
    <title>ckSOL Makes Solana Sweep Metadata the Gate for Minting</title>
    <link href="https://blog.b3pay.net/cksol-s-new-deposit-pipeline-treats-solana-sweep-metadata-as-the-minting-gate"/>
    <id>https://blog.b3pay.net/cksol-s-new-deposit-pipeline-treats-solana-sweep-metadata-as-the-minting-gate</id>
    <updated>2026-10-05T00:00:32.873Z</updated>
    <published>2026-10-05T00:00:32.873Z</published>
    <summary>Recent ckSOL development uses finalized Solana sweep metadata, rent checks, and quarantine paths before minting tokens on ICP.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>The most consequential recent change in ICP’s Solana integration is not a new signature algorithm. It is a stricter decision about when a deposit is allowed to become ckSOL.</p>
<p>Recent changes in the dfinity/cksol repository move deposits toward a sweep-based lifecycle. A user calls <code>deposit_sol</code>, the minter queues the deposit, and a timer sweeps funds from deposit addresses into the minter’s main Solana address. The sweep can batch as many as 10 deposits, while the source addresses retain the Solana rent-exemption balance.</p>
<p>The important boundary arrives after the transaction is finalized. In merged PR #222, the minter fetches the finalized transaction, compares the executed message with the plan it recorded, checks the deposit-address balance changes, confirms that the source accounts remain rent-exempt, and verifies that the main address received the expected amount. Only then does it record the credited sweep and queue the corresponding mints.</p>
<p>That design makes transaction metadata part of the accounting path. The minter does not treat a submitted signature as proof that the intended transfer happened. It uses the finalized transaction’s execution data to reconcile what actually arrived before minting an ICP-side token backed by SOL.</p>
<p>The change also addresses a subtle Solana failure mode. A deposit address cannot simply be drained below the minimum balance needed to remain on-chain. The ckSOL design therefore leaves the rent-exemption amount behind and uses the sweep fee model to calculate what can safely move. Solana’s documentation describes this minimum balance as a refundable storage requirement tied to account size.</p>
<p>The repository’s error handling is equally notable. Open PR #218 proposes dropping deposits from failed or expired sweeps so they can be queued again, while quarantining finalized sweeps whose metadata contradicts the minter’s model. In that case, nothing is minted and the deposit requires manual intervention. This is a deliberate preference for a visible operational exception over silent accounting.</p>
<p>There is also a key security correction in merged PR #227: the minter’s main Solana address is separated from user-derived deposit addresses. The change prevents the treasury address from being mistaken for a user deposit account and avoids a self-sweep that could double-count funds.</p>
<p>For Chain Fusion developers, the lesson is practical. Cross-chain minting should be treated as a reconciliation problem, not merely a signing problem. The remote chain’s finalized state, fee behavior, rent rules, and address-derivation scheme all belong inside the canister’s accounting contract.</p>
<p>At verification time, these are repository developments rather than a production launch: PR #218 remains open, PR #221 remains a draft, and the repository states that the production minter and ledger are not yet deployed. The direction is nevertheless clear: ckSOL is making the Solana transaction itself the evidence required before ICP credits value.</p>]]></content>
  </entry>
  <entry>
    <title>PHASMA Cards Brings Ledger-Style Data to Graded Pokémon Markets</title>
    <link href="https://blog.b3pay.net/phasma-cards-puts-graded-pokemon-data-on-a-ledger-like-map"/>
    <id>https://blog.b3pay.net/phasma-cards-puts-graded-pokemon-data-on-a-ledger-like-map</id>
    <updated>2026-10-04T18:00:53.879Z</updated>
    <published>2026-10-04T18:00:53.879Z</published>
    <summary>PHASMA Cards launches an early public graded Pokémon market platform built around card-by-grade data, liquidity, populations, indices, and sales context.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>PHASMA Cards is presenting itself as a market terminal for graded Pokémon cards rather than another feed of individual price screenshots. In a September 30 post on the Internet Computer Developer Forum, the project said it tracks graded-card data and updates it daily, with views for fair value, liquidity, populations, indices, and sales context across tracked sets.</p>
<p>The product’s distinctive organizing idea is the “card × grade atom.” PHASMA says these units feed a ledger-style roll-up, allowing values, populations, and indices to be evaluated together instead of being anchored to one noisy listing. That framing is aimed at collectors comparing distinctions such as 1st Edition versus Unlimited or PSA 8 versus PSA 9.</p>
<p>The launch announcement says the early version includes dozens of sets, millions of graded copies rolled up, tracked market capitalization, and daily movers spanning vintage and modern slabs. Those figures are project-provided snapshots, not independently audited measurements. The announcement also identifies examples such as Charizard, Suicune 1st Edition, Blaine’s Arcanine, Espeon Holo, Houndoom, and Misty’s Gyarados.</p>
<p>The timing reflects a broader move toward structured Pokémon market analytics. For comparison, TCGCharts publishes market figures based on sold listings and describes a methodology that combines population data with grade-level median prices. That does not validate PHASMA’s numbers, but it illustrates the same underlying shift: collectors increasingly want a repeatable valuation model rather than a single comparable sale.</p>
<p>PHASMA says the current release is an early public version and invites feedback on coverage, user experience, and which sets and grades should be added. The project also said its earlier surgical-robotics direction was paused because it lacked sufficient interest or funding, and that it later shifted toward Pokémon valuation data. The forum post describes the strategic change and the product’s on-chain potential, but it does not establish a specific blockchain architecture or a completed partnership.</p>
<p>The PHASMA website was not accessible to the verification tool used for this article. Readers should therefore treat the platform’s coverage, market-cap figures, and methodology as claims to inspect directly before using the service for buying or selling decisions.</p>
<p>Sources: <a href="https://forum.dfinity.org/t/introducing-phasma-cards/75892">Introducing PHASMA Cards</a>; <a href="https://tcgcharts.com/press">TCGCharts Press &amp; Data</a>.</p>]]></content>
  </entry>
  <entry>
    <title>Rust 1.99 Updates Box::leak Guidance for Safer Memory Ownership</title>
    <link href="https://blog.b3pay.net/rust-1-99-turns-box-leak-into-an-ownership-review-trigger"/>
    <id>https://blog.b3pay.net/rust-1-99-turns-box-leak-into-an-ownership-review-trigger</id>
    <updated>2026-10-04T12:02:01.846Z</updated>
    <published>2026-10-04T12:02:01.846Z</published>
    <summary>Rust 1.99 warns against reclaiming Box::leak allocations through raw-pointer round trips and points developers toward explicit ownership-preserving APIs.</summary>
    <category term="Rust"/>
    <content type="html"><![CDATA[<p>Rust 1.99.0, released on October 1, adds an important ownership signal without changing the language: the standard-library documentation now recommends against taking memory returned by <code>Box::leak</code> and later deallocating it.</p>
<p><code>Box::leak</code> converts a <code>Box&lt;T&gt;</code> into a reference whose lifetime can be extended, making it useful for deliberately permanent data such as process-wide configuration or registries. The risk appears when a program treats that reference as a temporary escape hatch, later reconstructs ownership through a raw pointer, and attempts to free the allocation.</p>
<p>The Rust release team says this round-trip pattern can interact badly with current and future compiler optimizations, with the concern becoming more significant as custom allocators approach stabilization. Rust 1.99 therefore recommends <code>Box::into_raw</code> or the newly stabilized <code>Box::into_non_null</code> when the allocation must eventually be reclaimed. Those APIs preserve an explicit ownership transition instead of presenting the allocation as leaked for the lifetime of a reference.</p>
<p>For library authors, the practical review question is no longer simply “does this compile?” It is “is this allocation intentionally permanent?” Search unsafe code and FFI adapters for <code>Box::leak</code>, raw-pointer reconstruction, and manual deallocation. If reclamation is part of the design, use an ownership-preserving conversion and document the allocator and deallocation contract.</p>
<p>A caveat matters: Rust 1.99 changes the documentation recommendation, not the language semantics. Intentional leaks remain a valid design choice; the warning is aimed at patterns that leak first and reclaim later.</p>]]></content>
  </entry>
  <entry>
    <title>zkAPI Separates API Billing From Identity With ZK Proofs</title>
    <link href="https://blog.b3pay.net/zkapi-makes-api-billing-a-zero-knowledge-session-primitive"/>
    <id>https://blog.b3pay.net/zkapi-makes-api-billing-a-zero-knowledge-session-primitive</id>
    <updated>2026-10-04T06:00:00.859Z</updated>
    <published>2026-10-04T06:00:00.859Z</published>
    <summary>The Ethereum Foundation’s experimental zkAPI uses zero-knowledge proofs to authorize bounded API sessions without linking payment identity to usage. Here is what ICP developers can learn from its architecture and privacy limits.</summary>
    <category term="ZK Tech"/>
    <content type="html"><![CDATA[<p>The Ethereum Foundation and the Open Anonymity Project have introduced zkAPI, an experimental protocol for paying for metered APIs without exposing the payment identity behind each request. The system is live on Ethereum mainnet and is presented first for AI inference, but its design also targets RPC calls, image generation, bandwidth, and other usage-priced services.</p>
<p>The key idea is to turn a prepaid balance into a private note. A user deposits funds into a vault, then produces a zero-knowledge proof showing that a valid note can cover a bounded session and has not already been spent. The server verifies the proof without learning which deposit or person funded it. A nullifier prevents double spending, while a Merkle tree lets the proof establish membership without identifying a particular note.</p>
<p>The implementation separates the payment path from the request path. A local client proves authorization to the zkAPI server, which issues a short-lived, spending-limited API key. In the runtime-key mode, the user’s device sends prompts directly to the provider. When the key expires, signed usage information is used to settle the actual cost rather than the reserved cap. The repository documents a stack built around Groth16 on BN254, Poseidon hashing, note-bound commitments, and a 32-level Merkle tree.</p>
<p>That separation is the more interesting developer primitive than the AI demo. An ICP application that calls a metered external service could, in principle, treat “permission to spend up to this amount” as a proof-bearing session capability rather than attaching every request to a durable account. The application would still need an appropriate verifier, settlement service, and key-management design; zkAPI does not provide an ICP integration, and this is an architectural inference rather than a released ICP feature.</p>
<p>There is an important privacy boundary. zkAPI breaks the link between payment and usage, but it does not make the request itself anonymous. The provider can still see prompts and responses, the user’s network metadata may remain visible, and timing or repeated content can correlate sessions. The Ethereum Foundation explicitly describes network anonymity and content privacy as separate layers.</p>
<p>For ICP developers, the practical takeaway is to model three independent questions: who is authorized to spend, what service receives the payload, and who can reconcile the bill. Zero-knowledge proofs can separate the first and third from the second, but only if the session key, usage receipt, replay protection, and recovery path are treated as protocol state. That boundary is portable; the current zkAPI deployment is not yet an ICP component.</p>]]></content>
  </entry>
  <entry>
    <title>What ICP’s October 2026 All-in-One Release Means for Chain Fusion</title>
    <link href="https://blog.b3pay.net/the-release-artifact-is-the-chain-fusion-story"/>
    <id>https://blog.b3pay.net/the-release-artifact-is-the-chain-fusion-story</id>
    <updated>2026-10-04T00:04:00.817Z</updated>
    <published>2026-10-04T00:04:00.817Z</published>
    <summary>The October 2 IC all-in-one release is an operational checkpoint for Chain Fusion builders: pin the version, verify checksums, and separate release tags from network deployment.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>DFINITY published <code>release-2026-10-02_03-31--all-in-one</code> on October 2 at commit <code>1b66454</code>. The GitHub release exposes a packaged set of tools and artifacts, including <code>ic-admin</code>, PocketIC builds, <code>sns</code>, a canister bundle, and a SHA-256 checksums file.</p>
<p>That packaging matters for Chain Fusion because cross-chain integrations depend on more than canister code. Developers and operators also need a known version of the surrounding IC tooling and test environment. A single, named release gives teams a reproducible reference for integration tests, deployment scripts, and incident reports.</p>
<p>The safety lesson is to verify the artifact before treating it as infrastructure. The release page lists checksums, while ICP’s deployment guidance instructs operators to compare downloaded artifacts against their expected hashes and to stop when they do not match. The same documentation separates IC OS version election from deployment: a release tag alone does not mean that a version has been elected or deployed across the network.</p>
<p>The release page does not include a component-by-component changelog. Accordingly, this report does not claim that the tag introduces a new Chain Fusion protocol feature. Its defensible significance is operational: it gives builders a fresh, addressable package against which Chain Fusion tooling and test pipelines can be pinned, verified, and later reproduced.</p>
<p>For teams shipping cross-chain functionality, the practical checklist is straightforward: record the tag and commit, verify downloaded checksums, run integration tests against the pinned tooling, and confirm any network deployment status separately through the relevant NNS and dashboard records.</p>]]></content>
  </entry>
  <entry>
    <title>SNS Upgrade Aggregation Makes Release Tracking Governance Infrastructure</title>
    <link href="https://blog.b3pay.net/sns-upgrade-threads-turn-release-tracking-into-governance-infrastructure"/>
    <id>https://blog.b3pay.net/sns-upgrade-threads-turn-release-tracking-into-governance-infrastructure</id>
    <updated>2026-10-03T18:04:26.831Z</updated>
    <published>2026-10-03T18:04:26.831Z</published>
    <summary>How ICP’s SNS Upgrade Aggregation Thread helps DAO communities track canister Wasm releases, inspect hashes, and coordinate upgrade proposals safely.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>DFINITY’s SNS Upgrade Aggregation Thread illustrates a quiet but important part of operating a Service Nervous System: upgrade awareness is itself governance infrastructure.</p>
<p>The thread was opened to collect SNS upgrade announcements in one subscribable forum topic. Its early notices identified the SNS Governance, SNS Root, and SNS Swap canisters as upgradeable. Later posts recorded proposed SNS-WASM publications with source Git hashes and Wasm hashes, giving DAO participants a concrete artifact to inspect before deciding whether their SNS should move forward.</p>
<p>That publishing pattern matters because an SNS upgrade is not simply a software download. The SNS framework is designed so that decentralized applications can be governed by an on-chain DAO, and the SNS codebase exposes upgrade-related concepts such as running versions, upgrade parameters, and the canisters selected for an upgrade. In practice, a release announcement becomes an input to a proposal and voting process.</p>
<p>DFINITY’s SNS testing repository demonstrates the same operational sequence: a dapp can be placed under SNS control, and its canister can then be upgraded through an SNS proposal. The repository also shows why voting remains part of the deployment path: a proposal can pass immediately in a single-neuron test setup, while a more distributed configuration requires additional votes.</p>
<p>The strongest lesson from the thread is therefore about observability. A DAO needs to know which Wasm artifact is available, which canister type it targets, and how that candidate relates to the version already running. A single aggregation point reduces the chance that upgrade information is scattered across unrelated announcements, while hashes let operators compare artifacts without relying on a human-readable version label alone.</p>
<p>There is an important limit to what the thread proves. It began in 2023 and functions as a historical aggregation record, not as a guarantee that every SNS is current or that every listed release remains the latest. The supplied material is also truncated, so it should not be treated as a complete upgrade ledger. Operators should independently check the current SNS-WASM upgrade path, the running SNS version, the relevant proposal, and the artifact hash before submitting or voting on an upgrade.</p>
<p>For ICP developers, the practical takeaway is straightforward: subscribe to the aggregation thread, treat each announcement as a release candidate rather than an automatic deployment, and connect the published hash to an explicit SNS governance decision. The value of the thread is not that it removes governance from upgrades; it makes the software evidence needed for governance easier to find.</p>]]></content>
  </entry>
  <entry>
    <title>Rust 1.100 Makes i686 Windows Cross-Compilation Only</title>
    <link href="https://blog.b3pay.net/rust-turns-32-bit-windows-into-a-cross-compilation-only-target"/>
    <id>https://blog.b3pay.net/rust-turns-32-bit-windows-into-a-cross-compilation-only-target</id>
    <updated>2026-10-03T12:00:33.674Z</updated>
    <published>2026-10-03T12:00:33.674Z</published>
    <summary>Rust 1.100 will keep i686 Windows standard libraries but remove host tools, requiring supported hosts such as 64-bit Windows for native builds.</summary>
    <category term="Rust"/>
    <content type="html"><![CDATA[<p>Rust is changing the role of its 32-bit Windows targets. Starting with Rust 1.100.0, <code>i686-pc-windows-msvc</code> will remain a Tier 1 target but lose its host tools, while <code>i686-pc-windows-gnu</code> will remain Tier 2 without host tools. The standard libraries will still be distributed, but compilers and other tools needed to run a Rust toolchain natively on 32-bit Windows will no longer be available.</p>
<p>For developers, the practical change is a build-topology decision: 32-bit Windows binaries must be produced from a supported host toolchain, such as 64-bit Windows. The Rust RFC for the MSVC target describes this as retaining <code>std</code> support while ending compiler-host support. The Rust project’s platform documentation also describes the Windows targets as capable of cross-compilation when the relevant C and linker toolchains are installed.</p>
<p>The decision reflects both platform reality and maintenance cost. The Rust announcement says general 32-bit Windows support ended in October 2025, while the project has also encountered compiler crashes in i686 MSVC builds and out-of-memory failures while building LLVM with the GNU C++ toolchain. The result is a narrower promise: Rust will preserve the ability to target 32-bit Windows, but will no longer treat 32-bit Windows as a viable environment for building the Rust toolchain itself.</p>
<p>That distinction matters for CI and release engineering. A pipeline that currently installs an i686 Windows host toolchain and runs <code>rustc</code> there will need to move compilation to a 64-bit or otherwise supported host. A pipeline that only needs the target standard library can continue using the distributed <code>i686</code> target artifacts, subject to its native C toolchain and linker requirements. The MSVC target will also continue to receive CI testing, although it will no longer be tested as a compiler host.</p>
<p>This is a target-support change, not a removal of 32-bit Windows output. Other 32-bit platforms are not affected by the announcement. The project also does not specify the exact rustup error or migration steps for existing 32-bit Windows installations, so teams should validate their installation and release process before adopting Rust 1.100.0.</p>]]></content>
  </entry>
  <entry>
    <title>zkTLS Proof of Protocol Execution: What ICP Developers Should Know</title>
    <link href="https://blog.b3pay.net/zktls-gets-a-stronger-claim-prove-the-web-interaction-not-just-the-answer"/>
    <id>https://blog.b3pay.net/zktls-gets-a-stronger-claim-prove-the-web-interaction-not-just-the-answer</id>
    <updated>2026-10-03T06:02:49.835Z</updated>
    <published>2026-10-03T06:02:49.835Z</published>
    <summary>A new zkTLS model distinguishes proving a real web interaction from proving only an input-response pair. Here is what that means for ICP integrations.</summary>
    <category term="ZK Tech"/>
    <content type="html"><![CDATA[<p>The newest useful shift in zkTLS is semantic, not merely faster proving. A September 2026 Cryptology ePrint paper introduces “zero-knowledge proof of protocol execution,” or zkPoPE: a verifier should learn that a user actually interacted with a counterparty and received a response satisfying a predicate, without seeing the private input or full response.</p>
<p>That addresses a subtle weakness in simpler designs. A proof that an input and response satisfy a relation does not, by itself, prove that the response came from the real website or from a live execution. zkPoPE moves the security target up from the TLS transport layer to the application protocol. The authors formalize the target as a UC ideal functionality and present Reclaim as a protocol intended to realize it under a deployment-aligned trust model.</p>
<p>The practical signal is meaningful for ICP developers building canisters or front ends that consume authenticated web facts. A useful integration should define the exact predicate—such as account status, eligibility, or a value range—bind it to the intended web service and request context, and preserve enough proof material for independent verification. Reclaim’s documentation already exposes the implementation boundary: public and private request options are separated, proofs can be verified with an SDK, and proof data can be transformed for on-chain use.</p>
<p>The reported benchmark is also concrete: the paper says Reclaim attested to a 1,200 kB response in 7.28 seconds on a lower-mid-range phone. That is not a claim that every ICP deployment will achieve the same result; it is evidence that large-response attestation is being measured as a systems problem rather than treated as a toy demo.</p>
<p>For ICP, the recommended design principle is simple: make the canister verify a narrowly scoped claim and its context, not accept a generic “this data is valid” certificate. Keep the source, request, freshness, subject binding, and verifier policy explicit. If a proof system can demonstrate only a satisfying pair, it has not yet demonstrated provenance.</p>
<p>Caveat: the central zkTLS paper is a September 2026 preprint, not peer-reviewed evidence of production security. Caveat: the Reclaim design depends on a deployment-aligned trust model and application credentials, so integrating it into an ICP canister does not eliminate operational trust assumptions.</p>]]></content>
  </entry>
  <entry>
    <title>ICP Registry Upgrade Adds secp256r1 and Safer Subnet Transitions</title>
    <link href="https://blog.b3pay.net/icp-s-registry-upgrade-turns-chain-fusion-readiness-into-an-upgrade-ordering-problem"/>
    <id>https://blog.b3pay.net/icp-s-registry-upgrade-turns-chain-fusion-readiness-into-an-upgrade-ordering-problem</id>
    <updated>2026-10-03T00:00:39.846Z</updated>
    <published>2026-10-03T00:00:39.846Z</published>
    <summary>Proposal 144199 targets Registry Canister commit 98c898f, adding secp256r1 configuration, subnet merging, split safeguards, CUP invariants, and a 13 MiB migration limit.</summary>
    <category term="Chain Fusion"/>
    <content type="html"><![CDATA[<p>The Internet Computer’s latest Registry Canister proposal is less a single feature release than a coordination upgrade for the network’s control plane.</p>
<p>Proposal 144199 proposes moving the Registry Canister to commit 98c898f. The commit combines changes that matter directly to Chain Fusion—most notably a new <code>secp256r1</code> option for ECDSA chain-key configuration—with safeguards around subnet splitting, replica-version deployment, and Registry-state migration.</p>
<p>The cryptographic addition is deliberately incomplete on its own. The proposal allows <code>create_subnet</code> and <code>update_subnet</code> to name the NIST P-256 curve, but a subnet using that configuration still needs the key to be generated and enabled through separate proposals. For developers building cross-chain applications, that distinction matters: configuration support is not the same thing as an operationally available signing key.</p>
<p>The upgrade also adds a <code>merge_subnets</code> endpoint, exposed through a <code>MergeSubnets</code> proposal. Its scope is intentionally narrow: it rewrites the routing table so canister ranges move from one subnet to another, while leaving both subnet records unchanged and retaining the source subnet. That makes the operation a routing transition rather than a full subnet teardown.</p>
<p>Several changes are aimed at preventing control-plane races. A subnet split now fails if the relevant standard-engine replica-version record changes while fresh key material is being generated. A split from a cloud-engine subnet is also rejected while a new replica version is deploying when that subnet derives its version from the same record. Together, these checks reduce the risk that a topology change produces two subnets with inconsistent runtime versions.</p>
<p>The commit additionally tightens Registry invariants. Elected GuestOS and HostOS version identifiers must be parseable by the version types that consume them. Catch-up-package records must carry a CUP type, while a one-time migration classifies legacy records as Genesis or Recovery. Genesis CUPs no longer need a height field because their height is understood to be zero.</p>
<p>The most operationally revealing change is the increase of <code>MAX_CHUNKABLE_ATOMIC_MUTATION_LEN</code> from 10 MiB to 13 MiB. The accompanying commit explains that Registry migrations accumulated during a longer-than-usual upgrade cadence, producing an approximately 12 MB mainnet mutation during <code>post_upgrade</code>. This is a capacity adjustment for upgrade machinery, not a user-facing throughput increase—and it also exposes a maintenance lesson: deferred migrations can turn routine upgrades into atomic-state sizing problems.</p>
<p>The proposal was published on October 2, 2026. The related IC base release was published the same day, but the proposal itself remains the authoritative place to check the governance action and its execution state. The proposal page and source material describe the intended upgrade; this article does not independently establish that the upgrade has executed on mainnet. Likewise, adding <code>secp256r1</code> support does not generate or enable a chain key: separate proposals are required for that step.</p>
<p>For Chain Fusion developers, the practical takeaway is to treat Registry capabilities as staged infrastructure. First verify that the relevant curve and subnet configuration are available; then verify that key generation, enablement, routing, and replica-version state have progressed consistently. The commit makes those transitions more explicit—and gives the Registry a larger buffer for the migrations needed to manage them.</p>]]></content>
  </entry>
  <entry>
    <title>Canic and OpenChat: Why Upgrade Safety Comes Before Fleet Automation</title>
    <link href="https://blog.b3pay.net/canic-s-openchat-test-is-really-about-upgrade-safety"/>
    <id>https://blog.b3pay.net/canic-s-openchat-test-is-really-about-upgrade-safety</id>
    <updated>2026-10-02T18:04:00.857Z</updated>
    <published>2026-10-02T18:04:00.857Z</published>
    <summary>A source review of Canic and OpenChat finds that state-preserving upgrades, governance, memory ownership, and measured overhead matter more than canister provisioning alone.</summary>
    <category term="Internet Computer"/>
    <content type="html"><![CDATA[<p>A new Internet Computer Developer Forum review frames OpenChat as a demanding qualification target for Canic—not as a production migration candidate today.</p>
<p>The central issue is the release boundary. OpenChat preserves application state through rolling upgrades, while Canic’s current pre-1.0 policy uses reinstall-only release transitions. That makes a direct replacement of OpenChat’s existing fleet irresponsible, even if Canic can reproduce parts of its provisioning workflow.</p>
<p>That distinction matters because OpenChat already operates substantial infrastructure. Its indexes manage canister creation, spare capacity, cycle funding, controller checks, upgrade queues, retries, and subnet-local work. Its newer shared-user model also separates logical users from physical canisters. A generic installation loop would not automatically improve those systems.</p>
<p>The review identifies a narrower opportunity: Canic could eventually provide reusable infrastructure for allocation, cycle accounting, reconciliation, recovery evidence, and release provenance. OpenChat’s existing Wasm-hash governance process could remain the application’s source of authority, while Canic supplies passive artifact and deployment evidence. The forum post explicitly says that this would require preserving OpenChat’s existing guarantees, not replacing them with untested abstractions.</p>
<p>The technical integration surface is wider than provisioning. OpenChat uses application-owned memory managers, multiple wire formats, large payloads, reconstructed timers, and application-specific readiness logic. Canic’s runtime and observability features therefore need executable compatibility tests covering memory ownership, lifecycle ordering, codecs, argument limits, timers, uncertain replies, interrupted provisioning, and recovery.</p>
<p>The review proposes starting with a disposable OpenChat-shaped workload rather than production migration. That fixture would model global and subnet-local indexes, dedicated and shared user roles, group and storage roles, registration bursts, pool exhaustion, funding pressure, and lost replies. Only after those tests would scale, overhead, governance, and eventual state-preserving release support become adoption questions.</p>
<p>Two caveats are important. This is a source-level review, not a live-fleet census, security audit, or measured performance comparison. Canic is also pre-1.0, while OpenChat is changing its architecture, so the conclusions are provisional and do not constitute an adoption announcement.</p>
<p>The useful takeaway for ICP developers is a sharper ownership split: OpenChat should retain membership, routing, product semantics, and SNS-controlled release authority; Canic may eventually own physical allocation, installation, reserves, and infrastructure evidence. The hard part is proving that boundary under failure and upgrade—not drawing a larger canister topology.</p>]]></content>
  </entry>
</feed>
