<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: U W A I S E</title>
    <description>The latest articles on DEV Community by U W A I S E (@notg0d).</description>
    <link>https://dev.to/notg0d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4236948%2Ffd52510b-c18e-4c17-8f4f-33b3e1d57335.jpg</url>
      <title>DEV Community: U W A I S E</title>
      <link>https://dev.to/notg0d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/notg0d"/>
    <language>en</language>
    <item>
      <title>SubSense: A Subscription Manager That Remembers Why You Kept It</title>
      <dc:creator>U W A I S E</dc:creator>
      <pubDate>Tue, 10 Nov 2026 12:04:32 +0000</pubDate>
      <link>https://dev.to/notg0d/subsense-a-subscription-manager-that-remembers-why-you-kept-it-5doe</link>
      <guid>https://dev.to/notg0d/subsense-a-subscription-manager-that-remembers-why-you-kept-it-5doe</guid>
      <description>&lt;p&gt;A design tool used twice a month can look like wasted money.&lt;/p&gt;

&lt;p&gt;Unless those two sessions are when you deliver work to a client.&lt;/p&gt;

&lt;p&gt;That is the gap I wanted to explore with &lt;strong&gt;SubSense&lt;/strong&gt;: subscription trackers can tell you what you pay, but a useful renewal decision also needs to remember &lt;strong&gt;why you kept paying&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'm building SubSense around a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember the reason behind a decision, then reconsider it when the facts change.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project is being prepared for the &lt;a href="https://tatum.io/hackathon/total-recall" rel="noopener noreferrer"&gt;Tatum × Walrus Total Recall hackathon&lt;/a&gt;, with &lt;strong&gt;Consumer Finance Apps&lt;/strong&gt; as its intended track. This post covers the working local experience, the integration design, and the live checks still outstanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with clarity, not another setup screen
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9wsr2o161l87etjpfrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9wsr2o161l87etjpfrr.png" alt="Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Actual application UI with synthetic demonstration records-not live billing accounts or Mainnet evidence.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first useful screen is a dashboard, not a request for provider API keys.&lt;/p&gt;

&lt;p&gt;After signed wallet login, users can add subscriptions, review recurring costs, set a monthly budget, and see what is renewing. Online memory and AI are optional.&lt;/p&gt;

&lt;p&gt;Some details matter more than they initially seem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An annual plan is spread across 12 months when comparing commitments, but its upcoming bill shows the &lt;strong&gt;full charge&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A trial includes its future price, so “free today” does not hide next month's commitment.&lt;/li&gt;
&lt;li&gt;A cheaper-plan or cancellation decision creates a follow-up. It does &lt;strong&gt;not&lt;/strong&gt; pretend the provider has changed your account.&lt;/li&gt;
&lt;li&gt;Budget scenarios are previews until the user explicitly records a change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SubSense does not connect to a bank, cancel subscriptions, or send payments. It helps users decide; it does not act as if the decision has already happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  What “learning” means here
&lt;/h2&gt;

&lt;p&gt;I wanted the core learning loop to be inspectable, rather than asking a model to invent a financial recommendation.&lt;/p&gt;

&lt;p&gt;In SubSense, learning means &lt;strong&gt;structured decision feedback&lt;/strong&gt;. It is not model training or fine-tuning.&lt;/p&gt;

&lt;p&gt;Here is the example used in the browser review:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Information&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before feedback&lt;/td&gt;
&lt;td&gt;A $15/month design tool is rarely used and marked optional&lt;/td&gt;
&lt;td&gt;Try a cheaper plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User decision&lt;/td&gt;
&lt;td&gt;Keep + Essential, with “Required for monthly client handoffs. Low usage does not mean low value.”&lt;/td&gt;
&lt;td&gt;Save the decision and its reason&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next recommendation&lt;/td&gt;
&lt;td&gt;The relevant saved decision is available&lt;/td&gt;
&lt;td&gt;Keep, with the remembered context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New price&lt;/td&gt;
&lt;td&gt;The same plan rises from $15 to $17-more than 10%&lt;/td&gt;
&lt;td&gt;Request a fresh review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The feedback does not silently rewrite the service's importance or billing status. It changes the recommendation, not the underlying facts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkf1yhrnuhtdye76qkjd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkf1yhrnuhtdye76qkjd2.png" alt="renewals"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Synthetic records in the real Renewals screen. The recommendation change was verified locally; this screenshot does not demonstrate remote recall.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The recommendation engine uses the latest applicable decision within a 180-day relevance window. It ignores unrelated and future-dated records, and a price increase above 10% can reopen the question. Services explicitly marked essential remain protected.&lt;/p&gt;

&lt;p&gt;That last point is important: memory should inform a decision, not freeze it forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small stack with clear boundaries
&lt;/h2&gt;

&lt;p&gt;The application uses &lt;strong&gt;React, TypeScript, Vite, and Express&lt;/strong&gt;, with one server process and one port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React + TypeScript
  ├─ Wallet-scoped workspace → browser storage
  ├─ Provider vault → browser-side encryption
  └─ Same-origin API requests
       Express → verified wallet session
         ├─ Tatum → read-only Sui data
         ├─ Walrus Memory → explicit decision save / recall
         └─ OpenRouter → optional, consented AI assistance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subscription calculations and recommendations remain deterministic. USD amounts use integer cents; native-SUI amounts use integer MIST. A USD budget is not mixed with a SUI balance as though they were interchangeable.&lt;/p&gt;

&lt;p&gt;The preview environment has no durable runtime database. Browser-local state makes the tracking experience work within that constraint, but it also creates an obligation: &lt;strong&gt;recovery must be explicit and understandable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A wallet is identity-not an automatic vault unlock
&lt;/h2&gt;

&lt;p&gt;Connecting a wallet is only the first step. The backend verifies a one-time personal-message signature before creating a session. Signing in costs no gas and does not authorize spending.&lt;/p&gt;

&lt;p&gt;The workspace and provider credentials have different storage boundaries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;th&gt;Where it lives&lt;/th&gt;
&lt;th&gt;Important limitation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Subscriptions and local decisions&lt;/td&gt;
&lt;td&gt;Wallet-scoped browser storage&lt;/td&gt;
&lt;td&gt;Unencrypted; not synchronized across devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saved provider credentials&lt;/td&gt;
&lt;td&gt;Encrypted browser vault&lt;/td&gt;
&lt;td&gt;Requires the original wallet identity and separate password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unlocked provider credentials&lt;/td&gt;
&lt;td&gt;Backend's temporary in-memory session&lt;/td&gt;
&lt;td&gt;Available to the app server while unlocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selected online decisions&lt;/td&gt;
&lt;td&gt;Walrus Memory integration&lt;/td&gt;
&lt;td&gt;Explicit upload; not a complete workspace backup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The vault uses browser Web Crypto: &lt;strong&gt;PBKDF2-SHA-256 with 600,000 iterations&lt;/strong&gt;, a fresh salt, and &lt;strong&gt;AES-256-GCM&lt;/strong&gt; with a fresh IV. Authenticated additional data binds the ciphertext to the normalized wallet identity and Mainnet.&lt;/p&gt;

&lt;p&gt;The password stays in the browser. Unlocking sends the decrypted provider keys to the app's backend; it is not a claim that the backend can never access them.&lt;/p&gt;

&lt;p&gt;Signing out or locking removes backend credentials without deleting the saved workspace or ciphertext. Reconnecting the same wallet restores its local context, but the provider vault still needs its password.&lt;/p&gt;

&lt;p&gt;Two exports are therefore necessary when moving devices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Workspace backup:&lt;/strong&gt; subscriptions, decisions, and the original profile ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted-vault backup:&lt;/strong&gt; provider credentials, still protected by its password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wallet scoping is not a defence against someone or a malicious script with access to the browser. There is no vault-password recovery, and clearing storage without backups can lose local data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Walrus integration stores decisions
&lt;/h2&gt;

&lt;p&gt;The useful memory is not just “this service costs $15.”&lt;/p&gt;

&lt;p&gt;It is the combination of the service, price, decision, value assessment, timestamp, and reason. That gives future recommendations context they can actually use.&lt;/p&gt;

&lt;p&gt;SubSense's Walrus Memory integration stores validated decision records in a profile-specific namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subsense-&amp;lt;workspace-profile-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means preserving the profile ID matters. Creating a new workspace is not the same as reopening the previous profile's memories.&lt;/p&gt;

&lt;p&gt;The implemented upload path uses a profile-and-decision idempotency key. A simplified excerpt of the SDK call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`${namespace}:${decision.id}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dedupe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uploads require confirmation. An accepted job is shown as &lt;strong&gt;pending&lt;/strong&gt;, not as a successful Mainnet memory. The application records completed storage only when the job reports &lt;code&gt;done&lt;/code&gt; and returns a blob ID for the matching namespace.&lt;/p&gt;

&lt;p&gt;Recall is also explicit. Returned text is validated as decision data—not treated as instructions-and valid records merge by decision ID rather than duplicating history. Recall is bounded to 50 relevant results, so it is not an account-wide export or full recovery system.&lt;/p&gt;

&lt;p&gt;There is also a privacy boundary worth stating plainly: &lt;strong&gt;the relayer processes decision plaintext before encrypted storage&lt;/strong&gt;. “Encrypted storage” does not mean nobody involved in the upload can see the content. Private client details and secrets do not belong in public demonstration memories.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tatum contributes
&lt;/h2&gt;

&lt;p&gt;Tatum's role is the read-only Sui data layer, separate from the subscription recommendation engine.&lt;/p&gt;

&lt;p&gt;The Mainnet adapter is implemented for native-SUI balance reads and verification of existing transactions through Tatum's Sui GraphQL endpoint.&lt;/p&gt;

&lt;p&gt;A transaction check is deliberately strict: the digest must match, execution must succeed, balance-change data must be complete, and the recipient's net increase must satisfy the requested amount. The check also validates the UTC payment date and an optional sender.&lt;/p&gt;

&lt;p&gt;That is &lt;strong&gt;transfer evidence&lt;/strong&gt;, not proof that a subscription vendor recognized a payment. It does not execute a transfer, and it does not make SubSense an autonomous payment agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI helps explain-it does not take control
&lt;/h2&gt;

&lt;p&gt;Optional OpenRouter assistance can answer questions, explain selected subscriptions, plan renewals, or search selected context semantically.&lt;/p&gt;

&lt;p&gt;It does not replace the deterministic recommendation rules or edit subscriptions on its own.&lt;/p&gt;

&lt;p&gt;Each request requires explicit sharing consent. The app sends the question and selected, validated context rather than the entire workspace. Wallet identifiers, credentials, payment evidence, and remote receipts are excluded from that context.&lt;/p&gt;

&lt;p&gt;Routing is limited to allowlisted free models with zero price caps and no paid fallback. Strict privacy routing is the default. Some free-model endpoints may not satisfy it; the app does not silently weaken the policy to make a request succeed.&lt;/p&gt;

&lt;p&gt;Free availability, quota, and provider policies can change. Those are real limitations, not details to hide behind an “AI-powered” label.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wallet-switch bug that made recovery better
&lt;/h2&gt;

&lt;p&gt;One of the most useful review cases was switching wallets &lt;strong&gt;A → B → A&lt;/strong&gt; while requests were in flight.&lt;/p&gt;

&lt;p&gt;Comparing only the current wallet address is not enough. An old request from A can finish after the user returns to A, making stale data look current.&lt;/p&gt;

&lt;p&gt;The API client now captures a wallet generation alongside the expected address and rejects the result if either changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Capture these before sending the request.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;walletAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;walletGeneration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Check again before applying its response.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="n"&gt;walletAddress&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;generation&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="n"&gt;walletGeneration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Wallet changed during the request. No result was applied.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wallet-scoped workspace keys were also aligned with the same address normalization used by login and the vault. Short, padded, and mixed-case representations should identify the same workspace-not accidentally create different ones.&lt;/p&gt;

&lt;p&gt;In the browser fixture, wallet B showed neither A's subscriptions nor its vault prompt. Returning to A restored its original profile and reason with the vault still locked. A wrong password preserved the ciphertext; the correct password unlocked it without rewriting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is verified-and what is not yet
&lt;/h2&gt;

&lt;p&gt;The latest local verification passed &lt;strong&gt;90 automated tests&lt;/strong&gt;, TypeScript checking, a production build, and an isolated production HTTP smoke test. All eight screens were checked at 390, 768, and 1440 CSS pixels in both themes: &lt;strong&gt;48 geometry checks with no document horizontal overflow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those tests use generated unfunded wallets, local servers, and mocked provider responses. They verify application behaviour, &lt;strong&gt;not real Mainnet integration success&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the time of writing, the live evidence still outstanding includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signed login using the intended real wallet extension.&lt;/li&gt;
&lt;li&gt;An authenticated Tatum Mainnet read.&lt;/li&gt;
&lt;li&gt;A real completed Walrus write and recall after reconnecting.&lt;/li&gt;
&lt;li&gt;The hackathon's required 10+ completed Mainnet memories.&lt;/li&gt;
&lt;li&gt;Real-key AI inference and the hosted demo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two Vite/esbuild dependency audit findings also remain-one moderate and one high. Public demos should serve built assets rather than expose the development server, but that is not a substitute for fixing the dependencies or conducting a security review.&lt;/p&gt;

&lt;p&gt;I want the evidence boundary to be visible: a passing mock test and a genuine completed Mainnet memory are different things.&lt;/p&gt;

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

&lt;p&gt;The biggest lesson was that a memory feature is only useful when it changes a visible product decision.&lt;/p&gt;

&lt;p&gt;“We stored some text” is not the same as “the next renewal recommendation understood why this tool mattered.”&lt;/p&gt;

&lt;p&gt;The second lesson was that remembering and recovering are different problems. Local workspace storage, an encrypted credential vault, and remote decision memory each need their own lifecycle and recovery story.&lt;/p&gt;

&lt;p&gt;SubSense began from a friend's SpendGuard scaffold. That foundation deserves attribution; the work described here focuses on SubSense's decision-feedback experience, wallet-specific recovery, provider boundaries, and verification. The visual direction was inspired by &lt;a href="https://www.behance.net/gallery/256834293/Neobank-web-mobile-app-UXUI-design-Emerald24" rel="noopener noreferrer"&gt;Goodface's Emerald24 case study&lt;/a&gt;, without including its artwork or branding.&lt;/p&gt;

&lt;p&gt;The goal is not a finance app that makes more decisions for you. It is one that helps your next decision begin with the context you already worked out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you want a subscription manager to remember: usage, budget trade-offs, work dependencies-or something else?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/notg0d/SubSense" rel="noopener noreferrer"&gt;SubSense source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tatum.io/hackathon/total-recall" rel="noopener noreferrer"&gt;Total Recall — Tatum × Walrus Memory Hackathon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.tatum.io/" rel="noopener noreferrer"&gt;Tatum documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.wal.app/walrus-memory" rel="noopener noreferrer"&gt;Walrus Memory documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>web3</category>
      <category>ai</category>
      <category>react</category>
      <category>api</category>
    </item>
    <item>
      <title>Building Keeva: Client-Side Encryption and Persistent Vault State with Tatum and Walrus Memory</title>
      <dc:creator>U W A I S E</dc:creator>
      <pubDate>Tue, 10 Nov 2026 11:46:07 +0000</pubDate>
      <link>https://dev.to/notg0d/building-keeva-client-side-encryption-and-persistent-vault-state-with-tatum-and-walrus-memory-2i2f</link>
      <guid>https://dev.to/notg0d/building-keeva-client-side-encryption-and-persistent-vault-state-with-tatum-and-walrus-memory-2i2f</guid>
      <description>&lt;p&gt;&lt;em&gt;A technical walkthrough of a credential vault built for the Total Recall - Tatum × Walrus Memory Hackathon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Remembering a user is not the same as securely restoring their private data.&lt;/p&gt;

&lt;p&gt;For a credential vault, persistence raises several difficult questions: Where does encryption happen? What authorizes access? How does the application identify the correct vault after a wallet reconnects? What happens when a password changes, two devices edit the same record, or a storage request succeeds but its response never arrives?&lt;/p&gt;

&lt;p&gt;I built Keeva to explore these questions during the &lt;strong&gt;Total Recall - Tatum × Walrus Memory Hackathon&lt;/strong&gt;, held from 20 October to 10 November 2026.&lt;/p&gt;

&lt;p&gt;Keeva stores passwords, API keys, and private notes as client-encrypted records. It combines a Sui Mainnet wallet for authorization, Walrus Memory for persistent storage, and Tatum for blockchain reads and transaction checks.&lt;/p&gt;

&lt;p&gt;The central architectural choice is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage receives ciphertext. The browser reconstructs the usable vault after explicit authorization.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This article explains that implementation, how persistent memory changes the application’s state over time, and the boundaries that remain.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Separating identity, encryption, storage, and blockchain reads
&lt;/h2&gt;

&lt;p&gt;Keeva does not treat “connected wallet” as a single permission that unlocks everything.&lt;/p&gt;

&lt;p&gt;Instead, the application separates several responsibilities:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sui wallet&lt;/td&gt;
&lt;td&gt;Supplies the connected account and personal-message signatures for authorization and supported key-derivation flows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser cryptography&lt;/td&gt;
&lt;td&gt;Derives vault keys, encrypts records, authenticates ciphertext, and decrypts authorized data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Walrus Memory SDK&lt;/td&gt;
&lt;td&gt;Authenticates delegated storage requests and persists or retrieves encrypted envelopes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tatum integration&lt;/td&gt;
&lt;td&gt;Checks Sui Mainnet identity, reads network telemetry, and supplies transaction/checkpoint evidence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vault projection&lt;/td&gt;
&lt;td&gt;Reconstructs current credentials from validated immutable revisions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local browser storage&lt;/td&gt;
&lt;td&gt;Retains encrypted gateway profiles, ciphertext caches, pending encrypted operations, and continuity checkpoints.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This separation matters because identifying a wallet does not automatically provide the correct storage configuration or authorize decryption.&lt;/p&gt;

&lt;p&gt;A user can connect the correct wallet while selecting the wrong memory account or namespace. Conversely, a valid storage delegate does not mean the application should expose credential plaintext without the vault’s unlock material.&lt;/p&gt;

&lt;p&gt;Keeva checks these concerns separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Encrypting before data reaches Walrus Memory
&lt;/h2&gt;

&lt;p&gt;Keeva uses the browser’s &lt;strong&gt;Web Crypto API&lt;/strong&gt; for its application-level encryption.&lt;/p&gt;

&lt;p&gt;The current implementation uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AES-256-GCM&lt;/strong&gt; for authenticated encryption.&lt;/li&gt;
&lt;li&gt;A fresh, randomly generated &lt;strong&gt;96-bit IV&lt;/strong&gt; for each encryption operation.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;128-bit authentication tag&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PBKDF2-SHA256&lt;/strong&gt; with a minimum of &lt;strong&gt;600,000 iterations&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A random &lt;strong&gt;32-byte salt&lt;/strong&gt; for key derivation.&lt;/li&gt;
&lt;li&gt;Non-extractable Web Crypto keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AES-GCM authenticates the encrypted content and its associated context. A damaged ciphertext, incorrect key, or mismatched context causes decryption to fail rather than producing data that the application accepts.&lt;/p&gt;

&lt;p&gt;The core operation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'AES-GCM'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;additionalData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tagLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;utf8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; is important. Keeva binds encrypted records to identifiers such as the memory account, namespace, vault, credential, and revision.&lt;/p&gt;

&lt;p&gt;A ciphertext should not become a valid record merely because someone places it in another storage location.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protecting the whole credential
&lt;/h3&gt;

&lt;p&gt;Encrypting only a password would still expose useful information about the user: service names, usernames, notes, categories, and rotation dates.&lt;/p&gt;

&lt;p&gt;Keeva therefore encrypts the secret field and then encrypts the complete credential record—including its metadata, revision relationship, and audit event—in an outer envelope.&lt;/p&gt;

&lt;p&gt;This layering is not a claim that “encrypting twice” independently doubles security. Its purpose is to give the secret and complete record explicit authenticated contexts. Security still depends on correct cryptographic use, key protection, and trusted application delivery.&lt;/p&gt;

&lt;p&gt;Walrus Memory receives the serialized encrypted envelope, not the plaintext credential body.&lt;/p&gt;

&lt;p&gt;The SDK’s own SEAL and storage processing is an additional layer, separate from Keeva’s browser-side encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Two distinct unlock boundaries
&lt;/h2&gt;

&lt;p&gt;One practical challenge was allowing a returning user to recover their configuration without confusing that operation with unlocking their passwords.&lt;/p&gt;

&lt;p&gt;Keeva separates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Unlocking the saved gateway configuration.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlocking the encrypted vault.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The gateway configuration includes the Tatum endpoint and API key, Walrus Memory account and delegate settings, and original namespace.&lt;/p&gt;

&lt;p&gt;Restoring these settings makes the application capable of contacting the correct services. It does not itself expose the vault’s credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wallet-protected gateway profiles
&lt;/h3&gt;

&lt;p&gt;Each wallet can have its own encrypted configuration stored on the current device.&lt;/p&gt;

&lt;p&gt;For supported wallets, Keeva derives the configuration-encryption key from a verified deterministic &lt;strong&gt;Ed25519 personal-message signature&lt;/strong&gt;, using PBKDF2.&lt;/p&gt;

&lt;p&gt;The derivation message includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application origin.&lt;/li&gt;
&lt;li&gt;Sui Mainnet identity.&lt;/li&gt;
&lt;li&gt;The wallet address.&lt;/li&gt;
&lt;li&gt;The profile format and KDF parameters.&lt;/li&gt;
&lt;li&gt;A random per-save salt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The signature and derived encryption key are not persisted with the encrypted profile.&lt;/p&gt;

&lt;p&gt;The signature is sensitive: it participates in deriving the key that protects saved API and delegate credentials. It should never be published or included in logs, screenshots, or demonstration recordings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vault unlock methods
&lt;/h3&gt;

&lt;p&gt;The vault itself supports two modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passphrase-derived encryption&lt;/strong&gt;, accompanied by a fresh wallet authorization signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallet-derived encryption&lt;/strong&gt;, using a vault-specific deterministic Ed25519 personal-message signature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The vault header records the selected method. Reopening the vault requires that original method; Keeva does not silently switch between them.&lt;/p&gt;

&lt;p&gt;Wallets without the required deterministic signature scheme can use passphrase protection instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Recovering the correct vault after a wallet reconnects
&lt;/h2&gt;

&lt;p&gt;The recovery problem is not simply “download everything again.” The application first needs to recover the correct relationship between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wallet → memory account → namespace → vault header → encrypted revisions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keeva’s returning-user flow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect the owning Sui Mainnet wallet.&lt;/li&gt;
&lt;li&gt;Explicitly unlock that wallet’s saved gateway profile.&lt;/li&gt;
&lt;li&gt;Restore its original account, namespace, and gateway credentials.&lt;/li&gt;
&lt;li&gt;Inspect existing storage.&lt;/li&gt;
&lt;li&gt;Authorize the vault unlock.&lt;/li&gt;
&lt;li&gt;Authenticate and reconstruct the encrypted inventory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once valid configuration and a Mainnet wallet are available, inspection runs automatically. This inspection is read-only: it does not initialize a vault, create memory records, or prompt for a wallet signature.&lt;/p&gt;

&lt;p&gt;The header is checked against the configured account, namespace, and connected wallet. Conflicting headers or mismatched identities stop the operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switching between wallets
&lt;/h3&gt;

&lt;p&gt;Consider a browser that has saved profiles for wallets A and B.&lt;/p&gt;

&lt;p&gt;After A’s vault has been unlocked, switching to B must not leave A’s credentials visible while B’s configuration loads.&lt;/p&gt;

&lt;p&gt;Keeva invalidates the previous session and clears active gateway secrets when the wallet changes. B must restore its own configuration and unlock its own vault. Switching back to A restores A’s profile rather than reusing B’s most recently selected namespace.&lt;/p&gt;

&lt;p&gt;This behavior was exercised in a controlled browser review: A’s inventory returned, B exposed only its own inventory, and switching back restored A without additional mock writes.&lt;/p&gt;

&lt;h3&gt;
  
  
  A wallet is not a complete backup
&lt;/h3&gt;

&lt;p&gt;Saved gateway profiles are specific to a browser profile and application origin.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;localhost&lt;/code&gt;, &lt;code&gt;127.0.0.1&lt;/code&gt;, different ports, and a hosted domain have separate browser storage.&lt;/p&gt;

&lt;p&gt;On a fresh device, the user still needs the original gateway configuration and vault unlock material. Keeva does not claim that a public wallet address alone can discover and decrypt every previously created vault.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Using Walrus Memory without plaintext semantic search
&lt;/h2&gt;

&lt;p&gt;Walrus Memory supports memory retrieval, but a credential vault has a different requirement from a conversational assistant.&lt;/p&gt;

&lt;p&gt;An assistant may retrieve a few semantically relevant memories. A vault must discover the records required to reconstruct its complete available history. Omitting a revision can change which password appears current.&lt;/p&gt;

&lt;p&gt;Keeva therefore does not use a capped similarity result as its inventory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paginated inventory discovery
&lt;/h3&gt;

&lt;p&gt;The storage service first calls &lt;code&gt;listMemories()&lt;/code&gt; and follows pagination.&lt;/p&gt;

&lt;p&gt;It checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inventory resynchronization requirements.&lt;/li&gt;
&lt;li&gt;Valid blob identifiers.&lt;/li&gt;
&lt;li&gt;Advancing cursors.&lt;/li&gt;
&lt;li&gt;Records within the configured root or revision namespaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An index inconsistency fails synchronization rather than being presented as an empty vault.&lt;/p&gt;

&lt;h3&gt;
  
  
  Immutable revision namespaces
&lt;/h3&gt;

&lt;p&gt;The root namespace contains the vault header. Each immutable record uses a distinct namespace beneath it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;root&amp;gt;.r.&amp;lt;record-uuid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeva retrieves an indexed record within its namespace using a constant discovery phrase, then matches the result to the exact expected blob ID.&lt;/p&gt;

&lt;p&gt;User-entered search terms are not sent to Walrus Memory. Credential metadata is decrypted and searched locally in the browser.&lt;/p&gt;

&lt;p&gt;This deliberately trades server-side semantic understanding for keeping credential content private.&lt;/p&gt;

&lt;p&gt;It does not hide all metadata. Account identifiers, namespaces, ciphertext sizes, timing, and access patterns remain observable. Ciphertext and the constant discovery phrase may still pass through the Memory service’s processing pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What Tatum contributes
&lt;/h2&gt;

&lt;p&gt;Tatum’s role is blockchain access and evidence-not credential encryption.&lt;/p&gt;

&lt;p&gt;Keeva supports configured Sui Mainnet GraphQL and JSON-RPC interfaces. The endpoint and interface must match what the provider actually supports.&lt;/p&gt;

&lt;p&gt;In GraphQL mode, network telemetry includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="nc"&gt;KeevaNetwork&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;chainIdentifier&lt;/span&gt;
  &lt;span class="n"&gt;checkpoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sequenceNumber&lt;/span&gt;
    &lt;span class="n"&gt;networkTotalTransactions&lt;/span&gt;
    &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;epochId&lt;/span&gt;
      &lt;span class="n"&gt;referenceGasPrice&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeva validates the returned chain identity against Sui Mainnet’s &lt;code&gt;35834a8a&lt;/code&gt; identifier. A wallet advertising Mainnet support is not sufficient if the configured blockchain endpoint points elsewhere.&lt;/p&gt;

&lt;p&gt;Returned fields are schema-validated. GraphQL errors, partial results, incompatible data, and non-advancing pagination are not accepted as successful readings.&lt;/p&gt;

&lt;h3&gt;
  
  
  A read-only backend boundary
&lt;/h3&gt;

&lt;p&gt;Browser requests reach Tatum through Keeva’s same-origin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/tatum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Node backend forwards the selected endpoint, API interface, key, and permitted blockchain request.&lt;/p&gt;

&lt;p&gt;The backend restricts supported operations, bounds request and response sizes, rejects redirects, and applies an upstream deadline. It also handles limited rate-limit retries.&lt;/p&gt;

&lt;p&gt;The application does not send master keys or plaintext credentials through this route. However, the backend necessarily receives the Tatum API key for forwarding, so it remains part of the gateway trust boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction verification and its limits
&lt;/h3&gt;

&lt;p&gt;Keeva can inspect an existing transaction by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieving its transaction bytes and signatures.&lt;/li&gt;
&lt;li&gt;Recomputing its digest locally.&lt;/li&gt;
&lt;li&gt;Verifying sender and sponsor signatures using the Sui SDK.&lt;/li&gt;
&lt;li&gt;Checking the expected wallet relationship.&lt;/li&gt;
&lt;li&gt;Requesting membership in the reported checkpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These checks have different strengths.&lt;/p&gt;

&lt;p&gt;Digest and signature verification are local cryptographic checks. Checkpoint membership, execution status, and event contents remain &lt;strong&gt;provider-attested&lt;/strong&gt;. Keeva does not independently verify a validator-certificate proof.&lt;/p&gt;

&lt;p&gt;Accordingly, the application records a checked transaction as &lt;code&gt;transaction.checked&lt;/code&gt;; it does not turn that result into a claim that an unspecified sharing or recovery contract executed successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Reconstructing state from immutable history
&lt;/h2&gt;

&lt;p&gt;Password rotation creates a new revision instead of overwriting the previous stored record.&lt;/p&gt;

&lt;p&gt;Each credential revision carries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A stable credential ID.&lt;/li&gt;
&lt;li&gt;A unique revision ID.&lt;/li&gt;
&lt;li&gt;Its parent revision ID.&lt;/li&gt;
&lt;li&gt;Creation and update timestamps.&lt;/li&gt;
&lt;li&gt;An authenticated audit event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After decrypting records, Keeva validates revision ancestry.&lt;/p&gt;

&lt;p&gt;Missing ancestors and cycles are rejected. If two devices create different descendants of the same revision, the application exposes the concurrent heads as a conflict.&lt;/p&gt;

&lt;p&gt;It does not silently decide that the record with the newest timestamp must be correct.&lt;/p&gt;

&lt;p&gt;The current implementation detects such conflicts and blocks unsafe overwrites. Automated conflict reconciliation is not yet implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling an uncertain write
&lt;/h3&gt;

&lt;p&gt;A storage request can succeed even when its response is lost.&lt;/p&gt;

&lt;p&gt;Retrying with freshly generated ciphertext and a new identifier could create an unintended extra revision or incur another paid operation.&lt;/p&gt;

&lt;p&gt;Before dispatching a write, Keeva retains the encrypted request and its idempotency identifier locally. An explicit retry reuses that original operation.&lt;/p&gt;

&lt;p&gt;This is a client-side idempotent retry strategy, not an unconditional guarantee of exactly-once storage under every network or service failure.&lt;/p&gt;

&lt;p&gt;Reconnecting or unlocking does not automatically replay paid writes.&lt;/p&gt;

&lt;p&gt;Browser Web Locks also serialize writes across same-origin tabs. This reduces local races but does not create a global lock across devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. How memory changes Keeva over time
&lt;/h2&gt;

&lt;p&gt;The hackathon asks how a product learns over time. For Keeva, the accurate answer is &lt;strong&gt;persistent, evolving security state-not machine learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Keeva does not train on passwords or claim to be an autonomous security agent.&lt;/p&gt;

&lt;p&gt;Instead, stored history changes the state reconstructed in later sessions.&lt;/p&gt;

&lt;p&gt;An illustrative workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user stores a credential.&lt;/li&gt;
&lt;li&gt;A local audit identifies password reuse or a rotation concern.&lt;/li&gt;
&lt;li&gt;The user deliberately replaces the password.&lt;/li&gt;
&lt;li&gt;Keeva stores a new encrypted revision and its audit event.&lt;/li&gt;
&lt;li&gt;The user locks the vault and returns later.&lt;/li&gt;
&lt;li&gt;Keeva reconstructs the updated credential and recomputes the audit from current state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The application remembers the change rather than reverting to the original record. Earlier revisions remain part of the authenticated history, subject to storage availability and retention.&lt;/p&gt;

&lt;p&gt;The Mainnet account associated with my wallet already contains &lt;strong&gt;more than 10 memories&lt;/strong&gt;. Those records are separate from synthetic fixtures used in automated and controlled browser testing.&lt;/p&gt;

&lt;p&gt;A memory-record count should also not be confused with a count of unique current credentials: headers, revisions, and audit records can contribute to stored history.&lt;/p&gt;

&lt;p&gt;The useful demonstration is therefore not only that records exist. It is that existing history changes what the user sees after returning.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Keeping security analysis local
&lt;/h2&gt;

&lt;p&gt;Keeva’s ordinary password checks run in the browser. They include password resistance estimates, reuse detection, and rotation analysis.&lt;/p&gt;

&lt;p&gt;Password generation uses Web Crypto randomness with rejection sampling rather than predictable pseudorandom values.&lt;/p&gt;

&lt;p&gt;The application also offers an explicit, optional Pwned Passwords check.&lt;/p&gt;

&lt;p&gt;For that lookup, Keeva hashes the password locally and sends only the first five hexadecimal characters of its SHA-1 hash. Matching against the remaining hash suffix happens locally. SHA-1 is used for this lookup protocol, not for vault encryption or key derivation.&lt;/p&gt;

&lt;p&gt;The provider still sees the hash prefix, IP address, and request timing. This is not a zero-disclosure operation.&lt;/p&gt;

&lt;p&gt;A failed lookup leaves the result unknown. A password not found in the dataset is not automatically safe, and a match does not prove that the particular account was breached.&lt;/p&gt;

&lt;p&gt;These checks support a user’s decision to rotate credentials; they do not make that decision autonomously.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Session safety includes asynchronous work
&lt;/h2&gt;

&lt;p&gt;Clearing a screen is not enough to lock an application if an earlier asynchronous operation can later restore its decrypted state.&lt;/p&gt;

&lt;p&gt;Keeva tracks session generations around wallet changes, configuration changes, locking, and pending operations. Results from an invalidated generation cannot reopen the workspace.&lt;/p&gt;

&lt;p&gt;The service also checks whether its session remains active after important asynchronous boundaries.&lt;/p&gt;

&lt;p&gt;One regression specifically covers locking while write-health preflight is pending: when the response arrives, the closed session must not start the storage write.&lt;/p&gt;

&lt;p&gt;This cannot revoke a request that the network has already accepted. It prevents stale work from proceeding where the application still controls dispatch.&lt;/p&gt;

&lt;p&gt;The vault locks on inactivity, page hiding, page exit, wallet changes, and configuration changes.&lt;/p&gt;

&lt;p&gt;Web Crypto keys are non-extractable, and temporary byte buffers are overwritten where practical. Nevertheless, JavaScript strings and browser memory cannot be reliably zeroized. Client-side encryption does not protect against a compromised browser, wallet extension, or application delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Testing, deployment, and remaining boundaries
&lt;/h2&gt;

&lt;p&gt;The release review passed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;180 automated tests&lt;/strong&gt; across 12 files.&lt;/li&gt;
&lt;li&gt;TypeScript checking.&lt;/li&gt;
&lt;li&gt;Web application and browser-companion production builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three live Tatum integration checks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A dependency audit reporting no known vulnerabilities at review time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recovery tests use real Web Crypto and Sui signatures with controlled transport responses. They cover wallet separation, recovery without the ciphertext cache, rotated credentials, incomplete history, interrupted sessions, and uncertain-write retries.&lt;/p&gt;

&lt;p&gt;The controlled browser test verified A → B → A recovery without additional mock writes.&lt;/p&gt;

&lt;p&gt;These results do not constitute an independent security audit. Automated Walrus integration checks in that review were skipped because the runner lacked the required account/delegate identifiers. They must not be represented as independent verification of the existing Mainnet account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;Keeva requires more than static hosting. Its Node server supplies the web application and the required Tatum and Walrus gateway routes.&lt;/p&gt;

&lt;p&gt;Shared deployments should use HTTPS and must not compile operator secrets into browser assets. Vite’s &lt;code&gt;VITE_*&lt;/code&gt; variables are public build inputs; API and delegate credentials should be entered through the protected runtime settings instead.&lt;/p&gt;

&lt;p&gt;The browser companion does not maintain another plaintext vault. It provides a local generator and opens the wallet-compatible web workspace, which retains the same authorization boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recovery limitations
&lt;/h3&gt;

&lt;p&gt;Index restoration depends on what the relayer can discover. A response reporting no truncation is not proof that every historical record was found.&lt;/p&gt;

&lt;p&gt;Device continuity checkpoints can detect some previously observed records disappearing, but a fresh device has no independent checkpoint. Stronger completeness guarantees would require an independently authenticated manifest or consensus-backed vault head.&lt;/p&gt;

&lt;p&gt;Encrypted export is implemented. Automated backup import is not.&lt;/p&gt;

&lt;p&gt;Rotation also does not erase earlier ciphertext, and Keeva makes no perpetual-retention or guaranteed-deletion promise.&lt;/p&gt;

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

&lt;p&gt;Building Keeva for the &lt;strong&gt;Total Recall - Tatum × Walrus Memory Hackathon&lt;/strong&gt; highlighted that persistence is only one part of a reliable private workflow.&lt;/p&gt;

&lt;p&gt;The harder work is restoring the correct configuration, authenticating records, preserving revision relationships, rejecting stale asynchronous results, and distinguishing cryptographic verification from provider claims.&lt;/p&gt;

&lt;p&gt;Walrus Memory supplies persistent encrypted records. Tatum supplies Sui Mainnet reads and transaction evidence. Keeva’s browser-side implementation connects those services into a user-authorized credential workflow.&lt;/p&gt;

&lt;p&gt;The result is a hackathon-stage implementation-not an audited production password manager or an autonomous AI agent. Its memory is useful because authenticated history survives sessions and changes the state reconstructed when the user returns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/notg0d/Keeva" rel="noopener noreferrer"&gt;Keeva source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tatum.io/hackathon/total-recall" rel="noopener noreferrer"&gt;Total Recall - Tatum × Walrus Memory Hackathon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.tatum.io/" rel="noopener noreferrer"&gt;Tatum documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.wal.app/walrus-memory" rel="noopener noreferrer"&gt;Walrus Memory documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>security</category>
      <category>api</category>
      <category>react</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
