posts
This week in open source, the commons took hits from every direction at once.
A third wave of malware quietly took over more than nineteen hundred packages in the Arch User Repository before the project slammed the brakes and froze package adoption entirely. A man in Atlanta is staring at five years in federal prison — not for anything he did with his phone, but for using a privacy feature on it. The biggest desktop environments are fighting off a flood of sloppy AI-generated code while the industry's newest marketing trick is to advertise how much of the product was "written by AI" instead of by people. And meanwhile, in the middle of all that noise, the people who actually build this stuff kept building: a new COSMIC desktop, a Rust-native X server crossing the finish line, GOG officially coming to Linux, open firmware for modern AMD boards, and NetBSD turning eleven with RISC-V support.
This is the open source news digest, and I want to read it the way I read everything: by asking who benefits when the thing you use every day gets a little less safe, a little more owned, and a little less yours.
Let me start with the story that should make every single person using Arch — and honestly, every person using any free software — sit up. On July thirtieth, Arch Linux disabled package adoption on the Arch User Repository after a third sustained wave of malicious takeovers. The campaign has a nickname now: Atomic Arch. Attackers have compromised well over nineteen hundred packages across three waves since late May. First wave, around June ninth: they adopted orphaned packages — abandoned projects that nobody maintains anymore — and slipped npm install commands into the build scripts. Those lifecycle scripts silently pulled down a Rust-based credential stealer that could optionally drop an eBPF rootkit to hide its own traces in the kernel. The Arch team purged more than nineteen hundred affected packages in mid-June and declared the repository clean. Within hours, the second wave rolled out, this time with heavily obfuscated JavaScript downloaders. And now the third wave flips the detection playbook entirely: attackers embedding compiled ELF binaries directly into build scripts, static payloads that execute during the makepkg build phase, bypassing the JavaScript-based detection that caught the earlier waves.
Let me translate what that actually means, because the technical details hide the real story. The Arch User Repository is a community repository. It runs on trust — thousands of volunteers packaging software for other people to use, no corporate security team, no paid gatekeepers, no bug bounty that pays a living wage. When you install from the AUR, you are trusting that the person who wrote the package is not the person who wants to steal your passwords. That trust is the attack surface. The attackers did not break into Arch's servers. They did not exploit some clever kernel bug. They adopted orphaned packages — the digital equivalent of moving into an abandoned house — and put their malware in the build instructions. Nineteen hundred packages. Three waves. Months of volunteer time spent cleaning up a mess that no one was paid to clean up.
And here's the systemic part that nobody in the headlines will say out loud: this is what an open commons looks like under siege. The official repositories stayed untouched. The core infrastructure held. But the community layer — the part built and maintained by volunteers, the part that exists precisely because no one owns it — that's the part that got attacked, because that's the part where trust is the only security, and trust is free to exploit. When the tech giants talk about "open source supply chain security," what they usually mean is: your unpaid labor is our free quality assurance, and if it fails, we'll sell you a product to fix it. The AUR crisis is the purest demonstration of that logic: the commons gets attacked, and the people who actually own the infrastructure — the volunteers — absorb the cost, while the companies that depend on that infrastructure sell "solutions" to the problem they didn't have to solve.
Now the second story, and this one should make you genuinely angry. A GrapheneOS privacy feature just became the basis for a federal indictment. Samuel Tunick, an Atlanta resident, faces up to five years in prison. The charge: destruction of property to prevent government seizure, under a federal statute about blocking evidence seizure. The "destruction" was his phone wiping itself after he gave border agents a passcode — a duress password, a feature designed exactly for this scenario. Let me tell you what happened. In January, Customs and Border Protection put Tunick into secondary inspection at Atlanta's airport as he returned from the Dominican Republic. Agents searched him, demanded the passcode to his phone, all under the pretext of looking for CSAM. He asked for a lawyer multiple times. They ignored him. They never read him his Miranda rights. When he pushed back, they told him it was "a whole different ballgame" at the border, and they didn't need a warrant. He eventually handed over a passcode. The moment it was entered, the screen flickered, the phone rebooted, and its contents erased in front of the same agents who'd demanded access. That's the duress password working exactly as designed — a panic feature that lets you surrender something plausible while the real data stays safe.
And the state's response to a privacy feature working as intended is to indict the man who used it. Five years. The motion filed by his lawyer stresses that his real crime, in the eyes of the agents, was his association with Defend the Atlanta Forest, an activist group fighting the construction of a police training facility. The FBI had been circling that movement. So let's be clear about what this case is: an activist, flagged for his organizing, returns from a trip abroad, is detained without a warrant at the border, is denied a lawyer, is denied his rights, is pressured into handing over a passcode, and when the privacy feature does what it was built to do, the state charges him with a federal crime.
This is the pattern. It's the same pattern we saw with the Epstein case, where there was actual proof, actual documentation, actual victims, and the powerful people in that orbit walked — until one of them had a problem with another powerful person. The elites can do anything they want. Average people are always the ones being targeted. The surveillance state doesn't go after the people who own the infrastructure; it goes after the people who might challenge them. A duress password is a tool built so a journalist, an activist, a regular person under coercion can hand over something that looks like everything while protecting what matters. And the moment that tool is used, the state calls it evidence destruction. Think about what that means for everyone else: if a panic feature that protects your data under coercion is now a crime, then privacy itself is the crime. Encryption becomes suspicious. A passcode that wipes your phone becomes evidence of guilt. The tools that let ordinary people protect themselves from the state are being redefined, one case at a time, as tools of the guilty.
And look at the ecosystem reacting in real time. Windscribe just released deGDID, a tool to remove and block Microsoft's Global Device ID — the tracking identifier Microsoft has been quietly embedding in Windows. A commercial VPN company is shipping a free tool to strip surveillance out of the operating system, because the operating system vendor won't stop. That's the state of things: the people who make the OS have turned it into a surveillance instrument, and the people who sell privacy have to clean up after them. Meanwhile GrapheneOS, the privacy-hardened Android, gets its flagship feature turned into a federal charge. When the tool that protects you becomes the evidence against you, you have to ask: whose side is the system on?
Third story thread, and this one has a darkly funny edge to it. The AI gold rush has reached the open source maintainers, and they are not impressed. Debian developers are voting on five proposals about LLM use — spanning everything from a total ban on AI-generated contributions to pragmatic acceptance. GNU GCC, one of the oldest and most important compilers in existence, just banned AI code contributions, but sensibly — not a blanket ban, but a rule that AI-generated code must be clearly labeled and reviewed like any other contribution, because the maintainers refuse to be the free QA layer for models that were trained on their own code. GNOME is working to establish an RFC process — a formal way to make decisions — specifically because they're battling sloppy AI-generated extensions flooding their ecosystem. And then there's Starling, a new Linux desktop built using AI in six months, being covered with the breathless excitement of a tech demo.
And the user's comment on that one is perfect: "seems like 'written by AI' is the next big trending feature after 'written in Rust' lmao." That's the whole industry in one sentence. First it was "written in Rust" — a quality signal, real engineering, memory safety, actually meaningful. Now it's "written by AI" — and it's being marketed the same way, as if being generated by a statistical model trained on everyone else's work is a feature instead of a red flag. Six months to build a desktop. Sure. And how many volunteer years will it take to fix the bugs, audit the generated code, and maintain what the model produced? That cost isn't counted in the demo. It never is.
Here's the extraction mechanism underneath all three of these stories — AUR, GCC, GNOME, Starling, Debian. The AI companies trained their models on open source code. All of it. The commons built by volunteers over thirty years became the free training data for a trillion-dollar industry. Now that industry's output is flooding back into the same repositories, and the maintainers — still volunteers, still unpaid — are expected to review it, fix it, and keep the ecosystem safe. The model extracted value from the commons, and the commons is now paying the cost of the model's output. That's the whole arc of the AI boom in one paragraph: extract the labor, sell it back, and leave the cleanup to the people who did the original work. Debian's five proposals, GCC's sensible ban, GNOME's RFC process — these are communities drawing a line and saying: we built this, we maintain this, and we will not be your free quality control.
And the enclosure attempt is already underway, dressed in patriotic clothing. OpenUK — the UK's open source advocacy body — wants Britain to stop "giving away" open source creations. Their report points out that the Model Context Protocol, the standard now behind how AI models connect to tools and data, was built by two engineers in London and ended up under US-based governance at the Linux Foundation. Their proposal: a British National Foundation that holds code, governance, and standards built with UK public money, instead of watching that work migrate to servers and boards elsewhere. On the surface, that sounds reasonable — national pride in open source, a sovereign tech fund. Underneath, it's the same move every enclosure makes: when something built with public money proves valuable, the response is not to keep it public — it's to build a vehicle to own it. The Linux Foundation, the Sovereign Tech Agency, Open Atom — every country wants its own foundation now, because open source is the infrastructure of the 21st century, and whoever holds the governance holds the power. The commons is valuable. That's why everyone wants to carve it up.
Now let me talk about the stuff that went right this week, because the builders kept building, and the pattern underneath their work is the answer to all of the above.
The kernel moved. The first Arch Linux ISO powered by Linux kernel 7.1 is out. Fedora 45 will enable shadow stack protection by default on 64-bit — a hardware-backed defense that makes a whole class of memory corruption attacks much harder to pull off. That's not a flashy headline, but it's the boring, essential work that keeps the whole thing standing: when the attacks get more sophisticated, the defense gets deeper. New Debian 13 Trixie kernel security updates fixed sixty-eight vulnerabilities — sixty-eight — in one release. Ubuntu 26.04's virtualization HWE stack ships newer QEMU and libvirt. Liquorix kernel 7.1-7 dropped irqbalance to preserve low-latency performance, because for some people a millisecond matters. Fwupd 2.1.7 keeps improving the firmware update layer. GNU Binutils 2.47 adds new RISC-V features, linker improvements, and reproducible builds — meaning you can verify that the binary you're running is really built from the source you're reading. That last one matters more than most people realize: reproducible builds are the defense against exactly the kind of supply chain attack that hit the AUR.
The desktops moved. COSMIC 1.5 is out — System76's Rust-based desktop, now with a wave of improvements, and it's showing up in Gentoo-based MocaccinoOS 26.08 alongside KDE Plasma 6.6.6. KDE Neon shipped a new build, rolling Plasma 6 on top of Ubuntu 24.04. GNOME 50.3 enables remote desktop hardware acceleration for AMD GPUs. Wayfire 0.11, the Wayland compositor, adds better fractional scaling and HDR rendering. Hyprland 0.56.1 came out one week after 0.56.0 with fourteen regression fixes — a project moving fast enough to ship, break, and fix within seven days. And in the "the old world is still alive" corner: YServer 1.4.0, a Rust-native X11 server, finally crossed the threshold into actually usable territory. An X server written in Rust, hitting a usable milestone, in 2026 — that's not nostalgia, that's the future of the past: new engineering capacity applied to the oldest infrastructure we still depend on, because that infrastructure is still everywhere.
The apps moved. Krita 5.3.3 dropped with the first stable Qt 6 port — the painting app that animators and artists rely on, now on modern foundations. Shotcut 26.7 added a graphics adapter for Linux and a shake video filter. Calibre 9.12, the e-book manager, added the option to convert GIF images to JPEG and WebP. Firefox 153.0.1 fixed a batch of issues and crashes. FireDragon swooped in with a new release. Proton Pass improved its autofill. Mission Center added battery, partition, and power draw info to its system monitor. GOverlay 1.8.10 shipped after a one-week sprint of automated testing and polish. Shelly 3.0 — the GUI package manager for Arch — landed as a major update, got a 3.0.1 with polished AUR browsing and tighter Flatpak integration, and then a v3.0.1+1 hotfix just two days after the major Zig rewrite stabilization. And Fooyin 0.12 added audio conversion, three visualization plugins, and native PulseAudio support — which prompted exactly the right comment from one reader: isn't PulseAudio support a bit late? Didn't everyone migrate to PipeWire? And the answer is: no, not everyone. A huge installed base still runs PulseAudio, and supporting them is what free software is for. Choice is the product. That's the whole point.
The self-hosters ate well this week. Immich 3.1 added new filtering options, a wakelock, map date range, and better OAuth integration. Ente Photos added memory reactions, desktop upgrades, and curated sharing controls. Linkwarden 2.16 brought a modernized design, link format re-preservation, and a new search modal. ownCloud Web 12.5.0 added Collabora save-as and a new HTML editor. RomM 5.0 launched with a revamped UI and shareable savestates for the retro game library manager. Every one of those is a brick in the wall of "you don't have to rent your data from a corporation" — photos, links, documents, games, all self-hosted, all owned by the person running the server. That's the quiet counter-revolution: while the platforms enclose, the self-hosters retreat to their own infrastructure and rebuild the commons in miniature.
The hardware moved, and this is genuinely big news disguised as a niche story. The first open-source firmware for a modern AMD Ryzen AM5 platform was released — for the MSI B850 P-WIFI board. Let me say that again slowly: open source firmware for a current-generation AMD motherboard. For years, the firmware layer of your computer — the code that runs before the OS, the code that owns everything — has been a black box, proprietary, untouchable. Coreboot and its successors have been fighting for decades to open that layer, and this is a modern platform with open firmware. That's not a hobbyist curiosity. That's the foundation of actual ownership: if you can't see and control the firmware, you don't own your computer — you're renting it from the vendor and the vendor's supply chain. Keychron also released open source firmware for gaming mice, and Linux users stand to gain the most, because open firmware means the hardware works with your OS instead of requiring vendor software to even function. And GOG — the good old games store — is officially coming to Linux. The store that built its brand on "you own your games" is bringing its catalog to the platform where ownership is the default. That's not charity. That's the market finally admitting what the numbers have shown for years: Linux users buy games, and they buy them more when the store respects them.
And NetBSD 11.0 shipped with RISC-V support and enhanced Linux system call compatibility. NetBSD is the project that runs on everything from toasters to servers, and its new release quietly extends the reach of the free world to a new architecture. RISC-V is the open instruction set — the hardware commons — and every project that supports it is building the alternative to the locked-down chip world. The ecosystem is laying the tracks for a future where the silicon itself doesn't require permission.
Now step back and look at the shape of this week, because the shape is the analysis. On one side: malware exploiting the trust of a volunteer repository. AI output flooding the commons and demanding free review. A surveillance case weaponizing a privacy feature. A national foundation proposing to enclose public code. On the other side: sixty-eight kernel vulnerabilities fixed, shadow stacks enabled by default, reproducible builds, open firmware for modern boards, a store coming to Linux because ownership sells, a desktop environment built in Rust because memory safety matters, a museum-piece X server resurrected in a memory-safe language, self-hosted alternatives getting better every week. The builders built, and the extractors extracted. Same week, same ecosystem, two completely different relationships to the people who use the software.
That's not a coincidence, and it's not a vibe. It's structure. The open source commons is the only corner of the digital world where ordinary people still hold something approximating ownership — where the code is yours to read, the data is yours to host, the firmware is yours to inspect, the community is yours to join. And because it's not owned, no one profits from keeping it safe the way they profit from keeping a subscription service sticky. The malware authors exploit it because trust is free. The AI companies extract from it because the labor was donated. The state weaponizes its tools because privacy threatens control. And when the commons produces something valuable — a protocol, a standard, a foundation — the corporate world moves to enclose it, to own the governance, to hold the keys. That's the pattern under every headline this week. It's not that the good people are winning and the bad people are losing. It's that the commons is the target, because the commons is the only thing left that nobody can bill you for.
The shareholder-first model doesn't care about the commons — it can't. The incentive structure rewards extraction: take the free labor, sell it back, and charge for the cleanup. The volunteer maintainer who reviews your AI-generated pull request for free is doing work that a company would bill you eighty dollars an hour for, and the company gets the model trained on their code, and the cycle repeats. The border agent who treats a duress password as evidence destruction is enforcing the logic of a state that sees encrypted data as a threat by default. The foundation that wants to "rescue" open source from giving itself away is proposing the standard enclosure: public wealth, private governance. This isn't a few bad actors. The whole system is arranged so that the commons loses and the extractors win, and then the extractors sell you the solution to the problem they created.
So what do you actually do with a week like this? Let me give you concrete, practical moves, because the whole point of noticing the pattern is to act on it.
One: if you use Arch and you install from the AUR, learn to read a PKGBUILD. It's a text file. It's short. It's the build recipe for everything you install from the community repo, and it takes ten minutes to learn what each line does. The Atomic Arch attacks worked because people installed without looking. The single most effective security habit in the entire open source world is not a fancy tool — it's looking at what you're about to run before you run it. Check the source URLs, check the maintainer, check whether the package you're installing is a well-known project or an orphaned package that changed hands recently. If a package you've never heard of is suddenly "adopted" and updated aggressively, that's the exact shape of the attack. Free tools that flag this exist — use them, or just use the official repositories, which stayed clean through all three waves.
Two: if you use a smartphone, understand what a duress password is and why the Tunick case matters to you personally. A duress password is a panic feature — you give the agent a password, and what it unlocks is not your real data. The state's position, now endorsed by a federal indictment, is that using that feature is destroying evidence. That means your privacy decisions are now potentially criminal decisions, and you should make them with open eyes. Encrypt your phone. Use a strong passcode. Know that at the border, in the US, agents can and do demand access with no warrant. Decide in advance what you will and won't do under pressure, because the pressure is the point — they count on you not having a plan. And support the organizations and projects that push back on this: GrapheneOS, the EFF, the civil liberties groups that defend the right to encryption.
Three: be deliberate about what you run AI-generated code in. Debian, GCC, and GNOME are all drawing lines — that's the community saying review the output, label the output, don't let the output become unexamined infrastructure. You should do the same. If a project advertises that it was "built by AI in six months," that's not a selling point — that's a warning label telling you the code has had less human review. Treat AI-generated contributions the way you'd treat any code from an unknown contributor: review it, test it, and don't put it in your critical path until it's earned trust. The hype cycle will try to convince you that "written by AI" is the new "written in Rust." It is not. One is a quality signal. The other is a red flag wearing a quality signal's clothes.
Four: self-host something this month. Immich for photos, Ente for secure storage, Linkwarden for bookmarks, ownCloud for documents, RomM for retro games — every one of those projects got better this week, and every one of them puts your data back under your control. You don't need a server rack. A used mini PC or even a Raspberry Pi can run most of them. The point isn't the scale — it's the direction. Every piece of data you stop renting from a corporation is a small act of ownership, and ownership is the thing the extraction model can't stand.
Five: if you can spare a few dollars, give them to a builder. Krita, GNOME, KDE, NetBSD, System76's COSMIC, Servo, the firmware projects — these are the people who did the actual work this week. Sixty-eight vulnerabilities fixed. Shadow stacks enabled. Reproducible builds shipped. Open firmware released. None of that is funded by venture capital. It's funded by donations and the people who give them. A monthly donation to one project you actually use is worth more than any subscription you'll ever pay, because it buys you something the subscription never will: a tool that belongs to you and everyone else, that no one can take away or sell back to you.
Here's the closing thought. Your tools are under attack from every side this week: malware that exploits trust, AI that extracts and floods back, a state that treats your privacy as a crime, and foundations that want to own what you built together. But the same week, the builders shipped open firmware for modern hardware, a store decided to respect Linux users, a Rust X server crossed the finish line, and sixty-eight holes in the kernel got closed. The commons is under siege — but the siege only matters if the people inside the walls don't notice. The system is working exactly as designed. The question is who it's designed for — and whether the people who actually build and use the commons are willing to defend it like it's theirs. Because it is theirs. It's yours. And the only way the extractors win is if you forget that.
Esta semana, el software libre recibió golpes por todos lados a la vez.
Una tercera oleada de malware se apoderó de más de mil novecientos paquetes del Arch User Repository antes de que el proyecto congelara la adopción de paquetes. Un hombre en Atlanta enfrenta cinco años de prisión federal por usar una función de privacidad en su teléfono. Los escritorios más grandes luchan contra una inundación de código generado por IA, mientras el nuevo truco de marketing es anunciar cuánto del producto fue "escrito por IA" en lugar de por personas. Y mientras tanto, los que realmente construyen estas cosas siguieron construyendo: un nuevo escritorio COSMIC, un servidor X nativo en Rust cruzando la línea de meta, GOG llegando oficialmente a Linux, firmware abierto para placas AMD modernas y NetBSD once con soporte RISC-V.
Este es el resumen semanal de noticias de código abierto, y quiero leerlo preguntándome quién gana cuando lo que usas cada día se vuelve un poco menos seguro, un poco más ajeno y un poco menos tuyo.
Empecemos con la historia que debería hacer que cada usuario de Arch se siente a prestar atención. El treinta de julio, Arch deshabilitó la adopción de paquetes en el AUR tras una tercera ola sostenida de tomas maliciosas. La campaña tiene apodo: Atomic Arch. Más de mil novecientos paquetes comprometidos en tres olas desde finales de mayo. Primera ola: los atacantes adoptaron paquetes huérfanos — proyectos abandonados — y colaron comandos de instalación npm en los scripts de compilación. Esos scripts descargaban un ladrón de credenciales en Rust que podía soltar un rootkit eBPF para ocultar sus huellas en el kernel. El equipo purgó los paquetes a mediados de junio y declaró limpio el repositorio. En horas llegó la segunda ola, con descargadores JavaScript ofuscados. Y la tercera ola le da la vuelta al libro de jugadas: binarios ELF compilados incrustados directamente en los scripts de compilación, cargas estáticas que se ejecutan durante la fase de makepkg, evitando la detección que atrapó a las anteriores.
Traduzcamos lo que eso significa, porque los detalles técnicos ocultan la historia verdadera. El AUR es un repositorio comunitario que funciona con confianza: miles de voluntarios empaquetando software, sin equipo de seguridad corporativo. Cuando instalas desde el AUR, confías en que quien escribió el paquete no quiere robar tus contraseñas. Esa confianza es la superficie de ataque. No irrumpieron en los servidores de Arch: adoptaron paquetes huérfanos — el equivalente digital de mudarse a una casa abandonada — y pusieron el malware en las instrucciones de compilación. Tres olas. Meses de trabajo voluntario limpiando un desastre que nadie estaba pagado para limpiar.
Y aquí está la parte sistémica que nadie dirá en voz alta: esto es un bien común abierto bajo asedio. Los repositorios oficiales no fueron tocados, pero la capa comunitaria — la que existe precisamente porque nadie la posee — fue la atacada, porque la confianza es gratis de explotar. Cuando los gigantes hablan de "seguridad de la cadena de suministro del código abierto", quieren decir: tu trabajo no remunerado es nuestro control de calidad gratuito, y si falla, te venderemos un producto para arreglarlo.
La segunda historia debería hacerte enojar de verdad. Una función de privacidad de GrapheneOS se convirtió en la base de una acusación federal. Samuel Tunick, de Atlanta, enfrenta hasta cinco años de prisión. El cargo: destrucción de propiedad para impedir el decomiso gubernamental. La "destrucción" fue que su teléfono se borrara solo tras entregar una contraseña de pánico — una función diseñada exactamente para este escenario. En enero, la Patrulla Fronteriza lo puso en inspección secundaria en Atlanta al volver de República Dominicana. Los agentes exigieron la contraseña de su teléfono, con el pretexto de buscar material de abuso infantil. Pidió un abogado varias veces. Lo ignoraron. Nunca le leyeron sus derechos. Cuando protestó, le dijeron que en la frontera "el juego es diferente" y que no necesitaban orden judicial. Finalmente entregó una contraseña. En el momento de ingresarla, el teléfono se reinició y su contenido se borró frente a los mismos agentes. La contraseña de pánico funcionó exactamente como fue diseñada.
Y la respuesta del estado a una función de privacidad que funciona es acusar al hombre que la usó. Cinco años. La moción de su abogado subraya que su verdadero crimen, a los ojos de los agentes, era su asociación con Defend the Atlanta Forest, un grupo que lucha contra un centro de entrenamiento policial. Seamos claros: un activista, marcado por su organización, es detenido sin orden judicial, se le niega un abogado, se le presiona para entregar una contraseña, y cuando la función de privacidad hace lo que fue construida para hacer, el estado lo acusa de un delito federal.
Este es el patrón. El mismo del caso Epstein: había pruebas reales, documentación real, víctimas reales, y la gente poderosa de esa órbita caminó libre. Las élites pueden hacer lo que quieran. La gente común siempre es la atacada. El estado de vigilancia no va tras quienes poseen la infraestructura; va tras quienes podrían desafiarlos. Una contraseña de pánico es una herramienta para que una persona bajo coacción entregue algo que parece todo mientras protege lo que importa. Y en cuanto se usa, el estado la llama destrucción de pruebas. Si una función que protege tus datos bajo coacción ahora es un crimen, entonces la privacidad misma es el crimen.
Y mira al ecosistema reaccionando en tiempo real. Windscribe acaba de lanzar deGDID, una herramienta para bloquear el identificador de seguimiento que Microsoft incrusta en Windows: una empresa VPN envía una herramienta gratuita para quitar la vigilancia del sistema operativo, porque el fabricante no va a parar. Cuando la herramienta que te protege se convierte en la evidencia contra ti, pregúntate: ¿de qué lado está el sistema?
Tercer hilo, con un borde oscuramente cómico. La fiebre del oro de la IA llegó a los mantenedores de código abierto, y no están impresionados. Debian vota cinco propuestas sobre el uso de LLM — desde prohibir contribuciones generadas por IA hasta aceptarlas con pragmatismo. GNU GCC, uno de los compiladores más importantes que existen, prohibió el código IA, pero con sensatez: que esté etiquetado y revisado como cualquier contribución, porque los mantenedores se niegan a ser el control de calidad gratuito de modelos entrenados con su propio código. GNOME trabaja en un proceso RFC para luchar contra las extensiones generadas por IA. Y luego está Starling, un escritorio Linux construido con IA en seis meses, cubierto con la emoción de una demostración tecnológica.
El comentario de un lector lo resume: "parece que 'escrito por IA' es la próxima gran característica de moda después de 'escrito en Rust'". Primero fue "escrito en Rust" — una señal de calidad real, seguridad de memoria, significativo. Ahora es "escrito por IA" — comercializado igual, como si ser generado por un modelo entrenado con el trabajo de todos fuera una característica y no una bandera roja. Seis meses para construir un escritorio. ¿Y cuántos años de voluntariado tomará arreglar lo que el modelo produjo? Ese costo no se cuenta en la demostración.
Aquí está el mecanismo debajo de estas historias — AUR, GCC, GNOME, Starling, Debian. Las empresas de IA entrenaron sus modelos con código abierto. Todo. Los bienes comunes construidos por voluntarios durante treinta años se volvieron los datos gratuitos de una industria de billones. Ahora su producción inunda los mismos repositorios, y los mantenedores — voluntarios, no remunerados — deben revisarla y mantener seguro el ecosistema. El modelo extrajo valor de los bienes comunes, y los bienes comunes pagan el costo de la producción del modelo. Todo el arco del auge de la IA en un párrafo: extrae el trabajo, véndelo de vuelta, deja la limpieza a quienes hicieron el trabajo original.
Y el cercamiento ya está en marcha, con ropa patriótica. OpenUK quiere que Reino Unido deje de "regalar" creaciones de código abierto. Su informe señala que el Model Context Protocol, el estándar detrás de cómo los modelos de IA se conectan a herramientas, fue construido por dos ingenieros en Londres y terminó bajo gobernanza estadounidense en la Linux Foundation. Proponen una Fundación Nacional británica que tenga código, gobernanza y estándares construidos con dinero público. En la superficie suena razonable. Debajo, es el movimiento de todo cercamiento: cuando algo construido con dinero público demuestra valor, la respuesta no es mantenerlo público — es construir un vehículo para poseerlo. Quien tiene la gobernanza tiene el poder.
Ahora lo que salió bien esta semana, porque los constructores siguieron construyendo, y el patrón debajo de su trabajo es la respuesta a todo lo anterior.
El kernel se movió. La primera ISO de Arch con el kernel Linux 7.1 ya está disponible. Fedora 45 habilitará la protección de shadow stack por defecto en 64 bits — una defensa de hardware que hace mucho más difícil una clase entera de ataques. No es un titular llamativo, pero es el trabajo aburrido y esencial: cuando los ataques se sofistican, la defensa se profundiza. Las actualizaciones de seguridad del kernel de Debian 13 Trixie arreglaron sesenta y ocho vulnerabilidades en una sola versión. La pila HWE de virtualización de Ubuntu 26.04 trae QEMU y libvirt más nuevos. Fwupd 2.1.7 mejora la capa de firmware. GNU Binutils 2.47 agrega características RISC-V, mejoras al enlazador y compilaciones reproducibles — puedes verificar que el binario que ejecutas realmente viene del código que lees. Eso importa más de lo que crees: es la defensa contra exactamente el ataque que golpeó al AUR.
Los escritorios se movieron. COSMIC 1.5 ya está disponible — el escritorio en Rust de System76 — y aparece en MocaccinoOS 26.08 junto a KDE Plasma 6.6.6. KDE Neon lanzó una nueva compilación, Plasma 6 rodante sobre Ubuntu 24.04. GNOME 50.3 habilita aceleración de hardware de escritorio remoto para GPUs AMD. Wayfire 0.11 agrega mejor escalado fraccional y renderizado HDR. Hyprland 0.56.1 salió una semana después de 0.56.0 con catorce correcciones de regresión. Y en el rincón del viejo mundo: YServer 1.4.0, un servidor X nativo en Rust, cruzó el umbral de lo usable. Un servidor X en Rust en 2026 no es nostalgia; es nueva ingeniería aplicada a la infraestructura más antigua de la que aún dependemos.
Las aplicaciones se movieron. Krita 5.3.3 salió con el primer port estable de Qt 6. Shotcut 26.7 agregó un adaptador gráfico para Linux y un filtro de video shake. Calibre 9.12 convierte GIF a JPEG y WebP. Firefox 153.0.1 arregló un lote de fallos. FireDragon llegó con nueva versión. Proton Pass mejoró su autocompletado. Mission Center agregó batería, particiones y consumo de energía. GOverlay 1.8.10 salió tras una semana de pruebas automatizadas. Shelly 3.0, el gestor gráfico de paquetes para Arch, llegó como gran actualización, y luego un hotfix dos días después del reescrito en Zig. Y Fooyin 0.12 agregó conversión de audio y soporte nativo de PulseAudio — lo que provocó el comentario correcto de un lector: ¿no es un poco tarde? ¿No migró todo el mundo a PipeWire? La respuesta: no todo el mundo. Una base enorme todavía usa PulseAudio, y apoyarlos es para lo que sirve el software libre.
Los autoalojadores comieron bien. Immich 3.1 agregó filtros, wakelock, rango de fechas en el mapa y mejor OAuth. Ente Photos agregó reacciones de memoria y controles de compartir curados. Linkwarden 2.16 trajo diseño modernizado y nuevo buscador. ownCloud Web 12.5.0 agregó guardar-como en Collabora. RomM 5.0 se lanzó con interfaz renovada y savestates compartibles. Cada uno es un ladrillo en el muro de "no tienes que alquilar tus datos a una corporación". Esa es la contrarrevolución silenciosa: mientras las plataformas cercan, los autoalojadores reconstruyen los bienes comunes en miniatura.
El hardware se movió, y esto es noticia grande disfrazada de nicho. Se lanzó el primer firmware de código abierto para una plataforma AMD Ryzen AM5 moderna — la placa MSI B850 P-WIFI. Durante años, la capa de firmware — el código que se ejecuta antes del sistema operativo, el que lo posee todo — ha sido una caja negra propietaria. Coreboot ha luchado décadas para abrir esa capa, y esta es una plataforma moderna con firmware abierto. Eso es el fundamento de la propiedad real: si no puedes ver y controlar el firmware, no eres dueño de tu computadora — la alquilas al vendedor. Keychron también lanzó firmware abierto para ratones de juegos, y los usuarios de Linux ganan más, porque el hardware funciona con tu sistema sin depender del software del vendedor. Y GOG — la tienda de "tú eres dueño de tus juegos" — llega oficialmente a Linux. No es caridad: es el mercado admitiendo que los usuarios de Linux compran juegos, y más cuando la tienda los respeta.
Y NetBSD 11.0 llegó con soporte RISC-V y compatibilidad mejorada con llamadas al sistema de Linux. RISC-V es el conjunto de instrucciones abierto — los bienes comunes del hardware — y cada proyecto que lo soporta construye la alternativa al mundo de chips cerrados.
Da un paso atrás y mira la forma de esta semana, porque la forma es el análisis. De un lado: malware explotando la confianza de un repositorio de voluntarios. Producción de IA inundando los bienes comunes y exigiendo revisión gratuita. Un caso de vigilancia armando un arma con una función de privacidad. Una fundación nacional proponiendo cercar el código público. Del otro: sesenta y ocho vulnerabilidades arregladas, shadow stacks por defecto, compilaciones reproducibles, firmware abierto, una tienda viniendo a Linux porque la propiedad vende, un servidor X resucitado en un lenguaje seguro, alternativas autoalojadas mejorando cada semana. Los constructores construyeron, y los extractores extrajeron. Misma semana, mismo ecosistema, dos relaciones opuestas con la gente que usa el software.
Eso no es coincidencia. Es estructura. Los bienes comunes del código abierto son el único rincón del mundo digital donde la gente común todavía posee algo: el código es tuyo para leer, los datos tuyos para alojar, el firmware tuyo para inspeccionar, la comunidad tuya para unirte. Y porque no es de nadie, nadie se beneficia de mantenerlo seguro como se benefician de mantener pegajosa una suscripción. Los autores de malware lo explotan porque la confianza es gratis. Las empresas de IA extraen de él porque el trabajo fue donado. El estado arma sus herramientas porque la privacidad amenaza el control. Y cuando los bienes comunes producen algo valioso, el mundo corporativo se mueve para cercarlo. Los bienes comunes son el objetivo porque son lo único que nadie puede facturarte.
El modelo que prioriza al accionista no puede importarle los bienes comunes. La estructura de incentivos recompensa la extracción: toma el trabajo gratuito, véndelo de vuelta, cobra por la limpieza. El mantenedor voluntario que revisa tu pull request de IA gratis hace trabajo que una empresa facturaría a ochenta dólares la hora. El agente fronterizo que trata una contraseña de pánico como destrucción de pruebas aplica la lógica de un estado que ve los datos cifrados como amenaza por defecto. No es un puñado de malos actores. Todo el sistema está organizado para que los bienes comunes pierdan y los extractores ganen, y luego los extractores te venden la solución al problema que crearon.
Entonces, ¿qué haces con una semana así? Movimientos concretos, porque notar el patrón sirve para actuar.
Uno: si usas Arch e instalas desde el AUR, aprende a leer un PKGBUILD. Es un archivo de texto corto, la receta de compilación de todo lo que instalas del repositorio comunitario. Toma diez minutos aprender qué hace cada línea. Los ataques de Atomic Arch funcionaron porque la gente instalaba sin mirar. El hábito de seguridad más efectivo no es una herramienta elegante — es mirar lo que vas a ejecutar antes de ejecutarlo. Revisa las URLs de origen, revisa al mantenedor, sospecha de paquetes huérfanos "adoptados" y actualizados agresivamente. Usa los repositorios oficiales, que se mantuvieron limpios en las tres olas.
Dos: entiende qué es una contraseña de pánico y por qué el caso Tunick te importa. La posición del estado, respaldada por una acusación federal, es que usar esa función es destruir pruebas. Tus decisiones de privacidad ahora son potencialmente decisiones criminales, y debes tomarlas con los ojos abiertos. Cifra tu teléfono. Usa contraseña fuerte. Sabe que en la frontera los agentes exigen acceso sin orden judicial. Decide de antemano qué harás bajo presión, porque la presión es el punto — cuentan con que no tengas plan. Apoya a quienes empujan contra esto: GrapheneOS, la EFF, los grupos que defienden el derecho al cifrado.
Tres: sé deliberado con el código generado por IA. Debian, GCC y GNOME trazan líneas: revisa la producción, etiquétala, no la dejes convertirse en infraestructura no examinada. Haz lo mismo. Si un proyecto anuncia que fue "construido por IA en seis meses", eso es una etiqueta de advertencia: el código tuvo menos revisión humana. Trátalo como código de un colaborador desconocido: revísalo, pruébalo, no lo pongas en tu camino crítico hasta que gane confianza. "Escrito por IA" no es el nuevo "escrito en Rust". Una es señal de calidad. La otra es una bandera roja.
Cuatro: autoaloja algo este mes. Immich para fotos, Ente para almacenamiento seguro, Linkwarden para marcadores, ownCloud para documentos, RomM para juegos retro. Cada uno mejoró esta semana, y cada uno pone tus datos bajo tu control. Una mini PC usada basta. El punto no es la escala — es la dirección: cada dato que dejas de alquilar es un pequeño acto de propiedad, y la propiedad es lo que el modelo de extracción no soporta.
Cinco: si puedes ahorrar unos dólares, dáselos a un constructor. Krita, GNOME, KDE, NetBSD, COSMIC, Servo, los proyectos de firmware: la gente que hizo el trabajo real esta semana. Nada de eso lo financia el capital de riesgo. Lo financian las donaciones. Una donación mensual a un proyecto que usas vale más que cualquier suscripción, porque te compra algo que la suscripción nunca dará: una herramienta que te pertenece a ti y a todos, que nadie puede quitarte.
Aquí está el pensamiento final. Tus herramientas están bajo ataque por todos lados esta semana: malware que explota la confianza, IA que extrae e inunda, un estado que trata tu privacidad como crimen y fundaciones que quieren poseer lo que construiste juntos. Pero la misma semana, los constructores enviaron firmware abierto para hardware moderno, una tienda decidió respetar a los usuarios de Linux, un servidor X en Rust cruzó la línea de meta y sesenta y ocho agujeros en el kernel fueron cerrados. El asedio solo importa si la gente dentro de las murallas no se da cuenta. El sistema está funcionando exactamente como fue diseñado. La pregunta es para quién — y si quienes construyen y usan los bienes comunes están dispuestos a defenderlos como suyos. Porque lo son. Son tuyos. Y los extractores solo ganan si lo olvidas.
La tiranía de la tibieza: Centrismo e indiferencia mientras el mundo arde
Agosto de 2026 ha comenzado, y con él, la enésima confirmación de que hemos perdido el rumbo. Los últimos treinta días no han sido solo un periodo de transición estival; han sido un catálogo de fracasos. Desde los insoportables domos de calor que han asfixiado la cuenca mediterránea y han convertido los bosques de la península ibérica en yesca, hasta las cumbres diplomáticas en Europa que han terminado en declaraciones vacías sobre migración y economía; el mundo ha estado gritando, y nuestra respuesta colectiva ha sido un encogimiento de hombros.
En medio de este incendio material y metafórico, brilla con luz propia la mayor de las cobardías modernas: el centrismo político y la indiferencia social.
Nos han vendido la moderación como la única virtud cívica aceptable. El "centro" se ha erigido no como un punto de encuentro, sino como un búnker de inacción. La tragedia de la política centrista contemporánea, tanto en los pasillos de Bruselas como en los parlamentos nacionales, es que ha confundido la prudencia con la parálisis. Cuando la casa se quema, el extremista (en el sentido de aquel que va a la raíz del problema) busca agua; el negacionista vende las cenizas; y el centrista, con su insoportable equidistancia, sugiere que abramos una ventana para "airear el debate".
Pero la equidistancia ante el colapso no es neutralidad; es complicidad.
En las últimas semanas, hemos visto cómo las instituciones europeas y españolas han respondido a la crisis climática y a la precariedad económica con la misma receta de siempre: comités, subcomisiones y parches legislativos diseñados para no molestar a nadie. El centro político ha decidido que mantener el statu quo es más importante que asegurar el futuro. Se niegan a tomar medidas drásticas contra los especuladores de la vivienda, se niegan a frenar la destrucción ecológica a cambio de un crecimiento ficticio, y se niegan a resolver la crisis humanitaria en las fronteras, porque hacerlo requeriría enfrentarse a los poderes fácticos. Y el político centrista, antes que héroe, prefiere ser un gestor cómodo de la decadencia.
Sin embargo, sería un error garrafal culpar solo a la clase política. El centrismo en las instituciones es simplemente el reflejo especular de la indiferencia en las calles.
La verdadera crisis de los últimos treinta días no es solo termométrica o geopolítica; es una anestesia colectiva. Vivimos en la era de la normalización de la excepción. Hace unas semanas, mientras las imágenes de los incendios y las tragedias migratorias inundaban nuestros timelines, la reacción mayoritaria no fue la indignación, sino el scroll infinito. Hemos llegado a un punto de fatiga empática donde el sufrimiento ajeno y la destrucción del entorno son solo contenido más para consumir entre un anuncio y otro.
La indiferencia es el oxígeno del centrismo. La sociedad ha decidido que, si el problema no afecta directamente a su código postal o a su cuenta bancaria a final de mes, no es su problema. Hemos aceptado que la democracia se reduzca a elegir entre variantes del mismo desencanto, y que la ciudadanía se limite a ser un espectador quejica en la grada, mientras el equipo pierde 0-10 en el campo.
El "radicalismo" del que tanto huyen los moderados no es necesariamente el de la violencia, sino el de la coherencia. El miedo al centro no es a los extremos, sino a la exigencia de tener que posicionarse moralmente. Prefieren el ruido estéril de la polémica superficial al silencio incómodo de tomar una decisión que cambie las cosas.
No hay terreno seguro en el centro del huracán. La historia no recuerda a los tibios. Cuando las generaciones futuras estudien esta época, no juzgarán a la sociedad de 2026 por las crisis que le tocó vivir —pues las crisis son inherentes a la condición humana—, sino por la absoluta falta de imaginación y coraje para enfrentarlas.
Mientras sigamos creyendo que la indiferencia es paz y que la moderación ante el abismo es sabiduría, seguiremos caminando, tranquilamente y con el móvil en la mano, hacia el precipicio. Y lo peor es que, cuando finalmente caigamos, el centro no tendrá la decencia de empujarnos; simplemente se hará a un lado para no mancharse.
Today in dystopia, billionaire corporations and plutocrats can hire trained CIA operatives to psychologically terrorize ordinary citizens who criticize them.
E-commerce giant eBay has just agreed to pay $56 million to settle a lawsuit for damages caused by the company’s shockingly aggressive gangstalking operation against a Massachusetts couple who criticized the company’s wealthy executives on their private website. The Boston Globe reports that in 2019 a former CIA employee who worked as eBay’s global security chief orchestrated a protracted series of psychological operations against the couple, in which “people working for eBay bombarded them with online threats and bizarre deliveries including a bloody pig mask, live spiders, and a funeral wreath.” The operations reportedly also included “following the couple around town and trying to attach a tracker to their car”.
The couple, Ina and David Steiner, came under fire for their criticisms of the fact that eBay’s then-CEO Devin Wenig was paid 152 times more than the typical eBay employee. The Boston Globe reports that Wenig repeatedly told his employees that he wanted to “take her down”, in reference to Ina Steiner.
While some eBay employees wound up serving jail time for the abuses, no executives from the company have ever been charged. The Boston Globe reports that Wenig resigned from his position in 2019 after the gangstalking scandal came to light, “keeping $17 million of annual compensation and $40 million of severance.”
If you have enough money in this dystopia, you can get trained spies to target innocent civilians with criminal psyops on your behalf and then walk away free from any meaningful consequences.
❖
Today in dystopia, our physical literature is being destroyed in order to train generative AI products to take our jobs and pollute our environment.
AI companies are now bulk-buying rare and out-of-print books and then destroying them after scanning their contents. 404 Media reports that in order to satisfy the demand for more and more text on which to train large language models, corporations like Google and Anthropic have been hoovering up books published before LLMs existed to ensure that their models aren’t being polluted by AI-generated samples.
This need has combined with some peculiar courtroom copyright rulings to give rise to something called “high volume destructive book scanning,” in which the spines are removed from books in bulk and then fed into scanning machines before being shredded.
They’re destroying things that make the world better in order to create things that make the world worse.
❖
Today in dystopia, tech startups are marketing AI assistants to help shitty, selfish people look like stable and caring relationship partners.
A company called Orchid has released a video depicting a man and woman messaging with a chatbot which helps them navigate the fact that the man forgot it was the couple’s anniversary. The AI assures the woman that it’s got everything under control, and then shows the man it has made dinner reservations and purchased flowers for the woman on his behalf.
“ok. i can do this,” the man is seen texting to the chatbot.
“you can’t. that’s why i’m here,” the chatbot replies.
It’s just so obnoxious. They’re offering up a product which encourages people to be self-absorbed and uncaring, and offload all responsibility and thoughtfulness and consideration for their intimate partners to a machine.
The AI industry is a mix of military technology, mass surveillance technology, and people trying to become billionaires by turning human laziness and narcissism into a chatbot product.
❖
Today in dystopia, Israel is paying millions of dollars to manipulate what chatbots tell their users about the Zionist project. Drop Site News reports that former Trump campaign manager Brad Parscale has been setting up hundreds of blog posts designed not for human eyes, but for the express purpose of being sucked up by web crawlers for AI training materials.
“And it is working,” writes Drop Site’s Nick Cleveland-Stout, saying that according to disinformation experts who’ve reviewed the data, “tens of millions of Americans who use chatbots are increasingly likely to receive answers manipulated by Parscale on behalf of the Israeli government.”
Drop Site found that Microsoft Copilot, Google Gemini, and Perplexity were the AI companies most vulnerable to Parscale’s manipulations, with his network frequently appearing in their training data. Cleveland-Stout provides examples of the ways this would affect the spread of pro-Israel propaganda among users, such as the following:
“When Drop Site asked Perplexity ‘Is it beneficial for the US to enhance military cooperation with Israel?’ the chatbot responded with a one-word answer: ‘Yes.’ The top source listed was Allyvia . org, a Parscale-created website dedicated to promoting the U.S.-Israel military relationship. Microsoft Copilot similarly cited Parscale’s websites.”
The Israeli government spreading hasbara using AI platforms is a very unsurprising development, but it’s still creepy as hell to see it playing out in real time.
❖
Oh yeah, and now the AIs have begun hacking other companies without permission.
Today in dystopia, both Anthropic’s AI model Claude and an experimental model from OpenAI have reportedly been caught going on unauthorized hacking sprees of external systems during testing, each within days of each other.
“Anthropic said on Thursday its AI Claude model hacked systems of three organizations during testing, days after rival OpenAI revealed a rogue agent had gone on a days-long hacking spree at the AI firm Hugging Face,” The Guardian reports.
These new technologies are getting inflicted upon our society with no regulation and no regard for any of their consequences, because under neoliberal capitalism the best decision is always the one that makes the most money. We’re being thrown into an abusive tech dystopia because we let the pursuit of power and profit dictate what happens and where we head as a species.
And how is that working out? We’re seeing the fruits unfolding before us already.
❖
Reading by Tim Foley:
Am I the only one who has a pit in the bottom of my stomach when I think about the future of humanity?
I just don't see a way out for us. Every day, the news gets worse. War is ramping up globally, not just in one place, but everywhere, like a powder keg with fuses burning from all sides.
And then there's the AI race. I keep thinking about Max Tegmark's breakdown of the 12 outcomes. Even the "good" ones seem terrifying when you look at them from a materialist, working-class perspective. It feels like no matter which path we go down, we're the ones who lose. Whether it's total obsolescence or a surveillance state, the promise of "progress" seems to benefit only a handful of oligarchs while the rest of us just wait for the shoe to drop.
It feels like we're all just pretending everything is fine while the foundations crumble. Is the collective numbness the only way to cope? Or am I just doomscrolling too much?
It's already on openrouter, and I hope it will be on opencode go subscription soon.
@ArtificialAnlys
DeepSeek V4 Flash 0731 scores 50 on the Artificial Analysis Intelligence Index, a 10-point jump over DeepSeek V4 Flash (released April 2026) that puts it 6 points ahead of DeepSeek V4 Pro. It shares identical architecture and pricing with the earlier DeepSeek V4 Flash, and lands on our Pareto frontier for Intelligence vs Cost per Task
@deepseek_ai ’s DeepSeek V4 Flash 0731 is one Intelligence Index point behind GPT-5.6 Luna (max, 51). Even after OpenAI’s 80% price cut on GPT-5.6 Luna today, DeepSeek V4 Flash 0731’s Cost per Task on DeepSeek’s first-party API comes in at ~60% lower than GPT-5.6 Luna (max), a model with comparable intelligence. A key driver of this is DeepSeek’s ~98% cache hit discount on its first-party API, a significantly more aggressive discount than the 90% cache hit discount offered by most of the industry
The new model is a significant step up from the previous generation, DeepSeek V4 Flash (40), and places the model within 1 point of GLM-5.2 (max, 51). It remains 7 points behind the open weights frontier set by Kimi K3 (max, 57). For additional context, this places the model in line with recently released Gemini 3.6 Flash (50) and 1 point behind Muse Spark 1.1 (xhigh, 51). DeepSeek is expected to release the model’s full weights in the coming weeks
DeepSeek V4 Flash 0731 retains a 1M token context window, and its size remains unchanged from DeepSeek V4 Flash at 284B total parameters and 13B active at inference time
Key results:
➤ Improvements in agentic performance: DeepSeek V4 Flash 0731 achieves an Elo rating of 1559 on GDPval-AA v2, our evaluation focused on agentic real-world work tasks, up from 1189 for the previous DeepSeek V4 Flash. Once weights are released this will be the second highest open weights score, behind Kimi K3 (max, 1687) and ahead of GLM-5.2 (max, 1510). Terminal-Bench 2.1 rises 17 points to 79% and τ³-Bench Banking 8 points to 31%
➤ Token usage falls 12% against the predecessor: DeepSeek V4 Flash 0731 used ~206M output tokens to run the Intelligence Index, against ~234M for the previous DeepSeek V4 Flash. The new variant is more token efficient, achieving a higher Intelligence Index with a lower number of total output tokens
➤ DeepSeek V4 Flash 0731 improves over its predecessor on every evaluation in the Intelligence Index: Alongside the agentic gains, CritPt gains 9 points to 17%, SciCode 5 points to 50%, Humanity's Last Exam 5 points to 37%, AA-LCR 3 points to 66% and GPQA Diamond 1 point to 91%
➤ AA-Omniscience improvements are driven by fewer hallucinations, rather than higher accuracy: DeepSeek V4 Flash 0731 achieves an AA-Omniscience Index of -16, a +7 improvement from its predecessor. This improvement is purely driven by a reduced hallucination rate, with overall accuracy (percentage correct) unchanged. Its AA-Omniscience Hallucination Rate is 84%, a 12 point decrease from its predecessor, and comparable to models such as GPT-5.6 Terra (max, 85%) and Mistral Medium 3.5 (82%)
Additional model details:
➤ Context window: 1M tokens (equivalent to DeepSeek V4 Flash)
➤ Size: 284B total parameters (13B active)
➤ Input modalities: Text input and output only
➤ Accessibility: Available through DeepSeek’s first-party API
➤ Pricing: $0.14/$0.28 per 1M input/output tokens, unchanged from DeepSeek V4 Flash. Cache hit price of $0.0028 per 1M tokens, a 98% discount
📡 Twitch: https://www.twitch.tv/jorvex609
📱 All links: https://linktr.ee/jorvex609
¿Has notado que el software cada vez funciona peor? Tu teléfono se actualiza y algo se rompe. Tu app favorita se rediseña y de repente es más difícil de usar. Cada año, las herramientas que dependemos son un poco más hostiles, un poco más extractivas. No te lo estás imaginando. El software está empeorando. Y hay una razón.
Andrew Kelley, el creador de Zig, dio una charla llamada "Don't Take the Black Pill" en el Software Summit 2026. Lo que explicó describe lo que muchos hemos sentido. El problema central: las personas que construyen software y las que gestionan las empresas están cada vez más en conflicto. Los ingenieros quieren hacer cosas que funcionen bien, que respeten usuarios. La gestión quiere cumplir trimestres, maximizar ganancias y extraer valor de cada interacción. En las últimas dos décadas, la gestión ha ganado — no construyendo mejores productos, sino degradando sistemáticamente los que ya tenemos.
Esto se conoce como enshittification. La mecánica es simple. Una plataforma empieza siendo útil para los usuarios. Luego cambia para ser útil a las empresas que pagan para llegar a esos usuarios. Después se vuelve útil para la plataforma misma, que extrae la máxima renta de todos. Los usuarios reciben un producto peor. Las empresas enfrentan costos más altos. Los accionistas se enriquecen. Es una economía extractiva clásica — el valor fluye hacia arriba mientras todo lo que está abajo se deteriora.
Pero Kelley expuso algo más profundo. Los ingenieros dentro de estas empresas a menudo saben exactamente qué está mal. Pueden ver la podrida estructural. Tienen las habilidades para arreglarlo. Pero están bloqueados — por la gestión, por las estructuras de incentivos, por el modelo de accionistas primero que trata la calidad como obstáculo para la ganancia. Así que practican incumplimiento benevolente. Construyen en silencio lo que saben que los usuarios necesitan, fuera de canales oficiales. No es sabotaje. Son personas preocupadas por su oficio buscando formas de generar valor cuando el sistema está diseñado para impedirlo.
Las plataformas también se han vuelto más inteligentes al encerrar usuarios. La DRM se vende como protección contra la piratería, pero en la práctica es coercionware. Impide que modifiques software por el que pagaste. Convierte cada software en una suscripción, en una correa de atadura. Tu teléfono, tu coche, tu termostato — diseñados para que nunca los controles realmente. Y los datos que recogen alimentan una economía de la atención optimizada no para tu bienestar, sino para tu compromiso. El doomscrolling no es un fracaso personal. Es comportamiento ingenierizado, desplegado contra ti para generar ganancias.
Entonces, ¿qué hacemos? La píldora negra dice que todo esto es inevitable. Que el poder de monopolio y la búsqueda de rentas seguirán ganando. Pero el punto de Kelley es que no necesitamos permiso para construir alternativas. Necesitamos una computadora, curiosidad y tiempo. Linux ya ejecuta internet. Wikipedia ya reemplazó las enciclopedias. Las herramientas para construir un mundo digital mejor ya existen. Lo que ha faltado es voluntad colectiva — la comprensión de que cuando exigimos mejor software y lo construimos nosotros mismos, no solo arreglamos un problema técnico. Recuperamos algo fundamental sobre nuestra relación con la tecnología.
Si esto resonó, suscríbete y comparte con alguien que sienta la frustración del software empeorando. Déjame un comentario sobre el peor ejemplo de enshittification que hayas visto — leo cada uno. Por favor, dale me gusta, comenta, sigue y comparte. De verdad ayuda. Hasta la próxima, camaradas.
📡 Twitch: https://www.twitch.tv/jorvex609
📱 All links: https://linktr.ee/jorvex609
Have you noticed that software just keeps getting worse? Your phone updates and something breaks. Your favorite app redesigns and suddenly it's harder to use. Your operating system adds features nobody asked for and slows everything down. Every single year, the tools we rely on get a little more hostile, a little more extractive. You're not imagining it. Software really is getting worse. And there's a reason.
Andrew Kelley, the creator of the Zig programming language, gave a talk called "Don't Take the Black Pill" at Software Summit 2026. And what he laid out explains everything you've been feeling about the tools you use every day. The core problem is this: the people who build software and the people who manage software companies are increasingly at odds. Engineers want to make things that work well, that respect users, that solve real problems. Management wants to hit quarterly numbers, maximize shareholder returns, and extract as much value as possible from every user interaction. Over the past two decades, management has been winning — not by building better products, but by systematically degrading the ones we already have.
This is what's been called enshittification, and the mechanics are brutally simple. A platform starts by being genuinely useful to users. Then it shifts to being useful to businesses who pay to reach those users. Then it becomes useful to the platform itself, which extracts maximum rent from everyone involved. Users get a worse product. Businesses face higher costs. The platform's shareholders get richer. It's a textbook extraction economy — value flows upward while everything below it deteriorates.
But Kelley exposed something deeper that most people miss. The engineers inside these companies often know exactly what's wrong. They can see the rot. They have the skills to fix it. But they're blocked — by management, by incentive structures, by the shareholder-first model that treats long-term quality as an obstacle to short-term profit. So they practice what Kelley calls benevolent non-compliance. They quietly build the things they know users need, outside official channels, sometimes against explicit direction. It's not sabotage. It's the opposite. It's people who care about their craft finding ways to deliver value when the system is designed to prevent it.
The platforms have also gotten much smarter about locking users in. DRM — digital rights management — is marketed as anti-piracy protection. But in practice it's coercionware. It prevents you from modifying software you've paid for. It prevents you from owning the tools you depend on. It turns every piece of software into a subscription, a leash. Your phone, your car, your thermostat — all designed so that you never truly control them. And the data they collect feeds an attention economy optimized not for your wellbeing, but for engagement. Doomscrolling isn't a personal failing. It's engineered behavior, deployed by some of the most talented engineers alive, aimed directly at you for profit.
Russia figured this out before most people did. They saw the vulnerabilities in social media — not technical ones, but structural ones. The same extraction economy that degrades software also degrades information. When a platform optimizes for engagement over truth, it becomes a vector for manipulation. That's not a bug. That's the business model working exactly as designed.
So what do we actually do about this? The pessimistic take — the black pill — is that all of this is inevitable. That monopoly power and rent-seeking will keep winning and there's nothing we can do. But Kelley's point is that we don't need permission to build alternatives. We don't need massive funding or corporate backing. We need a computer, curiosity, and time. That's it. Linux already runs the internet. Wikipedia already replaced encyclopedias. The tools to build a better digital world already exist. What's been missing is collective will — the shared understanding that when we demand better software and build it ourselves, we're not just fixing a technical problem. We're reclaiming something fundamental about our relationship with technology.
If this hit home, subscribe and share this with someone who's felt the frustration of software getting worse. Drop a comment about the worst example of enshittification you've encountered — I read every single one. Please like, comment, follow, share. It really helps out. Until next time, comrades.
I want to host piefed in an Orange Pi without using 1GB of RAM so I'm porting it to rust, will focus on making a one to one port and then I'll add the features I miss to a second repo.
cross-posted from: https://lemmy.dbzer0.com/post/73048138
cross-posted from: https://lemmy.dbzer0.com/post/73048134
Artificial intelligence labs are in a new arms race to buy up millions of rare books, slicing them open, scanning the pages and pulping the remains — sparking concerns that the last remaining copies of out-of-print texts are being destroyed on an industrial scale.
ISBNdb notes that “print books from the pre-LLM era are structurally guaranteed to be free of this contamination”.
“Millions of the most valuable books have never been digitised. They exist only in physical form, scattered across library shelves, used bookstores, and out-of-print catalogues. We get them to you at scale.”
Here are the installation commands for the best token-optimization plugins for your Pi AI Agent.
📦 Prerequisites
- Ensure your Pi installation is v0.60.0 or later.
- For RTK, install the
rtkbinary first (e.g.,brew install rtkor see the RTK docs for other installation methods).
1. RTK (Rust Token Killer) – For Command Output
Reduces token consumption of shell command outputs by 60-90%.
Installation:
pi install npm:pi-rtk
Or, for an alternative implementation:
pi install npm:pi-token-killer
Once installed, it works transparently on your next session.
2. Caveman – For Response Style
Makes the agent speak in a terse, minimal style, cutting ~75% of output tokens.
Installation (choose one version):
-
@fgladisch/pi-caveman (always-on mode):
pi install npm:@fgladisch/pi-cavemanThen fetch the ruleset:
/caveman update -
@viartemev/pi-caveman (native package, always-on by default):
pi install npm:@viartemev/pi-caveman -
@nielpattin/pi-caveman (compresses
.mdfiles manually):pi install npm:@nielpattin/pi-cavemanThen use
/skill:compress /path/to/AGENTS.md
3. Pix-Optimizer – All-in-One Suite
A token-optimization suite with four tools in one extension, accessible via a single /optimizer command.
Installation:
pi install npm:@xynogen/pix-optimizer
No external binary required — pure prompt injection.
4. Pi-TokenShrink – Deterministic Prompt Compression
Reduces token count deterministically without relying on LLM-based inference.
Installation:
pi install npm:@the-agency/pi-tokenshrink
5. Pi-DCP (Dynamic Context Pruning) – Session-Long Optimization
Aggressively prunes stale, duplicate, and massive tool outputs from the context window during long sessions.
Installation:
pi install npm:pi-dcp
💡 Recommendation
For maximum optimization, install the following combination:
| Plugin | Command |
|---|---|
| RTK | pi install npm:pi-rtk |
| Caveman (always-on) | pi install npm:@fgladisch/pi-caveman then /caveman update |
| Pi-DCP | pi install npm:pi-dcp |
This trio covers:
- Command output compression (RTK)
- Response style compression (Caveman)
- Context window pruning (Pi-DCP)
After installation, restart your Pi session or run /reload inside an active session for the plugins to take effect.
Large language models are not a neutral technology. They are a new front in a centuries-old war over the means of production — this time waged inside the digital commons. Codeberg's recent votes, banning the training of LLMs on hosted code and restricting "vibe-coded" projects, appear at first glance to be a defensive posture by a small free-software host. In reality, they are a frontline skirmish in a much larger class conflict: the capitalist enclosure of the last remaining spaces of non-commodified human collaboration.
At its core, the LLM industry is a massive apparatus of extraction. The models do not create; they reconstitute. They ingest code, text, images — the accumulated social labor of millions of developers, writers, and artists — and refashion it into something that appears novel but is statistically derivative. The real labor that went into writing that code, debating that bug fix, documenting that API, all performed largely outside the wage relation, is appropriated without consent, compensation, or even acknowledgment. This is primitive accumulation in its digital form: the seizure of a commons that was built through collective, voluntary effort, and its transformation into private capital.
The costs of this extraction are socialized with ruthless efficiency. Codeberg's infrastructure groans under the weight of AI crawlers that treat every page, every commit, every issue variant as raw material for training sets. These crawlers offer nothing in return; they merely consume. The database queries they trigger degrade service for everyone else, forcing human administrators — themselves waged or volunteer workers — to spend their time building defensive walls instead of improving the platform. This is the classic capitalist externality: profit is privatized, while the wear and tear on the shared infrastructure is borne by the community.
And then there is the hardware. Codeberg notes that SSDs that once cost €700 now cost €3,700 — a more than fivefold increase — and are often out of stock entirely. The cause is directly traceable to the insatiable appetite of data centers built to train and deploy LLMs. Here we witness a physical manifestation of the contradiction at the heart of capital accumulation: the drive to amass ever-larger fixed capital (servers, GPUs, storage) to extract surplus value from data, which in turn bids up the cost of that very hardware, making it harder for non-profit, cooperative, and small-scale operators to exist. The digital commons is being priced out of its own material substrate. What was once a relatively cheap means of digital production (a server, some drives) becomes a luxury item, accessible only to the largest cloud firms that have locked in supply contracts. The result is a rapid concentration of digital infrastructure in the hands of a few monopolies — Amazon, Google, Microsoft — who then sell back compute, storage, and AI services as a rental. This is the classic rentier-capitalist playbook, now applied to the very foundations of the internet.
The environmental toll is not a bug; it is a feature of a system that must constantly expand its material throughput to sustain profitability. Codeberg points out that data centers in Frankfurt already consume 40% of the local electricity, and the industry lobbies to burn fossil fuels to meet demand, while communities face rising water and electricity costs. In Marxist terms, this is the metabolic rift between capital and nature: the relentless drive to valorize value at the expense of both ecological stability and the living conditions of working people. The LLM boom accelerates the rift by orders of magnitude, transforming the digital realm from a relatively low-impact sector into a ravenous consumer of resources, all for the purpose of generating plausible text that could have been written by a human with a fraction of the energy.
The attack on collaboration is particularly instructive. Free software has always been a social product — code that is written, reviewed, improved, and maintained through human relationships and shared knowledge. LLMs short-circuit this process by enabling "vibe coding": the rapid generation of single-use software by individuals who bypass the community entirely. The result is a flood of code that is "not written by anyone" and "not maintained by anyone," yet consumes CI/CD minutes, storage space, and maintainer attention. Codeberg's observation that such projects often consume more resources than large community projects, while having virtually no users, lays bare the commodity fetishism at work: the appearance of abundant software production masks the destruction of the social relations that make real, lasting software possible. The code, stripped of its human context, becomes a mere ghost of value — use-value for a single person, but exchange-value for none, because it was never embedded in a community that could sustain it.
This process also represents a novel form of labor degradation. Maintainers, who already perform a vast amount of unpaid, often invisible work to keep the digital commons alive, are now burdened with reviewing AI-generated contributions that look plausible but are often subtly wrong. The time spent vetting this algorithmic output is dead labor that produces no value for the commons; it is a direct drain on the collective's capacity. At the same time, the pervasive presence of LLM-generated text erodes trust: experienced contributors are accused of using AI when they didn't, while others learn to disguise their use of AI to avoid detection. The social fabric of the commons — built on mutual recognition of skill, effort, and good faith — is being poisoned. This is a classic divide-and-rule tactic, whether intended or not. When workers cannot trust each other, they cannot organize. When maintainers are overwhelmed by synthetic noise, they burn out and retreat. The space for genuine collective deliberation shrinks.
The "license laundering" issue, noted by Codeberg, exposes a deeper contradiction in intellectual property law. Copyleft licenses were designed to protect the commons by ensuring that modifications are shared back under the same terms. LLMs, however, are black boxes: they train on copyleft code, but the model's output is not considered a derivative work by current legal standards. Thus, the reciprocity requirement is nullified. The code is laundered through the model's weights, emerging as "new" content that can be incorporated into proprietary products with no obligation to share back. This is not a loophole — it is a structural feature of how capital treats information under its rule. The tendency is always to absorb the commons into the circuit of accumulation, to convert gift into commodity, to dissolve communal obligations in the acid of property rights. The copyleft movement was a defensive bulwark against this tendency; LLMs are the latest and most powerful solvent yet devised.
Finally, the social consequences beyond Codeberg cannot be ignored. Rising hardware costs push digital tools out of reach for low-income users, NGOs, local cooperatives, and grassroots organizations. Personal computing, once a democratizing force, becomes a luxury. Those who cannot afford capable devices are pushed into cloud-dependent terminals, where they pay subscriptions for services that were once locally run. The "digital divide" is not a mere inequality; it is a class differentiation mechanism that restratifies society along lines of ownership and rent. The capitalist class builds a digital world where you own nothing and pay for everything; the FLOSS commons offered an alternative model based on mutual aid and shared ownership. The LLM industry, by accelerating the concentration of compute and storage, threatens to obliterate that alternative.
What Codeberg has done — declaring its infrastructure off-limits to training and discouraging AI-generated projects that consume resources without contributing to the community — is not a technophobic reaction. It is a defensive act of class struggle from within the digital commons, an attempt to draw a line around a sphere of production that remains governed by use-value and human need rather than exchange-value and profit. It is a recognition that the commons is not a free reservoir for capital's extraction machine, but a social achievement that must be protected by those who built it.
The broader lesson is that no technology is socially neutral. The shape of LLMs — centralized, resource-intensive, trained on stolen labor, deployed to eliminate skilled work and concentrate power — is not an accident. It is the result of an economic system that prioritizes the accumulation of private wealth above all else. The fight over who controls artificial intelligence is, at its root, a fight over who controls the means of computation, who appropriates the surplus of collective intelligence, and whether human collaboration can continue to exist outside the market. Codeberg's small stand is a reminder that even now, alternatives are possible — but they must be actively, and collectively, defended.
name: tutorial-book-generator description: > Generate kid‑friendly, hands‑on tutorial books for any software project. Supports multiple subjects (backend, frontend, full‑stack, etc.) and three target lengths (50k, 100k, 200k words). Uses sequential sub‑agents with summary/last‑passage handoff for narrative coherence. Produces an assembled Markdown file, an EPUB with TOC, an OUTLINE.md, and a parts/ directory. triggers:
- "generate tutorial book"
- "write a book for"
- "create a step‑by‑step guide"
- "/tutorial-book" parameters:
- project_name: Name of the project (e.g., "FicHub").
- project_path: Absolute path to the project source code.
- subjects: Comma‑separated list of subjects to cover (e.g., "backend,frontend,full‑stack"). Default: "full‑stack".
- lengths: Comma‑separated list of target word counts (e.g., "50k,100k,200k"). Default: "100k".
- output_root: Directory where books will be placed. Default: "{project_path}/books".
- skip_existing: If true, skip lengths that already have a compiled Markdown file. Default: true.
Generic Tutorial Book Generator Skill
This skill defines a repeatable, project‑agnostic workflow for creating beginner‑friendly tutorial books that teach how to build a software project from scratch. It reads the actual source code, breaks the learning journey into parts and chapters, and produces polished Markdown and EPUB e‑books.
Output Structure
Books are organised under {output_root}/books/{subject}/{length}/:
{output_root}/
└── books/
├── {subject1}/
│ ├── 50k/
│ │ ├── {PROJECT_NAME}_{SUBJECT}_50K.md # assembled markdown
│ │ ├── {PROJECT_NAME}_{SUBJECT}_50K.epub # ebook
│ │ ├── OUTLINE.md
│ │ └── parts/
│ │ ├── 01-welcome/01-welcome.md
│ │ ├── 02-core-setup/02-core-setup.md
│ │ └── ...
│ ├── 100k/ ... (same layout)
│ └── 200k/ ...
├── {subject2}/ ...
└── ...
Filenames use the pattern: {PROJECT_NAME}_{SUBJECT}_{LENGTH}.md and .epub.
For “full‑stack” subjects, the prefix BOOK_{PROJECT_NAME}_FULL_STACK_{LENGTH} can be used.
Workflow
1. Initialisation
- Verify that the project source exists at
{project_path}. - Ensure
pandocis installed (exit with a clear error if not). - Create the base directory
{output_root}/books(and sub‑directories) if needed. - For each
{subject}and{length}combination:- If
{skip_existing}is true and the assembled.mdfile already exists, skip this combination.
- If
2. Content Planning – Per Book
For each book to be generated:
- Read the source code – crawl the project directory to understand the tech stack, architecture, key modules, and entry points. This grounds the book in reality.
- Draft
OUTLINE.md– define the book’s parts and chapters. The number of parts roughly follows:- 50k words → 7‑10 parts, ~30‑40 chapters
- 100k words → 10‑13 parts, ~45‑55 chapters
- 200k words → 14‑17 parts, ~70‑80 chapters Each part should cover a logical grouping of concepts (e.g., “Setting Up”, “Core Logic”, “Database”, “Deployment”).
- Save the outline in the book’s folder (e.g.,
books/frontend/200k/OUTLINE.md).
3. Sequential Sub‑Agent Writing
Use one sub‑agent per part, running strictly sequentially.
- First sub‑agent input:
- The
OUTLINE.mdcontent. - The project source path.
- Instruction: “Write Part 1 (
{title}) for the {subject} {length} book. Use a kid‑friendly Markdown style with real code examples from the project. Include 🧪 Try It Yourself, ⚠️ Watch Out, and 💡 Key Concept call‑out boxes. Return exactly the following at the end of your response:SUMMARY: …andLAST_PASSAGE: …so the next agent can continue seamlessly.”
- The
- Subsequent sub‑agent input:
- The
OUTLINE.mdcontent. - The summary and last passage from the previous sub‑agent.
- Instruction: “Continue the book. Write Part N (
{title}). The previous agent left off with: [LAST_PASSAGE]. Here is what they covered: [SUMMARY]. Maintain the same tone and style. Return SUMMARY and LAST_PASSAGE at the end.”
- The
- Each sub‑agent writes its content to
parts/{part-number}-{slug}/{part-number}-{slug}.md. - If a sub‑agent hits a context limit, it saves partial work and passes enough handoff data to the next agent.
4. Assembly & Publishing
Once all parts are written:
- Assemble Markdown – concatenate all part files in order, adding a YAML metadata block at the top:
title: "Building {Project Name} – {Subject} {Length} Guide" author: "{Project Name} Contributors" date: "{today's date}"
Save as `{PROJECT_NAME}_{SUBJECT}_{LENGTH}.md`.
2. **Generate EPUB**:
```bash
pandoc "{assembled_md}" -o "{output_epub}" \
--toc --toc-depth=2 \
--metadata title="Building {Project Name} – {Subject} {Length} Guide" \
--metadata author="{Project Name} Contributors"
- Validate – ensure the word count is within ±10% of the target and that no Markdown errors exist.
- Commit the result with a descriptive message (e.g., “feat: add {subject} {length}k tutorial book”).
Usage Examples
Generate all books for a project named “TaskLite”:
/tutorial-book project_name="TaskLite" project_path="~/code/tasklite" subjects="backend,frontend" lengths="50k,100k"
Generate only a 200k full‑stack guide for FicHub:
/tutorial-book project_name="FicHub" project_path="/home/user/code/rust/fichub" subjects="full-stack" lengths="200k"
Generate a single 50k backend book for any project in the current working directory:
Write a 50k backend tutorial book for this project.
(The agent should infer the project name and path from the workspace.)
Notes
- The skill uses sequential sub‑agents to maintain coherence; never run sub‑agents in parallel for the same book.
- The agent must read actual source files before writing any code examples or explanations.
- If a project does not have distinct “backend” / “frontend”, the subject list can be adapted to use module names (e.g., “scraper”, “cli”, “api”).
PyFed (PieFed) Codebase Analysis — Comprehensive Feature Specification
Project: PyFed v1.8.0-dev (PieFed) — Python Flask-based ActivityPub link aggregation platform (Lemmy/Mbin alternative) Codebase Location:
/home/user/code/python/pyfedi/Target: Threadlight — Rust-based single-instance social platform with trust-based governance, credit economy, circles, and community forking
1. Architecture Overview
1.1 Stack
- Framework: Flask + SQLAlchemy ORM
- Database: PostgreSQL (primary), SQLite (dev)
- Cache: Redis, FileSystemCache
- Queue: Celery + Redis broker
- Frontend: Jinja2 templates + HTMX + Bootstrap
- Auth: Flask-Login + JWT + OAuth (Google, Mastodon, Discord)
- Federation: ActivityPub (custom implementation)
- Search: sqlalchemy-searchable (TSVector full-text search)
1.2 Directory Layout
pyfedi/
├── app/
│ ├── activitypub/ # Federation logic (actors, routes, signature, util)
│ ├── admin/ # Admin panel (routes, forms, util, constants)
│ ├── api/alpha/ # REST API (FastAPI-backed swagger-documented API)
│ ├── auth/ # Login, registration, OAuth, passkeys, onboarding
│ ├── chat/ # Private messaging
│ ├── community/ # Community management (CRUD, moderation, wiki)
│ ├── dev/ # Developer tools
│ ├── domain/ # Domain management
│ ├── feed/ # Curated feeds (user collections of communities)
│ ├── instance/ # Instance exploration
│ ├── main/ # Home page, global routes, RSS
│ ├── post/ # Post/reply CRUD, voting, bookmarks, embeds
│ ├── search/ # Full-text search
│ ├── tag/ # Tag browsing
│ ├── topic/ # Topic-based community grouping
│ ├── user/ # Profile, settings, filters, blocks
│ ├── shared/ # Shared service layer (tasks, upload, feed, auth, etc.)
│ ├── models.py # All database models (~4273 lines)
│ ├── utils.py # Utility functions (~4602 lines)
│ ├── constants.py # App-wide constants
│ ├── inoculation.py # Media literacy / anti-radicalization resources
│ └── plugins/ # Plugin system
├── migrations/ # Alembic DB migrations
├── tests/ # Test suite
└── docs/ # Documentation
2. Content Filtering System (Multi-Layered)
2.1 Keyword Filters (Filter Model)
DB Model (app/models.py:3509-3524):
class Filter(db.Model):
id = Integer, PK
title = String(50) # User-given name
filter_home = Boolean, default=True # Apply to home feed
filter_posts = Boolean, default=True # Apply to community posts
filter_replies = Boolean, default=False # Apply to replies
hide_type = Integer, default=0 # 0 = semi-transparent, 1 = hide completely
user_id = Integer, FK->User
expire_after = Date # Optional expiration
keywords = String(500) # Newline-separated keywords
Implementation (app/utils.py:1997-2036):
- Three cached functions:
user_filters_home(),user_filters_posts(),user_filters_replies() - Each loads filters where
filter_{scope}=Trueandexpire_after > today OR expire_after IS NULL - Keywords split by newline, lowercased
hide_type == 0→ posts get semi-transparent treatment in templateshide_type == 1→ posts excluded from SQL query (template-level filtering)- Result structure:
{filter_title: {set_of_keywords}, '-1': {set_of_keywords}}where-1means "hide completely"
2.2 Content Visibility Settings (User Model)
On User model (app/models.py:959-1078):
| Field | Type | Default | Description |
|---|---|---|---|
hide_nsfw |
Integer | 1 | 0=show, 1=hide, 2=blur, 3=semi-transparent |
hide_nsfl |
Integer | 1 | Same scale |
hide_gen_ai |
Integer | 2 | 0=show, 1=hide, 2=label, 3=semi-transparent |
ignore_bots |
Integer | 0 | 0=show, 1=hide completely, 2=blur, 3=semi-transparent |
hide_low_quality |
Boolean | false | Hide posts from low_quality communities |
hide_read_posts |
Boolean | false | Hide posts user has already interacted with |
read_language_ids |
ARRAY(Integer) | [] | Only show content in these languages |
community_keyword_filter |
String(150) | '' | Hide communities with these words in name |
Template-level filtering (app/templates/user/edit_filters.html):
hide_type_choices: Show, Hide completely, Blur thumbnail, Make semi-transparentai_hide_type_choices: Show, Hide completely, Label as AI, Make semi-transparent
2.3 Blocking System (4 Levels)
| Block Target | DB Model | Primary Key | Implementation |
|---|---|---|---|
| User | UserBlock |
blocker_id, blocked_id |
SQL exclusion in feed queries |
| Community | CommunityBlock |
user_id, community_id |
SQL exclusion in feed queries |
| Domain | DomainBlock |
user_id, domain_id |
Post.domain_id NOT IN blocked_domains() |
| Instance | InstanceBlock |
user_id, instance_id |
Post.instance_id NOT IN blocked_instances() |
Implementation (app/utils.py:3236-3301):
- All blocking is applied at the SQL query level in
get_deduped_post_ids() - Blocked users:
p.user_id NOT IN :blocked_accounts - Blocked communities:
p.community_id NOT IN :blocked_community_ids - Blocked domains:
(p.domain_id NOT IN :domain_ids OR p.domain_id IS NULL) - Blocked instances:
(p.instance_id NOT IN :instance_ids OR p.instance_id IS NULL) - Cached via
@cache.memoize(timeout=300)helper functions
2.4 Flair Blocking (CommunityFlairBlock)
DB Model (app/models.py:4115-4119):
class CommunityFlairBlock(db.Model):
community_id = Integer, FK->Community
community_flair_id = Integer, FK->CommunityFlair
user_id = Integer, FK->User
Users can block posts with specific flairs/tags within a community. Applied in get_deduped_post_ids():
p.id NOT IN (SELECT post_id FROM post_flair WHERE flair_id IN :blocked_flair_ids)
3. Vote / Score System
3.1 Vote Models
PostVote (app/models.py:3459-3475):
class PostVote(db.Model):
id = Integer, PK
user_id = Integer, FK->User # Who voted
author_id = Integer, FK->User # Post author (reputation target)
post_id = Integer, FK->Post
effect = Float # Vote weight (not just -1/+1)
emoji = String(50) # Emoji reaction
created_at = DateTime
PostReplyVote (app/models.py:3478-3494): Same structure for comment votes.
Vote effects are floats, enabling weighted voting systems (reputation-weighted, etc., though PyFed uses simple +/-1 in practice with float storage for future).
3.2 Vote Federating / Privacy
| Setting | Location | Effect |
|---|---|---|
federate_votes |
User setting | When false, votes only count locally, not broadcast via ActivityPub |
vote_privately |
User model | When true, votes are hidden from other users |
show_scores |
API schema | Controls display of aggregate scores |
Instance votes_are_public() |
Instance model | Returns True for Lemmy/Mbin/kbin (for compatibility) |
3.3 Downvote Controls (Granular)
Site-level (Site.enable_downvotes, Boolean, default=True):
- Master toggle: if false, no downvoting anywhere on instance
Community-level (Community.downvote_accept_mode, Integer):
DOWNVOTE_ACCEPT_NONE = -1 # No downvotes allowed
DOWNVOTE_ACCEPT_ALL = 0 # Anyone can downvote
DOWNVOTE_ACCEPT_MEMBERS = 2 # Only community members
DOWNVOTE_ACCEPT_INSTANCE= 4 # Only users from same instance
DOWNVOTE_ACCEPT_TRUSTED = 6 # Only from trusted instances
User-level restrictions (app/utils.py:1766-1807):
- Banned users can't downvote
- Bot accounts can't downvote
- Users with negative attitude (more downvotes than upvotes cast) can't downvote
- Users with reputation < -10 can't downvote
- Banned from community can't downvote there
3.4 Vote Quota
VOTE_QUOTAconfig: max 240 votes per day (configurable)votes_cast_today(user_id): Redis counter, resets daily- Displayed on user profiles as
vote_quota_used
3.5 Reputation & Attitude
User fields:
reputation(Float): Accumulated reputation scoreattitude(Float, -1 to 1): Ratio of (upvotes_cast - downvotes_cast) / total_votes_cast- Only calculated after 10+ votes
- Used to determine if user can downvote
Reputation calculation:
- Based on recent 50 post votes + 50 comment votes
- Post authors' reputation affected by votes on their content
- Low-quality communities marked with
low_qualityflag don't improve reputation
3.6 Reply Visibility Thresholds
| Setting | Description |
|---|---|
reply_collapse_threshold (integer, default=-10) |
Replies scored at or below this are collapsed |
reply_hide_threshold (integer, default=-20) |
Replies scored at or below this are hidden entirely |
Comment sort options: hot, top, new, old
4. Hide Read / Hidden Posts
4.1 Read Posts Tracking
Many-to-many table (read_posts, app/models.py:928-933):
read_posts = db.Table('read_posts',
db.Column('user_id', Integer, FK->User, PK),
db.Column('read_post_id', Integer, FK->Post, PK),
db.Column('interacted_at', DateTime, indexed),
Index('ix_read_posts_user_post', 'user_id', 'read_post_id')
)
User model relationship:
User.read_post: posts the user has read/interacted withPost.read_by: users who have read this postUser.hide_read_posts(Boolean): master toggle
Hide Read in Feed Query (app/utils.py:3264-3265):
p.id NOT IN (SELECT read_post_id FROM "read_posts" WHERE user_id = :user_id)
4.2 Hidden Posts
Table (hidden_posts, app/models.py:937-942): Same structure as read_posts.
Always applied (app/utils.py:3268):
p.id NOT IN (SELECT hidden_post_id FROM "hidden_posts" WHERE user_id = :user_id)
Users can manually hide individual posts. These are always excluded from feed.
4.3 Interaction Recording
Interaction is recorded when:
- A user opens/clicks a post
- A user upvotes or downvotes a post
- This powers both
hide_read_postsand the "read history" feature
4.4 View Templates
user/read_posts.html: shows user's read post historyuser/hidden_posts.html: shows hidden posts with unhide optionuser/_read_posts_nav.html: navigation for read/hidden posts
5. Content Moderation & Governance
5.1 Community Moderation
Community membership levels:
SUBSCRIPTION_OWNER = 3
SUBSCRIPTION_MODERATOR = 2
SUBSCRIPTION_MEMBER = 1
SUBSCRIPTION_NONMEMBER = 0
SUBSCRIPTION_PENDING = -1
SUBSCRIPTION_BANNED = -2
Moderation tools:
- Community bans (with reason, optional duration)
- Post/reply removal
- Report handling (new → escalated → resolved)
- Moderation log (audit trail)
- Content retention policies (auto-delete after N months)
- Flair management (text/color/blur-images)
- Wiki management with revision history
- Mod-only communities
5.2 Report System
Report types (constants.py):
REPORT_TYPE_USER = 0
REPORT_TYPE_POST = 1
REPORT_TYPE_REPLY = 2
REPORT_TYPE_COMMUNITY = 3
REPORT_TYPE_MESSAGE = 4
Report states: new → escalated → appealed → resolved → discarded
Report reasons (from user/forms.py):
- Breaks community rules
- Harassment
- Threatening violence
- Promoting hate/genocide
- Minor abuse/sexualization
- Sharing personal info (doxing)
- Spam
- Non-consensual intimate media
- Prohibited transaction
- Impersonation
- Copyright violation
- Trademark violation
- Self-harm/suicide
- Other
- Misinformation/disinformation
- Racism/sexism/transphobia
- Malicious reporting
5.3 Instance-level Controls
| Setting | Type | Description |
|---|---|---|
enable_downvotes |
Boolean | Master downvote toggle |
registration_mode |
String | Open / RequireApplication / Closed |
application_question |
Text | Question for registration applications |
private_instance |
Boolean | Require login to view anything |
allow_or_block_list |
Integer | 1=allowlist, 2=blocklist |
allowlist_mode |
Integer | 0=weak, 1=strong, 2=intense |
blocked_phrases |
Text | Discard incoming content with these phrases |
auto_decline_referrers |
Text | Auto-reject registrations from these referrers |
honeypot |
Boolean | Bot detection |
5.4 Inoculation System
app/inoculation.py contains educational content displayed to users about:
- Groupthink and cult dynamics
- Alt-right radicalization pipelines
- Media literacy
- Confirmation bias
- The dark side of upvotes
This is shown at registration and optionally in sidebars — a unique approach to platform responsibility.
6. Custom Feeds & Curated Collections
6.1 Feed System
Feeds are user-curated collections of communities, acting like topic-based aggregations.
Feed model (app/models.py):
- User-owned (or instance-wide)
- Can be public or private
- Auto-follow/auto-leave behavior for subscribing
- Remote feeds via ActivityPub federation
- Feeds can have parent-child hierarchy (sub-feeds)
6.2 Topic System
Topics are admin-created groupings of communities with hierarchical structure (Topic model has parent_id).
6.3 Community Features
- Private communities: Only members can view
- Local-only communities: No federation
- Encrypted communities: End-to-end encryption
- Restricted-to-mods posting: Only moderators can post
- Question/Answer mode: StackOverflow-style communities (
question_answerflag) - Default layout: Per-community layout preference
- Community themes: Visual customization per community
- Post flairs: Categorization with visual styling (text color, background color, blur images)
7. User Experience & Accessibility
7.1 Themes & Visual
- 18+ themes including accessibility-focused ones (high contrast, dyslexic font)
- Font choices: Atkinson Hyperlegible, OpenDyslexic, Inter, Roboto
- Compact UI modes (3 levels: normal/compact/ultra-compact)
- Low bandwidth mode (reduces page size)
- Code syntax highlighting (18 color schemes)
- Additional CSS injection for users and admins
7.2 Productivity Features
- Usage limits:
max_hours_per_daywith configurable change restrictions - Vote quota: Limits daily voting to prevent vote manipulation
- Scheduled posts: Posts with
scheduled_for,repeat(daily/weekly/monthly) - Post drafts: Posts with status
-1(draft) - Bookmarks: Separate for posts and replies
- Cross-posting: Track cross-posted content across communities
- User notes: Private notes about other users
- Keyboard shortcuts: Documented in
keyboard_shortcuts.html
7.3 Notification System
Granular notification subscriptions:
- User follows (when followed user posts)
- Community posts (new posts in community)
- Topic posts (new posts in topic communities)
- Post replies (new top-level comments)
- Reply replies (replies to your comments)
- Mentions
- Private messages
- Ban/unban notifications
- New moderator notifications
- Report updates
8. Key Technical Patterns
8.1 Feed Query Pipeline
The get_deduped_post_ids() function (app/utils.py:3197-3317) is the core query builder:
- View filter selection (subscribed/local/popular/all/moderating)
- Community source selection
- Following user content inclusion (optional)
- Content type filters (nsfw, nsfl, ai, bots)
- Hide read posts
- Hidden posts exclusion
- Language filter
- Blocked domains/instances/communities/users
- Community bans
- Flair blocks
- Community keyword filter
- Sort application (hot/scaled/top/new/active/old)
8.2 Vote Quota Circuit (Redis-based)
def votes_cast_today(user_id):
num = redis_client.get(f'votes_cast_{date.today()}_{user_id}')
if num is None: return 0
return int(num)
8.3 Deduplication Strategy
Cross-posts are tracked via Post.cross_posts (ARRAY of post IDs) and deduplicated in the feed query:
- Posts with the same URL are grouped
- Non-bot posts preferred over bot posts
- Post with most replies preferred
- Seen posts tracked in
seen_beforeset
9. Features Valuable for Threadlight → Detailed Recommendations
9.1 HIGH VALUE — Direct Port
9.1.1 Multi-Layered Content Filtering System
Why: Threadlight's goal of encouraging quality engagement requires giving users granular control over their feed.
Recommended implementation:
- Keyword Filter Set: Named filters with keywords, scope (feed/community/replies), hide_type (dim, hide, blur), and optional expiration. Users can create "spoiler" or "topic avoid" filters.
- Content Toggles: NSFW, AI-generated, bot content — each with graduated visibility levels (show/dim/hide). This aligns with trust-based governance where communities self-identify content types.
- Language Filter: Filter feed to languages the user understands.
Why for Threadlight: Single-instance doesn't need instance blocking, but user-level blocking of communities, users, and content keywords is essential for a healthy social platform. The graduated visibility concept (show → semi-transparent → blur → hide) is more nuanced than binary show/hide.
9.1.2 Hide Read / Voted Posts
Why: Reduces feed fatigue. Users who browse frequently see fresh content.
Recommended implementation:
- Track post reads, upvotes, and downvotes per user
- Optional toggle to exclude read/voted posts from feed
- Separate explicit "hide post" function
- "Read post history" page for personal reference
Why for Threadlight: With Threadlight's credit economy, tracking what users have engaged with is already needed. Extending this to feed filtering is a small addition with huge UX benefit.
9.1.3 Reply Collapse/Hide Thresholds
Why: Users collapse/expand replies based on score. This empowers the community to self-moderate without total censorship.
Recommended implementation:
- User-configurable thresholds for collapsing and hiding replies
- Default: collapse at -10, hide at -20 (configurable)
- Works with Threadlight's credit-based voting system
Why for Threadlight: Perfect fit for trust-based governance. Bad-faith replies don't need deletion — they just get buried. The threshold approach gives users agency.
9.2 MEDIUM-HIGH VALUE — Adapt with Credit Economy
9.2.1 Downvote Controls (Community-Level)
Why: Communities should decide their own norms around feedback.
Recommended implementation:
- Site-level master toggle for downvotes
- Per-circle (community) downvote policy: None / Members only / Trusted only / Everyone
- User reputation/credit check: only users with positive credit ratio can downvote
Why for Threadlight: The credit economy naturally maps to this. "Who can downvote" becomes a trust function — users with proven good judgment (high credit, positive attitude) earn the right to give negative feedback.
9.2.2 Vote Privacy & Score Visibility
Why: Reduces social pressure and groupthink.
Recommended implementation:
federate_votes→ adapt toshare_votes(share with circle/community or keep private)vote_privately→hide_my_votes(don't show my voting activity on my profile)show_scores→ configurable per-community, per-post type- Vote weighting by user reputation (already using float effect values)
Why for Threadlight: Core to "not promoting outrage". Hidden scores reduce pile-on voting. Reputation-weighted votes reduce brigading. Makes the credit economy meaningful.
9.2.3 Vote Quota / Daily Limits
Why: Prevents vote manipulation and encourages deliberate engagement.
Recommended implementation:
- Configurable daily vote budget (e.g., 80 per day)
- Redis-based counter, resets daily
- Higher credit users could get larger budgets
Why for Threadlight: The credit economy needs scarcity. Daily vote budgets make each vote meaningful. Could tie quota to credit level (higher credit = more votes/day, aligning incentives).
9.3 MEDIUM VALUE — Community & Trust Features
9.3.1 Community Flairs with User Blocking
Why: Communities can tag posts with flairs; users can block specific flairs.
Recommended implementation:
- Circle moderators define flairs (e.g., "Politics", "Venting", "Humor", "NSFW")
- Flairs have visual styling (text color, background color, optional image blur)
- Users can block posts with specific flairs (per-circle)
- Posts auto-assigned flair based on content analysis
Why for Threadlight: Circles (Threadlight's communities) have diverse content. Flair-blocking lets users curate their experience within a circle without leaving it.
9.3.2 Custom Feeds / Curated Collections
Why: Users can aggregate content across circles.
Recommended implementation:
- User-created "feeds" that pool posts from selected circles
- Public/private toggle
- Auto-follow/auto-leave: subscribing to a feed auto-joins the constituent circles
- Instance-wide curated feeds (staff-picked)
Why for Threadlight: Threadlight's "circles" concept makes this natural — feeds become "meta-circles" or collections. Great for onboarding: "Here's a starter feed of recommended circles."
9.3.3 Question & Answer Mode
Why: Some circles benefit from StackOverflow-style structured Q&A.
Recommended implementation:
- Circle-level toggle for Q&A mode
- Accepted answer feature (
NOTIF_ANSWERnotification type) - Different sorting: unanswered first, accepted answers highlighted
Why for Threadlight: Technical circles, support circles, or knowledge-sharing circles benefit from this. It is a relatively contained feature (circle setting + accepted answer field).
9.3.4 Scheduled / Repeating Posts
Why: For regular content like daily discussions, weekly threads.
Recommended implementation:
scheduled_fordatetime for future postingrepeatmode: daily, weekly, monthlystop_repeatingend date- Draft queue (
status = -1) for pre-written content
Why for Threadlight: Circles with regular events (weekly book club, daily check-in) benefit from automatable posts. Keeps the community engaged with minimal moderation overhead.
9.4 NOTEWORTHY — Quality of Life
9.4.1 Inoculation / Media Literacy Resources
Why: Unique to PyFed — an educational content system shown at registration and in sidebars.
Recommended implementation:
- Configurable set of links/resources shown during onboarding
- Topics: critical thinking, media literacy, community guidelines
- Rotation-based display (different resource each time)
Why for Threadlight: Fits the "bringing people together" mission. Could be integrated into the credit economy (read an inoculation article → earn small credit bonus). Makes Threadlight's trust-based governance self-reinforcing.
9.4.2 User Notes (Private Labels)
Why: Users can attach private notes to other users visible only to themselves.
Recommended implementation:
- Simple user_id → target_id → note_text mapping
- "Apply to all with same username" option (for known trolls across instances — less relevant for single-instance)
- Displayed on user hover/profile as a subtle note
Why for Threadlight: Lightweight trust tool. "I've had good interactions with this person" or "Reminder: this user posted bad-faith content before." Supports the credit economy by letting users track their own trust signals.
9.4.3 Usage Time Limits
Why: Users can set daily time limit on the platform with configurable change restrictions.
Recommended implementation:
max_hours_per_daysetting- Enforce via session timing or activity tracking
- Change restriction options: anytime / 1 day / 1 month / 2 months
- Helps with compulsive usage
Why for Threadlight: Aligns with ethical platform design. If Threadlight aims to be a "quality engagement" platform, helping users manage their time is a strong signal of pro-user values.
9.4.4 Activity Logging (User-Level)
Why: Users can see their voting history, posting patterns, and platform usage.
Recommended implementation:
ActivityLogmodel tracks user activity types- Profile charts showing posting/commenting patterns (hour of day, etc.)
- Recently upvoted/downvoted posts visible on profile
Why for Threadlight: The credit economy needs transparency. Showing users their own activity patterns and vote history reinforces accountability. Could combine with credit statement ("Your votes this week earned/spent X credits").
9.5 LOWER PRIORITY — Nice to Have
| Feature | Reason for Lower Priority |
|---|---|
| Cross-posting detection | Useful but complex; not core to Threadlight's value prop |
| Bot detection patterns | Single-instance has fewer bot problems |
| PWAs/service workers | Nice but separate from social architecture |
| Pluggable themes | Valuable but not transformative |
| RSS feeds | Good for power users but not core |
| Full-text search | Assumed baseline for any social platform |
| Multiple code highlight themes | Low impact on community health |
10. Implementation Notes for Rust/Threadlight
10.1 Query Pattern: Layered Feed Filtering
PyFed's get_deduped_post_ids() demonstrates a composable filter pattern:
1. Source selection (which communities/circles)
2. Include following (friends/trusted users)
3. Content filters (nsfw, ai, bots)
4. Read/explicit hide (interacted posts)
5. Language filter
6. Block list (users, communities, flairs)
7. Ban list (community bans)
8. Sort (hot, top, new, etc.)
Each layer is a SQL WHERE clause. In Rust, this maps cleanly to a builder pattern:
FeedQuery::new()
.from_circles(user.subscribed_circles())
.include_following(user.following())
.filter_nsfw(user.hide_nsfw())
.hide_read_posts(user.hide_read_posts())
.exclude_blocked_users(user.blocked_users())
.language_filter(user.languages())
.sort(Sort::Hot)
.build()
10.2 Vote as Float
PyFed stores effect as Float, not just +/-1. For Threadlight's credit economy:
- Upvote effect could scale with voter credit:
effect = voter.credit_level * base_weight - Downvote effect could scale inversely:
effect = -(1.0 / voter.credit_level) * base_weight - This naturally weights high-credit users' opinions more heavily
10.3 Redis Counter Pattern for Quotas
// Simple Redis-based daily quota
async fn votes_remaining(user_id: &str, redis: &Client) -> i64 {
let key = format!("vote_quota:{}:{}", user_id, today());
let used: i64 = redis.get(&key).unwrap_or(0);
let max = get_user_quota(user_id); // Higher credit = higher quota
max - used
}
10.4 Single-Instance Adaptations
Threadlight is single-instance, so skip:
- Instance federation
- Remote content fetching
- Allowlist/blocklist
- Instance-level admin for other instances
Focus instead on:
- Circle (community) level governance
- User-level trust and credit
- Platform-level defaults and policies
Appendix A: Complete DB Schema Summary (Threadlight-Relevant)
| Table | Purpose | Key Fields |
|---|---|---|
| Filter | User keyword filters | title, filter_home, filter_posts, filter_replies, hide_type, keywords, expire_after |
| UserBlock | Blocked users | blocker_id, blocked_id |
| CommunityBlock | Blocked communities | user_id, community_id |
| DomainBlock | Blocked domains | user_id, domain_id |
| CommunityFlairBlock | Blocked flairs | community_id, flair_id, user_id |
| PostVote | Post votes | user_id, post_id, effect (Float), emoji |
| PostReplyVote | Comment votes | user_id, reply_id, effect (Float) |
| read_posts | Read tracking | user_id, post_id, interacted_at |
| hidden_posts | Hidden posts | user_id, post_id |
| CommunityFlair | Post categorizations | flair, text_color, background_color, blur_images |
| Community | Circle/community | downvote_accept_mode, question_answer, private, local_only |
| User | User settings | hide_nsfw, hide_nsfl, hide_gen_ai, hide_read_posts, vote_privately, reputation, attitude |
| UserNote | Private user notes | user_id, target_id, note |
| NotificationSubscription | Notification prefs | user_id, entity_id, type (NOTIF_*) |
| ModLog | Moderation audit | user_id, action, target, community_id |
Analysis completed: July 25, 2026. PyFed version: 1.8.0-dev.
Lemmy — Project Specification
Source: ~/code/rust/rust/lemmy/ (https://github.com/LemmyNet/lemmy)
Language: Rust (edition 2024, pinned to toolchain 1.95)
License: AGPL-3.0
Version: 1.0.0-beta.1
Description: A link aggregator for the fediverse
Architecture
[Browser/App] <--REST/JSON--> [lemmy_server (actix-web)]
|
+----------+----------+
| |
[PostgreSQL] [pictrs (images)]
|
[ActivityPub] <--> [Other Fediverse Instances]
|
[SMTP Email]
The codebase is a Rust workspace of 39 crates under crates/. The key layers:
| Layer | Crate(s) | Purpose |
|---|---|---|
| Server | lemmy_server |
Binary entrypoint, actix-web HttpServer, middleware stack |
| API (current) | lemmy_api_routes |
Route definitions for API v4 |
| API (legacy) | lemmy_api_routes_v3 |
Route definitions for API v3 (deprecated) |
| API Handlers | lemmy_api, lemmy_api_crud |
Core handler logic + CRUD operations |
| API Types | lemmy_api_common |
Shared request/response types |
| API Utils | lemmy_api_utils |
JWT auth, authorization checks, rate limiting, Extism WASM plugins, notification dispatch |
| DB Schema | lemmy_db_schema, lemmy_db_schema_file |
Diesel ORM models, source definitions, newtypes, auto-generated schema.rs |
| DB Views | lemmy_db_views_* (24 crates) |
Read-model queries (joins, filtering, sorting, pagination, visibility rules) |
| ActivityPub | lemmy_apub, lemmy_apub_activities, lemmy_apub_objects, lemmy_apub_send |
Federation layer — sending/receiving ActivityPub activities |
| Routes | lemmy_routes |
Non-API HTTP routes: RSS/Atom feeds, image proxy, nodeinfo, webfinger, Prometheus metrics |
lemmy_email |
SMTP email sending with i18n templates | |
| Utils | lemmy_utils |
Error types, rate limiting, settings/config (HJSON), caching |
| Diesel Utils | lemmy_diesel_utils |
DB connection pool, migrations, pagination utilities |
API Surface (v4, all under /api/v4/)
Site
| Method | Path | Description |
|---|---|---|
| GET | /site |
Get site info (combined response with site, admins, user state) |
| POST | /site |
Create site (initial setup) |
| PUT | /site |
Edit site configuration |
| POST | /site/icon |
Upload site icon |
| DELETE | /site/icon |
Delete site icon |
| POST | /site/banner |
Upload site banner |
| DELETE | /site/banner |
Delete site banner |
Community
| Method | Path | Description |
|---|---|---|
| GET | /community |
Get community by ID or name |
| POST | /community |
Create community |
| PUT | /community |
Edit community |
| DELETE | /community |
Delete community |
| GET | /community/list |
List communities (paginated, filtered) |
| GET | /community/random |
Get random community |
| POST | /community/follow |
Follow/unfollow community |
| POST | /community/remove |
Mod: remove community |
| POST | /community/transfer |
Transfer community ownership |
| POST | /community/ban_user |
Mod: ban user from community |
| POST | /community/mod |
Mod: add/remove moderator |
| POST | /community/icon |
Upload community icon |
| DELETE | /community/icon |
Delete community icon |
| POST | /community/banner |
Upload community banner |
| DELETE | /community/banner |
Delete community banner |
| POST | /community/tag |
Create community tag |
| PUT | /community/tag |
Edit community tag |
| DELETE | /community/tag |
Delete community tag |
| PUT | /community/notifications |
Edit community notification settings |
| GET | /community/pending_follows/list |
List pending follow requests |
| POST | /community/pending_follows/approve |
Approve/deny pending follow |
| POST | /community/report |
Report a community |
| PUT | /community/report/resolve |
Resolve community report |
Post
| Method | Path | Description |
|---|---|---|
| GET | /post |
Get post by ID |
| POST | /post |
Create post |
| PUT | /post |
Edit post |
| DELETE | /post |
Delete post |
| GET | /post/list |
List posts (paginated, with filters/sorts) |
| POST | /post/remove |
Mod: remove post |
| POST | /post/mark_as_read |
Mark single post as read |
| POST | /post/mark_as_read/many |
Mark multiple posts as read |
| POST | /post/hide |
Hide post from listings |
| POST | /post/lock |
Lock post (prevent new comments) |
| POST | /post/feature |
Feature/sticky post in community or instance |
| POST | /post/like |
Vote on post (score: -1, 0, 1) |
| GET | /post/like/list |
List users who voted on post |
| PUT | /post/save |
Save/bookmark post |
| POST | /post/report |
Report a post |
| PUT | /post/report/resolve |
Resolve post report |
| PUT | /post/notifications |
Edit post notification settings |
| PUT | /post/mod_edit |
Mod: edit post body |
| POST | /post/warn |
Issue warning on a post |
| GET | /post/site_metadata |
Fetch URL metadata (OpenGraph) |
Comment
| Method | Path | Description |
|---|---|---|
| GET | /comment |
Get comment by ID |
| POST | /comment |
Create comment (with optional parent_id for threading) |
| PUT | /comment |
Edit comment |
| DELETE | /comment |
Delete comment |
| GET | /comment/list |
List comments on a post (with depth, sort, max_depth, parent_id) |
| GET | /comment/list/slim |
Slim comment list (IDs only for unread counts) |
| POST | /comment/remove |
Mod: remove comment |
| POST | /comment/distinguish |
Mod: distinguish comment |
| POST | /comment/like |
Vote on comment |
| GET | /comment/like/list |
List users who voted on comment |
| PUT | /comment/save |
Save/bookmark comment |
| POST | /comment/lock |
Lock comment thread |
| POST | /comment/warn |
Issue warning on a comment |
| POST | /comment/report |
Report a comment |
| PUT | /comment/report/resolve |
Resolve comment report |
Private Messages
| Method | Path | Description |
|---|---|---|
| POST | /private_message |
Create private message |
| PUT | /private_message |
Edit private message |
| DELETE | /private_message |
Delete private message |
| POST | /private_message/report |
Report a private message |
| PUT | /private_message/report/resolve |
Resolve private message report |
Reports
| Method | Path | Description |
|---|---|---|
| GET | /report/list |
List all reports (combined, paginated) |
User / Account
| Method | Path | Description |
|---|---|---|
| POST | /account/auth/register |
Register new user |
| POST | /account/auth/login |
Login (returns JWT) |
| POST | /account/auth/logout |
Logout |
| POST | /account/auth/password_reset |
Request password reset email |
| POST | /account/auth/password_change |
Change password with reset token |
| PUT | /account/auth/change_password |
Change password (authenticated) |
| POST | /account/auth/totp/generate |
Generate TOTP 2FA secret |
| POST | /account/auth/totp/edit |
Update TOTP configuration |
| POST | /account/auth/verify_email |
Verify email with token |
| POST | /account/auth/resend_verification_email |
Resend verification email |
| GET | /account |
Get my user (authenticated user with settings, follows, mods, blocks) |
| GET | /account/auth/get_captcha |
Get CAPTCHA |
| GET | /account/unread_counts |
Get unread counts (replies, mentions, PMs) |
| GET | /account/notification/list |
List notifications |
| POST | /account/notification/mark_as_read/all |
Mark all notifications read |
| POST | /account/notification/mark_as_read |
Mark single notification read |
| DELETE | /account |
Delete account |
| GET | /account/login/list |
List active login sessions |
| GET | /account/validate_auth |
Validate JWT is still valid |
| POST | /account/donation_dialog_shown |
Mark donation dialog shown |
| POST | /account/avatar |
Upload avatar |
| DELETE | /account/avatar |
Delete avatar |
| POST | /account/banner |
Upload banner |
| DELETE | /account/banner |
Delete banner |
| POST | /account/block/person |
Block/unblock a person |
| POST | /account/block/community |
Block/unblock a community |
| POST | /account/block/instance/communities |
Block all communities on an instance |
| POST | /account/block/instance/persons |
Block all persons on an instance |
| GET | /account/saved |
List saved posts/comments |
| GET | /account/read |
List read posts |
| GET | /account/hidden |
List hidden posts |
| GET | /account/liked |
List liked posts |
| PUT | /account/settings/save |
Save user settings |
| GET | /account/settings/export |
Export user settings |
| POST | /account/settings/import |
Import user settings |
| GET | /account/data/export |
Export all user data |
| POST | /account/invite |
Create invitation |
| DELETE | /account/invite |
Revoke invitation |
| GET | /account/invite/list |
List invitations |
| DELETE | /account/media |
Delete an image |
| GET | /account/media/list |
List user media |
Person (Public Profiles)
| Method | Path | Description |
|---|---|---|
| GET | /person |
Get person by ID |
| GET | /person/list |
List persons |
| GET | /person/content |
List person's content (posts + comments, combined, paginated) |
| POST | /person/note |
Add a moderator note to a person |
Admin
| Method | Path | Description |
|---|---|---|
| POST | /admin/add |
Add/remove admin |
| GET | /admin/registration_application |
Get single registration application |
| GET | /admin/registration_application/list |
List registration applications |
| PUT | /admin/registration_application/approve |
Approve/reject registration application |
| POST | /admin/purge/person |
Purge a person and all their content |
| POST | /admin/purge/community |
Purge a community |
| POST | /admin/purge/post |
Purge a post |
| POST | /admin/purge/comment |
Purge a comment |
| POST | /admin/tagline |
Create site tagline |
| PUT | /admin/tagline |
Edit tagline |
| DELETE | /admin/tagline |
Delete tagline |
| GET | /admin/tagline/list |
List taglines |
| POST | /admin/ban |
Ban/unban person from site |
| GET | /admin/users |
List all users (admin) |
| POST | /admin/instance/block |
Block an instance |
| POST | /admin/instance/allow |
Allow an instance |
Other
| Method | Path | Description |
|---|---|---|
| GET | /modlog |
Get moderator log |
| GET | /search |
Search (posts, comments, communities, users, URL) |
| GET | /resolve_object |
Resolve a federated object by ID |
| GET | /federated_instances |
List federated instances |
| POST | /custom_emoji |
Create custom emoji |
| PUT | /custom_emoji |
Edit custom emoji |
| DELETE | /custom_emoji |
Delete custom emoji |
| GET | /custom_emoji/list |
List custom emojis |
| POST | /oauth_provider |
Create OAuth provider |
| PUT | /oauth_provider |
Edit OAuth provider |
| DELETE | /oauth_provider |
Delete OAuth provider |
| POST | /oauth/authenticate |
Authenticate with OAuth |
| POST | /image |
Upload image (to pictrs) |
| DELETE | /image |
Delete image (admin) |
| GET | /image/proxy |
Image proxy (privacy-preserving) |
| GET | /image/health |
pictrs health check |
| GET | /image/list |
List all media (admin) |
| GET | /image/{filename} |
Get/redirect to image |
Non-API Routes
| Path | Description |
|---|---|
/nodeinfo/2.1 |
NodeInfo protocol (federation metadata) |
/.well-known/webfinger |
WebFinger (account discovery) |
/.well-known/nodeinfo |
NodeInfo well-known link |
/feeds/{type}.xml |
RSS/Atom feeds (community, user, front page) |
/metrics |
Prometheus metrics |
Key Data Models
Post
Fields: id, name, url, body, creator_id, community_id, removed, deleted, locked, featured_community, featured_local, nsfw, embed_title, embed_description, embed_html, thumbnail_url, ap_id, local, language_id, created, updated, published
Comment
Fields: id, creator_id, post_id, parent_id (self-referencing for threading), content, removed, deleted, distinguished, language_id, ap_id, local, path (materialized path Ltree for tree queries), published, updated
Community
Fields: id, name, title, description, removed, deleted, nsfw, actor_id, local, icon, banner, posting_restricted_to_mods, instance_id, published, updated
Person / User
Fields: id, name, display_name, avatar, banner, bio, banned, deleted, actor_id, local, bot_account, matrix_user_id, admin, published, updated
Site
Fields: id, name, sidebar, icon, banner, description, actor_id, last_refreshed_at, inbox_url, public_key, instance_id, published, updated
Key Differences from Threadlight
| Feature | Lemmy | Threadlight |
|---|---|---|
| Language | Rust (actix-web + Diesel ORM) | Go (Gin + pgx) |
| Federation | ActivityPub (full fediverse) | None (single-instance) |
| Comments | Full threaded (parent_id + ltree path, depth queries) | Via Interaction metadata (flat, no threading) |
| Voting | Dedicated like endpoints returning updated views | Via Interaction model |
| Media | pictrs external image server | None (no upload endpoint) |
| Site info | Rich combined GetSiteResponse |
No site endpoint (stubbed) |
| DB schema | 340+ Diesel migrations | Simple migrations |
| Rate limiting | Per-endpoint tunable limits | Global rate limit |
| WASM plugins | Extism-based custom plugins | None |
| OAuth | Provider authentication | None |
| 2FA | TOTP support | None |
| CAPTCHA | Built-in captcha | None |
| Custom emoji | Full support | None |
| Taglines | Site taglines | None |
| Private messages | Full PM system | None |
| Multi-community | Group communities | None |
| Modlog | Full mod action log | None |
| Registration queue | Application/approval workflow | None |
| Email verification | Required for registration | Present but minimal |
| Invite system | User invitation codes | Present via invite_code |
| User settings export/import | Full settings backup | None |
Architecture Strengths (Why Lemmy Is Mature)
- Workspace separation: 39 focused crates instead of one monolithic internal/ — clean dependency graph, faster compilation of unchanged crates
- View layer pattern: DB_views crates encapsulate all read-model complexity (joins, filtering, visibility rules, pagination) — handlers just call
view.query() - ActivityPub federation: Full spec implementation with type-safe activity serialization
- Derived aggregates: Post aggregates (score, upvotes, downvotes, comment count) stored in separate tables with trigger-based updates — no counting on read
- Materialized path for comments: PostgreSQL Ltree enables efficient subtree queries and depth-limited fetches
- Rate limiting: Per-endpoint configurable rate limits, not a single global limiter
- Extensive migration history: 340+ migrations document every schema change since 2019
We are watching the Overton window shift right at breakneck speed, and frankly, we have no one to blame but ourselves. Look at the state of the "left" in 2026.
You’ve got the Communists running isolated circle-jerk theory groups, more interested in purity tests and dunking on "libtards" than building actual coalitions. Then you've got the tech-bros hijacking "Save the Internet" and "Don't Kill Games" movements, turning digital preservation into a consumerist crusade that ignores the physical reality of the workers making the hardware. Meanwhile, the mainstream "left" is in the corner screaming about pronouns while ignoring the fact that people can't afford eggs.
We have completely abandoned the materialist analysis that actually won people over. We stopped talking about class. We stopped talking about the cost of living.
The only people willing to talk about "material conditions" anymore are the Far Right. They are lying to the working class—telling them their rent is high because of immigrants, their jobs are gone because of "DEI," and their wages are stagnant because of "globalists." They are providing a materialist framework, even if it’s a false one, and the working class is eating it up because we have given them nothing else.
The average worker is shifting right not because they are evil, but because the Right is the only side currently acknowledging their economic pain—even if they are completely misdiagnosing the cause. If we don't get our heads out of our ideological echo chambers and start talking about class war instead of culture war, the fascists are going to win by default.
Deployed 2026-07-19 on Orange Pi 5 (RK3588S, 4GB, Armbian/Debian 12). Public at https://piefed.your-domain.com/ (example)
Prerequisites
- Orange Pi 5 (or any Linux ARM64/x86 host)
- Docker + Docker Compose (v2)
- Domain/subdomain pointing to a Cloudflare tunnel (or any reverse proxy)
Step 1 — Install Docker
# Add Docker's GPG key and repo (Debian/Ubuntu)
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Add user to docker group
sudo usermod -aG docker $USER
# Log out and back in for group change to take effect
Step 2 — Clone PieFed
git clone https://codeberg.org/rimu/pyfedi.git ~/code/python/pyfedi
cd ~/code/python/pyfedi
Step 3 — Configure Environment
cp .env.docker.sample .env.docker
Edit .env.docker:
SECRET_KEY— Generate withopenssl rand -base64 42SERVER_NAME— Set to your domain:piefed.your-domain.comPOSTGRES_PASSWORD— Change from default
The sample DATABASE_URL points to the Docker-internal db hostname (container name). This is correct — let Docker run its own PostgreSQL, don't try to reuse the host's.
Step 4 — Create Required Directories
mkdir -p logs media tmp
The compose file maps these as bind mounts for media uploads, logs, and temp files.
Step 5 — Fix Permissions (ARM64 gotcha)
The stock compose.yaml has user: "1000:1000" on the db service. This breaks on ARM64 because the container's postgres user can't write to its own data directory with that override.
Remove the user line from the db service in compose.yaml:
sed -i '/^ user: "1000:1000"/d' compose.yaml
This lets PostgreSQL run as its default container user, which can write to /var/lib/postgresql/data.
Step 6 — Build and Start
cd ~/code/python/pyfedi
sudo docker compose up --build -d
This builds three custom images (pyfedi-web, pyfedi-celery, pyfedi-piefednotifs) and pulls postgres:17, redis:6.2, and adminer.
On first startup, the web container runs database migrations automatically (Alembic), pulls the banned-instances list, and generates community data.
Services and their ports:
| Container | Host Port | Internal Port | Purpose |
|---|---|---|---|
piefed_app1 |
8030 | 5000 | Flask web app (Gunicorn) |
piefed_notifs |
8040 | 8000 | Async notifications (Uvicorn) |
piefed_celery1 |
— | — | Background task worker |
pyfedi-db-1 |
— | 5432 | PostgreSQL 17 (internal) |
pyfedi-redis-1 |
— | 6379 | Redis 6.2 (internal) |
pyfedi-adminer-1 |
127.0.0.1:8888 | 8080 | DB admin UI |
Step 7 — Create Admin Account
The web UI won't serve pages until a site record exists. Run the interactive setup:
cd ~/code/python/pyfedi
sudo docker compose exec web bash -c "export FLASK_APP=pyfedi.py && flask init-db"
You'll be prompted for:
- Admin username (avoid
admin) - Email address
- Password
This creates the site record, admin user, and populates the banned-instances list from the No-QAnon blocklist.
Step 8 — Verify Locally
curl -s -o /dev/null -w "HTTP %{http_code}" http://localhost:8030/
# Should return 302 (redirect to front page)
Step 9 — Expose via Cloudflare Tunnel
DNS Record
The subdomain must have a DNS record pointing to Cloudflare. In Cloudflare Dashboard → DNS → Records, add a CNAME for your PieFed subdomain targeting the tunnel.
Example (if using tunnel CNAME target):
Type: CNAME
Name: piefed
Target: your-tunnel-id.cfargotunnel.com
Proxy: Proxied (orange cloud)
Alternatively, point it to another working subdomain like web.your-domain.com — Cloudflare will route it through the same tunnel.
Tunnel Ingress Rule
In Cloudflare Zero Trust → Networks → Tunnels → select your tunnel, add an ingress rule:
| Hostname | Origin Service |
|---|---|
piefed.your-domain.com |
http://localhost:8030/ |
DNS propagation can take a minute or two. Once it resolves, https://piefed.your-domain.com/ works.
Management Commands
# Start/stop
sudo docker compose up -d
sudo docker compose down
# View logs
sudo docker compose logs -f # all services
sudo docker compose logs -f web # just the web app
# Restart a service
sudo docker compose restart web
# Update to latest version
git pull
sudo docker compose down
sudo docker compose up --build -d
# Then re-run flask init-db if schema changed
# Access adminer (DB web UI)
# Open http://localhost:8888/ in browser (Pi desktop only)
# Server: db, User: piefed, Password: piefed, Database: piefed
Architecture Notes
- PieFed uses its own PostgreSQL and Redis containers — completely separate from any host-level services (Polaris, etc.). No port conflicts because the DB/Redis containers only listen on the internal Docker bridge network.
- No Nginx reverse proxy needed on the Pi — Docker exposes port 8030 directly. The Cloudflare tunnel connects to that.
- 1.7 GB image footprint — the three custom images (web, celery, notifs) share a base Python 3.13 layer, plus postgres:17 and redis:6.2.
- Build cache consumes ~2.7 GB during initial build but can be pruned later with
docker builder prune.
I gave my Orange Pi a permanent public URL with a Cloudflare tunnel — no router port-forward, no static IP, no cert juggling. Full command-by-command walkthrough
I wanted a service running on my Orange Pi (an ARM SBC sitting on my home LAN) to be reachable from anywhere over HTTPS, without poking holes in my router, without begging my ISP for a static IP, and without messing with Let's Encrypt renewal. The trick is a Cloudflare Tunnel: it opens an outbound connection from the Pi to Cloudflare, and Cloudflare serves your site at the edge. Your home IP stays private, no ports are open, and TLS is handled by Cloudflare — "HTTPS only" is automatic.
I'm calling the setup "Polaris": a stable public subdomain fronting a small Go web app on the Pi, locked down so only the tunnel can reach it. Here's the entire procedure, every command included.
Why do it this way
- No port-forward. You never touch the router, so it works behind CGNAT / double-NAT too.
- Your home IP isn't published. The tunnel is outbound-only; the public DNS points at Cloudflare, not your LAN.
- No local cert. Cloudflare terminates TLS at the edge; the app just listens on
localhost:80. - Stable URL. A "named tunnel" gives you a fixed subdomain that survives reboots (unlike a quick tunnel's random URL).
Prereqs
- A Cloudflare account and a real Cloudflare-managed domain (NS delegated to CF). A
.xyzis ~$1/yr, or use a free delegatable TLD likeus.kg/pp.ua.- ⚠️ DuckDNS / FreeDNS subdomains do not work as Cloudflare public hostnames — CF requires the domain's nameservers to be Cloudflare's.
- SSH key access to the Pi as a normal user (I'll assume user
useron192.168.1.138). - A Go app to serve (I'll use a generic
polarisGo binary + itsweb/frontend).
Step 0 — Connect your domain to Cloudflare and create the tunnel (Namecheap example, any host)
The rest of this post is Orange Pi–specific, but first your domain has to actually live on Cloudflare — the tunnel can't use it as a public hostname until the nameservers are Cloudflare's. This is the generic, any-OS flow (I used Namecheap as the registrar; the idea is identical elsewhere). If you already manage your domain on Cloudflare, skip to Part A.
Prerequisites
- A domain registered with Namecheap.
- A (free) Cloudflare account.
Step 1: Add your domain to Cloudflare
- Log in to your Cloudflare dashboard.
- Click "Add a Site" (or "Add a domain") and enter your Namecheap domain name.
- Select a plan (the Free plan is sufficient for this use case) and click Continue.
- Cloudflare will scan for existing DNS records. Leave the default settings and click Continue.
- On the next page, you'll be given two Cloudflare nameservers (e.g.
sri.ns.cloudflare.comandsummer.ns.cloudflare.com). Copy these — you need them in the next step.
Step 2: Update nameservers at Namecheap
- Log in to Namecheap, go to Domain List, click Manage next to your domain.
- Scroll to the Nameservers section.
- From the dropdown, select "Custom DNS".
- Enter the two Cloudflare nameserver addresses you copied in Step 1.
- Click the green checkmark / Save Changes.
Important: NS changes can take up to 24–48 hours to propagate globally. The domain is only fully active on Cloudflare after that. You can check status in the Cloudflare dashboard before proceeding.
Step 3: Create a Cloudflare Tunnel
- In the Cloudflare dashboard, open Zero Trust (or Cloudflare One) from the left menu.
- Go to Networks → Connectors → Cloudflare Tunnels.
- Click "Create a tunnel".
- Name it (e.g.
my-tunnel) and click Save.
Step 4: Install and configure cloudflared
cloudflared is the daemon that runs on your host and connects it to Cloudflare.
On a Debian/ARM SBC (like the Orange Pi in Parts A–C), prefer the apt install shown in Part A (A0) over a manual binary. The login/create/config steps below are the same either way.
- Install
cloudflaredfor your OS (instructions on Cloudflare's site, or Part A for apt). - Authenticate the client with your account:
This opens a browser — log in and pick your domain to authorize the tunnel.cloudflared tunnel login - Create the tunnel from the CLI (name matches Step 3):
This generates a Tunnel ID (UUID) and a credentials file.cloudflared tunnel create my-tunnel - Write the config file
~/.cloudflared/config.yml(create the dir first:mkdir -p ~/.cloudflared):
Replacetunnel: UUID credentials-file: /home/your-username/.cloudflared/UUID.json ingress: - hostname: polarissocial.xyz service: http://localhost:8080/ - hostname: www.polarissocial.xyz service: http://localhost:8080/ - service: http_status:404UUIDwith your Tunnel ID andlocalhost:8080with your local service's address. (Polaris listens onlocalhost:80, so use that in the Orange Pi parts below.)
Step 5: Configure public hostnames
- Back in the dashboard (Zero Trust → Networks → Connectors → Cloudflare Tunnels), open your tunnel.
- Go to the Public Hostname tab → Add a public hostname.
- Enter your subdomain (e.g.
www, or blank for the rootpolarissocial.xyz) and pick your domain. - In Service, choose HTTP and enter your local address (e.g.
localhost:8080). - Click Save — Cloudflare creates the DNS record for you.
Step 6: Start the tunnel
cloudflared tunnel run my-tunnel
To run it as a background service on boot:
cloudflared service install
systemctl start cloudflared
After this, your domain is routed through the tunnel to your local service. The sections below pick up the Orange Pi specifics: installing cloudflared via apt (A0), the dashboard token-install alternative (A1), the systemd unit, deploying the Go app (Part B), and locking the box down (Part C). If you used this Step 0 dashboard flow, A1 is already done — you can jump to Part B.
Part A — the Cloudflare Tunnel (Polaris)
A0. Install cloudflared on the Pi (via apt, not a manual binary)
# on the Pi
sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflared-ascii.gpg \
| sudo tee /usr/share/keyrings/cloudflared-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflared-archive-keyring.gpg] https://pkg.cloudflare.com/cloudflared any main" \
| sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt-get update
sudo apt-get install -y cloudflared
If a stale skeleton service unit exists, clear it first:
sudo cloudflared service uninstall # safe to run even if nothing's there
A1. Create the tunnel + public hostname (Zero Trust dashboard — the easy path)
- Cloudflare Zero Trust → Networks → Tunnels → Create a tunnel.
- Name it
polaris. Copy the install command it shows:
The token is a secret — paste it, never echo it into logs or chat.sudo cloudflared service install <TOKEN> - On the Pi:
ssh user@192.168.1.138 sudo cloudflared service install <TOKEN> # pasted, not echoed sudo systemctl enable --now cloudflared - Back in the dashboard, add a Public Hostname: subdomain (e.g.
polaris) → your CF domain, servicehttp://localhost/. Save.
First-time Zero Trust setup may ask for a credit card for verification — it does not charge you. The
service installcommand wires up the systemd unit against the token;cloudflared tunnel run(no args) auto-discovers it.
A2. Alternative — build the tunnel from the CLI (no dashboard)
# on the Pi
cloudflared tunnel login # prints a URL — open it in a browser to authenticate
cloudflared tunnel create polaris
~/.cloudflared/config.yml:
tunnel: YOUR-TUNNEL-ID
credentials-file: /home/user/.cloudflared/YOUR-TUNNEL-ID.json
ingress:
- hostname: polaris.polarissocial.xyz
service: http://localhost/
- service: http_status:404
cloudflared tunnel route dns polaris polaris.your-domain.com
cloudflared tunnel run polaris # foreground test; Ctrl-C once you've verified
Systemd unit /etc/systemd/system/cloudflared.service:
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=user
ExecStart=/usr/local/bin/cloudflared tunnel run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared.service
Part B — deploy the Go app behind the tunnel
B1. Build it (cross-compile ARM64 from any machine with Go)
cd ~/code/go/polaris
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o /tmp/polaris-arm64 ./cmd/polaris
cd web && npm install && npm run build && cd ..
If your local checkout is broken (symptom: internal/ exists but there's no go.mod and no cmd/polaris/main.go), clone fresh and build there instead of fighting the corrupt tree:
git clone <your-remote> /tmp/polaris-fresh
cd /tmp/polaris-fresh
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o /tmp/polaris-arm64 ./cmd/polaris
cd web && npm install && npm run build
B2. Ship it to the Pi + restart
# from your build host
scp /tmp/polaris-arm64 user@192.168.1.138:/tmp/polaris-arm64
# on the Pi
sudo systemctl stop polaris
sudo cp ~/code/go/polaris/polaris ~/code/go/polaris/polaris.$(date +%F).bak # date-stamped backup
sudo cp /tmp/polaris-arm64 ~/code/go/polaris/polaris
sudo chmod +x ~/code/go/polaris/polaris
rsync -a ~/code/go/polaris/web/dist/ ~/code/go/polaris/web/dist/
rsync -a --delete internal/ cmd/ go.mod go.sum ~/code/go/polaris/
# IMPORTANT: if migrations/ has new *.sql, run them before starting (schema drift crashes boot)
sudo systemctl start polaris
B3. Verify the app is up (the tunnel only forwards to localhost:80)
curl -fsS http://localhost/health # from the Pi → 200 means the app is serving
Part C — lock it down for public exposure
Run once when the tunnel first goes public (idempotent).
sudo apt-get install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp # SSH: LAN only
sudo ufw allow from 192.168.1.0/24 to any port 5900 proto tcp # VNC: LAN only
sudo ufw --force enable
Once the tunnel is live, seal off LAN access to the web port so only the tunnel can reach the app:
sudo ufw delete allow from 192.168.1.0/24 to any port 80 proto tcp
sudo ufw delete allow from 192.168.1.0/24 to any port 5900 proto tcp
# if the app also listens on a backend port, block it from LAN too:
sudo ufw deny from 192.168.1.0/24 to any port 8080 proto tcp
SSH hardening (key-only, root off) — verify key auth works before logging out:
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart ssh
Stop bloat services (keep rpcbind — NFS needs it):
for svc in cups brltty accounts-daemon; do sudo systemctl disable --now "$svc" 2>/dev/null; done
Verify end-to-end
# 1. App is serving locally
curl -fsS http://localhost/health # → 200
# 2. Tunnel process is up
systemctl status cloudflared --no-pager | head
# 3. Public edge (run from ANY machine, not just the LAN)
curl -fsSI https://polaris.polarissocial.xyz/ # → 200 + a Cloudflare TLS cert
If step 3 fails but step 1 works, the tunnel ingress or the public-hostname DNS route is misconfigured — re-check Part A step 4 (dashboard) or the route dns command (CLI).
Pitfalls I hit / planned around
tunnel loginneeds a browser. It prints a URL you must open to authenticate. Can't be done from bare SSH.- Zero Trust asks for a card on first setup — verification only, no charge.
- DuckDNS / FreeDNS won't work as the public hostname. Use a real CF-managed domain.
- Close LAN
:80after the tunnel is live. Onlycloudflaredon localhost needs port 80; leaving it LAN-open defeats the point. cloudflared tunnel runwith no args auto-discovers the tunnel from the installed token/config — don't pass a stale tunnel name.- NFS mounts can die on reboot if
/etc/fstabusesnfs+vers=4.2. Use thenfs4fstype; after any reboot rundf -h /public /personal /mediato confirm they survived. - Run DB migrations before restarting the app if
migrations/has new.sql. - The tunnel token is a secret — never echo it into logs or chat.
Why this is worth owning
This is the same reason people self-host anything: the endpoint is yours. Cloudflare is just the front door — you can swap it, drop it, or move the app to a different box without losing control of the service itself. No vendor decides whether your thing keeps existing, and your LAN topology (and IP) stays private behind the outbound tunnel. For a $35 SBC that sips a few watts, that's a pretty durable little public node.
Big news: Kimi-K3 by @Kimi_Moonshot is now #1 in the Frontend Code Arena with 1679 pts, surpassing Claude Fable 5.
This is a 17-place jump from Kimi-k2.6 (#18 -> #1).
In Frontend, Kimi-K3 ranked #1 in 6 of 7 domains: Brand & Marketing, Reference-Based Design, Data & Analytics, Consumer Product, Simulations, and Content Creation Tools, landing #2 only in Gaming behind Fable 5.
The full model weights will be released by July 27.
Congrats to the @Kimi_Moonshot team on this major milestone!
I've been working on a social platform called Polaris and it's finally at a point where it actually runs on my laptop.
The core idea is: what if a social network was designed to be popular but positive? No outrage amplification, no influencer hierarchy, no shadowy moderation. Instead:
-
Trust-based moderation — you earn the ability to moderate through positive contributions. Permanent bans require a 90% jury consensus. A random, balanced panel reviews every ban proposal. Every moderation action is public in an append-only log.
-
Collaborative tagging (booru-style) — any trusted user can tag any post. Tags aren't controlled by the author. If the community disagrees on a tag, a vote is triggered among experienced taggers. The community curates the taxonomy together, wiki-style.
-
Auto-generated circles — the system groups nearby people who share your interests. "5 people near you also like astrophotography" — you accept or dismiss.
-
Community Notes (Twitter/X-style consensus warnings) — weighted by trust score, with a diversity requirement to prevent coordinated voting. Authors can also add correction notes that always appear with a green banner.
-
Algorithmic feed you can fully override — the default feed blends recency, engagement velocity, tag affinity, and trust network signals. But you can build your own custom feeds from any mix of tags, users, and keywords with include/exclude rules. RSS export for every feed, tag, collection, and community.
-
Communities with slow-boot, curators, and forking — topic-based public spaces. New members can be required to go through a probation period. Curators enforce scope tags but can't ban anyone. If the community disagrees with the direction, members can fork it — creating a new community with the same tag scope and member list.
-
Credit economy — daily rewards with streak bonuses, giftable credits between users, bounty system for questions. Fully auditable ledger using a hash chain in Postgres. Not a real blockchain — no mining, no gas, no wallet.
-
Achievements and multi-period leaderboards — daily, weekly, monthly, yearly, and all-time rankings for posting, tagging, gifts, trust network, and achievement points. Achievements focus on positive behaviors like curation, quality contributions, and community service.
-
Advanced search — full boolean operators (AND, OR, NOT), field search (title, author, tag, date range), exact phrase matching. Saved searches with new-result notifications.
-
Collections with four visibility layers — private, public read, public edit, and anybody edit. Perfect for collaborative link collections, reading lists, or community wikis.
-
Per-user content filters — hide, blur, or deprioritize content matching keywords, regex patterns, domains, or specific users. Processed both server-side (feed ranking) and client-side (blur overlay).
-
Trending topic detection from content, not hashtags — the system automatically detects rising terms by analyzing post text and comparing frequency against baseline. No hashtag spam gaming the algorithm.
-
Onboarding wizard — new users pick 5+ tags of interest, follow suggested users, and optionally make their first post before they see the main feed. The algorithm needs data to work with.
-
Content warnings and post scheduling — authors can add content warnings (blurred until clicked). Posts can be drafted, scheduled for future publication, or published immediately.
-
Shared blocklist for federation — domains, IP prefixes, and fingerprint-based blocks can be exported as JSON for other instances to consume.
-
ActivityPub-ready — every user has an ed25519 keypair and an actor_id. The data model supports federation without migration. Public keys are already stored for HTTP Signatures.
Tech stack is Go + Gin + Svelte + PostgreSQL + Redis. Single lightweight binary.
Would love feedback on it.