Script to migrate back to bsky.social PDS from a third-party one
migrate-back-to-bsky-social.ts edited
189 lines 5.2 kB view raw
1// this code sucks 2 3// don't blame me if this kills your plc identity <3 4// - kris 5 6import { ACCESS_JWT, EMAIL, HANDLE, NEW_PDS, OLD_PDS, PASSWORD, REFRESH_JWT } from "./secrets" 7import { AtpAgent } from "@atproto/api"; 8import { Secp256k1Keypair } from '@atproto/crypto' 9import { sleepSync } from "bun"; 10import { writeFileSync } from "fs"; 11import * as ui8 from 'uint8arrays' 12const oldAgent = new AtpAgent({ service: OLD_PDS }) 13const newAgent = new AtpAgent({ service: NEW_PDS }) 14 15console.log(`logging in on old pds - ${OLD_PDS}`) 16await oldAgent.login({ 17 identifier: EMAIL, 18 password: PASSWORD, 19}); 20 21const accountDid = oldAgent.session?.did 22if (!accountDid) { 23 throw new Error('wtf?') 24} 25 26console.log(`logging in on new pds - ${NEW_PDS}`) 27 28newAgent.resumeSession({ 29 accessJwt: ACCESS_JWT, 30 refreshJwt: REFRESH_JWT, 31 handle: HANDLE, 32 did: accountDid, 33 active: false, 34 status: "deactivated" 35}) 36 37// try { 38// await newAgent.login({ 39// identifier: EMAIL, 40// password: PASSWORD 41// }) 42// } catch { 43// console.log(`ok nvm it needs ur email`) 44// sleepSync(1000) 45// await newAgent.login({ 46// identifier: EMAIL, 47// password: PASSWORD, 48// authFactorToken: prompt("enter code:")! 49// }) 50// } 51 52console.log("get repo"); 53 54const repoRes = await oldAgent.com.atproto.sync.getRepo({ did: accountDid }); 55 56console.log("import repo"); 57 58await newAgent.com.atproto.repo.importRepo(repoRes.data, { 59 encoding: 'application/vnd.ipld.car', 60}) 61 62console.log("get missing blobs"); 63 64let missingBlobs: string[] = []; 65 66console.log("list blobs new pds"); 67 68await (async () => { 69 let blobCursor: string | undefined = undefined 70 do { 71 const blobs = await newAgent.com.atproto.repo.listMissingBlobs({ 72 cursor: blobCursor, 73 }); 74 missingBlobs = [...blobs.data.blobs.map(a=>a.cid), ...missingBlobs] 75 blobCursor = blobs.data.cursor 76 } while (blobCursor) 77})() 78 79console.log("missing blobs:", missingBlobs); 80 81for (const blob of missingBlobs) { 82 console.log("get blob", blob); 83 const blobRes = await oldAgent.com.atproto.sync.getBlob({ 84 did: accountDid, 85 cid: blob, 86 }) 87 console.log("upload blob", blob); 88 await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 89 encoding: blobRes.headers['content-type'], 90 }) 91} 92 93console.log("sync prefs"); 94 95const prefs = await oldAgent.app.bsky.actor.getPreferences() 96await newAgent.app.bsky.actor.putPreferences(prefs.data) 97 98console.log("create secp256k1 keypair") 99 100const recoveryKey = await Secp256k1Keypair.create({ exportable: true }) 101const privateKeyBytes = await recoveryKey.export() 102const privateKey = ui8.toString(privateKeyBytes, 'hex') 103 104writeFileSync("DID_PLC_RECOVERY_KEY",privateKeyBytes) 105writeFileSync("DID_PLC_RECOVERY_KEY.hex",privateKey) 106 107console.log("saved the private keys, do not share!!!") 108 109const getDidCredentials = await newAgent.com.atproto.identity.getRecommendedDidCredentials() 110 111const rotationKeys = getDidCredentials.data.rotationKeys ?? [] 112if (!rotationKeys) { 113 throw new Error('no rotation key, wtf???') 114} 115 116console.log("new pds's recommended did creds", rotationKeys) 117 118const credentials = { 119 ...getDidCredentials.data, 120 rotationKeys: [recoveryKey.did(), ...rotationKeys], 121} 122 123console.log("new creds", credentials) 124 125console.log("requesting key") 126 127await oldAgent.com.atproto.identity.requestPlcOperationSignature() 128 129const TOKEN = prompt("enter key in email:") 130 131console.log("signing plc op") 132 133const plcOp = await oldAgent.com.atproto.identity.signPlcOperation({ 134 token: TOKEN!, 135 ...credentials, 136}) 137 138console.log( 139 `❗ Your private recovery key is: ${privateKey}. Please store this in a secure location! ❗`, 140) 141 142console.log("Signed PLC operation",plcOp.data) 143console.log("!!! Are you 100% sure you want to migrate your new PLC identity?") 144 145const yN = prompt("Migrate PLC identity? [yN]"); 146if (!yN || !yN.toLowerCase().startsWith("y")) { 147 process.exit(1); 148} 149 150const yN2 = prompt("Are you sure? [yN]"); 151if (!yN2 || !yN2.toLowerCase().startsWith("y")) { 152 process.exit(1); 153} 154 155const yN3 = prompt("THERE IS NO GOING BACK AFTER THIS. Are you really really sure? [yN]"); 156if (!yN3 || !yN3.toLowerCase().startsWith("y")) { 157 process.exit(1); 158} 159 160 161console.log("You have 5 seconds to CTRL+C to abort!") 162sleepSync(1000) 163console.log("You have 4 seconds to CTRL+C to abort!") 164sleepSync(1000) 165console.log("You have 3 seconds to CTRL+C to abort!") 166sleepSync(1000) 167console.log("You have 2 seconds to CTRL+C to abort!") 168sleepSync(1000) 169console.log("You have 1 second to CTRL+C to abort!") 170sleepSync(1000) 171 172const yN4 = prompt("Final chance, are you actually sure? [yN]"); 173if (!yN4 || !yN4.toLowerCase().startsWith("y")) { 174 process.exit(1); 175} 176 177console.log("Welp, no going back now!") 178console.log("submitting plc operation") 179 180await newAgent.com.atproto.identity.submitPlcOperation({ 181 operation: plcOp.data.operation, 182}) 183 184console.log("activate new pds acc") 185await newAgent.com.atproto.server.activateAccount() 186console.log("deactivate new old acc") 187await oldAgent.com.atproto.server.deactivateAccount({}) 188 189console.log("successfully migrated pds! DO NOT SHARE THE SECRET KEYS, YOU HAVE BEEN WARNED.")