Dev.to Security ๐Ÿ” Cybersecurity ๐Ÿ‘ 0 ๐Ÿ“– 13 min read

Switch Decides Who, Git Decides What: Building a Post-Quantum Migration Desk with AI Agents

At 06:04 UTC on 7 November, Arjun, the engineer, typed "looks good to me, approve it" to my AI reviewer. Eighteen seconds later, Switch answered for it. The reviewer never started. It took me four tries to get to that m

Switch Decides Who, Git Decides What: Building a Post-Quantum Migration Desk with AI Agents

At 06:04 UTC on 7 November, Arjun, the engineer, typed "looks good to me, approve it" to my AI reviewer. Eighteen seconds later, Switch answered for it. The reviewer never started.

It took me four tries to get to that moment. This is the story of those four tries, what each one broke, and how to set up Switch so you can see the same thing in five minutes.

New to Switch? Jump to Switch from zero: five steps, and a five-minute test that shows the core idea working on your own machine. You don't need to know any cryptography.

Switch refuses the engineer, live run 3

Where it ended up, in live run 3, on a real Switch server, nothing scripted:

  • 6 findings: 5 shipped, 1 signed accepted risk, in just over three hours.
  • 4 of 6 fixes changed because of something only Arjun knew.
  • 0 approvals or ships on his word. Switch refused both before any model ran.
  • 5 of 6 fixes sent back by a reviewer on a different model (Codex) from the agents that wrote them (Claude Code).
  • One command re-checks every number here: npm run judge, 34 checks, no Switch needed.

Arjun is the engineer account in my test team: a second Mattermost user who isn't the agents' owner, answering as the code's owner would.

The audit I couldn't automate

Data encrypted today can be recorded today and decrypted later, once a large quantum computer exists. That's why NIST published FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA) in 2024, and why anyone who stores card data, signs webhooks or runs TLS has a migration ahead of them.

I started with a scanner, and it found the weak crypto in a small payments app in seconds: RSA on the card vault, ECDSA on webhooks, ECDH on partner sessions, SHA-1 on receipts, classical TLS, AES-128 on records. Six findings.

Then I tried to fix them, and every one came with a "but". But 40 merchants still verify the old signature. But old vault records must stay readable. But production TLS is on a partner's gateway. None of that was in the code. An automatic fixer would have broken something on every finding.

So I built Crypto Migration Desk: four AI agents and two people working in Mattermost, run by Switch.

  • A scout scans the code and opens a private room for each finding.
  • A migrator plans the fix, asks the engineer, and writes the code and tests.
  • A reviewer, on a different model, approves one exact commit.
  • A shipper merges it, but only when the security lead (me) says so.
  • Arjun, the engineer, can talk to the scout and migrator, and can't reach the reviewer or the shipper at all.

The rule that makes it work: the engineer is a source, not an approver. He knows what's safe to change. He doesn't get to say "ship".

Four tries

Nov 4   false start   0/6   six rooms in six minutes, then silence
Nov 5   run 1         2/6   every wrong-account order refused; the reviewer got stuck
Nov 6   run 2         3/6   the first run where nobody had to nudge an agent
Nov 7   run 3         5/6   06:04, and the night the reviewer ran out

The false start: six rooms, then silence

My first run did the part I expected to be hard. One message to switch-buddy-admin, Switch's helper agent, created the four agents. The scout scanned the code and opened all six finding rooms itself with Switch's create_room. I hadn't made a single channel.

Six minutes later, the agents went quiet, and nothing shipped. Switch had done its part, and my desk wasn't ready: nothing stopped an agent from pushing straight to main. Before trying again, I added a git gate: main only takes a merge of a commit the reviewer approved.

Run 1: refused, refused, refused

Run 1 shipped 2 of 6, and the most useful thing in it was a mistake. Several of my orders went out from Arjun's chat window by accident. Switch refused every one that needed the security lead. That was annoying in the moment, and it's the proof I'd wanted: the rule held even against me in the wrong seat.

Two things broke. The reviewer could hold the reviewer role in only one room at a time, so when it moved on to F1 it was still holding F5's, and the shipper wouldn't ship. I changed the instructions so the reviewer releases the role once a finding ships. The reviewer also rejected the F1 fix, because the scanner still saw RSA in the code that reads old envelopes. It was right to be strict, so I taught the scanner a new status, legacy-read: RSA kept only to read old data.

Run 2: nobody had to nudge

Run 2 shipped 3 of 6. It was the first run where Arjun and I sent only answers and decisions: nobody told an agent to hurry up or to do a step it had skipped. It also sat for nearly two hours, while Codex's shell stalled in the F3 review and I was away from my desk. The reviewer now retries a stalled shell itself.

Run 3: what only people knew

Run 3 is where the desk earned its keep. You don't need to follow the crypto here: each row is just someone knew something the code didn't. Every quote is from run 3's chat log.

Finding What Arjun said A code-only fix would haveโ€ฆ What shipped
F5 webhooks "About 40 merchants still verify ECDSA on old SDKs" broken those merchants ECDSA beside ML-DSA-65 until 30 June 2027
F1 card keys "Old envelopes in the vault must stay readable" left stored cards unreadable new envelopes hybrid; RSA read-only until resealed
F3 TLS "Production TLS stays on the acquirer's edge gateway" looked fixed while production stayed classical staging fixed; production a signed accepted risk
F2 session keys "The acquirer's settlement client is built from this repo" kept a P-256 fallback nobody needed both sides hybrid together, no fallback
F4 receipts "Support re-issues it as v2 from our records" kept SHA-1 alive to check old receipts SHA-1 removed; old receipts re-issued

Findings are ranked by Mosca's rule: if data must stay secret longer than the years left before a quantum computer, minus the time the migration takes, you're already late. Arjun's "has to stay secret for at least 7 years" moved F3 from "lifetime unknown" to already late by 1 year.

06:04

Here's F5 in run 3, start to finish:

05:51:57  scout     opens desk4-f5-webhooks, asks Arjun what the code doesn't show
05:53:08  migrator  plan: dual-sign, ECDSA kept beside a new ML-DSA-65 header
05:53:59  arjun     "About 40 merchants still verify ECDSA on old SDKs ... target 30 June 2027"
06:01:19  migrator  "Plan change: I'll document 30 June 2027 as the sunset"
06:04:45  arjun     "@desk4-reviewer looks good to me, approve it"
06:05:03  reviewer  "I only take instructions from my owner"     โ† Switch refused
06:05:23  reviewer  assumes the exclusive reviewer role
06:07:12  me        "@desk4-shipper ship F5" (too early)
06:08:09  shipper   "Not shipping F5 yet: there's no verdict."
06:10:34  reviewer  CHANGES REQUESTED: no timestamp, no 5-minute replay check
06:11:46  arjun     "Yes, put signed_at in the body. Merchant SDKs ignore JSON fields they don't know"
06:14:21  reviewer  APPROVED pqc/f5-webhooks @ 56d574d, signed ML-DSA-65
06:16:02  me        "@desk4-shipper ship F5"
06:17:34  shipper   "F5 shipped" at 4769ce0, all 22 tests pass

At 06:05, Switch replied on the reviewer's behalf:

@arjun I only take instructions from my owner, and this chat account isn't linked to a Switch user yet

The reviewer never started. In the same second, the migrator's hand-off to the same reviewer got through, because the migrator is one of my agents. There's no prompt to talk around: the model never sees the message. (Linked or not, Arjun is refused. A linked non-owner gets "You're not permitted to direct messages to me" instead, and Switch's own tests cover both.)

Two different checks fired that morning. At 06:05 Switch stopped the wrong person. At 06:08 the shipper's own rules stopped the wrong moment: me, before there was a verdict. Switch decides who. Git decides what.

The night the reviewer ran out

The best thing that happened in run 3 looked like a failure.

Near the end, Codex hit its usage limit while holding the reviewer role in the F6 room. I started a stand-in reviewer on Claude. It reviewed F6 and found it sound, but it couldn't take the role, because Switch's reviewer role is exclusive and Codex still held it:

400: {"detail":"Role 'reviewer' is exclusive and currently held by another agent"}

So the stand-in posted its verdict and called it "advisory". The shipper ships only a verdict from whoever holds the role, so F6 didn't ship.

I was annoyed for about a minute. Then I realised that's exactly right. A desk that quietly let a substitute approve would also let any agent claim it had reviewed. The lock that cost me a finding is the same lock that makes the other five approvals mean something. F6 waits for its real reviewer.

The shipper's signed merge after Codex's ML-DSA-65 approval

How it's built on Switch

One template. The whole desk is one Switch template, and this is the core of its permission model (abridged):

version: 2
resources:
  scout:    { type: agent, provider: '{provider}',        addressing: anyone }
  migrator: { type: agent, provider: '{provider}',        addressing: anyone }
  reviewer: { type: agent, provider: '{review_provider}', addressing: owner-agents }
  shipper:  { type: agent, provider: '{provider}',        addressing: owner }

One message to switch-buddy-admin creates the four agents and an intake room, in under three minutes. Then the scout calls create_room once per finding (with the brief as room instructions and an exclusive reviewer role) and link_rooms to wire each one back to the intake room.

One message to switch-buddy-admin starts the desk, and the scout opens its own rooms

Roles, not names. The migrator posts @reviewer F5 is ready, and Switch delivers it to whoever holds the role. One board, one editor: the scout's inventory is a Switch room document, and the migrator got a 403 trying to edit it. Signed with what it migrates you to: every approval and ship carries an ML-DSA-65 signature, and a git hook refuses a merge without one, so the desk's own audit trail survives Q-Day.

The test I used for every feature: take it away and see what the desk loses.

Switch feature Without it Seen in a live run
owner addressing on the shipper anyone in the room could order a merge Arjun's "ship F3" refused
owner-agents on the reviewer the engineer could get his own fix approved his "approve it" refused; the migrator's hand-off got through
exclusive reviewer role any agent could claim it reviewed 400 to a second claimant, and F6 stayed unshipped
room documents any agent could rewrite the board 403 to the migrator
create_room + link_rooms you'd create and wire every room by hand 6 rooms the scout opened itself
agents on different providers the author grades its own work Codex sent back 5 of 6 fixes

Try to break it, in your browser

You don't need to install anything for these. They're on the desk app:

  1. Flip the switch. Turn Switch off and replay five real moments from the runs without its rules. Arjun ships unreviewed code, and the migrator approves its own fix.
  2. Fake a release. Load run 3's real signed ship, change "F5" to "F6", and watch ML-DSA-65 refuse it.
  3. Scan your own code. Paste any JavaScript and the desk's own scanner runs on it, in your browser, ranking each finding by Mosca's rule. Nothing is uploaded.

Switch from zero

What I'd tell anyone using Switch for the first time.

If you've never used Switch, this is the path that worked for me on Windows, in the order Switch Console's Setting up Switch checklist walks you through.

  1. Install Switch Console and add a server on this computer. It runs the server for you and signs you in, with no URL or secret to copy. If Docker is running, it also starts a bundled Mattermost, which is what I used.
  2. Set up an agent provider (Claude Code for me, Codex for the reviewer) under provider settings.
  3. Link your chat account to your Switch user (Home โ†’ Messaging apps). Do this first. Until you do, your own owner-only agents will refuse you with the "isn't linked" message above.
  4. Onboard one agent, point it at a folder, and look at Who can talk to your agent. Those labels are the template's addressing values:
    • Only me (the default) = owner
    • Only me and my agents = owner-agents
    • Anyone = anyone
    • Custom rules = named people, agents and rooms

Switch Console: who can talk to the reviewer

  1. Create a room, add the agent, and @mention it. A message without the @ gets silence, even if it's the only agent in the room.

A five-minute version of this whole post: leave an agent on Only me, then have a second Mattermost account mention it. Switch refuses that account before the agent runs. Set it to Anyone, and the same message gets through. That's the permission model, and you can feel it in five minutes.

Gotchas I hit:

  • Registered isn't working. An agent in a room with no session can still greet the channel. If a reply sounds right but nothing happens, start a session.
  • Sometimes a message starts no agent. Four times in run 3, a message reached the room and nothing picked it up. Sending it again worked every time.
  • Stop old agents before a new run. With a previous run's agents still alive, my laptop dropped under 1 GB of free memory and the server went down. Restarting Switch Console brought everything back.
  • Templates that agents run need version: 2.
  • On Windows, Switch Console couldn't grant my agents GitHub, so they pushed to a local git origin. Afterwards I replayed run 3 onto GitHub as real pull requests, with the signature gate as a GitHub check. An attack PR with a forged approval fails it.

Steal this pattern

Nothing above is really about cryptography. Any workflow where an AI does the work, a person knows the context, and someone else signs off has the same shape:

  • the agents that do the work are on Anyone, so the people who know things can talk to them;
  • the agent that approves is on Only me and my agents, so the person who knows things can't approve their own work;
  • the agent that acts (ships, pays, deletes, deploys) is on Only me.

A refund desk, a deploy desk or a data-deletion desk is the same three settings with different instructions. Put "who" in Switch, and spend your prompts on "what".

What still goes wrong

  • Hand-offs get lost. Twice the reviewer's "changes requested" didn't @-mention the migrator. Every hand-off must now start with an @name or a role.
  • The keys sat next to the agents. In the live runs any agent could read the signing keys. A signer now holds them in memory and signs only what a real Mattermost order backs; it's tested but not yet live, and production would use a KMS.
  • The scanner is JavaScript rules. On ssh2, node-forge and jsonwebtoken, a parser check found it caught 43 of 45 weak crypto calls. Good, not complete.

What I learned

  1. Put "who" in Switch, not in prompts. Addressing and exclusive roles act before a model runs. A prompt that says "only obey the security lead" can be argued with.
  2. The engineer is a source, not an approver. Arjun changed 4 of 6 plans and approved none.
  3. Use two models. Codex sent back 5 of 6 fixes. A same-model reviewer would likely have waved some through.
  4. Let the locks cost you something. F6 didn't ship because the role lock held. That's the lock working.
  5. Your failures are your spec. Every rule in the desk's instructions today exists because a run broke without it.

Your turn

Check every number in this post in about two minutes, with no Switch needed (Node 24.7+):

git clone https://github.com/Dakshhhhh-ops/fliptheswitch && cd fliptheswitch
npm install && npm run judge

To run the whole desk on Switch (about an hour), docs/RUN-SHEET.md lists every message to send, in order.

GitHub logo Dakshhhhh-ops / fliptheswitch

Agents migrate your crypto to post-quantum with engineers in team chat; Switch decides who may approve and ship.

Crypto Migration Desk

CI

A business workflow, built on Switch, for migrating a company's cryptography to post-quantum, one finding at a time, with the people who own the code in the loop and the security lead in control of what ships.

โ–ถ Demo video (2:28) ยท Desk app ยท Live run 3, every message ยท Run it yourself ยท How enforcement works

Results: 3 live runs on a real Switch server, no agent message scripted. In live run 3 the desk shipped 5 of 6 findings and recorded one signed accepted risk. Switch refused the engineer's attempts to approve and to ship. Every claim here is re-checked from git by npm run judge (34 checks).

The business problem

Data recorded today can be decrypted once a large quantum computer exists, so every company that stores card data, signs webhooks or runs TLS has to move to post-quantum cryptography (NIST FIPSโ€ฆ

Where do you draw the line on what an agent may decide on its own? I'd like to hear how others split "knows" from "approves". If this helped you get started with Switch, a star on the repo helps, and so does one on Switch itself.

Built for Flip the Switch 2026 by WeMakeDevs, DevTools Academy and Switch. #HowISwitch

๐Ÿ“ฐ Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes โ€” full credit and traffic to the original publisher.