|
18551
|
Untitled by @RadiatingDucks | Stardance
|
RadiatingDucks
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Untitled by @RadiatingDucks | Stardance
|
1793876121.8023782
|
1794740121.8023782
|
|
18552
|
Revoue by @Vxrs1fy | Stardance
|
Vxrs1fy
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Revoue by @Vxrs1fy | Stardance
|
1793980382.002379
|
1794585182.002379
|
|
18553
|
3D Space Demo by @Jack | Stardance
|
Jack
|
0
|
1.27
|
0
|
[]
|
[{"post_id": 9020, "ts": "2026 [{"post_id": 9020, "ts": "2026-06-15T01:34:53Z", "hours": 1.2675, "text": "This is primarily an exercise in shaders, as shaders are something I’ve only dabbled in here and there, but this is turning into something like Elite. Graphically, definitely inspired by Frontier: Elite II, but gameplay-wise will be more exploration based rather than economics and trade. So, in this devlog, I’ve made shaders and added gravity (all the bodies are affected in real time! idk about making the player affected too though.)"}]...
|
Title: 3D Space Demo by @Jack | Stardance
Devlogs Title: 3D Space Demo by @Jack | Stardance
Devlogs:
This is primarily an exercise in shaders, as shaders are something I’ve only dabbled in here and there, but this is turning into something like Elite. Graphically, definitely inspired by Frontier: Elite II, but gameplay-wise will be more exploration based rather than economics and trade. So, in this devlog, I’ve made shaders and added gravity (all the bodies are affected in real time! idk about making the player affected too though.)...
|
1793451262.825422
|
1794056062.825422
|
|
18554
|
My very own WebOS by @tfry1579 | Stardance
|
tfry1579
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My very own WebOS by @tfry1579 | Stardance
|
1793465304.1296833
|
1794070104.1296833
|
|
18555
|
My awesome macropad! by @HoCaoKhoa | Stardance
|
HoCaoKhoa
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @HoCaoKhoa | Starda Title: My awesome macropad! by @HoCaoKhoa | Stardance...
|
1793926462.121269
|
1794531262.121269
|
|
18556
|
My awesome macropad! by @arwanzaheer | Stardance
|
arwanzaheer
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @arwanzaheer | Star Title: My awesome macropad! by @arwanzaheer | Stardance...
|
1793987630.04091
|
1794592430.04091
|
|
18557
|
Umbrashift by @incomplete_tree | Stardance
|
incomplete_tree
|
0
|
0.33
|
0
|
[]
|
[{"post_id": 8728, "ts": "2026 [{"post_id": 8728, "ts": "2026-06-14T14:37:36Z", "hours": 0.3292, "text": "I added an input buffer to facilitate frame-perfect inputs in my game. Before this change, if you wanted to jump immediately on landing, you had to do a frame-perfect input. Now, you just need to do it within 0.08 seconds."}]...
|
Title: Umbrashift by @incomplete_tree | Stardance
Title: Umbrashift by @incomplete_tree | Stardance
Devlogs:
I added an input buffer to facilitate frame-perfect inputs in my game. Before this change, if you wanted to jump immediately on landing, you had to do a frame-perfect input. Now, you just need to do it within 0.08 seconds....
|
1793479816.2391953
|
1794084616.2391953
|
|
18558
|
Custom Mini Humanoid by @willow_ | Stardance
|
willow_
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Custom Mini Humanoid by @willow_ | Stardanc Title: Custom Mini Humanoid by @willow_ | Stardance...
|
1793415424.614024
|
1794020224.614024
|
|
18559
|
A Wall-E by @Gawdsome | Stardance
|
Gawdsome
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: A Wall-E by @Gawdsome | Stardance
|
1793537606.869705
|
1794142406.869705
|
|
18560
|
My awesome macropad! by @abdullahshakil193 | Stard My awesome macropad! by @abdullahshakil193 | Stardance...
|
abdullahshakil193
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @abdullahshakil193 Title: My awesome macropad! by @abdullahshakil193 | Stardance...
|
1793825251.8930166
|
1794430051.8930166
|
|
18561
|
Dense by @alex_l | Stardance
|
alex_l
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Dense by @alex_l | Stardance
|
1793875660.5918264
|
1794480460.5918264
|
|
18562
|
My awesome macropad! by @A_L_E_K_S_E_Y | Stardance
|
A_L_E_K_S_E_Y
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @A_L_E_K_S_E_Y | St Title: My awesome macropad! by @A_L_E_K_S_E_Y | Stardance...
|
1793786070.547744
|
1794390870.547744
|
|
18563
|
My very own WebOS by @Pragnika | Stardance
|
Pragnika
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My very own WebOS by @Pragnika | Stardance
|
1793292909.6327863
|
1793897709.6327863
|
|
18564
|
My awesome macropad! by @spoor_p | Stardance
|
spoor_p
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @spoor_p | Stardanc Title: My awesome macropad! by @spoor_p | Stardance...
|
1793339674.6088812
|
1793944474.6088812
|
|
18565
|
Frictionless by @ayankhan | Stardance
|
ayankhan
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Frictionless by @ayankhan | Stardance
|
1793832533.2241113
|
1794437333.2241113
|
|
18566
|
My awesome macropad! by @Rundo | Stardance
|
Rundo
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @Rundo | Stardance
|
1793504947.266193
|
1794109747.266193
|
|
18567
|
SecureVault by @F3rro_32 | Stardance
|
F3rro_32
|
2
|
39.0
|
367
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-09-19T18:11:39Z", "hours": 4.0, "multiplier": 12.33, "payout": 49, "blessing": "neutral", "text": "Just improved some things, mostly advices from other users.", "repo": "https://github.com/eLandoAD/Rila", "demo": "https://securevault.alessio.hackclub.app/"}, {"ship_num": 2, "ts": "2026-07-15T09:49:13Z", "hours": 35.0, "multiplier": 9.05, "payout": 318, "blessing": "neutral", "text": "SecureVault, zero knowledge, end-to-end encrypted cloud storage. Your files and their names are encrypted in the browser before they ever reach the server, which only ever stores ciphertext it can’t read.", "repo": "https://github.com/eLandoAD/Rila", "demo": "https://securevault.alessio.hackclub.app/"}]...
|
[{"post_id": 70745, "ts": "202 [{"post_id": 70745, "ts": "2026-09-19T18:11:08Z", "hours": 3.2156, "text": "devlog Hey guys, how’s going? I’m pretty well, I have loads of things to do, but there’s always time for my biggest passion, coding. Yesterday I turned on a strinct CSP (content security policy) for this project, so today the pricing section looked like the one in the first screen. The Popular badge had no pill behind it, it was just the word, with the card’s border running straight through it. The higlighted card’s border was white, instead of indigo. Also the Get Pro was the same flat grey as the two buttons next to it. It looked like a CSS mistake, but actually it wasn’t. “It worked on my machine” ng serve rendered it correctly. Same commit, same browser. That is the part that cost me the most time: I kept hunting for a rule that only existed in production. So I stopped reading CSS and read the live page instead: --color-primary → \"\" (empty) badge background → rgba(0, 0, 0, 0) (transparent) card border → rgb(255, 255, 255) --color-primary wasn’t defined at all. And when a custom property is undefined, background-color: var(--color-primary) computes to transparent , while border-color: var(--color-primary) falls back to currentColor , which on that section is white. One missing variable, and every symptom follows from it, including the ones that look unrelated. Where the variable went Angular’s production build enables inlineCritical . It inlines the critical CSS and defers the rest of the stylesheet like this: <link rel= \"stylesheet\" href= \"styles-XXXX.css\" media= \"print\" onload= \"this.media='all'\" > That onload is an inline event handler , and script-src 'self' blocks inline handlers. So the handler never ran, the stylesheet stayed at media=\"print\" , and it was never applied. For a whole day the site had been running on nothing but the inlined critical CSS, and the theme tokens live in the file that never loaded. ng serve doesn’t run inlineCritical . That’s the entire reason it only happened in production. The fix is one flag in angular.json : \"optimization\" : { \"styles\" : { \"minify\" : true , \"inlineCritical\" : false } } The stylesheet now ships as a plain blocking <link> . One render-blocking request, ~20 KB gzipped, and the CSP stays exactly as strict as it was."}, {"post_id": 32224, "ts": "2026-07-21T08:07:59Z", "hours": 0.7983, "text": "I don’t know why, but the db crashed, so the backend wasn’t working and many of you couldn’t test out the app. I just fixed this problem, and added in docker compose the restart clause, so things like this won’t happen again. Sorry for the problem"}, {"post_id": 28809, "ts": "2026-07-15T09:41:17Z", "hours": 1.2811, "text": "Finished the project. I used a seo checker to improve the seo, at least, what I can actually improve. I then focused on writing the md files. The readme and the security.md, that displays all infos about how crypto works and in general terms, security. I hope you like the project."}, {"post_id": 28422, "ts": "2026-07-14T17:36:22Z", "hours": 6.4156, "text": "Hey guys, here I am again. Lately I focused a lot on improving both the website and the overall user experience. The first thing I did was completely redesign the landing page. I switched everything to a dark theme, added a new hero section with a background image and gradient overlay, and converted every section into dark cards to make the design feel much more consistent. I also reworked the pricing section Finally, I added smooth navigation between sections using anchor links. I also redesigned the navigation bar. On desktop and tablets it stays at the top as expected, while on mobile it moves to the bottom of the screen for easier access. During this process I removed the old hamburger menu. The file manager received one of the biggest UI updates. On mobile there’s now a bottom navigation bar with a floating upload button in the center. The Files page no longer uses a traditional table; instead, files are displayed as modern cards with encryption badges and a cleaner action menu that groups all available operations together. I also fixed some spacing issues on the login page caused by the fixed navbar. Besides the UI, I spent some time improving the website from a technical perspective. I configured gzip compression, hid unnecessary server information, fixed some SEO issues like duplicated headings, and added both a robots.txt file and a sitemap. Another important feature I implemented is the new Settings page. It now includes profile information, security settings, storage usage, and account management. I decided to keep the interface honest: only features that are actually supported by the backend are available, while future ones are clearly disabled. I also implemented permanent account deletion, both on the backend and frontend, making sure all related data is safely removed before the user is deleted. I added a lot, I hope everything works as I expect. See ya"}, {"post_id": 25831, "ts": "2026-07-10T09:45:28Z", "hours": 3.4414, "text": "Okay guys, big news: I was finally able to get an SMTP server from a friend of mine, so now email verification and password recovery are working. I’m pretty satisfied; I really wanted this app to work as well as possible, and with email support, it feels even more complete. Stay tuned, because it won’t be long before it’s ready."}, {"post_id": 24603, "ts": "2026-07-08T09:57:29Z", "hours": 0.7383, "text": "Hey guys, I know this isn’t a proper devlog, but I wanted to show you this new tool I used yesterday, called graphify. Basically you take an AI, and you install graphify on it. It will create this graph, that represents the project. I find it really useful for my actual process, reviewing. As you can see, I just need to start from the biggest Node, which is the “center” of the project, and then link every other Node. The nice thing is that you can also ask questions, and the AI will respond also taking in consideration the graph, so the response will be more accurate. I strongly advice this tool for reviewing the code and improving the “style” of the code. I hope you like it"}, {"post_id": 24026, "ts": "2026-07-07T09:39:58Z", "hours": 4.4797, "text": "Hi guys Lately I did much work. First of all, security audit fixes. I bound internal ports to localhost; moved JWT secret to a real random value in .env; hardened rate limiting (to avoid spoofing); set file uploading limit to 40MB and switched uploads/downloads to streaming so files aren’t loaded whole into RAM. Sharing rework (the main piece). Before, each user had one master DEK that encrypted all their files, and sharing sent that master DEK to the server in cleartext, so the server could decrypt shared files, and sharing one file handed over the key to every file. Now the master DEK only wraps other keys. Each file has its own random DEK (encrypting its content and name), stored wrapped by the master DEK. Each user has an RSA keypair: the public key is stored in cleartext, the private key is wrapped by the master DEK (so password reset and recovery keep working without extra code). To share, the sender fetches the recipient’s public key and RSA-encrypts only that file’s DEK; the recipient decrypts it with their private key. The server never sees a key in cleartext, and the recipient gets access to only that one file. Public links work the same way, carrying the DEK for each file in the URL fragment (never sent to the server), with a revoke action. To clean the code I also made an interfaces refactor. Moved all TypeScript interfaces into core/interfaces/, one per file (I), updated imports, removed the old model files. I’m quite done, I still have to review some graphical aspects and improve some things in the code, but I think I’ll be done this week."}, {"post_id": 21523, "ts": "2026-07-03T15:14:16Z", "hours": 0.6814, "text": "Okay guys, big problem. I realized I haven’t solved my problem with sharing files. I actually made things worse. Now each user has one DEK that is used for encryption of all their files. Two issues: the server is able to read the key (it decrypts shared files, thus E2EE is not provided), and when you give a person access to one file, you give the person access to all files. The solution I need to implement: the master DEK will not be used for encrypting the files anymore, but will become a wrapping key, each file will have its own DEK which encrypts data in this particular file, and this DEK will be wrapped by the master DEK. Each user should also have a key pair for RSA encryption: the public part of the key will be in cleartext, while the private part will be wrapped by the master DEK. When a user shares a file, they encrypt only the DEK of this file with the recipient’s public RSA key, the recipient decrypts it with the private RSA key, and the server does not see any key in cleartext. Wrapping the private key with the master DEK (not the password) means it’s recovered for free via the recovery key and survives password resets, so the existing login/recovery code stays untouched. I hope to be able to fix this problem in reasonable time. I’ll stay in touch for further clarification and devlogs P.S. in the screen you can see the DEK, proof for my thesis."}, {"post_id": 21487, "ts": "2026-07-03T13:52:44Z", "hours": 1.5675, "text": "Still improving the application. I want everything to be the best as possible. I fixed some minor bugs in fronted, mostly with the crypto service. I had some problems with the sharing of files, I realized I was also sharing the DEK of the user, which is really a problem since the receiver could, from that moment, decrypt every sender file. After fixing it I improved some things in the backend, like the process of uploading and downloading files. Another important thing I fixed is the preview. The problem is that before I was opening the file directly in RAM, which, as I explained in some prev devlog, is a problem, since I have only 2 gb of ram. The important thing is that I was able to fix it, by opening it on disk."}, {"post_id": 20927, "ts": "2026-07-02T17:28:01Z", "hours": 3.2586, "text": "Still working for improving the app. I’m slowly changing simple details, and fixing bugs, like rate limiting for api, uploading dimensions and so on. As you can see from the screens, I also added the “fake” email inbox, that allows to verify the email. This was a solution I came up yesterday, to avoid using an SMTP server. I hope you find it reasonable."}, {"post_id": 19956, "ts": "2026-07-01T12:38:11Z", "hours": 3.2114, "text": "Still improving the website and checking security. I hope everything works well and is secure. I’m now focusing on fixing small bugs, and improving UI. Something I still have to figure out, is how to manage SMTP. I don’t know if I’ll just skip this part and open a link in the browser, that works like the mail. Idk In the meanwhile I’m also addressing some memory issues, since the container given by Nest is just 2 GB in ram, so I have to optimize everything, to make the app work correctly. A thing I found really useful in this project is committing with claude. Many times I don’t know what to write, and many commits are too vague, so Claude is really helpful for this task."}, {"post_id": 18321, "ts": "2026-06-29T12:57:25Z", "hours": 5.4161, "text": "I’m slowly compelting this project. I uploaded everything on #Nest, very useful. I still have to find a solution for the SMTP service, but I’m sure I’ll find something. I’m currently fixing some graphical isseus, for example I fixed the responsivety of the application, mostly the lateral navbar. Before the release, I want to fix some other problems and things I don’t like. Additionaly I want to test everything and be sure that the encryption is secure and working correctly."}, {"post_id": 9105, "ts": "2026-06-15T07:01:04Z", "hours": 4.8314, "text": "I’m currently working on secureVault, a E2E platform which allows people to store securely files and organize them in folder. Additionaly there’s the possibility to share files. It follows a lot of directives to mantain everything crypted. I had already started this project, so it isn’t fully logged. Stay tuned."}]...
|
Title: SecureVault by @F3rro_32 | Stardance
Devlo Title: SecureVault by @F3rro_32 | Stardance
Devlogs:
I’m currently working on secureVault, a E2E platform which allows people to store securely files and organize them in folder. Additionaly there’s the possibility to share files. It follows a lot of directives to mantain everything crypted. I had already started this project, so it isn’t fully logged. Stay tuned.
I’m slowly compelting this project. I uploaded everything on #Nest, very useful. I still have to find a solution for the SMTP service, but I’m sure I’ll find something. I’m currently fixing some graphical isseus, for example I fixed the responsivety of the application, mostly the lateral navbar. Before the release, I want to fix some other problems and things I don’t like. Additionaly I want to test everything and be sure that the encryption is secure and working correctly.
Still improving the website and checking security. I hope everything works well and is secure. I’m now focusing on fixing small bugs, and improving UI. Something I still have to figure out, is how to manage SMTP. I don’t know if I’ll just skip this part and open a link in the browser, that works like the mail. Idk In the meanwhile I’m also addressing some memory issues, since the container given by Nest is just 2 GB in ram, so I have to optimize everything, to make the app work correctly. A thing I found really useful in this project is committing with claude. Many times I don’t know what to write, and many commits are too vague, so Claude is really helpful for this task.
Still working for improving the app. I’m slowly changing simple details, and fixing bugs, like rate limiting for api, uploading dimensions and so on. As you can see from the screens, I also added the “fake” email inbox, that allows to verify the email. This was a solution I came up yesterday, to avoid using an SMTP server. I hope you find it reasonable.
Still improving the application. I want everything to be the best as possible. I fixed some minor bugs in fronted, mostly with the crypto service. I had some problems with the sharing of files, I realized I was also sharing the DEK of the user, which is really a problem since the receiver could, from that moment, decrypt every sender file. After fixing it I improved some things in the backend, like the process of uploading and downloading files. Another important thing I fixed is the preview. The problem is that before I was opening the file directly in RAM, which, as I explained in some prev devlog, is a problem, since I have only 2 gb of ram. The important thing is that I was able to fix it, by opening it on disk.
Okay guys, big problem. I realized I haven’t solved my problem with sharing files. I actually made things worse. Now each user has one DEK that is used for encryption of all their files. Two issues: the server is able to read the key (it decrypts shared files, thus E2EE is not provided), and when you give a person access to one file, you give the person access to all files. The solution I need to implement: the master DEK will not be used for encrypting the files anymore, but will become a wrapping key, each file will have its own DEK which encrypts data in this particular file, and this DEK will be wrapped by the master DEK. Each user should also have a key pair for RSA encryption: the public part of the key will be in cleartext, while the private part will be wrapped by the master DEK. When a user shares a file, they encrypt only the DEK of this file with the recipient’s public RSA key, the recipient decrypts it with the private RSA key, and the server does not see any key in cleartext. Wrapping the private key with the master DEK (not the password) means it’s recovered for free via the recovery key and survives password resets, so the existing login/recovery code stays untouched. I hope to be able to fix this problem in reasonable time. I’ll stay in touch for further clarification and devlogs P.S. in the screen you can see the DEK, proof for my thesis.
Hi guys Lately I did much work. First of all, security audit fixes. I bound internal ports to localhost; moved JWT secret to a real random value in .env; hardened rate limiting (to avoid spoofing); set file uploading limit to 40MB and switched uploads/downloads to streaming so files aren’t loaded whole into RAM. Sharing rework (the main piece). Before, each user had one master DEK that encrypted all their files, and sharing sent that master DEK to the server in cleartext, so the server could decrypt shared files, and sharing one file handed over the key to every file. Now the master DEK only wraps other keys. Each file has its own random DEK (encrypting its content and name), stored wrapped by the master DEK. Each user has an RSA keypair: the public key is stored in cleartext, the private key is wrapped by the master DEK (so password reset and recovery keep working without extra code). To share, the sender fetches the recipient’s public key and RSA-encrypts only that file’s DEK; the recipient decrypts it with their private key. The server never sees a key in cleartext, and the recipient gets access to only that one file. Public links work the same way, carrying the DEK for each file in the URL fragment (never sent to the server), with a revoke action. To clean the code I also made an interfaces refactor. Moved all TypeScript interfaces into core/interfaces/, one per file (I), updated imports, removed the old model files. I’m quite done, I still have to review some graphical aspects and improve some things in the code, but I think I’ll be done this week.
Hey guys, I know this isn’t a proper devlog, but I wanted to show you this new tool I used yesterday, called graphify. Basically you take an AI, and you install graphify on it. It will create this graph, that represents the project. I find it really useful for my actual process, reviewing. As you can see, I just need to start from the biggest Node, which is the “center” of the project, and then link every other Node. The nice thing is that you can also ask questions, and the AI will respond also taking in consideration the graph, so the response will be more accurate. I strongly advice this tool for reviewing the code and improving the “style” of the code. I hope you like it
Okay guys, big news: I was finally able to get an SMTP server from a friend of mine, so now email verification and password recovery are working. I’m pretty satisfied; I really wanted this app to work as well as possible, and with email support, it feels even more complete. Stay tuned, because it won’t be long before it’s ready.
Hey guys, here I am again. Lately I focused a lot on improving both the website and the overall user experience. The first thing I did was completely redesign the landing page. I switched everything to a dark theme, added a new hero section with a background image and gradient overlay, and converted every section into dark cards to make the design feel much more consistent. I also reworked the pricing section Finally, I added smooth navigation between sections using anchor links. I also redesigned the navigation bar. On desktop and tablets it stays at the top as expected, while on mobile it moves to the bottom of the screen for easier access. During this process I removed the old hamburger menu. The file manager received one of the biggest UI updates. On mobile there’s now a bottom navigation bar with a floating upload button in the center. The Files page no longer uses a traditional table; instead, files are displayed as modern cards with encryption badges and a cleaner action menu that groups all available operations together. I also fixed some spacing issues on the login page caused by the fixed navbar. Besides the UI, I spent some time improving the website from a technical perspective. I configured gzip compression, hid unnecessary server information, fixed some SEO issues like duplicated headings, and added both a robots.txt file and a sitemap. Another important feature I implemented is the new Settings page. It now includes profile information, security settings, storage usage, and account management. I decided to keep the interface honest: only features that are actually supported by the backend are available, while future ones are clearly disabled. I also implemented permanent account deletion, both on the backend and frontend, making sure all related data is safely removed before the user is deleted. I added a lot, I hope everything works as I expect. See ya
Finished the project. I used a seo checker to improve the seo, at least, what I can actually improve. I then focused on writing the md files. The readme and the security.md, that displays all infos about how crypto works and in general terms, security. I hope you like the project.
I don’t know why, but the db crashed, so the backend wasn’t working and many of you couldn’t test out the app. I just fixed this problem, and added in docker compose the restart clause, so things like this won’t happen again. Sorry for the problem
devlog Hey guys, how’s going? I’m pretty well, I have loads of things to do, but there’s always time for my biggest passion, coding. Yesterday I turned on a strinct CSP (content security policy) for this project, so today the pricing section looked like the one in the first screen. The Popular badge had no pill behind it, it was just the word, with the card’s border running straight through it. The higlighted card’s border was white, instead of indigo. Also the Get Pro was the same flat grey as the two buttons next to it. It looked like a CSS mistake, but actually it wasn’t. “It worked on my machine” ng serve rendered it correctly. Same commit, same browser. That is the part that cost me the most time: I kept hunting for a rule that only existed in production. So I stopped reading CSS and read the live page instead: --color-primary → "" (empty) badge background → rgba(0, 0, 0, 0) (transparent) card border → rgb(255, 255, 255) --color-primary wasn’t defined at all. And when a custom property is undefined, background-color: var(--color-primary) computes to transparent , while border-color: var(--color-primary) falls back to currentColor , which on that section is white. One missing variable, and every symptom follows from it, including the ones that look unrelated. Where the variable went Angular’s production build enables inlineCritical . It inlines the critical CSS and defers the rest of the stylesheet like this: <link rel= "stylesheet" href= "styles-XXXX.css" media= "print" onload= "this.media='all'" > That onload is an inline event handler , and script-src 'self' blocks inline handlers. So the handler never ran, the stylesheet stayed at media="print" , and it was never applied. For a whole day the site had been running on nothing but the inlined critical CSS, and the theme tokens live in the file that never loaded. ng serve doesn’t run inlineCritical . That’s the entire reason it only happened in production. The fix is one flag in angular.json : "optimization" : { "styles" : { "minify" : true , "inlineCritical" : false } } The stylesheet now ships as a plain blocking <link> . One render-blocking request, ~20 KB gzipped, and the CSP stays exactly as strict as it was.
Ships:
SecureVault, zero knowledge, end-to-end encrypted cloud storage. Your files and their names are encrypted in the browser before they ever reach the server, which only ever stores ciphertext it can’t read.
Just improved some things, mostly advices from other users....
|
1793930000.5915813
|
1794016400.5915813
|
|
18568
|
NEX — Non-Extractive eXecution system by @QKing | NEX — Non-Extractive eXecution system by @QKing | Stardance...
|
QKing
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: NEX — Non-Extractive eXecution system by @Q Title: NEX — Non-Extractive eXecution system by @QKing | Stardance...
|
1793814757.085066
|
1794419557.085066
|
|
18569
|
My awesome macropad! by @Samyak | Stardance
|
Samyak
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @Samyak | Stardance
|
1793454851.2396476
|
1794059651.2396476
|
|
18570
|
Enigma by @reimunyancat | Stardance
|
reimunyancat
|
1
|
31.75
|
135
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-06-24T04:49:50Z", "hours": 7.0, "multiplier": 19.69, "payout": 135, "blessing": "neutral", "text": "Enigma I built a working Enigma machine you can use, take apart, and see the electricity move through — all in the browser. Not a diagram of one, not a gif: the real cipher engine, wired to a 3D model you can rotate, peel open, and rewire with your hands. What it is The brain is a historically correct Enigma engine written once in C++ and shared two ways — a CLI binary and a WASM module compiled with Emscripten — so the thing in your browser ciphers letter-for-letter the same as the command line. Double-stepping, ring settings, reflector, plugboard, all of it. It passes the standard test vector (rotors I·II·III / reflector B / rings TOU / start HOU / plugs ABCDXZ → TOUHOU encrypts to MQGGFI ). On top of that engine sits a Three.js machine you can actually operate: type on the keyboard, the rotors turn to their real angles and the matching lamp lights up. Flip on X-ray and the wooden case goes translucent, revealing all 26 wires inside — and the exact current path of the key you just pressed lights up and sparks along the wire, traced straight from the engine’s own answer. What was hard The engine was the easy part. Making it look like the real thing is what fought back. The case isn’t one lid — a real Enigma comes apart in three: the main hinged lid, a fold-down flap over the plugboard, and a separate rotor cover with little letter windows. Each one had to hinge and occlude correctly without clipping through the others. The plugboard cables kept poking through the closed front cover. I spent two rounds shrinking how far they sagged before realizing the problem was never depth — the cables sat in front of the closed cover on the z-axis, so they’d punch through the wood no matter how short I made them. Pull them back behind the cover face and it finally read right. Reflector and entry wheel looked like “a rotor with the letters erased.” Turns out on the real machine they’re fixed inside the housing and never visible from outside — so I hid them in normal view and only expose the contacts under X-ray. What I’m proud of The plugboard you wire with your hands. Grab a socket, drag to another, and a cable snaps in; the wire stretches under your cursor while you drag, the target socket highlights blue, and the patch feeds straight back into the engine so the encryption and the X-ray wiring update live. Pulling a cable out works the same way. It feels like handling the real hardware, not clicking a form. And the honesty of it: one engine, verified against a known test vector, with nothing faked between what you see and what actually ciphers. How to test it Type something. Watch the rotors step and the lamps light. Long messages show the rotors carrying over, double-step and all. Hit X-ray. The case turns to glass; press a key and follow the lit current path through plugboard → rotors → reflector → back out to the lamp. Wire the plugboard. Drag between two sockets to pair them, drag a cable out to remove it — the cipher changes immediately. Open it up. Toggle the lid, the rotor cover, and the front flap to see how the real case comes apart. Trust but verify. Set rotors I·II·III, reflector B, rings TOU, start HOU, plugs ABCDXZ, and type TOUHOU — you should get MQGGFI , the canonical Enigma test vector. ", "repo": "https://github.com/reimunyancat/enigma", "demo": "https://reimunyancat.github.io/enigma/"}]...
|
[{"post_id": 64094, "ts": "202 [{"post_id": 64094, "ts": "2026-09-08T08:48:35Z", "hours": 6.5314, "text": "Devlog #11 Last devlog ended with me calling statistics-only scoring the last fake thing in the cracker, and promising the real attack. This is that The actual attack A crib is a guessed fragment of plaintext — operators were creatures of habit, and words like “Meldung” (report) show up in everything. With a crib, the cracker stops scoring vibes entirely: every candidate setting decrypts the text, and the crib either appears in it letter-for-letter or it doesn’t. Six exact letters out of 17,576 start positions leaves one setting standing. The five short intercepts that statistics couldn’t touch now each carry a crib — FUNKEN, MORGEN, STEINECKE (the Germans really did sign Steinecke twice) — and all of them break in under a second. One historical filter made it in: a letter never encrypts to itself, so a crib can only sit where no letter collides with the ciphertext. Fixed-offset guesses get rejected on that rule before the search even runs. The answer that was wrong and right The fun started in testing. One intercept’s documented start position is SWV, but my cracker’s top hit came back RVV — and RVV decrypts the message perfectly. Letter for letter, not just the crib window. I was convinced my engine was broken, so I compiled the reference C++ natively and compared: identical output on both. It’s not a bug. This key’s middle rotor sits exactly on its notch, so the famous double-step kicks the left rotor forward on the very first keypress — (R,V,V) and (S,W,V) converge to the same machine state before a single letter is encrypted. Two daily keys, one message. The codebreakers knew this class of equivalent settings; I rediscovered it by being confused for an hour. Next up The guided demo — the machine types, X-rays its own wiring, and cracks its own message while a first-time visitor watches."}, {"post_id": 60827, "ts": "2026-09-03T13:34:28Z", "hours": 8.8478, "text": "Devlog #10 This one is two small things instead of one big thing — share links, and a challenge mode built from real 1941 intercepts — plus an embarrassing amount of time spent not writing code. Most of it still isn’t properly wired in, but the hours happened, so here’s the honest checkpoint. Share links The whole machine state — rotors, reflector, rings, start position, plugboard — plus a message now fits in the URL hash, like #I.II.III.B:TOU:HOU:ABCDXZ:HELLOWORLD . Opening the link configures the machine and runs the message through it, so the person on the other end watches their lampboard spell out the plaintext. “I encrypted this, try reading it” finally works as a link. My first version of the format forgot the reflector. Anyone opening those links would have gotten confident garbage, and I only noticed while writing the parser. It costs four characters in the URL. Real intercepts, verified I didn’t want to invent fake historical messages, so I went looking for real ones — and that hunt ate the first chunk of the day. Ended up at Ostwald and Weierud’s Cryptologia paper (2017)( https://cryptocellar.org ), which lists 13 authentic Wehrmacht messages from 1941, transcribed from the actual message forms, with keys recovered by modern cryptanalysis. I picked six — from a three-word “morning report canceled” up to an SS tank division complaining that two other divisions are blocking its road. Each form starts with a 5-letter group that looks like ciphertext but isn’t — it’s the Kenngruppe, a network discriminant. The paper warns you to skip it before decrypting, and for once I read the instructions first. All six decrypt on the site’s engine to exactly the plaintext on the forms. The cracker only spoke English The crack mode scored decryptions by English letter frequencies. These messages are German. Added a German frequency table and scored by whichever language fits better. Still not enough for the short ones. I ran a parameter sweep over the scoring weights and the 18-to-76-letter intercepts never rank first under any combination. That’s not a bug, it’s information theory — and it’s exactly why Bletchley Park needed cribs instead of brute force. So the panel is honest about it: only the 214-letter intercept gets a “Crack positions” button (it recovers the real start position, SDV, in about half a second), and the short ones say why they’re decrypt-only. Where the hours actually went Reading a cryptology paper instead of writing components. Rewriting the challenge card layout more than once. Re-running the sweep every time I changed my mind about the scoring. The code diff is small; the day was not. The remaining wiring is still in progress. Next up Crib-based cracking, the known-plaintext attack. That’s what actually breaks the short ones, and it’s the historically accurate method"}, {"post_id": 57387, "ts": "2026-08-29T15:02:55Z", "hours": 9.3711, "text": "Improvements made based on feedback English + Korean support has been applied. English is the default. There was a point raised in the feedback that the translator did not work properly, making it difficult to read. I apologize for not being considerate of you all. A guide panel has been added. It was added because feedback suggested that it would be helpful to have text explanations of how it works. A brief explanation of the Enigma and its principles has been added using a translator. A performance mode has been added. I received feedback that the browser was lagging, so I was struggling to figure out how to handle it. Depending on the mode, I lowered the pixel ratio or optimized the wiring calculations. The logo engraving has been changed. The black background plate has been removed, and a transparent texture with a dark groove and a bright rim has been created using a dual pass to make it look like it was carved into wood. To be honest, it still hasn’t reached the level I wanted, but I have wasted so much time here that I am honestly exhausted and plan to postpone it until next time. Rotation lock and camera presets have been added. I simply did exactly what was requested in the feedback, so there isn’t much to say about it. The crack mode was created simply because I was caught up in the strange idea that if there is an original cipher, there should also be a decryption function. It was the absolute hell; I will never do it again. Web workers use the same WASM engine as the simulator to [set] initial positions Scoring through brute force is difficult, involving the Interval of Concordance (IoC) as the primary axis plus the chi-square auxiliary for character frequency, plaintext previews for the top 5, and a button to apply directly to the machine… Trial and error: When I scored only the first 48 characters of each candidate in the full scope, a completely wrong rotor order happened to get a higher IoC and beat the correct answer (0.0677 vs 0.0651). However, when I selected the top 8 from a 64-character sample and re-scored and reordered the entire sentence, the correct answer won by a landslide (0.058 vs 0.012). Complete reconstruction of X-ray wiring. Honestly, I left the existing wiring as it was because it was a hassle, but it was tough working on it this time. I roughly standardized the thickness and connected the wires properly. Honestly, I still need to tidy it up a bit more, but I’m exhausted. Lastly, since some people claim my devlog was written by AI, I intentionally didn’t write a title or use Markdown this time. However, I’m not sure what the best approach would be. Please give me lots of feedback in the comments."}, {"post_id": 14774, "ts": "2026-06-24T00:17:32Z", "hours": 1.18, "text": "Devlog #6 I asked the question — “is this actually accurate?” — and the answer was no. A real Enigma’s case isn’t one big lid you flip up. It splits into three: the main hinged lid, a fold-down flap in front of the plugboard, and a separate cover over the rotors with little letter windows. So I rebuilt all three. What actually works now Plugboard front flap. On the real machine the plugboard sat behind a panel that folds down from a hinge at the bottom. I added that front panel and tied it to the lid state, so closing the lid folds the flap up over the front too — the way it would look packed away for transport. Rotor cover (Walzenabdeckung). Instead of leaving the rotors fully exposed, there’s now a cover that hides everything except three letter windows and the finger-wheel slits, with brass bezels around each window. It rides its own ▢ 로터 toggle — closed gives the authentic operating look, open shows the rotors. Lid interior. The inside of the lid used to be empty; the real one carried spare bulbs, red/green filters, and a metal plate. Added a bulb-holder strip, the two filters, and a nameplate — and kept them on independent materials so they stay solid in X-ray instead of going transparent along with the case. Stuff that tripped me up Fully sinking the rotors into a well — so only the finger wheel and a single letter peek out, like the real machine — tangles with the X-ray wiring coordinates I’d already placed. Rather than move all the internal geometry, I got the same read from the cover: close it and you only ever see the letter windows. Same look, none of the rework."}, {"post_id": 14017, "ts": "2026-06-23T00:16:51Z", "hours": 1.0214, "text": "Devlog #5 The engine has been correct for a while now — rotor stepping, ring settings, reflector, all of it. This session wasn’t about correctness, it was about feel : making the wooden lid open and shut for real, and making a keypress light up an actual path of current through the machine instead of just blinking a lamp at the end. Then I committed all three changes in one go and the build blew up in my face. Worth it. What actually works now The lid hinges open and closed. A ▣ 뚜껑 toggle drives a hinge pivot, and the render loop lerps it toward the target angle so it swings instead of snapping shut. Rebuilt the lid as a real box. My first lid was a single flat plank — it clipped straight through the rotors and only covered half the keyboard. Now it’s a hollow box (top + front + two side walls) with the hinge raised above the rotor tops, so it clears everything when it closes. Live current path. Until now, pressing a key did nothing to the cabling — the keyboard-to-plugboard route was never visualized at all. Now I take the trace the engine hands back and draw the whole route — keycap → plugboard → rotor contacts → lamp — as one glowing tube, with a spark running along it. Heavier materials pass. Nudged the wood/brass/steel roughness, metalness, and env-map strength a notch heavier to kill the last of the plastic look. Stuff that tripped me up I committed all three changes at once and the build died on boot — three TypeScript errors, all from one mistake. My new lid store collided with an existing lid mesh variable, so the compiler threw Property 'subscribe' does not exist... , an implicit- any fell out of that, and on top of it I’d pasted over and deleted a partnerOf helper. Aliasing the import to lidStore and restoring the helper cleared all three at once. Lesson, paid the usual way: when you add a store with the same name as an existing mesh variable, alias it at the import line first — before TypeScript makes you. Next up The case opens and the wiring lights up, but it isn’t historically right yet. The real Enigma’s case came apart in three pieces — the main lid, a fold-down flap in front of the plugboard, and a separate cover over the rotors — and mine is still one lid sitting over fully exposed rotors. Next session: make it match the real thing."}, {"post_id": 12366, "ts": "2026-06-20T05:50:39Z", "hours": 2.8122, "text": "X-ray mode This is the part I said I was actually here for. Flip a switch and the wooden case turns to glass so you can see what’s been sealed inside the whole time. Getting it to look right was the fun part. Getting it to stay working cost me the better part of an afternoon. What I built Case → glass. The casing shells (body, rotor housing, plugboard panel) drop to ~28% opacity with depth-writing turned off, so you can see straight through the wood. A lit interior. A warm point light kicks on inside the case the moment X-ray turns on — otherwise the guts just read as a dark smudge instead of actual hardware. The hidden hardware. Built everything that’s normally sealed away: a battery block with brass terminals, and thick orange cable looms routing down beneath the rotors. The wiring map. A 26-strand bundle draws the actual internal permutation for the current rotor / ring / position setup, so what you see under the glass matches what the engine is really doing. Then it went dark — on a fresh load only Everything looked perfect while I was building it. Then I cleared the cache, cold-booted the dev server to check it clean… and X-ray did nothing . Solid wood. The button highlighted, the wiring was clearly toggling on, the opacity was being set — and the case flat-out refused to turn transparent. I burned a good while convinced a git reset had eaten the fix. The one-line culprit It wasn’t git — the code was byte-for-byte the version I’d already signed off on. The real reason: in three.js (r137+), if you flip a material’s transparent flag at runtime, the change won’t take effect unless you also set material.needsUpdate = true . My toggle was setting transparent and opacity but never needsUpdate , so blending never actually switched on and opacity = 0.28 was silently ignored. It “worked” during development purely because hot-reload kept rebuilding the materials from scratch — a real cold load exposed it instantly. One line in the toggle loop and the glass came back. Next up: make the current actually flow — each keypress lighting up the live signal path through the rotors and off the reflector, with a real cable running from the key you pressed into the loom, so the glow starts where your finger does."}, {"post_id": 11108, "ts": "2026-06-18T05:15:28Z", "hours": 1.0419, "text": "Hardware fidelity pass — “wait, is this actually what the real Enigma looks like?” After the electrical signal path was rendering, I pulled up real Kriegsmarine M4 photos to compare — and the 3D model didn’t hold up. So before adding anything new, this one is a “make it historically accurate first” pass. What was wrong Floating letters. The rotor alphabet was hovering in mid-air beside each wheel instead of being on it. On the real machine the letters are engraved around the rotor’s circumference and you read one through a small window. The reflector and entry wheel looked fake. They were basically copy-pasted rotors with the letters wiped off. In reality the reflector (UKW) and entry wheel (ETW) are fixed and tucked inside the housing — there are no spinning lettered drums there at all. Lopsided UI. A huge dead zone under the input field was squashing the 3D view into a thin strip. Input/output drift. The I/O readout was center-aligned, so every keypress shoved the existing letters further left. Genuinely uncomfortable to type on. What I did Wrapped the alphabet onto the rotor circumference with engraved grooves, contact rings, and brass window bezels, so you read it through the top window like the real thing. Rebuilt the reflector and ETW as smaller dark drums seated in the housing, and set them to stay hidden in the normal view — they’ll surface later in X-ray mode, where the internal wiring belongs. Set the 3D viewport to flex: 1 so it fills the space, and re-framed the camera. Switched the I/O readout to a fixed-width, left-anchored layout, so input and output line up and grow rightward instead of sliding around. A lesson paid for in blood Squashed a stack of messy single-digit commits into one clean commit — then ran git reset --hard with an uncommitted layout fix still in the working tree and watched it evaporate. Re-did it in two minutes, but the takeaway stands: run git status before --hard . Always. Next up: the X-ray translucent mode that exposes the internal wiring and lights up the live signal path through the rotors. That’s the part I actually started this whole thing for."}, {"post_id": 9008, "ts": "2026-06-15T00:56:55Z", "hours": 0.5225, "text": "Electrical signal implementation"}, {"post_id": 8746, "ts": "2026-06-14T15:27:12Z", "hours": 0.3472, "text": "I created an initial version to beautifully implement the existing CLI Enigma on the web."}]...
|
Title: Enigma by @reimunyancat | Stardance
Devlog Title: Enigma by @reimunyancat | Stardance
Devlogs:
I created an initial version to beautifully implement the existing CLI Enigma on the web.
Electrical signal implementation
Hardware fidelity pass — “wait, is this actually what the real Enigma looks like?” After the electrical signal path was rendering, I pulled up real Kriegsmarine M4 photos to compare — and the 3D model didn’t hold up. So before adding anything new, this one is a “make it historically accurate first” pass. What was wrong Floating letters. The rotor alphabet was hovering in mid-air beside each wheel instead of being on it. On the real machine the letters are engraved around the rotor’s circumference and you read one through a small window. The reflector and entry wheel looked fake. They were basically copy-pasted rotors with the letters wiped off. In reality the reflector (UKW) and entry wheel (ETW) are fixed and tucked inside the housing — there are no spinning lettered drums there at all. Lopsided UI. A huge dead zone under the input field was squashing the 3D view into a thin strip. Input/output drift. The I/O readout was center-aligned, so every keypress shoved the existing letters further left. Genuinely uncomfortable to type on. What I did Wrapped the alphabet onto the rotor circumference with engraved grooves, contact rings, and brass window bezels, so you read it through the top window like the real thing. Rebuilt the reflector and ETW as smaller dark drums seated in the housing, and set them to stay hidden in the normal view — they’ll surface later in X-ray mode, where the internal wiring belongs. Set the 3D viewport to flex: 1 so it fills the space, and re-framed the camera. Switched the I/O readout to a fixed-width, left-anchored layout, so input and output line up and grow rightward instead of sliding around. A lesson paid for in blood Squashed a stack of messy single-digit commits into one clean commit — then ran git reset --hard with an uncommitted layout fix still in the working tree and watched it evaporate. Re-did it in two minutes, but the takeaway stands: run git status before --hard . Always. Next up: the X-ray translucent mode that exposes the internal wiring and lights up the live signal path through the rotors. That’s the part I actually started this whole thing for.
X-ray mode This is the part I said I was actually here for. Flip a switch and the wooden case turns to glass so you can see what’s been sealed inside the whole time. Getting it to look right was the fun part. Getting it to stay working cost me the better part of an afternoon. What I built Case → glass. The casing shells (body, rotor housing, plugboard panel) drop to ~28% opacity with depth-writing turned off, so you can see straight through the wood. A lit interior. A warm point light kicks on inside the case the moment X-ray turns on — otherwise the guts just read as a dark smudge instead of actual hardware. The hidden hardware. Built everything that’s normally sealed away: a battery block with brass terminals, and thick orange cable looms routing down beneath the rotors. The wiring map. A 26-strand bundle draws the actual internal permutation for the current rotor / ring / position setup, so what you see under the glass matches what the engine is really doing. Then it went dark — on a fresh load only Everything looked perfect while I was building it. Then I cleared the cache, cold-booted the dev server to check it clean… and X-ray did nothing . Solid wood. The button highlighted, the wiring was clearly toggling on, the opacity was being set — and the case flat-out refused to turn transparent. I burned a good while convinced a git reset had eaten the fix. The one-line culprit It wasn’t git — the code was byte-for-byte the version I’d already signed off on. The real reason: in three.js (r137+), if you flip a material’s transparent flag at runtime, the change won’t take effect unless you also set material.needsUpdate = true . My toggle was setting transparent and opacity but never needsUpdate , so blending never actually switched on and opacity = 0.28 was silently ignored. It “worked” during development purely because hot-reload kept rebuilding the materials from scratch — a real cold load exposed it instantly. One line in the toggle loop and the glass came back. Next up: make the current actually flow — each keypress lighting up the live signal path through the rotors and off the reflector, with a real cable running from the key you pressed into the loom, so the glow starts where your finger does.
Devlog #5 The engine has been correct for a while now — rotor stepping, ring settings, reflector, all of it. This session wasn’t about correctness, it was about feel : making the wooden lid open and shut for real, and making a keypress light up an actual path of current through the machine instead of just blinking a lamp at the end. Then I committed all three changes in one go and the build blew up in my face. Worth it. What actually works now The lid hinges open and closed. A ▣ 뚜껑 toggle drives a hinge pivot, and the render loop lerps it toward the target angle so it swings instead of snapping shut. Rebuilt the lid as a real box. My first lid was a single flat plank — it clipped straight through the rotors and only covered half the keyboard. Now it’s a hollow box (top + front + two side walls) with the hinge raised above the rotor tops, so it clears everything when it closes. Live current path. Until now, pressing a key did nothing to the cabling — the keyboard-to-plugboard route was never visualized at all. Now I take the trace the engine hands back and draw the whole route — keycap → plugboard → rotor contacts → lamp — as one glowing tube, with a spark running along it. Heavier materials pass. Nudged the wood/brass/steel roughness, metalness, and env-map strength a notch heavier to kill the last of the plastic look. Stuff that tripped me up I committed all three changes at once and the build died on boot — three TypeScript errors, all from one mistake. My new lid store collided with an existing lid mesh variable, so the compiler threw Property 'subscribe' does not exist... , an implicit- any fell out of that, and on top of it I’d pasted over and deleted a partnerOf helper. Aliasing the import to lidStore and restoring the helper cleared all three at once. Lesson, paid the usual way: when you add a store with the same name as an existing mesh variable, alias it at the import line first — before TypeScript makes you. Next up The case opens and the wiring lights up, but it isn’t historically right yet. The real Enigma’s case came apart in three pieces — the main lid, a fold-down flap in front of the plugboard, and a separate cover over the rotors — and mine is still one lid sitting over fully exposed rotors. Next session: make it match the real thing.
Devlog #6 I asked the question — “is this actually accurate?” — and the answer was no. A real Enigma’s case isn’t one big lid you flip up. It splits into three: the main hinged lid, a fold-down flap in front of the plugboard, and a separate cover over the rotors with little letter windows. So I rebuilt all three. What actually works now Plugboard front flap. On the real machine the plugboard sat behind a panel that folds down from a hinge at the bottom. I added that front panel and tied it to the lid state, so closing the lid folds the flap up over the front too — the way it would look packed away for transport. Rotor cover (Walzenabdeckung). Instead of leaving the rotors fully exposed, there’s now a cover that hides everything except three letter windows and the finger-wheel slits, with brass bezels around each window. It rides its own ▢ 로터 toggle — closed gives the authentic operating look, open shows the rotors. Lid interior. The inside of the lid used to be empty; the real one carried spare bulbs, red/green filters, and a metal plate. Added a bulb-holder strip, the two filters, and a nameplate — and kept them on independent materials so they stay solid in X-ray instead of going transparent along with the case. Stuff that tripped me up Fully sinking the rotors into a well — so only the finger wheel and a single letter peek out, like the real machine — tangles with the X-ray wiring coordinates I’d already placed. Rather than move all the internal geometry, I got the same read from the cover: close it and you only ever see the letter windows. Same look, none of the rework.
Improvements made based on feedback English + Korean support has been applied. English is the default. There was a point raised in the feedback that the translator did not work properly, making it difficult to read. I apologize for not being considerate of you all. A guide panel has been added. It was added because feedback suggested that it would be helpful to have text explanations of how it works. A brief explanation of the Enigma and its principles has been added using a translator. A performance mode has been added. I received feedback that the browser was lagging, so I was struggling to figure out how to handle it. Depending on the mode, I lowered the pixel ratio or optimized the wiring calculations. The logo engraving has been changed. The black background plate has been removed, and a transparent texture with a dark groove and a bright rim has been created using a dual pass to make it look like it was carved into wood. To be honest, it still hasn’t reached the level I wanted, but I have wasted so much time here that I am honestly exhausted and plan to postpone it until next time. Rotation lock and camera presets have been added. I simply did exactly what was requested in the feedback, so there isn’t much to say about it. The crack mode was created simply because I was caught up in the strange idea that if there is an original cipher, there should also be a decryption function. It was the absolute hell; I will never do it again. Web workers use the same WASM engine as the simulator to [set] initial positions Scoring through brute force is difficult, involving the Interval of Concordance (IoC) as the primary axis plus the chi-square auxiliary for character frequency, plaintext previews for the top 5, and a button to apply directly to the machine… Trial and error: When I scored only the first 48 characters of each candidate in the full scope, a completely wrong rotor order happened to get a higher IoC and beat the correct answer (0.0677 vs 0.0651). However, when I selected the top 8 from a 64-character sample and re-scored and reordered the entire sentence, the correct answer won by a landslide (0.058 vs 0.012). Complete reconstruction of X-ray wiring. Honestly, I left the existing wiring as it was because it was a hassle, but it was tough working on it this time. I roughly standardized the thickness and connected the wires properly. Honestly, I still need to tidy it up a bit more, but I’m exhausted. Lastly, since some people claim my devlog was written by AI, I intentionally didn’t write a title or use Markdown this time. However, I’m not sure what the best approach would be. Please give me lots of feedback in the comments.
Devlog #10 This one is two small things instead of one big thing — share links, and a challenge mode built from real 1941 intercepts — plus an embarrassing amount of time spent not writing code. Most of it still isn’t properly wired in, but the hours happened, so here’s the honest checkpoint. Share links The whole machine state — rotors, reflector, rings, start position, plugboard — plus a message now fits in the URL hash, like #I.II.III.B:TOU:HOU:ABCDXZ:HELLOWORLD . Opening the link configures the machine and runs the message through it, so the person on the other end watches their lampboard spell out the plaintext. “I encrypted this, try reading it” finally works as a link. My first version of the format forgot the reflector. Anyone opening those links would have gotten confident garbage, and I only noticed while writing the parser. It costs four characters in the URL. Real intercepts, verified I didn’t want to invent fake historical messages, so I went looking for real ones — and that hunt ate the first chunk of the day. Ended up at Ostwald and Weierud’s Cryptologia paper (2017)( https://cryptocellar.org ), which lists 13 authentic Wehrmacht messages from 1941, transcribed from the actual message forms, with keys recovered by modern cryptanalysis. I picked six — from a three-word “morning report canceled” up to an SS tank division complaining that two other divisions are blocking its road. Each form starts with a 5-letter group that looks like ciphertext but isn’t — it’s the Kenngruppe, a network discriminant. The paper warns you to skip it before decrypting, and for once I read the instructions first. All six decrypt on the site’s engine to exactly the plaintext on the forms. The cracker only spoke English The crack mode scored decryptions by English letter frequencies. These messages are German. Added a German frequency table and scored by whichever language fits better. Still not enough for the short ones. I ran a parameter sweep over the scoring weights and the 18-to-76-letter intercepts never rank first under any combination. That’s not a bug, it’s information theory — and it’s exactly why Bletchley Park needed cribs instead of brute force. So the panel is honest about it: only the 214-letter intercept gets a “Crack positions” button (it recovers the real start position, SDV, in about half a second), and the short ones say why they’re decrypt-only. Where the hours actually went Reading a cryptology paper instead of writing components. Rewriting the challenge card layout more than once. Re-running the sweep every time I changed my mind about the scoring. The code diff is small; the day was not. The remaining wiring is still in progress. Next up Crib-based cracking, the known-plaintext attack. That’s what actually breaks the short ones, and it’s the historically accurate method
Devlog #11 Last devlog ended with me calling statistics-only scoring the last fake thing in the cracker, and promising the real attack. This is that The actual attack A crib is a guessed fragment of plaintext — operators were creatures of habit, and words like “Meldung” (report) show up in everything. With a crib, the cracker stops scoring vibes entirely: every candidate setting decrypts the text, and the crib either appears in it letter-for-letter or it doesn’t. Six exact letters out of 17,576 start positions leaves one setting standing. The five short intercepts that statistics couldn’t touch now each carry a crib — FUNKEN, MORGEN, STEINECKE (the Germans really did sign Steinecke twice) — and all of them break in under a second. One historical filter made it in: a letter never encrypts to itself, so a crib can only sit where no letter collides with the ciphertext. Fixed-offset guesses get rejected on that rule before the search even runs. The answer that was wrong and right The fun started in testing. One intercept’s documented start position is SWV, but my cracker’s top hit came back RVV — and RVV decrypts the message perfectly. Letter for letter, not just the crib window. I was convinced my engine was broken, so I compiled the reference C++ natively and compared: identical output on both. It’s not a bug. This key’s middle rotor sits exactly on its notch, so the famous double-step kicks the left rotor forward on the very first keypress — (R,V,V) and (S,W,V) converge to the same machine state before a single letter is encrypted. Two daily keys, one message. The codebreakers knew this class of equivalent settings; I rediscovered it by being confused for an hour. Next up The guided demo — the machine types, X-rays its own wiring, and cracks its own message while a first-time visitor watches.
Ships:
Enigma I built a working Enigma machine you can use, take apart, and see the electricity move through — all in the browser. Not a diagram of one, not a gif: the real cipher engine, wired to a 3D model you can rotate, peel open, and rewire with your hands. What it is The brain is a historically correct Enigma engine written once in C++ and shared two ways — a CLI binary and a WASM module compiled with Emscripten — so the thing in your browser ciphers letter-for-letter the same as the command line. Double-stepping, ring settings, reflector, plugboard, all of it. It passes the standard test vector (rotors I·II·III / reflector B / rings TOU / start HOU / plugs ABCDXZ → TOUHOU encrypts to MQGGFI ). On top of that engine sits a Three.js machine you can actually operate: type on the keyboard, the rotors turn to their real angles and the matching lamp lights up. Flip on X-ray and the wooden case goes translucent, revealing all 26 wires inside — and the exact current path of the key you just pressed lights up and sparks along the wire, traced straight from the engine’s own answer. What was hard The engine was the easy part. Making it look like the real thing is what fought back. The case isn’t one lid — a real Enigma comes apart in three: the main hinged lid, a fold-down flap over the plugboard, and a separate rotor cover with little letter windows. Each one had to hinge and occlude correctly without clipping through the others. The plugboard cables kept poking through the closed front cover. I spent two rounds shrinking how far they sagged before realizing the problem was never depth — the cables sat in front of the closed cover on the z-axis, so they’d punch through the wood no matter how short I made them. Pull them back behind the cover face and it finally read right. Reflector and entry wheel looked like “a rotor with the letters erased.” Turns out on the real machine they’re fixed inside the housing and never visible from outside — so I hid them in normal view and only expose the contacts under X-ray. What I’m proud of The plugboard you wire with your hands. Grab a socket, drag to another, and a cable snaps in; the wire stretches under your cursor while you drag, the target socket highlights blue, and the patch feeds straight back into the engine so the encryption and the X-ray wiring update live. Pulling a cable out works the same way. It feels like handling the real hardware, not clicking a form. And the honesty of it: one engine, verified against a known test vector, with nothing faked between what you see and what actually ciphers. How to test it Type something. Watch the rotors step and the lamps light. Long messages show the rotors carrying over, double-step and all. Hit X-ray. The case turns to glass; press a key and follow the lit current path through plugboard → rotors → reflector → back out to the lamp. Wire the plugboard. Drag between two sockets to pair them, drag a cable out to remove it — the cipher changes immediately. Open it up. Toggle the lid, the rotor cover, and the front flap to see how the real case comes apart. Trust but verify. Set rotors I·II·III, reflector B, rings TOU, start HOU, plugs ABCDXZ, and type TOUHOU — you should get MQGGFI , the canonical Enigma test vector. ...
|
1793738883.6641135
|
1794602883.6641135
|
|
18571
|
Flit by @reimunyancat | Stardance
|
reimunyancat
|
1
|
18.0
|
344
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-07-14T16:35:56Z", "hours": 18.0, "multiplier": 17.73, "payout": 344, "blessing": "neutral", "text": "Flit is my answer to a problem I hit every single day: moving a link, a bit of text, or a file between my own devices. The normal fixes all have friction. You email yourself, message yourself, or upload to a cloud drive just to download it thirty seconds later on the machine sitting right next to it. Every one of those involves accounts, apps, and steps that have nothing to do with the actual job. Flit is a self-hosted drop inbox. One Rust binary, no accounts, no cloud. Open the URL on any device and everything you drop shows up on every other device in real time. What it actually improves day to day: Real-time sync over SSE. Paste on the desktop and it’s on your phone before you pick it up, no refresh button. QR pairing, because scanning a code beats typing an IP address and a token on a phone keyboard. Share links and drop links. Hand someone exactly one item (one-time or expiring if you want), or give them a post-only page where they can send files into your box without seeing what’s already in it. It installs as a PWA, so on Android “share to Flit” works from any app. Optional browser-side end-to-end encryption (PBKDF2 into AES-GCM), so a public box stores nothing the server can read. On top of that there’s dark mode, an English/Korean toggle, rate limiting, and an ephemeral mode that spins the box down after it sits idle. It’s live at the link, it survived me recording a demo (barely, devlog #5 has the carnage), and it’s built from scratch in Rust with axum.", "repo": "https://github.com/reimunyancat/Flit", "demo": "https://flit.reimunyancat.com"}]...
|
[{"post_id": 28379, "ts": "202 [{"post_id": 28379, "ts": "2026-07-14T16:31:03Z", "hours": 9.1375, "text": "Flit Devlog #5 Last time I ended on “it’s live, it’s public, anyone can hit the URL.” That felt great for about a day, until I thought about what “anyone can hit the URL” actually means. A public drop box where every visitor can read every drop isn’t a feature, it’s a liability. So this entry is me getting paranoid and cleaning up after myself. The public box problem I wanted encryption without a server that holds keys. If the box is public, the server should store gibberish. So it happens in the browser: type a passphrase and everything runs through PBKDF2 (150k rounds, SHA-256) into an AES-256-GCM key before it leaves the tab. The server only ever sees a FLITENC1: blob. Great, until I tested over the LAN and got TypeError: Cannot read properties of undefined (reading 'deriveKey') . crypto.subtle wasn’t there. Web Crypto only exists in a secure context, so localhost and HTTPS work, but http://192.168.x.x on another device is undefined. So I documented it (HTTPS or a VPN hostname for off-localhost encryption) instead of pretending. A status light that lied The “Live” dot is driven by the SSE onopen . Locally it fired instantly; on the deployed box it sat on “Connecting…” while drops streamed in fine. The proxy was buffering the first chunk, so onopen fired late. Fix: send one byte on connect (a ready event prepended to the stream) so the proxy flushes and the client knows it’s open. A server that wouldn’t die Added graceful shutdown; Ctrl+C then did nothing. with_graceful_shutdown waits for every connection to close, and the SSE stream never closes, that’s its whole point. So it waited forever. I stopped being clever: print, spawn a 500ms timer, std::process::exit(0) . Not elegant, but it actually turns off. Handing over one thing, not the keys Two mirrored features. Share links ( /s/{id} ) hand someone a single item, optionally one-time or expiring, without exposing the inbox. Drop links ( /d/{id} ) are post-only: a guest can send something into my box but can’t see what’s inside. Making it a real app A manifest and service worker so Flit installs to the home screen and loads offline. Web Share Target so Android’s “share” can target it. A server-rendered SVG QR ( qrcode , default-features = false ) so pairing a phone is a scan, not a typed IP. And then it all broke on camera Not proud of this. I’d rewritten the whole front-end in one sitting, shipped it, hit record for the demo, and watched it faceplant. Clicked “Share”: dead button, no request. Console: Uncaught ReferenceError: POST is not defined . I’d written method: POST instead of \"POST\" — a bare identifier that threw before fetch . The drop link “worked”: it copied the string undefined . The culprit was await r.json without the () , so j.url read off the function, not the body. One () fixed it. Once I actually read the file, the rest tumbled out: uploads sent fd.append(\"file\", blob.name) (the filename as text, not the file), download was a.href - URL.createObjectURL(...) (a minus sign, evaluates to NaN), files never rendered because the check was it.kind === \"kind\" , and the status indicator pointed at a #status element that no longer existed. The infuriating part: the Rust backend was innocent. I traced the whole request path expecting a bug in create_drop or the router and found nothing wrong. The crime scene was entirely the front-end, a pile of one-character typos I’d never have caught without reading it line by line. “It compiles / it loads” is not “it works,” and the front-end has no compiler to yell at you. You find out on camera. Where Flit stands Real-time public/private drop box: text, links, files Browser-side E2E encryption Share links and post-only guest drops Installable PWA: offline shell, share target, QR pairing Dark mode, EN/KO, rate limiting, ephemeral mode One binary, Docker image, on Render"}, {"post_id": 24756, "ts": "2026-07-08T15:20:28Z", "hours": 4.8031, "text": "Flit Devlog #4 Devlog #3 ended on software’s most dangerous sentence: it works great on my machine. This one’s about every other machine — a real flit command, a lock on the door, prebuilt binaries for three OSes, a copy on the open internet, and the devices with no terminal at all. Where Flit stops being my tool and becomes a tool. A CLI that respects the pipe The flit client is a thin wrapper around curl with opinions: flit \"text\" , flit -f file , cmd | flit , flit -l . The stdin branch is my favorite — if the script sees it’s on the end of a pipe, whatever flows in becomes a drop, no flags, no quoting. So git log --oneline -5 | flit lands on my iPad before I’ve switched windows. Then I wrote the PowerShell sibling and relearned it isn’t “bash with different keywords”: headers are a hashtable, multipart a whole other ritual, and one missing dash cost me embarrassing time. A lock for the door Flit was wide open — fine on localhost, horrifying anywhere else. The rule: no FLIT_TOKEN , nothing changes; set it, and everything except /health demands it. The interesting part wasn’t whether to check a token but how you hand it over . A CLI sends Authorization: Bearer ; but you can’t staple a header onto a QR code. So the middleware speaks three dialects — Bearer header, ?token= , or cookie — and when the token arrives by URL, the server upgrades it to a cookie so it doesn’t live in your address bar: if from_query { if let Ok ( v ) = format! ( \"flit_token={expected}; Path=/; HttpOnly; SameSite=Lax\" ) .parse () { res .headers_mut () .insert ( header :: SET_COOKIE , v ); } } Open the link once, authed from then on. I also turned the upload cap into a knob ( FLIT_MAX_MB ). Three OSes, one tag Push a v* tag and GitHub Actions builds on three native runners — ubuntu, macos, windows — while a fourth gathers the artifacts into one release. But three OSes means three sets of opinions: Windows insists on .exe , wants shell: bash spelled out, and each names its artifact differently so the release job merges them cleanly. None of it hard in hindsight; all of it a red X on the first try. v0.1.0 now ships Linux, macOS, and Windows binaries from one git tag . Finding a home (the actual hard part) Nobody says this: deploying was easy, finding where was the fight. My first pick ambushed me mid-signup with a credit-card wall on the “free” tier. Closed the tab, went to Render, which read the Dockerfile from the repo and worked first try — thanks to one patch: check PORT first, fall back to FLIT_ADDR , else 0.0.0.0:7777 . Every platform injects PORT and expects you to listen on it; respect that one variable and the container runs anywhere, ignore it and you stare at “deploy succeeded” beside a site that never answers. Flit’s public face: https://flit-xw2a.onrender.com — open on purpose, drops expire in 10 min, uploads cap at 5 MB. The free box dozes when idle, so the first hit yawns before it wakes. The last mile: devices with no terminal The piece it was born for. My laptop has a shell; my iPad and phone don’t. Same trick on both — hijack the system share sheet so “Share → Flit” fires an HTTP request. On iPad that’s Apple Shortcuts; Android uses the open-source HTTP Shortcuts app — identical job. One mental model: the share sheet is the send button. A screenshot goes thumb-to-laptop with no computer in the middle — the entire point. Where Flit stands Real-time receive — drops appear instantly (#2) Clipboard-direct — text/links hit your clipboard (#3) Auto-expiry — drops delete themselves on a TTL (#3) Send from anything — Linux/macOS/Windows CLI, any browser, iPad, Android One binary, downloadable for three OSes, curl -able like a caveman or token-locked if you’re not, plus a live demo at https://flit-xw2a.onrender.com . That’s the build. Flit is shipped."}, {"post_id": 16996, "ts": "2026-06-27T16:05:04Z", "hours": 2.9097, "text": "Flit Devlog #3 Flit has one job: move a thing from one device to another without making you think about it. After devlog #2 it could already do the hard part — receive in real time over SSE, no refresh. But there was still a dumb gap at the end: a link would land, and I’d reach for the mouse, click the card, hit copy. For a project whose whole personality is “frictionless,” that last inch was embarrassing. This entry kills it: paste-on-arrival, and drops that take themselves out when they’re done. Paste before you ask for it The plan felt trivial. The SSE stream already fires an item event whenever something new shows up, so: listen for it, grab the newest text, drop it on the clipboard. No button. You copy on your phone, tab to your laptop, and it’s already under Ctrl+V. source . addEventListener ( \" item \" , async () => { const items = await fetch ( \" /api/items \" ). then (( r ) => r . json ()); if ( items [ 0 ]?. text ) await navigator . clipboard . writeText ( items [ 0 ]. text ); }); Ship it, right? …except the browser quietly says no It didn’t work — and the cruel part is how . No crash, no console error, the text just never landed. The only tell was a buried NotAllowedError . Here’s what nobody warns you about: clipboard.writeText() needs transient user activation . The browser only allows a clipboard write in the short window right after a real gesture — a click, a keypress. A message arriving over SSE is not a gesture. To the browser, a background script grabbing your clipboard is exactly what it should block. Fair. This also explained a bug that was driving me up the wall: auto-copy “only worked after toggling it off and on.” Of course — the toggle click itself was the gesture. So the honest fix was to lean in: the auto-copy switch IS the gesture. You arm it with a click, and the browser trusts the page to keep copying. Not magic — just asking permission in the only language the browser accepts. Drops that clean up after themselves The other half of frictionless is never having to remember to delete things. A drop inbox that fills up forever is just a junk drawer with a URL. So every item gets an expires timestamp at creation, and a reaper task wakes up every 30 seconds to sweep anything past it: let mut tick = tokio :: time :: interval ( Duration :: from_secs ( 30 )); loop { tick .tick () .await ; let t = now (); state .items .lock () .unwrap () .retain (| _ , it | it .expires == 0 || it .expires > t ); } I chose an active reaper over lazy “check on read” expiry on purpose: lazy expiry lets stale items linger until someone looks, and the list can briefly lie about what’s still alive. A background sweep keeps memory and the UI honest. Default is one hour ( FLIT_TTL_SECS=3600 ); 0 means keep forever. Where Flit stands The three quality-of-life wins are all in: Real-time receive — drops appear instantly (devlog #2) Clipboard-direct — text and links land on your clipboard Auto-expiry — drops delete themselves on a TTL And it’s still one binary you can curl at like a caveman if you feel like it. Next up It works great on my machine — the most dangerous sentence in software. Step 5 makes it real for other machines: a proper flit CLI (plus a PowerShell sibling), an auth layer so it isn’t wide open the second it leaves localhost, and a release workflow that ships prebuilt binaries for all three platforms. See you in #4."}, {"post_id": 16063, "ts": "2026-06-26T03:13:29Z", "hours": 1.2758, "text": "Devlog #2 session Flit could swallow things — text, links, files — but the only way to see what was in there was to keep poking it with curl . Functional, sure, but it felt like checking a mailbox by reaching in with my eyes closed. This time I wanted the moment that actually makes Flit worth building: you drop something on one device and it just appears on another, no refresh, no waiting. So I built the inbox page, and then I taught the server how to tap you on the shoulder. The tap is the fun part. My first thought was to have the page ask “anything new?” every couple of seconds, which works but always feels a half-step behind, and it hammers the server for nothing most of the time. WebSockets would do it, but that’s a full two-way phone line when all I need is the server occasionally shouting “hey, look!” down a one-way hallway. That’s exactly what Server-Sent Events are — plain HTTP, the browser reconnects on its own, and I get to keep the whole thing stupidly simple. Under the hood there’s a little broadcast channel. Whenever something gets stored, the server drops the new item’s id into the channel, and every browser that’s currently watching /api/events hears it and re-pulls the list. I left a 15-second background poll in there too, just as a safety net for the one time a connection quietly dies — belt and suspenders, because the entire promise of this thing is “it just shows up,” and I’d rather it be boringly reliable than clever. The first time it worked I had two terminals open. One sitting on curl -N /api/events , the other posting a note — and the event popped out the instant I hit enter, while the browser tab in the corner of my eye updated in the same breath. I may have said “oh, nice” out loud to an empty room. Next I want the drop to land where my hands already are : auto-copy the newest text straight to the clipboard the second it arrives, and let old stuff quietly expire so there’s never anything to tidy up. That clipboard moment is the one I’m most excited about — it’s the whole difference between “a place I go to check” and “it’s already there.“"}, {"post_id": 13662, "ts": "2026-06-22T12:44:23Z", "hours": 0.0, "text": "Devlog #1 — From an empty folder to a working drop box So I finally started building Drop — the little “throw a file/link/note from one device and just grab it on another” thing I keep wishing existed pretty much every day. The whole pitch is no friction: no login, no app-store download, no database to babysit. You run one binary and suddenly there’s a shared inbox sitting on your network. What actually works now I started from literally nothing — cargo new drop –name drop-server — and by the end of the session the server accepts stuff and hands it back. Stood up an axum 0.8 + tokio server with / and /health just to confirm it was breathing. Designed one Item type and tossed everything into an in-memory Arc<Mutex>. No database, on purpose — the point is to be instant and disposable, not durable. POST /api/text takes raw text and is smart enough to tag it as a link when it looks like a URL, otherwise text. POST /api/file accepts a multipart upload and keeps the original filename + content type. GET /api/items returns everything newest-first as JSON. I deliberately strip the raw bytes out of the list so it stays light. GET /api/items/{id}/raw streams the original file back with Content-Disposition: inline. Stuff that tripped me up axum 0.8 changed route params from :id to {id}. I pasted a 0.7 example first and the server panicked on boot. Easy fix once I knew what I was looking at. Multipart is a stream you drain with next_field(), so it has to be mut multipart — the borrow checker kept yelling until I gave in. My first instinct was to shove file bytes straight into the JSON list. Terrible idea for anything big, so the bytes are now #[serde(skip_serializing)] and only ever come out of the /raw endpoint. Quick proof it works curl -d “ship it” localhost:7777/api/text # ok curl -F “ [email protected] ” localhost:7777/api/file # ok curl -s localhost:7777/api/items | jq ‘.[].kind’ # “file” “link” “text” Next up Right now it’s all curl, which is fine for me but not exactly the dream. Next session I’m building the actual inbox web page and wiring up SSE, so a new drop pops up on my other devices instantly — that real-time moment is the whole reason I’m making this."}]...
|
Title: Flit by @reimunyancat | Stardance
Devlogs: Title: Flit by @reimunyancat | Stardance
Devlogs:
Devlog #1 — From an empty folder to a working drop box So I finally started building Drop — the little “throw a file/link/note from one device and just grab it on another” thing I keep wishing existed pretty much every day. The whole pitch is no friction: no login, no app-store download, no database to babysit. You run one binary and suddenly there’s a shared inbox sitting on your network. What actually works now I started from literally nothing — cargo new drop –name drop-server — and by the end of the session the server accepts stuff and hands it back. Stood up an axum 0.8 + tokio server with / and /health just to confirm it was breathing. Designed one Item type and tossed everything into an in-memory Arc<Mutex>. No database, on purpose — the point is to be instant and disposable, not durable. POST /api/text takes raw text and is smart enough to tag it as a link when it looks like a URL, otherwise text. POST /api/file accepts a multipart upload and keeps the original filename + content type. GET /api/items returns everything newest-first as JSON. I deliberately strip the raw bytes out of the list so it stays light. GET /api/items/{id}/raw streams the original file back with Content-Disposition: inline. Stuff that tripped me up axum 0.8 changed route params from :id to {id}. I pasted a 0.7 example first and the server panicked on boot. Easy fix once I knew what I was looking at. Multipart is a stream you drain with next_field(), so it has to be mut multipart — the borrow checker kept yelling until I gave in. My first instinct was to shove file bytes straight into the JSON list. Terrible idea for anything big, so the bytes are now #[serde(skip_serializing)] and only ever come out of the /raw endpoint. Quick proof it works curl -d “ship it” localhost:7777/api/text # ok curl -F “ [email protected] ” localhost:7777/api/file # ok curl -s localhost:7777/api/items | jq ‘.[].kind’ # “file” “link” “text” Next up Right now it’s all curl, which is fine for me but not exactly the dream. Next session I’m building the actual inbox web page and wiring up SSE, so a new drop pops up on my other devices instantly — that real-time moment is the whole reason I’m making this.
Devlog #2 session Flit could swallow things — text, links, files — but the only way to see what was in there was to keep poking it with curl . Functional, sure, but it felt like checking a mailbox by reaching in with my eyes closed. This time I wanted the moment that actually makes Flit worth building: you drop something on one device and it just appears on another, no refresh, no waiting. So I built the inbox page, and then I taught the server how to tap you on the shoulder. The tap is the fun part. My first thought was to have the page ask “anything new?” every couple of seconds, which works but always feels a half-step behind, and it hammers the server for nothing most of the time. WebSockets would do it, but that’s a full two-way phone line when all I need is the server occasionally shouting “hey, look!” down a one-way hallway. That’s exactly what Server-Sent Events are — plain HTTP, the browser reconnects on its own, and I get to keep the whole thing stupidly simple. Under the hood there’s a little broadcast channel. Whenever something gets stored, the server drops the new item’s id into the channel, and every browser that’s currently watching /api/events hears it and re-pulls the list. I left a 15-second background poll in there too, just as a safety net for the one time a connection quietly dies — belt and suspenders, because the entire promise of this thing is “it just shows up,” and I’d rather it be boringly reliable than clever. The first time it worked I had two terminals open. One sitting on curl -N /api/events , the other posting a note — and the event popped out the instant I hit enter, while the browser tab in the corner of my eye updated in the same breath. I may have said “oh, nice” out loud to an empty room. Next I want the drop to land where my hands already are : auto-copy the newest text straight to the clipboard the second it arrives, and let old stuff quietly expire so there’s never anything to tidy up. That clipboard moment is the one I’m most excited about — it’s the whole difference between “a place I go to check” and “it’s already there.“
Flit Devlog #3 Flit has one job: move a thing from one device to another without making you think about it. After devlog #2 it could already do the hard part — receive in real time over SSE, no refresh. But there was still a dumb gap at the end: a link would land, and I’d reach for the mouse, click the card, hit copy. For a project whose whole personality is “frictionless,” that last inch was embarrassing. This entry kills it: paste-on-arrival, and drops that take themselves out when they’re done. Paste before you ask for it The plan felt trivial. The SSE stream already fires an item event whenever something new shows up, so: listen for it, grab the newest text, drop it on the clipboard. No button. You copy on your phone, tab to your laptop, and it’s already under Ctrl+V. source . addEventListener ( " item " , async () => { const items = await fetch ( " /api/items " ). then (( r ) => r . json ()); if ( items [ 0 ]?. text ) await navigator . clipboard . writeText ( items [ 0 ]. text ); }); Ship it, right? …except the browser quietly says no It didn’t work — and the cruel part is how . No crash, no console error, the text just never landed. The only tell was a buried NotAllowedError . Here’s what nobody warns you about: clipboard.writeText() needs transient user activation . The browser only allows a clipboard write in the short window right after a real gesture — a click, a keypress. A message arriving over SSE is not a gesture. To the browser, a background script grabbing your clipboard is exactly what it should block. Fair. This also explained a bug that was driving me up the wall: auto-copy “only worked after toggling it off and on.” Of course — the toggle click itself was the gesture. So the honest fix was to lean in: the auto-copy switch IS the gesture. You arm it with a click, and the browser trusts the page to keep copying. Not magic — just asking permission in the only language the browser accepts. Drops that clean up after themselves The other half of frictionless is never having to remember to delete things. A drop inbox that fills up forever is just a junk drawer with a URL. So every item gets an expires timestamp at creation, and a reaper task wakes up every 30 seconds to sweep anything past it: let mut tick = tokio :: time :: interval ( Duration :: from_secs ( 30 )); loop { tick .tick () .await ; let t = now (); state .items .lock () .unwrap () .retain (| _ , it | it .expires == 0 || it .expires > t ); } I chose an active reaper over lazy “check on read” expiry on purpose: lazy expiry lets stale items linger until someone looks, and the list can briefly lie about what’s still alive. A background sweep keeps memory and the UI honest. Default is one hour ( FLIT_TTL_SECS=3600 ); 0 means keep forever. Where Flit stands The three quality-of-life wins are all in: Real-time receive — drops appear instantly (devlog #2) Clipboard-direct — text and links land on your clipboard Auto-expiry — drops delete themselves on a TTL And it’s still one binary you can curl at like a caveman if you feel like it. Next up It works great on my machine — the most dangerous sentence in software. Step 5 makes it real for other machines: a proper flit CLI (plus a PowerShell sibling), an auth layer so it isn’t wide open the second it leaves localhost, and a release workflow that ships prebuilt binaries for all three platforms. See you in #4.
Flit Devlog #4 Devlog #3 ended on software’s most dangerous sentence: it works great on my machine. This one’s about every other machine — a real flit command, a lock on the door, prebuilt binaries for three OSes, a copy on the open internet, and the devices with no terminal at all. Where Flit stops being my tool and becomes a tool. A CLI that respects the pipe The flit client is a thin wrapper around curl with opinions: flit "text" , flit -f file , cmd | flit , flit -l . The stdin branch is my favorite — if the script sees it’s on the end of a pipe, whatever flows in becomes a drop, no flags, no quoting. So git log --oneline -5 | flit lands on my iPad before I’ve switched windows. Then I wrote the PowerShell sibling and relearned it isn’t “bash with different keywords”: headers are a hashtable, multipart a whole other ritual, and one missing dash cost me embarrassing time. A lock for the door Flit was wide open — fine on localhost, horrifying anywhere else. The rule: no FLIT_TOKEN , nothing changes; set it, and everything except /health demands it. The interesting part wasn’t whether to check a token but how you hand it over . A CLI sends Authorization: Bearer ; but you can’t staple a header onto a QR code. So the middleware speaks three dialects — Bearer header, ?token= , or cookie — and when the token arrives by URL, the server upgrades it to a cookie so it doesn’t live in your address bar: if from_query { if let Ok ( v ) = format! ( "flit_token={expected}; Path=/; HttpOnly; SameSite=Lax" ) .parse () { res .headers_mut () .insert ( header :: SET_COOKIE , v ); } } Open the link once, authed from then on. I also turned the upload cap into a knob ( FLIT_MAX_MB ). Three OSes, one tag Push a v* tag and GitHub Actions builds on three native runners — ubuntu, macos, windows — while a fourth gathers the artifacts into one release. But three OSes means three sets of opinions: Windows insists on .exe , wants shell: bash spelled out, and each names its artifact differently so the release job merges them cleanly. None of it hard in hindsight; all of it a red X on the first try. v0.1.0 now ships Linux, macOS, and Windows binaries from one git tag . Finding a home (the actual hard part) Nobody says this: deploying was easy, finding where was the fight. My first pick ambushed me mid-signup with a credit-card wall on the “free” tier. Closed the tab, went to Render, which read the Dockerfile from the repo and worked first try — thanks to one patch: check PORT first, fall back to FLIT_ADDR , else 0.0.0.0:7777 . Every platform injects PORT and expects you to listen on it; respect that one variable and the container runs anywhere, ignore it and you stare at “deploy succeeded” beside a site that never answers. Flit’s public face: https://flit-xw2a.onrender.com — open on purpose, drops expire in 10 min, uploads cap at 5 MB. The free box dozes when idle, so the first hit yawns before it wakes. The last mile: devices with no terminal The piece it was born for. My laptop has a shell; my iPad and phone don’t. Same trick on both — hijack the system share sheet so “Share → Flit” fires an HTTP request. On iPad that’s Apple Shortcuts; Android uses the open-source HTTP Shortcuts app — identical job. One mental model: the share sheet is the send button. A screenshot goes thumb-to-laptop with no computer in the middle — the entire point. Where Flit stands Real-time receive — drops appear instantly (#2) Clipboard-direct — text/links hit your clipboard (#3) Auto-expiry — drops delete themselves on a TTL (#3) Send from anything — Linux/macOS/Windows CLI, any browser, iPad, Android One binary, downloadable for three OSes, curl -able like a caveman or token-locked if you’re not, plus a live demo at https://flit-xw2a.onrender.com . That’s the build. Flit is shipped.
Flit Devlog #5 Last time I ended on “it’s live, it’s public, anyone can hit the URL.” That felt great for about a day, until I thought about what “anyone can hit the URL” actually means. A public drop box where every visitor can read every drop isn’t a feature, it’s a liability. So this entry is me getting paranoid and cleaning up after myself. The public box problem I wanted encryption without a server that holds keys. If the box is public, the server should store gibberish. So it happens in the browser: type a passphrase and everything runs through PBKDF2 (150k rounds, SHA-256) into an AES-256-GCM key before it leaves the tab. The server only ever sees a FLITENC1: blob. Great, until I tested over the LAN and got TypeError: Cannot read properties of undefined (reading 'deriveKey') . crypto.subtle wasn’t there. Web Crypto only exists in a secure context, so localhost and HTTPS work, but http://192.168.x.x on another device is undefined. So I documented it (HTTPS or a VPN hostname for off-localhost encryption) instead of pretending. A status light that lied The “Live” dot is driven by the SSE onopen . Locally it fired instantly; on the deployed box it sat on “Connecting…” while drops streamed in fine. The proxy was buffering the first chunk, so onopen fired late. Fix: send one byte on connect (a ready event prepended to the stream) so the proxy flushes and the client knows it’s open. A server that wouldn’t die Added graceful shutdown; Ctrl+C then did nothing. with_graceful_shutdown waits for every connection to close, and the SSE stream never closes, that’s its whole point. So it waited forever. I stopped being clever: print, spawn a 500ms timer, std::process::exit(0) . Not elegant, but it actually turns off. Handing over one thing, not the keys Two mirrored features. Share links ( /s/{id} ) hand someone a single item, optionally one-time or expiring, without exposing the inbox. Drop links ( /d/{id} ) are post-only: a guest can send something into my box but can’t see what’s inside. Making it a real app A manifest and service worker so Flit installs to the home screen and loads offline. Web Share Target so Android’s “share” can target it. A server-rendered SVG QR ( qrcode , default-features = false ) so pairing a phone is a scan, not a typed IP. And then it all broke on camera Not proud of this. I’d rewritten the whole front-end in one sitting, shipped it, hit record for the demo, and watched it faceplant. Clicked “Share”: dead button, no request. Console: Uncaught ReferenceError: POST is not defined . I’d written method: POST instead of "POST" — a bare identifier that threw before fetch . The drop link “worked”: it copied the string undefined . The culprit was await r.json without the () , so j.url read off the function, not the body. One () fixed it. Once I actually read the file, the rest tumbled out: uploads sent fd.append("file", blob.name) (the filename as text, not the file), download was a.href - URL.createObjectURL(...) (a minus sign, evaluates to NaN), files never rendered because the check was it.kind === "kind" , and the status indicator pointed at a #status element that no longer existed. The infuriating part: the Rust backend was innocent. I traced the whole request path expecting a bug in create_drop or the router and found nothing wrong. The crime scene was entirely the front-end, a pile of one-character typos I’d never have caught without reading it line by line. “It compiles / it loads” is not “it works,” and the front-end has no compiler to yell at you. You find out on camera. Where Flit stands Real-time public/private drop box: text, links, files Browser-side E2E encryption Share links and post-only guest drops Installable PWA: offline shell, share target, QR pairing Dark mode, EN/KO, rate limiting, ephemeral mode One binary, Docker image, on Render
Ships:
Flit is my answer to a problem I hit every single day: moving a link, a bit of text, or a file between my own devices. The normal fixes all have friction. You email yourself, message yourself, or upload to a cloud drive just to download it thirty seconds later on the machine sitting right next to it. Every one of those involves accounts, apps, and steps that have nothing to do with the actual job. Flit is a self-hosted drop inbox. One Rust binary, no accounts, no cloud. Open the URL on any device and everything you drop shows up on every other device in real time. What it actually improves day to day: Real-time sync over SSE. Paste on the desktop and it’s on your phone before you pick it up, no refresh button. QR pairing, because scanning a code beats typing an IP address and a token on a phone keyboard. Share links and drop links. Hand someone exactly one item (one-time or expiring if you want), or give them a post-only page where they can send files into your box without seeing what’s already in it. It installs as a PWA, so on Android “share to Flit” works from any app. Optional browser-side end-to-end encryption (PBKDF2 into AES-GCM), so a public box stores nothing the server can read. On top of that there’s dark mode, an English/Korean toggle, rate limiting, and an ephemeral mode that spins the box down after it sits idle. It’s live at the link, it survived me recording a demo (barely, devlog #5 has the carnage), and it’s built from scratch in Rust with axum....
|
1793771260.060802
|
1794635260.060802
|
|
18572
|
My first slack bot! by @Samyak | Stardance
|
Samyak
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My first slack bot! by @Samyak | Stardance
|
1793534148.2553911
|
1794138948.2553911
|
|
18573
|
My very own WebOS by @michael67 | Stardance
|
michael67
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My very own WebOS by @michael67 | Stardance
|
1793764556.44779
|
1794369356.44779
|
|
18574
|
VeloxPad🤪 by @Skyler | Stardance
|
Skyler
|
0
|
5.47
|
0
|
[]
|
[{"post_id": 40652, "ts": "202 [{"post_id": 40652, "ts": "2026-08-04T10:29:51Z", "hours": 2.6483, "text": "Well uh, last time i thought I had finished my PCB, but I made some small changes and have finished my PCB case. It took me a lot of time to find and apply the 3d models of components from the PCB, but here it is. I made my case in FreeCAD because I am pretty familiar with the software. Sadly my laptop decided to give up twice during the progress and I had to reset it. But luckily I didn’t lost much. The only weird thing is that I can’t find the mounting hole that is visible in the PCB-editor in the schematic. I guess I’ll look into it later."}, {"post_id": 39484, "ts": "2026-08-02T13:41:14Z", "hours": 2.2906, "text": "I finished my PCB design and spent a lot of time working on the wiring, which was quite challenging. (Had to watch several tutorials and asked some stuff in slack.) I decided not to use all the features of the rotary encoder because the XIAO does not have enough available pins. So, I chose to only use the rotation function of the encoder. I also added several LEDs to the design."}, {"post_id": 10528, "ts": "2026-06-17T08:58:14Z", "hours": 0.5292, "text": "I just made a start today and am pretty proud of my small start. Drawing pcb’s is quite fun, but setting up hackatime and downloading KiCad did took a lot of time. I almost gave up because my laptop started doing strange and didn’t had enough storage to download the installer for KiCad T_T. But after lots of effort I did manage to set everything up (hopefully correct)… After setting up I drew some stuff in the sketch editor and imported it in the PCB editor. I ran into some problems but I have trust that I’ll be able to fix it next time. Also I added 4 switches to my sketch but I can’t find them in the pcb editor… This is though my first time making a pcb. So I think it is probably normal. :)"}]...
|
Title: VeloxPad🤪 by @Skyler | Stardance
Devlogs:
Title: VeloxPad🤪 by @Skyler | Stardance
Devlogs:
I just made a start today and am pretty proud of my small start. Drawing pcb’s is quite fun, but setting up hackatime and downloading KiCad did took a lot of time. I almost gave up because my laptop started doing strange and didn’t had enough storage to download the installer for KiCad T_T. But after lots of effort I did manage to set everything up (hopefully correct)… After setting up I drew some stuff in the sketch editor and imported it in the PCB editor. I ran into some problems but I have trust that I’ll be able to fix it next time. Also I added 4 switches to my sketch but I can’t find them in the pcb editor… This is though my first time making a pcb. So I think it is probably normal. :)
I finished my PCB design and spent a lot of time working on the wiring, which was quite challenging. (Had to watch several tutorials and asked some stuff in slack.) I decided not to use all the features of the rotary encoder because the XIAO does not have enough available pins. So, I chose to only use the rotation function of the encoder. I also added several LEDs to the design.
Well uh, last time i thought I had finished my PCB, but I made some small changes and have finished my PCB case. It took me a lot of time to find and apply the 3d models of components from the PCB, but here it is. I made my case in FreeCAD because I am pretty familiar with the software. Sadly my laptop decided to give up twice during the progress and I had to reset it. But luckily I didn’t lost much. The only weird thing is that I can’t find the mounting hole that is visible in the PCB-editor in the schematic. I guess I’ll look into it later....
|
1793407739.9513328
|
1794012539.9513328
|
|
18575
|
My first slack bot! by @puneethnunna | Stardance
|
puneethnunna
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My first slack bot! by @puneethnunna | Star Title: My first slack bot! by @puneethnunna | Stardance...
|
1793321825.8447576
|
1793926625.8447576
|
|
18576
|
My awesome macropad! by @soonii | Stardance
|
soonii
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @soonii | Stardance
|
1793281769.42926
|
1793886569.42926
|
|
18577
|
My awesome macropad! by @lakshan | Stardance
|
lakshan
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @lakshan | Stardanc Title: My awesome macropad! by @lakshan | Stardance...
|
1793293003.219666
|
1793897803.219666
|
|
18578
|
My very own WebOS by @unicornslayer | Stardance
|
unicornslayer
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My very own WebOS by @unicornslayer | Stard Title: My very own WebOS by @unicornslayer | Stardance...
|
1793526809.0954993
|
1794131609.0954993
|
|
18579
|
AI Sign language to speech translator by @Sahoj | AI Sign language to speech translator by @Sahoj | Stardance...
|
Sahoj
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: AI Sign language to speech translator by @S Title: AI Sign language to speech translator by @Sahoj | Stardance...
|
1793829095.7764616
|
1794433895.7764616
|
|
18580
|
PostureIQ by @jshrihankumar | Stardance
|
jshrihankumar
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: PostureIQ by @jshrihankumar | Stardance
|
1793900923.271168
|
1794505723.271168
|
|
18581
|
OMD.lol [Name TBC] by @Jace | Stardance
|
Jace
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: OMD.lol [Name TBC] by @Jace | Stardance
|
1793357754.5413911
|
1793962554.5413911
|
|
18582
|
Giant Robot Arm by @Bnelson | Stardance
|
Bnelson
|
0
|
3.0
|
0
|
[]
|
[{"post_id": 13541, "ts": "202 [{"post_id": 13541, "ts": "2026-06-22T04:49:48Z", "hours": 1.5603, "text": "Servo Mount!"}, {"post_id": 10705, "ts": "2026-06-17T15:55:43Z", "hours": 1.1286, "text": "Screw Brackets! -4 brackets connected to base for 5mm screws -my idea is to screw the whole arm down to a plywood board that I can hopefully add wheels or another type of transportation to."}, {"post_id": 10125, "ts": "2026-06-16T18:43:31Z", "hours": 0.3097, "text": "Base of Arm -holder for raspberry pi 3 -holder for PMW 16channel Driver"}]...
|
Title: Giant Robot Arm by @Bnelson | Stardance
De Title: Giant Robot Arm by @Bnelson | Stardance
Devlogs:
Base of Arm -holder for raspberry pi 3 -holder for PMW 16channel Driver
Screw Brackets! -4 brackets connected to base for 5mm screws -my idea is to screw the whole arm down to a plywood board that I can hopefully add wheels or another type of transportation to.
Servo Mount!...
|
1793443892.7718775
|
1794048692.7718775
|
|
18583
|
My awesome macropad! by @srip | Stardance
|
srip
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @srip | Stardance
|
1793433129.5084438
|
1794037929.5084438
|
|
18584
|
Polars IO plugin for DuckDB by @natalie | Stardanc Polars IO plugin for DuckDB by @natalie | Stardance...
|
natalie
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Polars IO plugin for DuckDB by @natalie | S Title: Polars IO plugin for DuckDB by @natalie | Stardance...
|
1793296210.1846528
|
1793901010.1846528
|
|
18585
|
BELTA NAV by @redoan | Stardance
|
redoan
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: BELTA NAV by @redoan | Stardance
|
1793980055.07218
|
1794584855.07218
|
|
18586
|
killswitchglove by @mqprime | Stardance
|
mqprime
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: killswitchglove by @mqprime | Stardance
|
1793523395.6196282
|
1794128195.6196282
|
|
18587
|
My awesome macropad! by @MysteryX | Stardance
|
MysteryX
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @MysteryX | Stardan Title: My awesome macropad! by @MysteryX | Stardance...
|
1793782179.6175342
|
1794386979.6175342
|
|
18588
|
Frictionless by @Anvita_P | Stardance
|
Anvita_P
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Frictionless by @Anvita_P | Stardance
|
1793638077.4232414
|
1794242877.4232414
|
|
18589
|
Frozen-Fruit-2K26 by @Joschi | Stardance
|
Joschi
|
0
|
0.3
|
0
|
[]
|
[{"post_id": 8742, "ts": "2026 [{"post_id": 8742, "ts": "2026-06-14T15:22:25Z", "hours": 0.3014, "text": "Added the License and a German translation of it"}]...
|
Title: Frozen-Fruit-2K26 by @Joschi | Stardance
D Title: Frozen-Fruit-2K26 by @Joschi | Stardance
Devlogs:
Added the License and a German translation of it...
|
1793479851.700095
|
1794084651.700095
|
|
18590
|
project by @SD | Stardance
|
SD
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: project by @SD | Stardance
|
1793767843.6967845
|
1794372643.6967845
|
|
18591
|
DVD CNC Plotter by @Lukypuky11cz | Stardance
|
Lukypuky11cz
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: DVD CNC Plotter by @Lukypuky11cz | Stardanc Title: DVD CNC Plotter by @Lukypuky11cz | Stardance...
|
1793292914.4350362
|
1793897714.4350362
|
|
18592
|
Game by @carol | Stardance
|
carol
|
1
|
13.0
|
0
|
[{"ship_num": 1, "ts": "2026-0 [{"ship_num": 1, "ts": "2026-06-21T12:40:42Z", "hours": 13.0, "multiplier": null, "payout": 0, "blessing": "neutral", "text": "Silly Monkey Scroller Game", "repo": "https://github.com/carolllltent/Gr-11", "demo": "https://drive.google.com/file/d/17--oe243K-rmj2cl-zDiEzytGUyeo-Ey/view?usp=sharing"}]...
|
[{"post_id": 10384, "ts": "202 [{"post_id": 10384, "ts": "2026-06-17T02:08:35Z", "hours": 13.2592, "text": "Game that develops motor skills of user by imitating dadish game"}]...
|
Title: Game by @carol | Stardance
Devlogs:
Game t Title: Game by @carol | Stardance
Devlogs:
Game that develops motor skills of user by imitating dadish game
Ships:
Silly Monkey Scroller Game...
|
1793944256.4675384
|
1794030656.4675384
|
|
18593
|
My very own WebOS by @elizabeth97009700 | Stardanc My very own WebOS by @elizabeth97009700 | Stardance...
|
elizabeth97009700
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My very own WebOS by @elizabeth97009700 | S Title: My very own WebOS by @elizabeth97009700 | Stardance...
|
1793425979.195629
|
1794030779.195629
|
|
18594
|
My awesome macropad! by @deadblast07 | Stardance
|
deadblast07
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @deadblast07 | Star Title: My awesome macropad! by @deadblast07 | Stardance...
|
1793926208.527483
|
1794790208.527483
|
|
18595
|
Fidhing game by @luke_g | Stardance
|
luke_g
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Fidhing game by @luke_g | Stardance
|
1793285351.593438
|
1793890151.593438
|
|
18596
|
My first slack bot! by @adityasaivadlamudi99 | Sta My first slack bot! by @adityasaivadlamudi99 | Stardance...
|
adityasaivadlamudi99
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My first slack bot! by @adityasaivadlamudi9 Title: My first slack bot! by @adityasaivadlamudi99 | Stardance...
|
1793407803.5360744
|
1794012603.5360744
|
|
18597
|
HelpNearby by @Pavel | Stardance
|
Pavel
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: HelpNearby by @Pavel | Stardance
|
1793814792.9566185
|
1794419592.9566185
|
|
18598
|
Frictionless by @Maanvitha_Madasu | Stardance
|
Maanvitha_Madasu
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: Frictionless by @Maanvitha_Madasu | Stardan Title: Frictionless by @Maanvitha_Madasu | Stardance...
|
1793861528.6956246
|
1794466328.6956246
|
|
18599
|
My awesome macropad! by @dishita | Stardance
|
dishita
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: My awesome macropad! by @dishita | Stardanc Title: My awesome macropad! by @dishita | Stardance...
|
1793354229.9411547
|
1793959029.9411547
|
|
18600
|
morse code translator by @hackervyralz | Stardance
|
hackervyralz
|
0
|
0.0
|
0
|
[]
|
[]
|
Title: morse code translator by @hackervyralz | St Title: morse code translator by @hackervyralz | Stardance...
|
1793904513.7012038
|
1794509313.7012038
|