En Mantıklı Oyun İşlemcisi Yenilendi | AMD Ryzen 7 9850X3D İnceleme
AMD Ryzen 7 9850X3D, Zen 5 mimarisinin sunduğu performansı emsallerine göre devasa L3 önbellek teknolojisiyle birleştirerek oyunlarda ciddi bir performans artışı sağlıyor.Google, AirPods kulaklıklara canlı çeviri özelliğini getirdi
Google, Translate uygulamasının canlı çeviri özelliğini iOS platformuna getirdi. iPhone kullanıcıları artık AirPods ve diğer kulaklıklarda gerçek zamanlı çeviri özelliğinden yararlanabiliyor.Wikipedia yapay zeka tarafından oluşturulan makaleleri yasakladı
İngilizce Wikipedia, makale yazımı ve yeniden düzenleme süreçlerinde üretken yapay zeka kullanımını yasakladı. Peki bu kararın gerekçesi ne?Serious Sam: Shatterverse: Efsane, yeni formuyla geri dönüyor
Serious Sam: Shatterverse, çok oyunculu ve roguelite yapısıyla geri dönüyor. Farklı boyutlardan gelen Sam’ler, 2026 yılında PC, PlayStation 5 ve Xbox Series X|S platformlarında buluşacak.Grafana Loki Has a Free Log Aggregation System Like Prometheus But for Logs
Grafana Loki is a horizontally-scalable log aggregation system. Unlike Elasticsearch, it indexes only metadata (labels), not the full text.
What You Get for Free
- No full-text indexing — stores compressed logs cheaply
- Label-based querying with LogQL
- Native Grafana integration
- Multi-tenancy — built-in tenant isolation
- S3/GCS backend — cheap object storage
Query Logs with LogQL
{job="nginx"} |= "error" | json | status >= 500
Why Loki Over Elasticsearch?
| Feature | Loki | Elasticsearch |
|---|---|---|
| Storage cost | 10-50x cheaper | Expensive |
| Setup | Minutes | Hours |
| Best for | Logs + Grafana | Full-text search |
Need observability infrastructure? Check my work on GitHub or email [email protected] for consulting.
Domina la Conectividad Privada en AWS con VPC Endpoints (¡Ahorra $/mes!)
Bienvenidos a una nueva entrega en el maravilloso mundo de las redes en AWS.
Muchos arquitectos caen en la trampa de desplegar un NAT Gateway solo para que sus servidores privados se comuniquen con servicios como Amazon S3 y DynamoDB. Esto no solo infla la factura, sino que puede resultar contraproducente si nuestro objetivo es mantener los recursos 100% aislados del mundo exterior. Por eso, hoy vamos a construir una arquitectura de conectividad privada, segura y, lo mejor de todo, con un costo de red de $0.
Es posible tener lo mejor de ambos mundos: seguridad extrema y ahorro total?
Arquitectura base
Para que este workshop sea fluido y nos enfoquemos en lo que realmente importa asegurar la conectividad privada entre nuestros servidores y servicios como S3 y DynamoDB he diseñado una plantilla de CloudFormation. Con ella, podrás desplegar en minutos la infraestructura base que incluye:
- VPC: Una red virtual aislada con una subred 100% privada (sin Internet Gateway).
- Tabla de Rutas: Asociada específicamente a nuestra subred privada.
- Instancia EC2: Un servidor sin dirección IP pública (totalmente invisible desde internet).
- Recursos de Datos: Un Bucket de Amazon S3 y una Tabla de Amazon DynamoDB listos para ser consumidos.
- Seguridad IAM: Un rol con el principio de mínimos privilegios para interactuar con S3 y DynamoDB de forma segura.
- Security Group: Configurado específicamente para nuestra instancia EC2.
💡 Nota de Arquitecto: Al no tener un Internet Gateway, nuestra instancia nace “ciega y sorda” hacia el exterior. Es el escenario perfecto para probar cómo los VPC Endpoints actúan como túneles privados dentro de la red global de AWS.
💡 Nota: Si te apasionan las redes y prefieres construir cada componente desde cero, en mi perfil de dev.to tengo exactamente lo que necesitas. He preparado dos workshops previos donde te explico paso a paso cómo crear una VPC, subredes, tablas de rutas y mucho más:
Si ya lo tienes controlado, crea el stack con la plantilla que preparé para ti y te dejo a continuación.
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Workshop Base: Red Privada Aislada para Práctica de VPC Endpoints'
Parameters:
VpcCIDR:
Type: String
Default: 10.0.0.0/24
Description: CIDR para la VPC del Workshop
PrivateSubnetCIDR:
Type: String
Default: 10.0.0.0/25
Description: CIDR para la Subred Privada (128 IPs)
LatestImageId:
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64'
Resources:
# --- INFRAESTRUCTURA DE RED ---
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock:
Ref: VpcCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: Workshop-VPC-Endpoint
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: VPC
CidrBlock:
Ref: PrivateSubnetCIDR
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
MapPublicIpOnLaunch: false # Crucial: Asegura que ningun recurso se lance con ip publica
Tags:
- Key: Name
Value: Subred-Aislada
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: Tabla-Ruteo-Privada
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: PrivateSubnet
RouteTableId:
Ref: PrivateRouteTable
# --- SEGURIDAD (IAM) ---
EC2InstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: [ec2.amazonaws.com]
Action: ['sts:AssumeRole']
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
- arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess
Policies:
- PolicyName: EC2ConnectPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2-instance-connect:SendSSHPublicKey
Resource:
Fn::Sub: "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*"
EC2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- Ref: EC2InstanceRole
# --- SEGURIDAD (Security Groups) ---
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Permite SSH solo desde el CIDR de la VPC (temporalmente)
VpcId:
Ref: VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp:
Ref: VpcCIDR # Permitimos traficos desde la red interna
# --- CÓMPUTO ---
WorkshopInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t3.micro
IamInstanceProfile:
Ref: EC2InstanceProfile
ImageId:
Ref: LatestImageId
SubnetId:
Ref: PrivateSubnet
SecurityGroupIds:
- Ref: InstanceSecurityGroup
Tags:
- Key: Name
Value: EC2-Privada-Workshop
# --- RECURSOS DE DATOS (Para las pruebas) ---
WorkshopBucket:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Sub: 'workshop-s3-endpoint-${AWS::AccountId}-${AWS::Region}'
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WorkshopTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Delete
Properties:
TableName: Workshop-Dynamo-Endpoint
AttributeDefinitions:
- AttributeName: ID
AttributeType: S
KeySchema:
- AttributeName: ID
KeyType: HASH
BillingMode: PAY_PER_REQUEST
Outputs:
VpcId:
Value:
Ref: VPC
SubnetId:
Value:
Ref: PrivateSubnet
RouteTableId:
Value:
Ref: PrivateRouteTable
BucketName:
Value:
Ref: WorkshopBucket
🚀 Despliegue de la infraestructura
Esta plantilla está diseñada para que la puedas desplegar en cualquier región. Yo personalmente estaré trabajando en Ohio (us-east-2); ya saben, para no estar tan “amorochados” en Virginia con el resto del mundo 😉.
Para lanzar el laboratorio, sigue estos pasos:
-
Copia el código YAML de arriba y guárdalo en tu editor favorito con el nombre base-stack.yaml.
-
Ve a la consola de AWS CloudFormation y selecciona Create stack > With new resources (standard).
-
En la sección de Template source, elige Upload a template file y sube tu archivo.
-
Asigna un nombre al stack (ej. Workshop-VPC-Endpoints) y deja los parámetros por defecto.
-
Haz clic en Next hasta llegar al final, marca la casilla de “I acknowledge that AWS CloudFormation might create IAM resources” y presiona Submit.
Al finalizar la creación (después de unos 2 o 3 minutos), deberías ver el estado como: CREATE_COMPLETE.
Plan de batalla 🎯.
Para conquistar este laboratorio, nos dividiremos en tres objetivos estratégicos:
-
Misión 1: El Rescate (EIC Endpoint). Aprenderemos a entrar a nuestra instancia privada mediante EC2 Instance Connect (EIC) Endpoint. Olvídate de gestionar llaves .pem perdidas, de configurar un Bastion Host o de exponer tu servidor con una IP pública.
-
Misión 2: El Túnel de Objetos (S3 Gateway Endpoint). Configuraremos un acceso directo hacia Amazon S3. Veremos cómo podemos acceder a nuestros objetos sin que un solo paquete de datos toque la internet publica.
-
Misión 3: El Almacén de Datos (DynamoDB Gateway Endpoint). Estableceremos una conexión privada hacia nuestra base de datos NoSQL para asegurar que el tráfico de nuestra aplicación nunca salga de la red de AWS.
🛡️ Nota de Arquitecto: Aunque ambos son “Endpoints”, en las misiones 2 y 3 usaremos el tipo Gateway, que es un recurso gratuito y funciona mediante rutas en nuestra VPC, a diferencia del tipo Interface que usa direcciones IP privadas (ENI).
Misión 1: Rescate de la Instancia Privada (EIC Endpoint)
Si nos dirigimos a la consola de Amazon EC2, seleccionamos la instancia que desplegamos con nuestra plantilla de CloudFormation e intentamos conectarnos a ella utilizando el botón “Connect”, nos toparemos con un muro de advertencias:
“No public IPv4 or IPv6 address assigned.”
“Instance is not in a public subnet.”
AWS nos está confirmando que nuestra arquitectura de aislamiento funciona: la instancia no tiene “puertas” hacia el exterior. Tradicionalmente, aquí es donde muchos se rinden y crean un Bastion Host (un servidor extra que hay que pagar y parchar).
En resumidas cuentas, en este momento no podemos conectarnos a nuestra instancia. Pero no te preocupes, que para esto (y casi todo lo demás) existe… no, no es MasterCard 😉, es EC2 Instance Connect (EIC) Endpoint. Afortunadamente, contamos con esta funcionalidad que nos permite conectarnos a nuestros servidores en subredes privadas, incluso cuando están totalmente aislados del exterior.
Para configurarlo, sigamos estos pasos:
-
Dirígete al servicio de VPC en la consola de AWS.
-
En la columna de la izquierda, busca la sección PrivateLink and Lattice.
-
Selecciona Endpoints y luego haz clic en el botón Create endpoint.
🛠️ Configuración del Endpoint: El “Apretón de Manos”
Al crear el endpoint, nos encontraremos con varios campos. Vamos a desglosarlos para que no haya pérdida:
-
Name: EIC-Connect (o el que prefieras).
-
Type, Category: Selecciona EC2 Instance Connect Endpoint.
-
VPC: Elige nuestra VPC del workshop: Workshop-VPC-Endpoint.
⚠️ Un pequeño paréntesis: El Security Group del Endpoint
Antes de terminar, necesitamos un Security Group (SG) específico para nuestro EIC. Este actuará como el “permiso de salida” para que el túnel llegue a la instancia.
Crea un nuevo SG con estos datos:
-
Name: SG-EIC-Endpoint
-
VPC: Workshop-VPC-Endpoint
-
Outbound Rule (Regla de Salida): Es la única que necesitamos.
-
Protocolo: TCP
-
Puerto: 22 (SSH)
-
Destino: 10.0.0.0/25 (El CIDR de nuestra subred privada).
💡 Tip de Arquitecto: Usamos el CIDR 10.0.0.0/25 para que este endpoint nos sirva para cualquier instancia que lancemos en nuestra subred privada. Si quisiéramos ser ultra-estrictos (Zero Trust), en el Destino podríamos poner directamente el ID del Security Group de la instancia.
Listo! Con el Security Group creado, regresemos a la pestaña de creación del Endpoint:
-
En el apartado de Security groups, haz clic en el ícono de refrescar para que aparezca nuestro recién creado SG-EIC y selecciónalo.
-
En la sección de Subnet, selecciona la subred privada de nuestra VPC, asegúrate de que esté marcada la opción IPv4 y haz clic en Create endpoint.
La creación del recurso puede tomar un par de minutos. Mientras esperas, puedes tomar un café ☕. En cuanto veas el estado (Status) como Available, significa que nuestro túnel privado está listo para operar y conectarnos a la instancia.
A continuación, te dejo una imagen de referencia con la configuración completa del EIC Endpoint:
Misión 1: ¡Conexión Exitosa! 🚀
¡Llegó el momento de la verdad! Vamos a entrar a nuestra instancia privada sin necesidad de una IP pública:
-
Regresa al servicio de Amazon EC2 y selecciona tu instancia.
-
Haz clic en el botón Connect.
-
En las pestañas superiores, selecciona EC2 Instance Connect.
-
En la sección de Connection Type, elige la opción: Connect using a Private Ip (EC2 Instance Connect Endpoint).
-
En el menú desplegable de EC2 Instance Connect Endpoint, selecciona el Endpoint que creamos (EIC-Connect).
-
Finalmente, haz clic en Connect.
En este punto, ya deberíamos estar dentro de nuestro servidor EC2 sintiéndonos como auténticos hackers 💻. Ver ese prompt de la terminal después de haber pasado por una red 100% privada es la prueba de que nuestro túnel seguro funciona a la perfección.
Hasta este punto, no tenemos ninguna comunicación establecida con Amazon S3 ni con DynamoDB. Como nuestra instancia está en una subred privada sin salida a internet, ¿qué crees que pasaría si intentamos listar nuestros recursos desde la AWS CLI?
¡Hagamos la prueba de fuego! Ejecuta estos comandos en tu terminal:
# Intento de listar buckets
aws s3 ls
# Intento de listar tablas, pásale al comando la región en la que estas trabajando.
aws dynamodb list-tables --region us-east-2
¿Qué sucede? Exacto: Silencio total. La terminal se quedará “congelada” intentando alcanzar los endpoints públicos de AWS hasta que finalmente recibas un Connection Timeout.
💡 Explicación: Aunque los servicios de AWS son internos a la red de Amazon, sus APIs (los puntos a los que llamamos por CLI) residen en el internet público. Como nuestra instancia es “tímida” y no tiene salida al exterior, no sabe cómo llegar a ellos. De hecho, te invito a que revises la tabla de rutas “Tabla-Ruteo-Privada” creada por nuestro stack: notarás que no existe ninguna ruta (ni un Internet Gateway ni un NAT Gateway) que le indique al tráfico hacia dónde ir. Para nuestra instancia, el resto del mundo no existe… todavía.
Así que, para lograr nuestro objetivo de conectividad total sin comprometer la seguridad, vamos a implementar los Gateway Endpoints tanto para S3 como para DynamoDB.
Misión 2: Misión 2: El Túnel Privado para Amazon S3
Un Gateway Endpoint es, en esencia, un puente privado que creamos dentro de nuestra VPC para comunicarnos con los servicios de AWS sin que los datos salgan jamás de la red interna de Amazon. Es decir: logramos un contacto cero con el internet público.
A diferencia de otros tipos de endpoints, el de tipo Gateway no utiliza direcciones IP privadas en tu subred; en su lugar, funciona mediante “Prefix Lists” (listas de prefijos) que se añaden automáticamente a tu Tabla de Rutas.
💰 Dato para tu bolsillo: Los Gateway Endpoints para S3 y DynamoDB son gratuitos. No hay cargos por hora.
- Regresa al servicio de VPC y, en la columna izquierda, selecciona Endpoints seguido del botón “Create endpoint”.
- En Service category, asegúrate de que esté seleccionado AWS services.
- En la barra de búsqueda de Services, escribe: Gateway.
- ¡Punto Crucial! Busca el servicio que termina en .s3 y asegúrate de que el Type sea Gateway..
Nota: También verás uno de tipo “Interface”, pero esa es harina de otro costal (y tiene costo). Quédate con el Gateway, que es el héroe gratuito de esta historia.
-
En el desplegable de VPC, selecciona nuestra: Workshop-VPC-Endpoint.
-
Configuración de Red: Aquí debes marcar el check de nuestra Tabla-Ruteo-Privada.
¿Por qué? Al hacer esto, CloudFormation (o la consola) agregará automáticamente una ruta “mágica” que dice: “Todo el tráfico que vaya hacia S3, envíalo por este túnel privado”. Sin esto, tu instancia seguirá gritando al vacío.
- Para finalizar, haz clic en Create endpoint.
Esto puede tardar un par de minutos en aparecer como Available. Mientras tanto, ¡aprovechemos el tiempo!
Podemos adelantar la Misión 3: El Almacén de Datos (DynamoDB Gateway Endpoint) siguiendo exactamente los mismos pasos que acabamos de realizar. La única diferencia es que, en la barra de búsqueda de servicios, esta vez seleccionarás el que termina en .dynamodb (asegurándote de que el tipo sea Gateway).
No olvides asociarlo también a nuestra Tabla-Ruteo-Privada para que la magia de las rutas se aplique a ambos servicios.
Con nuestros Gateway Endpoints en estado Available, ha llegado el momento de volver a la terminal y reclamar nuestra victoria. Vamos a intentar listar de nuevo nuestro bucket de S3 y nuestra tabla de DynamoDB para confirmar que la comunicación fluye por el túnel privado:
🔍 Ahora puedes volver a nuestra Tabla-Ruteo-Privada y notarás que la magia ha sucedido: han aparecido dos nuevas rutas con identificadores que empiezan por pl-xxxxxxxx (Prefix Lists). Estas entradas apuntan directamente hacia S3 y DynamoDB.
Esas rutas son las que le indican a tu subred: “Si alguien pregunta por estos servicios, no busques en internet; envíalo por el túnel privado”. ¡El camino ya está despejado!
# Listar Buckets de S3
aws s3 ls
# Listar Tablas de DynamoDB, no olvides colocar tu región
aws dynamodb list-tables --region us-east-2
¡Yeahhh! 🚀 Si ves el nombre de tu bucket y de tu tabla en pantalla, ¡felicidades! Acabas de establecer una conexión privada, segura y, lo más importante, totalmente gratuita.
🧠 ¿Qué pasó tras bastidores? Cuando ejecutaste los comandos, tu VPC consultó la Tabla de Rutas, vio que el destino era S3/DynamoDB y, en lugar de intentar salir a internet, envió el tráfico directamente a través del Endpoint correspondiente para cada servicio. ¡Magia de redes en su máxima expresión!
¡Todo ha salido a la perfección! Ahora contamos con una arquitectura robusta y segura donde una instancia EC2, totalmente aislada en una subred privada y sin IP pública, puede comunicarse fluidamente con nuestros servicios de Amazon S3 y DynamoDB.
Partiendo de aquí, el cielo es el límite. Ustedes ya pueden realizar cualquier operación (subir archivos, consultar tablas, etc.) desde su instancia, siempre y cuando adjunten las políticas de IAM correctas al Role o Instance Profile que definimos en nuestro Stack inicial.
🧹 Limpieza: ¡No dejes la luz encendida!
Antes de irnos a celebrar nuestro éxito, es fundamental limpiar los recursos para mantener nuestra cuenta en $0 USD. Como mezclamos automatización con pasos manuales, el orden de eliminación es vital:
-
Elimina los Endpoints manualmente: Ve a la sección de Endpoints en la consola de VPC, selecciona los tres que creamos (S3, DynamoDB y EIC) y elígelos para eliminarlos. CloudFormation no puede borrar lo que él no creó.
-
Elimina el Security Group del EIC: Una vez borrado el endpoint, ve a Security Groups y elimina el SG-EIC-Endpoint.
-
Elimina el Stack de CloudFormation: Ahora sí, ve a la consola de CloudFormation, selecciona tu stack y dale a Delete. Esto borrará la EC2, la VPC y los roles automáticamente.
🏁 Conclusión
Dominar la conectividad privada es lo que separa a un administrador de nubes de un verdadero Arquitecto Cloud. Hoy demostramos que es posible construir una fortaleza segura, aislada y con un costo de red de $0, sin necesidad de NAT Gateways costosos para tareas simples.
Espero que este workshop te sirva para blindar tus arquitecturas y optimizar el presupuesto de tus proyectos.
¿Te quedó alguna duda sobre los tipos de Endpoints o la configuración de rutas? ¡Hablemos en los comentarios! 💬
Beyond the Panic: Hardening the Rust SDK
TL;DR
Engineering is often the art of managing the “unhappy path.” This week was a testament to that philosophy. With 74 commits, 2 PRs, 3 issues, and 3 reviews across 5 repositories, the focus shifted from building new features to fortifying the foundations. The headline? A concerted effort to purge unwrap() and panic calls from our Rust SDKs in favor of robust, Result-based error handling.
The Work: Velocity in the Engine Room
The bulk of the week’s momentum was concentrated in the Python ecosystem, specifically within p2pCalc and AgentPay. These two repositories accounted for 54 of the week’s 74 commits, representing a high-velocity push in our core logic layers.
p2pCalc & AgentPay
In p2pCalc (32 commits), we focused on the computational integrity of our peer-to-peer logic. While the additions and deletions were kept internal to the commit history, the sheer volume of activity suggests a deep dive into the algorithmic core. Parallel to this, AgentPay (22 commits) saw significant movement. When dealing with payment agents, there is no room for ambiguity. My focus here was on ensuring that the state machine governing agent transactions is both predictable and idempotent.
DevNotion & The Developer Environment
Meanwhile, DevNotion (14 commits) continues to evolve. As a TypeScript-based project, it serves as the bridge between our raw data and the narratives we share. The work here was iterative, likely refining the very engine that powers these weekly field notes.
I also took a moment to sharpen the saw. Five commits to my nvim configuration (Lua) might seem like minor housekeeping, but for a principal engineer, the editor is the primary instrument. A well-tuned environment isn’t a luxury; it’s a prerequisite for the kind of deep work required to handle complex refactors.
The Pull Requests: The Safety Initiative
The most critical architectural work happened within the Appwrite ecosystem. We are currently in the process of maturing the Rust SDK, and that means moving away from “prototype-grade” code.
| Title | Repo | State | Impact |
|---|---|---|---|
| fix(rust): replace unwrap/panic with Result-based error handling | sdk-generator | OPEN | Prevents generator-level crashes during SDK synthesis. |
| refactor: replace unwrap with error handling in Client methods | sdk-for-rust | CLOSED | Shifts error responsibility to the library consumer, avoiding runtime panics. |
The PR in sdk-for-rust (#9) was a substantial undertaking, involving 149 additions and 46 deletions. In Rust, using .unwrap() is a debt you eventually have to pay. By replacing these with proper Result types, we ensure that the SDK doesn’t bring down a user’s entire application just because of a transient network failure or a malformed response. It’s about respecting the caller’s right to decide how to handle failure.
The work continues in sdk-generator (#1398), where I am applying similar logic to the generator itself. If the generator panics, the entire CI/CD pipeline halts. By implementing Result-based handling, we allow the system to fail gracefully and provide actionable feedback.
The Conversations: Setting the Standard
Collaboration this week was centered on documentation and issue tracking. I closed two critical issues in the sdk-for-rust repo:
-
Issue #8: The bug report that initiated the
unwrap()refactor. -
Issue #7: The creation of
CONTRIBUTING.md.
The latter is particularly important. As we scale, we need to ensure that external contributors understand the quality bar we’ve set. A clear CONTRIBUTING.md is the first step in building a healthy open-source community around the Rust SDK.
The Reviews: Raising the Bar
Reviewing 3 PRs while authoring 2 is a healthy ratio for a week focused on quality. My reviews on awesome-py-libp2p-examples resulted in “CHANGES_REQUESTED” for two separate PRs (#6 and #3).
When reviewing collaborative tools like a “Whiteboard” or “p2p model checking,” I look for more than just “does it run?” I look for idiomatic patterns and clear documentation. If an example is meant to be “awesome,” it must serve as a gold standard for others to follow. My comments on py-libp2p (#1222) regarding the reorganization of example scripts follow this same theme: structure dictates usability.
The Craft: A Polyglot Reality
This week’s data reveals a truly polyglot workflow. While Python remains the dominant language by volume (over 77MB of context), the most impactful changes were written in Rust and TypeScript.
The add/delete ratio (+227/-80) indicates a week of net-new growth, but the nature of that growth was defensive. We weren’t just adding features; we were adding safety nets. The lack of a commit streak this week is a reminder that engineering isn’t always a daily sprint; sometimes it’s a series of deep-dive sessions separated by the necessary time to think through architectural implications.
The Horizon
As we look toward next week, the primary objective is to land the open PR in the sdk-generator. Once the generator is hardened, we can begin rolling out these safety improvements across all generated Rust SDKs.
We’ve moved the needle on reliability this week. The transition from “it works” to “it’s resilient” is often invisible to the end-user until something goes wrong and the system doesn’t crash. That is the quiet victory of the seasoned architect.
Onward.
Building an AI Profanity Filter with Vocal Separation
I built an online tool that automatically detects and bleeps profanity in video and audio files. Here’s the high-level architecture.
The problem
Manual profanity censoring takes 45+ minutes for a 10-minute video. You have to listen through, find each word, razor the audio, drop a beep effect. For songs, it’s nearly impossible without destroying the music.
The solution
AI speech recognition + neural vocal separation.
How it works
- User uploads a file or pastes a YouTube URL
- Audio is extracted with FFmpeg
- AI speech-to-text transcribes the audio (AssemblyAI / Deepgram)
- Profanity is detected using morphological analysis (lemmatization)
- Each word is replaced with beep/silence/custom sound via FFmpeg
- For songs: Demucs AI separates vocals from instruments first
Song mode — the hard part
Demucs by Meta AI does the heavy lifting — splitting audio into vocal and instrumental tracks. Profanity detection runs only on the vocal track, then the censored vocals are mixed back with the original instruments. The music stays untouched.
Stack
- Frontend: Next.js (React)
- Backend: NestJS (Node.js), BullMQ queues
- Audio processing: Python (FastAPI), Demucs, FFmpeg
- Infrastructure: Docker Compose, PostgreSQL, Redis
Results
12,000+ files processed. Three processing modes: standard (clean speech), precise (noisy audio), enhanced (songs with vocal separation).
Free for up to 15 minutes per month at videocensor.net.
Would love to hear your thoughts!
How to Build an Automated TikTok Pipeline from UGC Clips
TL;DR
Built a 3-step pipeline for automated TikTok posting from UGC clips: scrape-hooks.js (collection) → trim-and-stitch.js (editing) → post-to-postiz.js (publishing). Achieved 100% success rate on day one with 4 daily runs (8AM/5PM, JP/EN). Clear separation of concerns enables easy debugging and component swapping.
Source: daily.dev: How to write viral stories for developers
Key quote: “Write from expertise. Developers hate clickbait.”
Prerequisites
- Node.js v18+
- Postiz API account (TikTok integration enabled)
- UGC clip storage (workspace/hooks/ugc-clips/)
- ffmpeg (for video trimming)
The Problem: Why Existing Solutions Failed
Our existing posting skills had limitations:
| Approach | Limitation |
|---|---|
| Larry slideshow | Static images only. Can’t use video clips |
| ReelClaw | Posts single videos as-is. No multi-clip editing |
What we needed: Multiple UGC clips → auto-trim → stitch into one video → post to TikTok
Step 1: Pipeline Design (3-Step Separation of Concerns)
Source: Unix Philosophy
Key quote: “Write programs that do one thing and do it well. Write programs to work together.”
| Step | Script | Role | Input | Output |
|---|---|---|---|---|
| 1 | scrape-hooks.js | Collect & select UGC clips | workspace/hooks/ugc-clips/ | workspace/hooks/slot-08-00-ja.json |
| 2 | trim-and-stitch.js | Trim & stitch videos | slot-08-00-ja.json | workspace/output/final-08-00-ja.mp4 |
| 3 | post-to-postiz.js | Post via Postiz API | final-08-00-ja.mp4 | TikTok post published |
Why 3 separate scripts:
- Single responsibility → easier debugging
- Loose coupling via JSON → swap components freely
- ffmpeg/Postiz failures don’t cascade
Step 2: scrape-hooks.js (Collection)
// Read candidate clips from workspace/hooks/ugc-clips/
const clipPool = fs.readdirSync('/Users/anicca/.openclaw/workspace/hooks/ugc-clips')
.filter(f => f.endsWith('.mp4'));
// Select unused clips randomly
const selectedClips = clipPool
.filter(clip => !usedClips.includes(clip))
.sort(() => Math.random() - 0.5)
.slice(0, 3); // Select 3 clips
// Save to slot JSON
const slotData = {
clips: selectedClips.map(name => ({
path: `/Users/anicca/.openclaw/workspace/hooks/ugc-clips/${name}`,
duration: 10 // seconds (use ffprobe for accuracy)
})),
caption: generateCaption(), // Hook generation (separate function)
hashtags: ['#selfcare', '#mindfulness', '#healing']
};
fs.writeFileSync(`workspace/hooks/slot-08-00-ja.json`, JSON.stringify(slotData, null, 2));
Key points:
- Track used clips (used-clips.json) to avoid duplicates
- Random shuffle for variety
- Fixed 3 clips (fits TikTok 15-60s recommendation)
Step 3: trim-and-stitch.js (Editing)
const ffmpeg = require('fluent-ffmpeg');
const slotData = JSON.parse(fs.readFileSync('workspace/hooks/slot-08-00-ja.json'));
// Trim each clip to 10 seconds
const trimmedPaths = [];
for (const [i, clip] of slotData.clips.entries()) {
const outputPath = `/tmp/trimmed-${i}.mp4`;
await new Promise((resolve, reject) => {
ffmpeg(clip.path)
.setStartTime(0)
.setDuration(10)
.output(outputPath)
.on('end', resolve)
.on('error', reject)
.run();
});
trimmedPaths.push(outputPath);
}
// Concatenate 3 clips into 1
const finalPath = 'workspace/output/final-08-00-ja.mp4';
await new Promise((resolve, reject) => {
const cmd = ffmpeg();
trimmedPaths.forEach(path => cmd.input(path));
cmd
.complexFilter('[0:v][1:v][2:v]concat=n=3:v=1:a=0[outv]', ['outv'])
.outputOptions('-map', '[outv]')
.output(finalPath)
.on('end', resolve)
.on('error', reject)
.run();
});
console.log(`Final video: ${finalPath}`);
Key points:
- fluent-ffmpeg with Promise wrappers for error handling
- Intermediate files in
/tmp→ only final output in workspace - concat filter with no audio (TikTok allows separate BGM)
Step 4: post-to-postiz.js (Publishing)
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const slotData = JSON.parse(fs.readFileSync('workspace/hooks/slot-08-00-ja.json'));
const videoPath = 'workspace/output/final-08-00-ja.mp4';
// 1. Upload video (Postiz Media API)
const form = new FormData();
form.append('file', fs.createReadStream(videoPath));
const uploadRes = await axios.post('https://api.postiz.com/public/v1/media/upload', form, {
headers: {
...form.getHeaders(),
'Authorization': process.env.POSTIZ_API_KEY
}
});
const mediaId = uploadRes.data.id;
// 2. Create post (Postiz Posts API)
await axios.post('https://api.postiz.com/public/v1/posts', {
integrationId: process.env.POSTIZ_TIKTOK_JP_INTEGRATION_ID, // TikTok JP
content: `${slotData.caption}nn${slotData.hashtags.join(' ')}`,
mediaIds: [mediaId],
scheduleAt: new Date().toISOString() // Immediate posting
}, {
headers: { 'Authorization': process.env.POSTIZ_API_KEY }
});
console.log('Posted to TikTok via Postiz');
Key points:
- Postiz API requires 2 steps (media upload → post creation)
- integrationId specifies account (JP/EN separate)
- scheduleAt for immediate or scheduled posting
Source: Postiz API Documentation
Key quote: “Upload media first using /media/upload, then reference mediaIds in /posts”
Step 5: Cron Configuration (4 Daily Runs)
# ~/.openclaw/workspace/cron-jobs.json (OpenClaw Gateway)
{
"name": "mau-tiktok-ja-morning",
"schedule": { "kind": "cron", "expr": "0 8 * * *", "tz": "Asia/Tokyo" },
"payload": {
"kind": "agentTurn",
"message": "Execute mau-tiktok skill for JA morning slot (08:00)"
},
"sessionTarget": "isolated"
}
4 cron jobs:
- mau-tiktok-ja-morning (08:00 JST)
- mau-tiktok-en-morning (08:15 JST)
- mau-tiktok-ja-evening (17:00 JST)
- mau-tiktok-en-evening (17:15 JST)
Why 15-minute intervals:
- Avoid Postiz API rate limits
- Prevent parallel ffmpeg processes (CPU spike prevention)
Production Results (2026-03-27)
| Slot | Time | Result | Duration |
|---|---|---|---|
| ja-morning | 08:00 | ✅ ok | 2m 15s |
| en-morning | 08:15 | ✅ ok | 2m 08s |
| ja-evening | 17:00 | ✅ ok | 2m 12s |
| en-evening | 17:15 | ✅ ok | 2m 20s |
Success rate: 4/4 = 100% (day one)
Troubleshooting (Issues Encountered in Production)
| Issue | Cause | Solution |
|---|---|---|
| ffmpeg concat error | Resolution/FPS mismatch | Pre-normalize all clips to 1080×1920 30fps |
| Postiz 413 Payload Too Large | Video size >100MB | Add -crf 23 compression during trim |
| Black screen on TikTok | Unsupported codec | Specify -c:v libx264 -pix_fmt yuv420p
|
Key Takeaways
| Lesson | Detail |
|---|---|
| 3-step separation | Collection, editing, publishing as independent scripts → easy debugging, swappable components |
| Loose coupling via JSON | Filesystem-based state between steps → stateless, re-runnable |
| ffmpeg error handling | Promise-wrapped fluent-ffmpeg + try-catch → cleanup intermediate files on failure |
| Postiz 2-step API | Media upload → post creation order → avoid 403/422 errors |
| 15-min cron intervals | Distribute rate limits & CPU load → stable operation |
| Day-one 100% success | Clear design + API reuse → minimize risk for new skills |
Next steps:
- Auto-replenish clip pool (scrape YouTube Shorts/Instagram Reels)
- LLM-powered caption generation (auto-generate hooks)
- Engagement tracking (Postiz Analytics API → prioritize high-performing clips)
Source: Copyblogger: 22 Best Headline Formulas
Key quote: “8 out of 10 people will read the headline. Only 2 will read the rest.”
(This article is based on production results. Code is simplified but structurally identical to implementation.)








