[{"data":1,"prerenderedAt":999},["ShallowReactive",2],{"navigation_docs_en":3,"-en-application-sshwifty-plus":77,"-en-application-sshwifty-plus-surround":994},[4,26,40,61],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F2.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"API & Interface","\u002Fen\u002Fgetting-started\u002Fapi","en\u002F1.getting-started\u002F3.api","i-lucide-braces",{"title":21,"path":22,"stem":23,"icon":24},"Application","\u002Fen\u002Fgetting-started\u002Fapplication","en\u002F1.getting-started\u002F4.application","i-lucide-boxes",false,{"title":27,"icon":19,"path":28,"stem":29,"children":30,"page":25},"API & Interfaces","\u002Fen\u002Fapi","en\u002F2.api",[31,36],{"title":32,"path":33,"stem":34,"icon":35},"Image URL to Base64","\u002Fen\u002Fapi\u002Fimage-to-base64","en\u002F2.api\u002F201.image-to-base64","i-lucide-brain",{"title":37,"path":38,"stem":39,"icon":35},"Random 6657 Meme","\u002Fen\u002Fapi\u002Frandom-6657-meme","en\u002F2.api\u002F202.random-6657-meme",{"title":21,"icon":24,"path":41,"stem":42,"children":43,"page":25},"\u002Fen\u002Fapplication","en\u002F3.application",[44,49,53,57],{"title":45,"path":46,"stem":47,"icon":48},"MokuMoku","\u002Fen\u002Fapplication\u002Fmokumoku","en\u002F3.application\u002F201.mokumoku","i-lucide-app-window",{"title":50,"path":51,"stem":52,"icon":48},"Telegram Sticker Transcoding Tool","\u002Fen\u002Fapplication\u002Ftg-sticker","en\u002F3.application\u002F202.tg-sticker",{"title":54,"path":55,"stem":56,"icon":48},"Sshwifty Plus","\u002Fen\u002Fapplication\u002Fsshwifty-plus","en\u002F3.application\u002F203.sshwifty-plus",{"title":58,"path":59,"stem":60,"icon":48},"Cloud Clipboard","\u002Fen\u002Fapplication\u002Fcloud-clipboard","en\u002F3.application\u002F204.cloud-clipboard",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":25},"Advanced","i-lucide-ellipsis","\u002Fen\u002Fadvanced","en\u002F4.advanced",[67,72],{"title":68,"path":69,"stem":70,"icon":71},"Privacy Policy","\u002Fen\u002Fadvanced\u002Fprivacy-policy","en\u002F4.advanced\u002F1.privacy-policy","i-lucide-shield-check",{"title":73,"path":74,"stem":75,"icon":76},"Changelog","\u002Fen\u002Fadvanced\u002Fchangelog","en\u002F4.advanced\u002F2.changelog","i-lucide-logs",{"id":78,"title":54,"body":79,"description":986,"extension":987,"links":988,"meta":989,"navigation":990,"path":55,"seo":991,"stem":56,"__hash__":993},"docs_en\u002Fen\u002F3.application\u002F203.sshwifty-plus.md",{"type":80,"value":81,"toc":969},"minimark",[82,92,95,109,114,123,128,153,157,168,195,199,202,213,217,220,228,888,892,914,918,953,957,965],[83,84,85,86],"p",{},"Repository: ",[87,88,89],"a",{"href":89,"rel":90},"https:\u002F\u002Fgithub.com\u002Fkrab-lab\u002Fsshwifty-plus",[91],"nofollow",[93,94],"hr",{},[96,97,98,99,103,104,108],"warning",{},"Using a service deployed by someone else is not recommended, unless you can completely trust that person. Anyone who retains the ",[100,101,102],"code",{},"SSHWIFTY_KEY_ENCRYPTION_SECRET"," can decrypt the database at any time and obtain ",[105,106,107],"strong",{},"your server addresses and SSH private keys",". Deploy the service yourself and protect it with appropriate Zero Trust access controls.",[110,111,113],"h2",{"id":112},"feature-changes","Feature Changes",[83,115,116,117,122],{},"The following changes have been made on top of the upstream ",[87,118,121],{"href":119,"rel":120},"https:\u002F\u002Fgithub.com\u002Fnirui\u002Fsshwifty",[91],"Sshwifty"," project.",[124,125,127],"h3",{"id":126},"workers-and-static-assets","Workers and Static Assets",[129,130,131,139,146],"ul",{},[132,133,134,135,138],"li",{},"A Cloudflare Worker hosts the static UI and handles ",[100,136,137],{},"\u002Fsshwifty\u002Fsocket"," WebSocket SSH traffic.",[132,140,141,142,145],{},"Only SSH is supported. Telnet, server hooks, SOCKS5, and server-side ",[100,143,144],{},"file:\u002F\u002F"," private-key loading are not available.",[132,147,148,149,152],{},"The SSH client uses ",[100,150,151],{},"ssh2"," with an algorithm set compatible with Cloudflare Workers.",[124,154,156],{"id":155},"github-sign-in-and-account-keys","GitHub Sign-In and Account Keys",[83,158,159,160,163,164,167],{},"GitHub OAuth is required when ",[100,161,162],{},"SSHWIFTY_AUTH_MODE"," is set to ",[100,165,166],{},"github",".",[129,169,170,173,176,182,189],{},[132,171,172],{},"Each GitHub account receives an Ed25519 key pair and an RSA 4096-bit key pair on first sign-in.",[132,174,175],{},"Users can view and copy public keys from the account window.",[132,177,178,179,181],{},"Private keys are encrypted with ",[100,180,102],{}," and stored in Cloudflare D1.",[132,183,184,185,188],{},"SSH Authentication supports ",[100,186,187],{},"Saved Key","; selecting it uses the managed key to connect to the remote server.",[132,190,191,192,194],{},"When ",[100,193,187],{}," is selected, the Worker tries the Ed25519 key first and then the RSA key.",[124,196,198],{"id":197},"remote-server-records","Remote Server Records",[83,200,201],{},"The Connected before list is stored in D1 for each GitHub account.",[129,203,204,207,210],{},[132,205,206],{},"Remote records are saved automatically after a successful SSH connection.",[132,208,209],{},"Users can edit or delete saved records.",[132,211,212],{},"Passwords, uploaded private keys, and other connection credentials are not stored in remote records.",[110,214,216],{"id":215},"deployment","Deployment",[83,218,219],{},"You need the following:",[129,221,222,225],{},[132,223,224],{},"A Cloudflare account with Workers and D1 enabled.",[132,226,227],{},"A fork of this repository in your own GitHub account.",[229,230,231,235,267,271,278,414,423,429,433,452,512,517,524,539,543,550,567,575,582,588,592,600,604,615,620,626,744,750,754,759,769,850,853,872,878,885],"steps",{},[124,232,234],{"id":233},"create-a-d1-database-in-the-cloudflare-dashboard","Create a D1 Database in the Cloudflare Dashboard",[236,237,238,241,251,261],"ol",{},[132,239,240],{},"Sign in to the Cloudflare Dashboard.",[132,242,243,244,247,248,167],{},"Open ",[105,245,246],{},"Storage & Databases",", then select ",[105,249,250],{},"D1 SQL Database",[132,252,253,254,257,258,167],{},"Click ",[105,255,256],{},"Create database"," and name it ",[100,259,260],{},"sshwifty",[132,262,263,264,167],{},"Copy the database ID after creation. It is a UUID such as ",[100,265,266],{},"84fa53bb-3b6b-4d23-9f28-bfdcc23aa243",[124,268,270],{"id":269},"check-the-repository-configuration","Check the Repository Configuration",[83,272,273,274,277],{},"Deployment is configured through the root ",[100,275,276],{},"wrangler.jsonc",". Before connecting the repository, confirm that the D1 binding looks like this:",[279,280,285],"pre",{"className":281,"code":282,"language":283,"meta":284,"style":284},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"d1_databases\": [\n  {\n    \"binding\": \"DB\",\n    \"database_name\": \"sshwifty\",\n    \"database_id\": \"YOUR_D1_DATABASE_ID\",\n    \"migrations_dir\": \"worker\u002Fmigrations\"\n  }\n]\n","json","",[100,286,287,309,315,341,361,382,402,408],{"__ignoreMap":284},[288,289,292,296,300,302,306],"span",{"class":290,"line":291},"line",1,[288,293,295],{"class":294},"sMK4o","\"",[288,297,299],{"class":298},"sfazB","d1_databases",[288,301,295],{"class":294},[288,303,305],{"class":304},"sTEyZ",": ",[288,307,308],{"class":294},"[\n",[288,310,312],{"class":290,"line":311},2,[288,313,314],{"class":294},"  {\n",[288,316,318,321,325,327,330,333,336,338],{"class":290,"line":317},3,[288,319,320],{"class":294},"    \"",[288,322,324],{"class":323},"spNyl","binding",[288,326,295],{"class":294},[288,328,329],{"class":294},":",[288,331,332],{"class":294}," \"",[288,334,335],{"class":298},"DB",[288,337,295],{"class":294},[288,339,340],{"class":294},",\n",[288,342,344,346,349,351,353,355,357,359],{"class":290,"line":343},4,[288,345,320],{"class":294},[288,347,348],{"class":323},"database_name",[288,350,295],{"class":294},[288,352,329],{"class":294},[288,354,332],{"class":294},[288,356,260],{"class":298},[288,358,295],{"class":294},[288,360,340],{"class":294},[288,362,364,366,369,371,373,375,378,380],{"class":290,"line":363},5,[288,365,320],{"class":294},[288,367,368],{"class":323},"database_id",[288,370,295],{"class":294},[288,372,329],{"class":294},[288,374,332],{"class":294},[288,376,377],{"class":298},"YOUR_D1_DATABASE_ID",[288,379,295],{"class":294},[288,381,340],{"class":294},[288,383,385,387,390,392,394,396,399],{"class":290,"line":384},6,[288,386,320],{"class":294},[288,388,389],{"class":323},"migrations_dir",[288,391,295],{"class":294},[288,393,329],{"class":294},[288,395,332],{"class":294},[288,397,398],{"class":298},"worker\u002Fmigrations",[288,400,401],{"class":294},"\"\n",[288,403,405],{"class":290,"line":404},7,[288,406,407],{"class":294},"  }\n",[288,409,411],{"class":290,"line":410},8,[288,412,413],{"class":294},"]\n",[83,415,416,417,419,420,422],{},"Write the database ID from the previous step into ",[100,418,368],{}," and commit it to the deployment branch. You can edit and commit ",[100,421,276],{}," in the GitHub web editor.",[424,425,426,428],"note",{},[100,427,276],{}," is the configuration source for Git deployments. Do not configure conflicting databases for the same binding in both the Dashboard and the repository.",[124,430,432],{"id":431},"connect-the-github-repository-in-cloudflare","Connect the GitHub Repository in Cloudflare",[236,434,435,440,446,449],{},[132,436,243,437,167],{},[105,438,439],{},"Workers & Pages",[132,441,442,443,167],{},"Create an application and select ",[100,444,445],{},"Continue with GitHub",[132,447,448],{},"Authorize Cloudflare to access GitHub and select the repository containing this project.",[132,450,451],{},"In the build configuration, enter the following values:",[453,454,455,468],"table",{},[456,457,458],"thead",{},[459,460,461,465],"tr",{},[462,463,464],"th",{},"Setting",[462,466,467],{},"Value",[469,470,471,482,492,502],"tbody",{},[459,472,473,477],{},[474,475,476],"td",{},"Project name",[474,478,479],{},[100,480,481],{},"sshwifty-plus",[459,483,484,487],{},[474,485,486],{},"Root directory",[474,488,489],{},[100,490,491],{},"\u002F",[459,493,494,497],{},[474,495,496],{},"Build command",[474,498,499],{},[100,500,501],{},"npm run generate",[459,503,504,507],{},[474,505,506],{},"Deploy command",[474,508,509],{},[100,510,511],{},"npx wrangler deploy",[236,513,514],{"start":363},[132,515,516],{},"Save the settings and start the first deployment.",[83,518,519,520,523],{},"After deployment, confirm that the following binding exists in the Worker's ",[105,521,522],{},"Bindings"," settings:",[129,525,526,529,534],{},[132,527,528],{},"Type: D1 database",[132,530,531,532],{},"Name: ",[100,533,335],{},[132,535,536,537],{},"Value: ",[100,538,260],{},[124,540,542],{"id":541},"create-a-github-oauth-app","Create a GitHub OAuth App",[83,544,545,546,549],{},"Copy the production URL from the Worker's ",[105,547,548],{},"Domains"," settings, or add a custom domain.",[236,551,552,559,564],{},[132,553,243,554,167],{},[87,555,558],{"href":556,"rel":557},"https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers",[91],"GitHub Settings -> Developer settings -> OAuth Apps",[132,560,253,561,167],{},[100,562,563],{},"New OAuth App",[132,565,566],{},"Set the Authorization callback URL to:",[279,568,573],{"className":569,"code":571,"language":572,"meta":284},[570],"language-text","https:\u002F\u002FYOUR_WORKER_DOMAIN\u002Fsshwifty\u002Fauth\u002Fgithub\u002Fcallback\n","text",[100,574,571],{"__ignoreMap":284},[83,576,577,578,581],{},"For example, if the Worker is available at ",[100,579,580],{},"https:\u002F\u002Fsshwifty.example.com",", the callback URL must be:",[279,583,586],{"className":584,"code":585,"language":572,"meta":284},[570],"https:\u002F\u002Fsshwifty.example.com\u002Fsshwifty\u002Fauth\u002Fgithub\u002Fcallback\n",[100,587,585],{"__ignoreMap":284},[589,590,591],"tip",{},"A custom domain is recommended.",[236,593,594],{"start":343},[132,595,253,596,599],{},[100,597,598],{},"Generate a new client secret"," and record the GitHub OAuth App Client ID and Client Secret. They are entered in the Cloudflare Dashboard in the next step.",[124,601,603],{"id":602},"set-variables-and-secrets-in-the-dashboard","Set Variables and Secrets in the Dashboard",[83,605,606,607,610,611,614],{},"After the first deployment, open the Worker's ",[105,608,609],{},"Settings"," -> ",[105,612,613],{},"Variables and Secrets"," and configure the following values.",[616,617,619],"h4",{"id":618},"variables","Variables",[589,621,622,623,625],{},"These variables are already declared in ",[100,624,276],{},"; you do not need to add them manually. You can change them when needed.",[453,627,628,640],{},[456,629,630],{},[459,631,632,635,637],{},[462,633,634],{},"Name",[462,636,467],{},[462,638,639],{},"Description",[469,641,642,655,670,685,701,716,731],{},[459,643,644,648,652],{},[474,645,646],{},[100,647,162],{},[474,649,650],{},[100,651,166],{},[474,653,654],{},"Requires GitHub sign-in",[459,656,657,662,667],{},[474,658,659],{},[100,660,661],{},"SSHWIFTY_DIAL_TIMEOUT",[474,663,664],{},[100,665,666],{},"120",[474,668,669],{},"SSH connection timeout in seconds",[459,671,672,677,682],{},[474,673,674],{},[100,675,676],{},"SSHWIFTY_HEARTBEAT_TIMEOUT",[474,678,679],{},[100,680,681],{},"10",[474,683,684],{},"Heartbeat interval in seconds",[459,686,687,692,698],{},[474,688,689],{},[100,690,691],{},"SSHWIFTY_ALLOWED_HOSTS",[474,693,694,695],{},"For example, ",[100,696,697],{},"ssh.example.com:22,10.0.0.10:2222",[474,699,700],{},"When set, only the listed hosts and ports may be connected to; wildcards and CIDR ranges are not supported",[459,702,703,708,713],{},[474,704,705],{},[100,706,707],{},"SSHWIFTY_PUBLIC_ACCESS",[474,709,710],{},[100,711,712],{},"true",[474,714,715],{},"Allows entry to Sshwifty without a passphrase",[459,717,718,723,728],{},[474,719,720],{},[100,721,722],{},"SSHWIFTY_PRESETS",[474,724,725],{},[100,726,727],{},"[]",[474,729,730],{},"SSH preset JSON",[459,732,733,738,741],{},[474,734,735],{},[100,736,737],{},"SSHWIFTY_SERVER_MESSAGE",[474,739,740],{},"Any message",[474,742,743],{},"Optional",[83,745,746,747,749],{},"To avoid differences between Dashboard values and repository configuration, commit long-lived non-sensitive values to ",[100,748,276],{}," and manage environment-specific values in the Dashboard.",[616,751,753],{"id":752},"secrets","Secrets",[96,755,756,757,167],{},"Do not expose these values anywhere, and never write them into ",[100,758,276],{},[83,760,761,762,610,765,768],{},"Use ",[105,763,764],{},"Add",[105,766,767],{},"Secret"," to add the following values:",[453,770,771,783],{},[456,772,773],{},[459,774,775,777,780],{},[462,776,634],{},[462,778,779],{},"Value source",[462,781,782],{},"Purpose",[469,784,785,798,810,826,839],{},[459,786,787,792,795],{},[474,788,789],{},[100,790,791],{},"GITHUB_CLIENT_ID",[474,793,794],{},"GitHub OAuth App Client ID",[474,796,797],{},"Required for GitHub sign-in",[459,799,800,805,808],{},[474,801,802],{},[100,803,804],{},"GITHUB_CLIENT_SECRET",[474,806,807],{},"GitHub OAuth App Client Secret",[474,809,797],{},[459,811,812,817,820],{},[474,813,814],{},[100,815,816],{},"SSHWIFTY_SHARED_KEY",[474,818,819],{},"A password you choose",[474,821,822,823,825],{},"Optional. When ",[100,824,707],{}," is disabled, users must enter this passphrase before using Sshwifty",[459,827,828,833,836],{},[474,829,830],{},[100,831,832],{},"SSHWIFTY_SESSION_SECRET",[474,834,835],{},"A strong random value you generate",[474,837,838],{},"Signs session and OAuth state cookies",[459,840,841,845,847],{},[474,842,843],{},[100,844,102],{},[474,846,835],{},[474,848,849],{},"Encrypts SSH private keys stored in D1",[83,851,852],{},"For the last two values, generate random strings of at least 32 bytes and paste them into the Dashboard. They must be different.",[83,854,855,860,861,864,865,868,869,167],{},[87,856,859],{"href":857,"rel":858},"https:\u002F\u002Fpswd.software",[91],"PSWD by MAZE"," is recommended. Enable ",[100,862,863],{},"Include Uppercase Letters",", ",[100,866,867],{},"Include Lowercase Letters",", and ",[100,870,871],{},"Include Numbers",[96,873,874,875,877],{},"Rotating ",[100,876,832],{}," signs out all active users.",[879,880,881,882,884],"caution",{},"Do not lose or casually rotate ",[100,883,102],{},". If it is lost, existing managed SSH private keys in D1 cannot be decrypted.",[83,886,887],{},"After saving variables or secrets, retry the deployment in the Dashboard or push an empty commit to GitHub to create a new Worker version.",[110,889,891],{"id":890},"d1-handling-after-the-first-deployment","D1 Handling After the First Deployment",[236,893,894,903,908,911],{},[132,895,896,897,899,900,902],{},"Confirm that the latest deployment is complete and that the ",[100,898,335],{}," binding points to the correct ",[100,901,260],{}," D1 database.",[132,904,243,905,167],{},[100,906,907],{},"https:\u002F\u002FYOUR_WORKER_DOMAIN\u002Fsshwifty\u002F",[132,909,910],{},"Select Sign in with GitHub and complete authorization.",[132,912,913],{},"The OAuth callback automatically runs the current v1 schema initialization check.",[110,915,917],{"id":916},"deployment-verification","Deployment Verification",[236,919,920,924,931,934,941,947,950],{},[132,921,243,922,167],{},[100,923,907],{},[132,925,926,927,930],{},"Select ",[100,928,929],{},"Sign in with GitHub"," and complete GitHub OAuth authorization.",[132,932,933],{},"Open the account menu and copy the Ed25519 or RSA public key.",[132,935,936,937,940],{},"Add the public key to the destination server user's ",[100,938,939],{},"~\u002F.ssh\u002Fauthorized_keys"," file.",[132,942,943,944,946],{},"Create a new SSH connection and select ",[100,945,187],{}," under Authentication.",[132,948,949],{},"Confirm the server fingerprint and verify that the shell opens.",[132,951,952],{},"Reopen the connection dialog and confirm that the remote appears in Connected before. Edit and delete actions should persist after a reload.",[110,954,956],{"id":955},"issues","Issues",[83,958,959,960,167],{},"Please open an ",[87,961,964],{"href":962,"rel":963},"https:\u002F\u002Fgithub.com\u002Fkrab-lab\u002Fsshwifty-plus\u002Fissues",[91],"Issue",[966,967,968],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":284,"searchDepth":311,"depth":311,"links":970},[971,976,983,984,985],{"id":112,"depth":311,"text":113,"children":972},[973,974,975],{"id":126,"depth":317,"text":127},{"id":155,"depth":317,"text":156},{"id":197,"depth":317,"text":198},{"id":215,"depth":311,"text":216,"children":977},[978,979,980,981,982],{"id":233,"depth":317,"text":234},{"id":269,"depth":317,"text":270},{"id":431,"depth":317,"text":432},{"id":541,"depth":317,"text":542},{"id":602,"depth":317,"text":603},{"id":890,"depth":311,"text":891},{"id":916,"depth":311,"text":917},{"id":955,"depth":311,"text":956},"Establish SSH connections through the browser with automatically managed SSH keys. Self-host on Cloudflare Workers without running a server.","md",null,{},{"icon":48},{"description":992,"title":54},"A WebSSH application that you can deploy yourself with Cloudflare Workers.","UBx9cXR-S10Tml78hrMGTktD_Ff1vSbMtxI36jlFJEE",[995,997],{"title":50,"path":51,"stem":52,"description":996,"icon":48,"children":-1},"A simple web app that can automatically batch-convert videos or GIF animations into the format required by Telegram.",{"title":58,"path":59,"stem":60,"description":998,"icon":48,"children":-1},"Encrypted cross-device clipboard with no login required and a serverless design.",1788341494478]