How RNNs Work — Remembering Previous States in Sequential Data

A normal neural network treats each input mostly as a fixed snapshot.

But many problems are not snapshots.

Text, speech, and time-series data depend on order.

That is why RNNs exist.

Core Idea

A Recurrent Neural Network is designed for sequential data.

It does not only look at the current input.

It also carries information from previous steps.

That carried information is called the hidden state.

So an RNN can process a sequence one step at a time while keeping memory of what came before.

The Key Structure

A simple RNN flow looks like this:

Previous Hidden State + Current Input → New Hidden State → Output

More compactly:

RNN = current input + previous state

At each time step:

h_t = f(x_t, h_{t-1})

Where:

  • x_t = input at the current time step
  • h_{t-1} = previous hidden state
  • h_t = updated hidden state

This recurrence is the core mechanism.

Implementation View

At a high level, an RNN processes a sequence like this:

initialize hidden state

for each time step in the sequence:
    read current input

    combine it with previous hidden state

    update hidden state

    optionally produce output

return final output or all outputs

This is why RNNs are useful for ordered data.

The model can carry context forward.

It does not restart from zero at every step.

Concrete Example

Imagine a sentence:

I love machine learning

A basic feedforward network may process words as independent inputs.

But an RNN reads them in order.

Step 1:

I

Step 2:

I love

Step 3:

I love machine

Step 4:

I love machine learning

At each step, the hidden state carries previous context.

That is how the model remembers earlier words while reading later ones.

Standard Neural Network vs RNN

This comparison makes the difference clear.

Standard neural network:

  • processes fixed-size input
  • has no built-in memory across time
  • works well for static feature vectors
  • does not naturally model order

RNN:

  • processes sequences step by step
  • carries hidden state forward
  • models temporal or ordered dependence
  • fits text, speech, and time-series tasks

The key difference is state.

A standard network transforms input.

An RNN transforms input while remembering previous context.

Why Hidden State Matters

The hidden state is the memory of the RNN.

It is not memory in the human sense.

It is a vector that summarizes previous information.

At each step, the hidden state is updated.

That updated state influences the next step.

This lets the model capture patterns like:

  • word order
  • temporal trends
  • repeated signals
  • dependency across earlier and later inputs

Without hidden state, the sequence becomes just a list of disconnected inputs.

Why Deep RNNs Exist

A basic RNN can model sequences.

But some patterns are more complex.

A Deep RNN stacks recurrent layers.

That allows the model to build richer sequence representations.

Basic RNN:

  • one recurrent layer
  • simpler sequence modeling
  • easier to understand
  • limited representational depth

Deep RNN:

  • multiple recurrent layers
  • more expressive sequence modeling
  • can capture more complex temporal patterns
  • harder to train

So Deep RNNs extend the same idea.

They do not replace recurrence.

They deepen it.

Where RNNs Fit in Deep Learning

RNNs became important because different data types need different architectures.

CNNs work well for images because images have spatial structure.

RNNs work well for sequences because sequences have order.

CNN:

  • local spatial patterns
  • image-centered tasks
  • convolution kernels

RNN:

  • temporal or sequential patterns
  • text, speech, time series
  • recurrent hidden state

This is why RNNs became one of the major deep learning architectures.

They match the structure of sequential data.

Practical Limits

RNNs are powerful, but they have limits.

Long sequences are hard.

Information from early steps can weaken over time.

Training can become unstable because gradients must pass through many time steps.

This is one reason later architectures became important.

Attention mechanisms and Transformers changed the landscape by making long-range relationships easier to model.

But RNNs remain the best starting point for understanding sequence modeling.

Recommended Learning Order

If RNNs feel abstract, learn them in this order:

  1. Neural Network
  2. Recurrent Neural Network
  3. Hidden State
  4. Deep RNN
  5. CNN vs RNN comparison
  6. Attention Mechanism
  7. Transformer

This order works because you first understand normal neural networks.

Then you see what changes when order matters.

Then you understand why modern sequence models moved beyond basic recurrence.

Takeaway

An RNN is a neural network designed for sequences.

The shortest version is:

RNN = current input + previous hidden state

It reads data step by step.

It carries context forward.

It uses that context to make better predictions on ordered data.

If you remember one idea, remember this:

RNNs make neural networks sequence-aware by passing hidden state from one time step to the next.

Discussion

When learning sequence models, do you find it easier to start from RNNs first, or jump directly to Attention and Transformers?

Originally published at zeromathai.com.
Original article: https://zeromathai.com/en/rnn-complete-hub-en/

GitHub Resources
AI diagrams, study notes, and visual guides:
https://github.com/zeromathai/zeromathai-ai

Building a Technical Literature Dashboard with Power Automate Power Apps LDX hub StructFlow

Verification Case Report #006 — 2026.05.18 | Time spent: ~3 days

Overview

Metric Value
Documents processed 18
Power Apps screens 4
Power Automate flows 2
StructFlow extracted fields 8

We built TechLit Viewer — an end-to-end technical literature management system that uses LDX hub StructFlow to extract structured data from documents, Power Automate to keep everything updated, and both Power Apps and a standalone HTML dashboard for visualization.

Background

Many customers express a need to continuously collect and evaluate technical literature across diverse fields. The old approach — storing PDFs and patent documents in folders — made it nearly impossible to answer questions like “which technologies are at which maturity level?” or “which documents are most relevant to our business?”

We set out to verify whether a practical technical literature management system could be built within a no-code/low-code stack by combining LDX hub StructFlow for AI extraction with Microsoft 365 tools for storage, automation, and display.

What we’re testing: Can StructFlow turn unstructured technical documents (PDFs, patents, reports) into structured data that integrates cleanly with SharePoint, Power Automate, and Power Apps?

System Architecture: 5 Layers

// TechLit Viewer — System Overview

[1] INPUT         Source files (PDF / patents / reports)
                  └─ Stored in SharePoint document library. 18 documents total.

[2] AI EXTRACTION LDX hub StructFlow
                  └─ Auto-extracts 8 fields:
                     Title / DocType / Authors / Year / FieldMajor /
                     TRL / RelevanceScore / Summary

[3] DATA LAYER    SharePoint Lists (TechLit_Master / TechLit_Metrics)
                  └─ Power Automate writes; Power Apps + HTML dashboard reads.

[4] AUTOMATION    Power Automate (2 flows)
                  └─ ① Auto-extract on item update  (TechLit_Pipeline_UPDATE)
                     ② Manual bulk processing       (TechLit_BulkUpdate)

[5] DISPLAY       Power Apps (TechLit_Viewer) + HTML dashboard
                  └─ 4-screen Power Apps + standalone HTML dashboard

Flow Design: Two Flows for Two Purposes

① TechLit_Pipeline_UPDATE (always-on)

Fires automatically when a SharePoint list item is updated. Detects new document registrations or changes to existing records, sends the file to StructFlow, and immediately writes the extracted results back. Day-to-day document additions are fully automated via this flow.

② TechLit_BulkUpdate (manual trigger)

A manual-trigger flow for bulk processing all 18 documents. Used after schema changes, prompt revisions, or for initial data loading. Iterates through all items with a foreach loop, waits for StructFlow polling to complete, and writes results back to the list.

Key design insight: The update trigger excels at immediacy but isn’t suited for full reprocessing. The manual trigger handles bulk operations well but requires operator action. Using both flows together covers both daily operations and administrative tasks.

StructFlow Extraction Schema: 8 Fields

Schema design started from the question: “what do we want to decide?” — not “what can we extract?”

Field Description Type
Title Document title string
DocType Document type (patent / paper / report / other) string
Authors Authors / applicants string
Year Publication / filing year integer
FieldMajor Primary technology domain (Materials Science, Energy Engineering, etc.) string
TRL Technology Readiness Level (1–9) integer
RelevanceScore Relevance to our business (high / medium / low) string
Summary 2–3 sentence technical summary string

About TRL (Technology Readiness Level): Originally developed by NASA and adopted by EU Horizon programs. Levels 1–3 = basic research, 4–6 = demonstration, 7–9 = operational/production. Quantifying maturity makes it easy to distinguish research-stage from production-ready technologies at a glance.

Power Apps: 4 Screens for Different Use Cases

Screen Features
Screen 1: Search Free-text search by title, author, or domain. Filtering by DocType, TRL, RelevanceScore. Real-time queries against SharePoint list.
Screen 2: Detail View Full field display for individual documents. Review StructFlow-extracted Summary. Visual TRL and relevance indicators.
Screen 3: Metrics Comparison TRL distribution by technology domain. Document count by relevance. Year-over-year trend charts.
Screen 4: Tech Dashboard Advanced visualization via embedded HTML component. Dynamic charts with Chart.js. Integrated full-text search over all 18 documents.

HTML Dashboard: Standalone Strategic View

Alongside Power Apps, we built a standalone HTML dashboard (techlit_dashboard.html) that runs entirely in the browser — no Power Apps dependency. This makes it easy to share with executives or external stakeholders.

// StructFlow extraction results stored directly as a data array
const data = [
  {
    id: 17,
    title: 'Immobilized Photocatalyst Paper',
    docType: 'patent',
    authors: '[Author name masked]',
    year: 2002,
    fieldMajor: 'Chemistry',
    trl: 4,
    relevance: 'high',
    url: ''  // SourceFileUrl — will become a live link once populated
  },
  // ... 18 documents total
];

The dashboard includes 4 charts (technology domain distribution, TRL distribution, year-over-year trend, document type breakdown) plus a searchable full-document table. Passing StructFlow’s structured data to Chart.js generates all analysis charts automatically.

Results

StructFlow Extraction Accuracy

15 of 18 documents (83%) had all fields extracted correctly. The remaining 3 showed mixed-language inconsistencies in FieldMajor (English vs. Japanese) — no impact on usability, but a clear prompt improvement opportunity.

Power Automate Automation

Average processing time per document: ~67 seconds (including StructFlow polling). Full run of all 18 documents: ~20 minutes. Both scheduled and trigger-based execution confirmed stable.

Technology Domain Visibility

Environmental Science was the largest category (6 documents), followed by Materials Science (4). TRL distribution: basic research (1–3) was highest at 8 documents; operational stage (7–9) had 3. For the first time, we had a quantitative picture of the entire portfolio.

Relevance Scoring

15 of 18 documents scored RelevanceScore = High. By embedding our business context (translation & localization) into the StructFlow system prompt, we achieved automatic screening aligned with our actual evaluation criteria.

Biggest insight: from “searching” to “discovering”
Technical documents that could previously only be searched by filename can now be filtered across TRL, technology domain, and relevance simultaneously. Being able to instantly narrow down to “TRL ≥ 4 AND Materials Science AND High relevance” fundamentally changes the speed of technology strategy decisions.

Lessons Learned

1. Lock down SharePoint column types before you start

Column types (hyperlink vs. single line of text) can’t be changed after the fact. Confirm the format Power Automate will write, then finalize column design before building.

2. Design the StructFlow schema from your decision criteria

Don’t start from “what can we extract?” — start from “what do we need to decide?” Including evaluation-axis fields like TRL and RelevanceScore from the beginning dramatically increases dashboard value.

3. Separate Power Apps and HTML dashboard by purpose

Power Apps suits day-to-day search and update operations; the HTML dashboard suits sharing and presentations. Using two UIs on the same data source expands both the audience and the use cases.

4. Control FieldMajor normalization via the prompt

Mixed English/Japanese values like “Materials Science” and “材料科学” split aggregations. Adding an explicit instruction to the StructFlow system prompt (“always output technology domains in English”) prevents this drift.

Next Steps

Item Details
Populate SourceFileUrl Add source URLs to the SharePoint list so users can reach original documents in one click from the dashboard
Schedule automation Move from manual triggers to fully automated monthly runs with auto-distribution to leadership
Normalize FieldMajor Add English-only instruction to StructFlow prompt and reprocess all 18 documents
Scale to 100+ documents Add ExtractDoc as a pre-processing step to handle scanned PDFs
RefineLoop integration Auto-translate and improve summaries of foreign-language documents (English, Chinese) into Japanese

Kawamura International is a translation and localization company documenting its AI process experiments in public. StructFlow, RefineLoop, RenderOCR — and whatever comes next.

🧭Diseñando VPCs en AWS: patrones reales (hub-spoke, mesh, multi-account).

Cuando se diseñan arquitecturas en AWS, uno de los errores más comunes es pensar en la VPC como un componente técnico aislado.

Pero en la práctica, la VPC es:

👉 La base sobre la que se construyen la seguridad, la conectividad y la escalabilidad de todo el sistema.

Y aquí es donde muchos diseños empiezan a romperse…
no el día uno, sino cuando la plataforma crece.

🧭 Antes de hablar de patrones hablemos de el error más común

La mayoría de arquitecturas empiezan así:

✔️ Una sola VPC
✔️ Subnets públicas y privadas
✔️ Un NAT Gateway
✔️ Todo funcionando… al inicio

Pero cuando aparecen múltiples equipos, múltiples entornos (dev, qa, prod), requisitos de seguridad estrictos o conectividad híbrida, esa VPC se convierte en un cuello de botella.

🧩 Entonces… ¿cómo se diseñan VPCs en el mundo real?

No hay una única forma correcta.
Pero sí hay patrones que aparecen constantemente en producción.

Aquí los más relevantes:

🔵 1. Hub-and-Spoke (el más usado en entornos enterprise)

Este patrón separa la conectividad central del resto de workloads.

Cómo funciona:
VPC central (hub): VPN / Direct Connect, NAT Gateways, firewalls e inspección de tráfico.

VPCs spoke: aplicaciones, microservicios, entornos aislados.
Conectadas mediante Transit Gateway (recomendado a escala) o VPC Peering en casos simples.

Cuándo usarlo:
Arquitecturas multi-account, control centralizado de red, requisitos fuertes de seguridad, conectividad híbrida.

Trade-offs reales:
Un Transit Gateway con 10 VPCs adjuntas en us-east-1 tiene un costo base de ~$219/mes solo en attachment fees, antes de procesar un solo byte. En arquitecturas con tráfico este-oeste intenso entre spokes, ese costo puede superar al de los propios workloads. A eso se suma la complejidad operativa de mantener route tables del TGW separadas de las route tables de VPC, y el riesgo de convertir el hub en cuello de botella si el dimensionamiento de tráfico no se hace bien desde el inicio.
Un punto que casi nunca se menciona: si añades inspección centralizada con AWS Network Firewall en el hub, las route tables se vuelven asimétricas. El tráfico entra por una ruta y sale por otra. Ese detalle rompe implementaciones en producción si no se diseña explícitamente desde el principio.
Opinión:

Funciona muy bien, siempre que el crecimiento del tráfico esté bien dimensionado y la inspección de tráfico esté considerada desde el diseño inicial, no como un añadido posterior.

🟣 2. Full Mesh (todo conectado con todo)

Cada VPC se conecta directamente con las demás mediante VPC Peering, sin punto central.

Cuándo usarlo:
Pocas VPCs, baja complejidad, latencia mínima entre servicios.

El problema real:
Escala muy mal. Con N VPCs necesitas N×(N-1)/2 peerings. Con 10 VPCs son 45 conexiones, cada una con sus propias route tables. Con 20 VPCs son 190. A eso se suma que no hay control centralizado, el troubleshooting es lento porque no hay un punto único de observabilidad, y las rutas son difíciles de auditar cuando hay múltiples equipos tocando distintos peerings.

Opinión:
Funciona en startups con 3-4 VPCs. Se rompe en enterprise antes de lo que parece.

🟢 3. Multi-Account (más que un patrón, una estrategia)

Paradójicamente, separar cuentas no ralentiza a los equipos. Les permite moverse más rápido sin interferirse.
Cómo funciona:
Separación por cuentas (producción, desarrollo, seguridad, networking) usando AWS Organizations, Transit Gateway y VPC sharing.
Cuándo usarlo:
Aislamiento real de blast radius, cumplimiento y auditorías, escalabilidad organizacional.

Trade-offs reales:
La complejidad inicial es real. El mayor dolor no es técnico sino de gobierno: ¿quién es dueño de la cuenta de networking? ¿Quién aprueba cambios en las route tables del TGW? Sin esas respuestas claras antes de desplegar, el modelo multi-account genera más fricción de la que resuelve.

Opinión:
A cierta escala no es opcional. Es inevitable.

Hub-and-Spoke y Multi-Account no son excluyentes

Este es el punto que más confusión genera en equipos que están adoptando estos patrones por primera vez.
No son patrones opuestos. Resuelven problemas distintos en capas distintas:

Multi-Account define los límites de confianza, gobierno y blast radius.
Hub-and-Spoke define cómo se conectan esas redes de forma controlada.

En entornos enterprise es muy común — y totalmente correcto — ver una arquitectura multi-account que utiliza una topología hub-and-spoke para la conectividad. Son complementarios.

🔥 Decisiones que realmente importan (y casi nadie menciona).

Más allá del patrón elegido, estas cuatro decisiones determinan si el diseño aguanta el crecimiento:

¿Dónde inspeccionas el tráfico?
Centralizado en el hub o distribuido en cada VPC. Impacta directamente en seguridad, latencia y coste. La inspección centralizada con Network Firewall es más barata operativamente pero introduce la asimetría de rutas mencionada antes.

¿Cómo sales a internet?
NAT por VPC o NAT centralizado en el hub. Aquí es donde muchas facturas explotan. Un NAT Gateway por VPC en una arquitectura con 15 spokes puede costar entre 3x y 5x más que un NAT centralizado, dependiendo del patrón de tráfico.

¿Cómo segmentas?
Por VPC, por subnet o por cuenta. El error más común: mezclar todo porque hoy funciona, sin pensar en cómo se auditará en 12 meses cuando haya un incidente de seguridad.

¿Cómo escalará esto en 6 meses?
Antes de elegir un patrón, responde estas preguntas: ¿Cuántas cuentas tendrás en 12 meses? ¿Tienes o tendrás conectividad híbrida? ¿Quién es el dueño técnico de la red? ¿Cuánto tráfico este-oeste esperas entre workloads? Las respuestas dictan el patrón, no al revés.

⚡ Ejemplo real (simplificado)

En un proyecto de pagos con 3 equipos y 8 cuentas AWS, el punto de quiebre llegó cuando un equipo de desarrollo desplegó un cambio en una route table compartida y tumbó conectividad en producción durante 40 minutos. No fue un problema técnico. Fue un problema de gobierno.

La solución no fue técnica tampoco: fue separar la cuenta de networking, transferir la propiedad de las route tables del TGW a un equipo dedicado, e implementar aprobación obligatoria para cualquier cambio de red mediante pipelines de IaC con revisión. Después de eso, cero incidentes de red por cambios no coordinados en 8 meses.

Esa evolución se ve constantemente:

  • Inicio: 1 VPC, todo dentro, funciona

  • Crecimiento: más servicios, problemas de seguridad y control

  • Evolución: multi-account + hub-and-spoke + networking centralizado

  • Resultado: mejor control, más seguridad, más complejidad — inevitable y manejable si se diseña bien

Un punto importante de aclarar es que Hub‑and‑Spoke y Multi‑Account no son patrones opuestos.

En arquitecturas reales, resuelven problemas distintos en capas distintas:

  • **Multi‑Account **define los límites de confianza, gobierno y blast radius.
  • Hub‑and‑Spoke define cómo se conectan esas redes de forma controlada.

Por eso, en entornos enterprise es muy común —y totalmente correcto— ver:

👉 Una arquitectura multi‑account que utiliza una topología Hub‑and‑Spoke para la conectividad.

🧭 Conclusión

Diseñar una VPC no es crear subnets.

Es decidir cómo se comunican los sistemas, cómo se aíslan, cómo escalan y cuánto cuesta operarlos. Y sobre todo, cómo evitar que tu arquitectura funcione hoy pero falle mañana.

El patrón correcto no existe en abstracto. Existe en función de cuántos equipos tienes, qué nivel de aislamiento necesitas y cuánto tráfico moverás. Empieza por esas preguntas, no por el diagrama.

Happy learning on AWS 🚀

CrawlForge v4.2.2: New CLI + 3 Tools for Local AI Scraping

Today we are shipping CrawlForge v4.2.2, our biggest release since launch. It brings three new tools, a standalone command-line interface, and a quiet shift in how we think about web scraping for AI: most of it should run locally, on your own machine, without API keys.

This post is the umbrella for everything in 4.2.2. Three deep-dive guides follow in the next nine days.

TL;DR: v4.2.2 adds @crawlforge/cli (a standalone CLI — no MCP client needed), extract_with_llm (local LLM extraction via Ollama, no OpenAI/Anthropic key required), scrape_template (one-line scrapers for 10 popular sites), and list_ollama_models (free model discovery). Tool count goes from 20 to 23. Free tier still includes 1,000 credits. Install: npm install -g @crawlforge/cli.

Table of Contents

  • What Shipped
  • The New CrawlForge CLI
  • Extract With LLM: Local AI Extraction
  • Scrape Template: Ten Sites, One Call
  • list_ollama_models: Free Model Discovery
  • Old Workflow vs v4.2.2 Workflow
  • Credit Costs
  • How to Upgrade
  • What Is Next

What Shipped

v4.2.2 adds four things:

  1. @crawlforge/cli — a standalone command-line tool exposing all 23 CrawlForge tools to your shell. No MCP client required.
  2. extract_with_llm — LLM-powered structured extraction that defaults to local Ollama. No external API key needed.
  3. scrape_template — pre-built scrapers for Amazon, LinkedIn, GitHub, YouTube, Reddit, Hacker News, Stack Overflow, npm, Product Hunt, and Twitter/X.
  4. list_ollama_models — a free discovery tool that lists models on your local Ollama instance.

Tool count goes from 20 to 23. The CLI is brand new — it is not a tool, it is a delivery channel.

+----------------+       +-------------------+       +----------------+
|   Your Shell   | <-->  |  @crawlforge/cli  | <-->  |  CrawlForge    |
|   (cron, CI)   |       |  (JSON in/out)    |       |   API + Tools  |
+----------------+       +-------------------+       +----------------+
                                  ^
                          No MCP handshake.
                          Just HTTPS + stdout.

The New CrawlForge CLI

The CLI is the shortest path from intent to scraped data. You install it once, set an environment variable, and every CrawlForge tool becomes a command:

npm install -g @crawlforge/cli
export CRAWLFORGE_API_KEY="cf_live_your_key_here"

crawlforge scrape https://example.com
crawlforge search "best MCP servers 2026"
crawlforge research "AI agent frameworks" --depth 3

Why does this matter? Because MCP is great for AI agents, but a lot of scraping work is not an AI agent task. It is a cron job. A CI step. A one-off pull from your terminal. For that, you want JSON on stdout that pipes into jq, not a JSON-RPC handshake.

Why have a CLI when MCP already exists?

MCP is optimized for AI agents picking tools dynamically. The CLI is optimized for humans typing commands and scripts piping JSON. Different shapes for different jobs:

Workflow Best fit
Claude/Cursor agent MCP
Cron job CLI
GitHub Actions step CLI
One-off terminal CLI
Server in a loop Raw API

All three paths hit the same backend, share the same credit balance, and use the same API key.

Read the complete CrawlForge CLI guide for the full command reference and real-world workflows.

Extract With LLM: Local AI Extraction

extract_with_llm is structured extraction powered by a language model. You hand it a URL and a schema, it gives you back JSON. The new part is that it defaults to local Ollama rather than calling OpenAI or Anthropic.

{
  "url": "https://news.ycombinator.com/item?id=123456",
  "schema": {
    "type": "object",
    "properties": {
      "title":    { "type": "string" },
      "points":   { "type": "number" },
      "comments": { "type": "number" }
    }
  },
  "provider": "ollama",
  "model": "llama3.1:8b"
}

Three things follow from the local-first default:

  • No third-party API costs. The LLM is free. You only pay 3 CrawlForge credits per extraction.
  • No data leaving your machine. Scraped content stays on localhost.
  • No new API key to manage. If Ollama is installed, you are done.

When to still use OpenAI or Anthropic

Local models are great for predictable schemas (titles, prices, counts, ratings). For long-form reasoning — summarizing a 10,000-word article, classifying nuanced sentiment, extracting fields that require world knowledge — a frontier model still wins.

Switch providers with one parameter:

crawlforge extract https://example.com 
  --provider anthropic 
  --model claude-sonnet-4-6

You pay the provider’s per-token cost plus 3 CrawlForge credits. Same schema, same output shape.

Detailed guide: extract data with local LLMs.

Scrape Template: Ten Sites, One Call

scrape_template is for the long tail of scraping requests that all look the same: “get me product data from Amazon”, “get me a GitHub repo’s metadata”, “get me the top posts on Hacker News today”. You should not need to write CSS selectors for these. We did it once, we maintain it, you call it.

crawlforge template amazon --url "https://www.amazon.com/dp/B0CHX1W1XY"
crawlforge template github --url "https://github.com/anthropics/anthropic-sdk-python"
crawlforge template hackernews --top 10

Ten templates ship in this release:

Template What it returns Credits
amazon Product title, price, rating, reviews, images 1
linkedin Profile name, headline, experience, skills 1
github Repo metadata, stars, languages, README 1
youtube Video title, views, channel, transcript 1
reddit Post title, score, comments, top replies 1
hackernews Story title, points, URL, comments 1
stackoverflow Question, answers, accepted, vote counts 1
npm Package metadata, weekly downloads, versions 1
producthunt Product name, tagline, upvotes, makers 1
tweet Tweet text, author, engagement, replies 1

Full walkthrough with code: scrape Amazon, LinkedIn, and GitHub with one tool.

list_ollama_models: Free Model Discovery

Most useful as a sanity-check before running extract_with_llm. Lists every model on your local Ollama instance with name, size, and modified date.

crawlforge extract --list-ollama-models

Costs zero credits. It does no scraping, no LLM call — it just queries Ollama’s local API on 127.0.0.1:11434 and returns the result. If you have ever wondered which model you actually have installed, this is the answer.

Old Workflow vs v4.2.2 Workflow

Task Pre-4.2.2 v4.2.2
Scrape from your terminal curl + custom parser, or boot a Node REPL crawlforge scrape <url>
Extract structured data with LLM extract_structured (CSS selectors) or roll your own with Puppeteer + OpenAI extract_with_llm (Ollama default)
Scrape Amazon, LinkedIn, GitHub scrape_structured with hand-maintained selectors scrape_template (we maintain selectors)
Run scraping in CI/cron curl with API key in headers crawlforge <cmd> with env var

Credit Costs

The three new tools follow our existing credit-cost model. No surprises:

Tool Credits Why
list_ollama_models 0 Free discovery helper
scrape_template 1 Single page, pre-built schema
extract_with_llm 3 LLM inference (provider-agnostic)

The CLI itself is free. It uses your existing API key and bills against your normal credit balance.

How to Upgrade

Existing users do not need to do anything. The new tools are live on all plans — Free, Hobby, Professional, and Business — and show up automatically in your MCP client.

Install the CLI

npm install -g @crawlforge/cli
export CRAWLFORGE_API_KEY="cf_live_..."
crawlforge --help

Add the export line to your shell profile (~/.zshrc, ~/.bashrc) so it persists. For CI, set CRAWLFORGE_API_KEY as a repository secret.

Try Ollama-powered extraction

# 1. Install Ollama (one-time)
curl -fsSL https://ollama.com/install.sh | sh

# 2. Pull a model (llama3.1:8b is a good start)
ollama pull llama3.1:8b

# 3. Run extraction through CrawlForge
crawlforge extract https://example.com 
  --provider ollama 
  --model llama3.1:8b

The first run pulls about 5 GB. After that, every extraction is local, free, and offline-capable.

What Is Next

We are working on three things for 4.3:

  • More templates — Etsy, eBay, TikTok, Instagram, Google Maps. Send us requests on Discord.
  • Webhook delivery for batch_scrape — get results pushed to your endpoint when long-running jobs complete.
  • CLI watch mode — crawlforge track --watch for live diffs on monitored pages.

Ready to try the new tools? Free tier still includes 1,000 credits and no credit card.

Get 1,000 Free Credits

Or jump straight into the deep dives:

Hushh Open Source Hiring Challenge 2026: contribute to Kai or hushhTech

We are running a free, public, one-month open-source contribution challenge for developers who want to work on real product code and turn strong pull requests into prizes, internships, and hiring conversations.

Dates: May 19, 2026 to June 19, 2026

Mode: Online

Regions: India, United States, UAE, and global remote contributors

Participation: Individual

Prize pool: INR 75,000

Discord is mandatory for announcements, contribution coordination, support, and submission guidance: https://discord.gg/6uKJZs2NNz

Tracks

1. hushhTech

Repository: https://github.com/hushh-labs/hushh_Tech_website

Good fit for contributors interested in frontend experience, APIs, repo hygiene, testing, performance, documentation, and developer-facing improvements.

2. Kai

Repository: https://github.com/hushh-labs/hushh-research

Application: https://kai.hushh.ai/

Good fit for contributors who want to understand a deeper AI-first product flow, study the architecture, identify real issues, and contribute meaningful improvements.

What to submit

Choose one track, read the repository docs, identify a real bug, missing test, documentation gap, UX gap, performance bottleneck, or useful feature opportunity, and raise a clean GitHub pull request.

Your final submission should include:

  • GitHub profile link
  • chosen track
  • repository name
  • issue link or clear problem statement
  • pull request link
  • short write-up covering what you found, why it matters, what changed, and how you validated it

Evaluation

We will evaluate relevance, technical depth, product thinking, code quality, maintainability, testing, security awareness, PR clarity, ownership, and execution quality.

Register

Unstop: https://unstop.com/p/hushh-open-source-hiring-challenge-2026-build-for-kai-hushhtech-duplicate-hushhai-1687824

Eventbrite: https://www.eventbrite.com/e/hushh-open-source-hiring-challenge-2026-build-for-kai-hushhtech-tickets-1989872887372?aff=oddtdtcreator

Luma: https://luma.com/6uf0atjk

If you are a developer in India, the US, UAE, or anywhere remote and want a practical open-source contribution window, join the Discord and start with one clean PR.

The second-order cost of a(nother) layoff year.

Layoff years compress organizations. Scopes overlap. Lanes that were clear during a growth year start running into each other. Most of the resulting friction works itself out. Some of it doesn’t.

In 2026, 138,000 tech workers have already been let go across 324 companies. Meta cut 8,000 in a memo. Oracle dropped somewhere between 10,000 and 30,000 in a 6 a.m. email. Microsoft launched the first voluntary retirement program in its 51-year history. Block, Coinbase, Cisco, and a long list of others all cited AI as the reason their teams could be smaller and flatter.

A market this tight raises the stakes on every working relationship. In a year where leadership is openly asking who can do whose job, that overlap becomes a resource question. When one party works to resolve it collaboratively and the other works to win it, the cost lands on whoever is doing the accommodating.

The thing to know up front is that the asymmetry doesn’t close by trying harder on your side. The other party isn’t trying to find common ground. They’re building a case. Engaging more just gives them better material.

What works is borrowing from infrastructure.

In systems work, there’s a concept called blast radius. It describes how far damage spreads when something breaks. A blast radius of one means a single service goes down. A blast radius of everything means a misconfigured deploy takes the whole company offline. The job is to design things so failures stay small.

The same logic applies when someone in your org has set you in their sights. You aren’t trying to repair anything. You’re trying to keep the damage local.

Shrink the surface

In security, attack surface is the sum of every place a system can be reached, queried, or pulled into. The first move is to make yours smaller without making the change obvious.

This can mean cutting things you used to think were good for your career. Impromptu coffees. The drop-in DM that turns into a thirty-minute therapy session about middle management. Pulled-aside-after-the-meeting conversations. None of it is bad on its own. All of it becomes edited and reframed, when it’s convenient.

Early drafts stop going into shared docs. Personal stuff stops showing up in team channels. The hobbies, the weekend plans, the half-joke about a manager. All of it is data, and the safer working assumption is that anything shared at work could end up in front of someone you haven’t met.

Make everything written

This single move does more work than anything else on this list.
Slack threads. Doc comments. Ticket updates. Email when it has to be. Anything but the hallway conversation and the unscheduled call.
The first reason is the obvious one. If things escalate later, you’ll need a record, and the record will not exist if you talked it out.

The second reason matters more. A lot of what gets said in person doesn’t survive being typed. You can almost feel the person editing themselves once they know they’re on the record.

When someone pushes for a quick call or wants to grab you for five minutes, redirect politely. “Happy to look once you send it over.” “Can you drop the question in the ticket so I can give it real attention.” Then go back to whatever you were doing.

This isn’t being difficult. It’s insisting on the baseline professional communication you’d ask of any other cross-team colleague. The fact that this one finds it inconvenient is sort of the point.

Side effect worth knowing: if they refuse to put anything in writing, that’s information too. Anyone who only wants to engage off the record is telling you what kind of conversation they want to be having.

Stop arguing with the framing

Problematic colleagues like to offer you stories about who you are. Sometimes flattering, sometimes critical. The thread is that the framing always shrinks your scope, or anchors your work to theirs.
You don’t have to argue with it. You also don’t have to accept it. A useful script for these moments: acknowledge what you heard, restate what you’re actually doing, move on.

“Got it. I’m still planning to ship the launch post next week. Happy to coordinate if there’s overlap.”

That’s the whole thing. No defense. No correction. No explanation of why their version of your role is wrong. The argument isn’t winnable anyway. They aren’t actually offering a view of your job. They’re offering a smaller version of you, and the only correct response to a smaller version of you is to keep being the size you are.

Protect what compounds

The real cost of one of these dynamics isn’t the conflicts. It’s what they do to your attention. The counter is to protect the parts of your work that compound.

This matters more right now than it did two years ago. Coinbase cut roughly 14 percent of its workforce and called the resulting team “lean, fast, and AI-native.” Block cut nearly half its staff, and Jack Dorsey told the company that smaller, flatter teams were the future. When that’s the prevailing logic across the industry, the people who survive are the ones whose work has obvious, legible, compounding leverage. You want to be expensive to lose, and you want the case for keeping you to be visible without anyone having to fight for you in a room you aren’t in.

A useful weekly check: would the next thing you’re shipping still matter if this person weren’t in your org. When the answer is no for too many weeks in a row, the dynamic has captured you, and it’s time to reset.

Keep the file

Somewhere on a personal machine, not anything synced to work, keep a plain text document with dates, what happened, who was there, what was said or written. No commentary. No theories. Just the receipts.
You’ll likely never need it. You’ll be glad you have it.

The file isn’t a place for venting. Venting goes to friends, a therapist, or a journal you’d be okay losing. The file’s job is to make a clear factual case if it ever has to be made to a manager or HR. Trying to reconstruct six months of incidents from memory while stressed and being asked pointed questions is a particular kind of awful. The file is insurance against that. It costs maybe four minutes a week.

Escalate carefully or not at all

Most of these dynamics don’t need escalation. They burn out. The colleague gets bored, the team reorgs, or more likely, the company has bigger problems. The default move is to outlast the thing, not confront it.

The threshold for taking it up the chain is concrete harm. Credit getting taken in a way that hits a perf review. Material misrepresentation of you to leadership. Sustained interference with shipping.

When escalation does happen, lead with impact on the work. “This is blocking shipping X by Y” gets traction. “This is making me uncomfortable” doesn’t, even though both can be true at the same time. Managers will move on the first. Many will quietly let the second die.

What to actually do this week

Three things, if any of this is hitting.

Pull the receipts on the last two months. Even if nothing is currently on fire. Especially if nothing is currently on fire, because the moment you’ll wish you had this information is the moment you’re least able to gather it.

Close one informal channel. The unscheduled call. The “can I grab you” Slack. The hallway side-meeting. Just one. The point isn’t to wall off the relationship in a day. The point is to start changing the geometry.

And look at what’s shipping next. If it’s a thing decided in reaction to something happening internally, swap it. Ship the thing that grows your own visibility instead. The colleague has a finite amount of energy for this. So do you. Spend yours on the work that compounds.

There’s no clean ending here. The breakthrough conversation doesn’t happen. The colleague doesn’t suddenly see what you’ve been seeing. What happens, with your attention back on the work, is that the work gets better. Doors open in rooms no one inside your team controls. Turns out, it was always running on access to you. By the time you notice it’s gone quiet, you’re somewhere else entirely.

That’s the win.

FMix: пакетный менеджер для Forth

В современных языках программирования мы почти всегда ожидаем, что рядом с языком будет удобный инструмент для работы с проектом.

В Elixir есть Mix. В Rust есть Cargo. В Ruby есть Bundler и RubyGems. В Haskell есть Cabal и Stack. В JavaScript есть npm, pnpm и Yarn. В Go есть modules.

Мы привыкли, что можно создать проект одной командой, описать зависимости в одном файле, подтянуть их из сети, запустить тесты, собрать релиз. Это кажется базовой частью экосистемы, а не отдельной роскошью.

Но у старых языков программирования часто другая история.

Когда многие из них создавались, такой подход ещё не был стандартом. Код распространялся иначе, проекты были устроены иначе, а экосистемы развивались без привычных сегодня package managers и build tools.

И это до сих пор чувствуется.

Почему я задумался об этом

Для одного из своих проектов я использую Forth.

Forth — очень интересный язык. Минималистичный, прямой, необычный. В нём есть что-то очень привлекательное: ты находишься близко к вычислениям, близко к стеку, близко к самому железу вычислительной машины (которую иногда я делаю сам).

Но когда я начал писать на Forth что-то более-менее похожее на проект, мне быстро стало не хватать привычных вещей:

  • создать новый проект;
  • описать зависимости;
  • скачать зависимости;
  • запускать тесты;
  • иметь понятную структуру проекта;
  • подключать библиотеки из Git;
  • не держать всё вручную в голове.

Да, можно использовать git submodules, можно копировать файлы руками, можно придумывать свои соглашения. Но для меня это не очень удобный путь. Я хотел что-то ближе к тому, к чему привык в современных языках.

Я нашёл библиотеку f.4th, связанную с theforth.net. Это полезный инструмент, но у него другой подход: библиотеки и версии хранятся на своём сервере.

Мне же хотелось уметь подтягивать зависимости прямо из Git-репозиториев. Например, указать GitHub URL, ветку или тег, и получить библиотеку в проект.

Так появилась идея сделать fmix.

Что такое FMix

fmix — это небольшой build tool и package tool для Forth, вдохновлённый Elixir Mix.

Репозиторий: github.com/VitaSound/fmix

Он не пытается быть большим фреймворком. Его задача проще: дать Forth-проекту минимальный набор привычных инструментов.

Сейчас fmix умеет:

fmix new example
fmix packages.get
fmix test
fmix version

То есть можно создать новый пакет, подтянуть зависимости, запустить тесты и посмотреть версию инструмента.

Создание проекта

Новый проект создаётся так:

fmix new example
cd example

После этого появляется базовая структура проекта:

example/
  example.4th
  package.4th
  README.md
  tests/
  forth-packages/

package.4th — это файл описания пакета. В нём указывается имя, версия, лицензия, основной файл и зависимости.

Пример:

forth-package
    key-value name example
    key-value version 0.1.0
    key-value license COPL
    key-value description example
    key-value main example.4th

    key-list dependencies f git https://github.com/VitaSound/f tag 0.2.4
    key-list dependencies ttester git https://github.com/VitaSound/ttester tag 1.1.0
end-forth-package

Мне нравится, что сам файл тоже написан в стиле Forth. Это не JSON, не YAML и не TOML, а маленький декларативный Forth-формат.

Зависимости из Git

Главная причина, по которой я начал делать fmix, — это зависимости из Git.

В package.4th можно указать зависимость так:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git branch main

Или так, если нужна конкретная версия через tag:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git tag 0.1.0

После этого достаточно выполнить:

fmix packages.get

Зависимости будут установлены в локальную папку проекта:

./forth-packages/

Структура позволяет хранить разные версии одной библиотеки рядом друг с другом:

forth-packages/
  f/
    0.2.4/
  ttester/
    1.1.0/

Подключать зависимость можно обычным require:

require ./forth-packages/f/0.2.4/f.4th

Поддержка theforth.net

Хотя мне хотелось Git-зависимостей, я не хотел полностью отказываться от существующей экосистемы.

Поэтому fmix также умеет работать с пакетами из theforth.net через f.4th.

Например:

key-list dependencies f 0.2.4

Таким образом, в одном проекте можно использовать и Git-зависимости, и пакеты из theforth.net.

Тесты

Ещё одна базовая вещь, которой мне не хватало, — запуск тестов.

В fmix есть команда:

fmix test

Она запускает тесты из директории tests/.

Можно запустить и конкретный файл:

fmix test tests/some_test.4th

Для тестов используется ttester. Если ttester есть в зависимостях проекта, fmix берёт его из ./forth-packages. Если нет — использует версию из установленного FMIX_HOME.

Для меня это важно: тесты должны запускаться одной командой. Даже если проект маленький.

Что было исправлено в последних версиях

fmix пережил уже несколько поколений изменений.

Первые версии я писал сам. Но я не очень сильный Forth-разработчик, поэтому многие вещи давались тяжело. Одну крупную переработку я сделал с помощью Gemini. Последнюю версию я уже доводил с помощью Cursor.

В последних релизах было исправлено довольно много практических проблем.

Появился GitHub Actions CI: workflow собирает GForth 0.7.9 из исходников и запускает:

gforth fmix.4th -e version
gforth fmix.4th -e test

Позже появился release pipeline: при push version tag автоматически создаётся GitHub Release, а текст релиза берётся из файла .github/RELEASE_NOTES_X.Y.Z.md.

Также были исправлены проблемы с путями. Раньше часть логики была завязана на PWD, теперь проект определяется через текущую директорию запуска. Это важно, потому что FMIX_HOME указывает на установленный fmix, а команды должны работать с текущим проектом.

Были исправлены ошибки при установке зависимостей:

  • git clone теперь проверяет результат;
  • неуспешные shell-команды завершают GForth с exit code 1;
  • перед git clone создаются родительские директории;
  • git fetch и checkout выполняются так, чтобы ошибка не маскировалась;
  • для Git-команд используется GIT_TERMINAL_PROMPT=0, чтобы команда не зависала в non-interactive окружении.

Ещё одна важная вещь — валидация входных данных. fmix проверяет имена пакетов, версии, пути, Git URL и Git refs перед тем, как вызывать cp, sed или git. Это не сложная система escaping, а простой whitelist, но он отсекает пробелы, ;, $, backticks, pipes и другие shell metacharacters.

Неожиданная проблема: терминал

Отдельная история была с терминалом. Перед тем, как пользоваться командой fmix в консоли, ее нужно прописать как alias.

Так вот при запуске через простой alias вроде:

alias fmix='gforth "$FMIX_HOME/fmix.4th" -e'

после выполнения команд терминал иногда оставался в плохом состоянии. На prompt появлялись странные символы вроде:

0c0c
[?2004l

Особенно это проявлялось в WSL.

В версии 0.4.4 я перешёл на launcher script bin/fmix. Он восстанавливает TTY после команд, сбрасывает bracketed paste mode и немного очищает накопившийся ввод из /dev/tty. На самом деле все эти вещи я бы точно не разрешил самостоятельно. Я терпеливо помогал нейросети раз за разом пробовать некоторые гипотезы, чтобы исправить эту проблему. В отличие от тех ИИ, что я пробовал раньше, в этот раз я только один раз решил прервать ИИ и создать новый контекст, потому что она как будто зациклилась в бесконечном исправлении.

Однако, получилось и теперь вместо alias рекомендуется использовать:

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"

И запускать просто:

fmix

Установка

Сейчас установка выглядит так:

git clone https://github.com/VitaSound/fmix.git ~/fmix

Затем нужно добавить в shell config (например .bashrc):

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"

После этого команда начнет работать прямо в консоли:

fmix version

Важно: сейчас fmix рассчитан на Linux, Git, sed, cp и GForth 0.7.9.

Я не рекомендую использовать GForth более ранних версий и версию из Snap. Snap запускает программы в изолированном окружении, из-за чего текущая директория и пути могут не совпадать с тем, что ожидает shell session. Это ломает команды вроде new и packages.get.

Лучше использовать GForth из apt, локальную сборку или tarball с официальной страницы GForth.

Зачем это всё

Мне кажется, что старым языкам очень нужны такие инструменты, чтобы соответсвовать современным реалиям.

Не обязательно огромные. Не обязательно идеальные. Но хотя бы такие, которые закрывают базовые сценарии. Когда у языка есть удобный инструмент вокруг проектов, он становится дружелюбнее.

Новому человеку проще попробовать язык. Опытному пользователю проще начать новый проект. Библиотеки проще переиспользовать. Тесты проще запускать.

Это не меняет сам язык, но меняет опыт работы с ним. Позволяет взять старый инструмент в новые проекты.

В случае Forth это особенно интересно. Forth часто воспринимается как что-то старое, странное и нишевое. Но, возможно, часть этой «старости» находится не только в языке, а в отсутствии привычной современной обвязки.

Если добавить удобные инструменты, язык может ощущаться совсем иначе.

programming-languages-personified-fmix

Что дальше

fmix пока маленький и экспериментальный.

Сейчас он умеет создавать пакеты, подтягивать зависимости из Git и theforth.net, запускать тесты.

В будущем хочется улучшать совместимость, добавить документацию, и, возможно, добавить больше команд вокруг разработки Forth-пакетов.

Но уже сейчас мне стало гораздо удобнее работать над своими Forth-проектами. Но это уже совсем другая история

Ссылки

FMix: a package manager for Forth

In modern programming languages, we almost always expect the language to come with a convenient tool for working with projects.

Elixir has Mix. Rust has Cargo. Ruby has Bundler and RubyGems. Haskell has Cabal and Stack. JavaScript has npm, pnpm and Yarn. Go has modules.

We are used to being able to create a project with one command, describe dependencies in one file, fetch them from the network, run tests, and prepare a release. It feels like a basic part of the ecosystem, not a luxury.

But older programming languages often have a different story.

When many of them were created, this approach was not yet a standard expectation. Code was distributed differently, projects were structured differently, and ecosystems grew without the package managers and build tools we now take for granted.

And you can still feel it today.

Why I Started Thinking About This

For one of my projects I use Forth.

Forth is a very interesting language. It is minimalistic, direct, and unusual. There is something very attractive about it: you are close to computation, close to the stack, and close to the actual hardware of the computer, which I sometimes build myself.

But when I started writing something in Forth that looked more or less like a project, I quickly began missing the usual things:

  • creating a new project;
  • describing dependencies;
  • downloading dependencies;
  • running tests;
  • having a clear project structure;
  • connecting libraries from Git;
  • not having to keep everything manually in my head.

Yes, you can use git submodules, copy files by hand, or invent your own conventions. But for me, this is not a very convenient path. I wanted something closer to what I am used to in modern languages.

I found the f.4th library, which is connected to theforth.net. It is a useful tool, but it follows a different approach: library code and versions are stored on its own server.

I wanted to be able to pull dependencies directly from Git repositories. For example, specify a GitHub URL, a branch or a tag, and get the library into the project.

That is how the idea for fmix appeared.

What Is FMix

fmix is a small build tool and package tool for Forth, inspired by Elixir Mix.

Repository: github.com/VitaSound/fmix

It does not try to be a large framework. Its goal is simpler: to give a Forth project a minimal set of familiar tools.

Right now fmix can do this:

fmix new example
fmix packages.get
fmix test
fmix version

In other words, you can create a new package, fetch dependencies, run tests, and check the tool version.

Creating a Project

A new project is created like this:

fmix new example
cd example

After that, you get a basic project structure:

example/
  example.4th
  package.4th
  README.md
  tests/
  forth-packages/

package.4th is the package description file. It contains the name, version, license, main file, and dependencies.

Example:

forth-package
    key-value name example
    key-value version 0.1.0
    key-value license COPL
    key-value description example
    key-value main example.4th

    key-list dependencies f git https://github.com/VitaSound/f tag 0.2.4
    key-list dependencies ttester git https://github.com/VitaSound/ttester tag 1.1.0
end-forth-package

I like that the file itself is also written in a Forth-like style. It is not JSON, YAML, or TOML. It is a tiny declarative Forth format.

Dependencies from Git

The main reason I started making fmix was Git dependencies.

In package.4th, you can define a dependency like this:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git branch main

Or like this, if you need a specific version via a tag:

key-list dependencies ftest git https://github.com/VitaSound/ftest.git tag 0.1.0

After that, you only need to run:

fmix packages.get

Dependencies will be installed into the local project directory:

./forth-packages/

This structure makes it possible to keep different versions of the same library side by side:

forth-packages/
  f/
    0.2.4/
  ttester/
    1.1.0/

You can include a dependency with the usual require:

require ./forth-packages/f/0.2.4/f.4th

Support for theforth.net

Although I wanted Git dependencies, I did not want to completely abandon the existing ecosystem.

So fmix can also work with packages from theforth.net through f.4th.

For example:

key-list dependencies f 0.2.4

This means one project can use both Git dependencies and packages from theforth.net.

Tests

Another basic thing I was missing was running tests.

fmix has this command:

fmix test

It runs tests from the tests/ directory.

You can also run a specific file:

fmix test tests/some_test.4th

Tests use ttester. If ttester exists in the project dependencies, fmix loads it from ./forth-packages. If not, it uses the version from the installed FMIX_HOME.

For me, this matters: tests should run with one command. Even if the project is small.

What Was Fixed in Recent Versions

fmix has already gone through several generations of changes.

I wrote the first versions myself. But I am not a very strong Forth developer, so many things were difficult for me. I made one major refactoring with the help of Gemini. I finished the latest version with Cursor.

The latest releases fixed quite a few practical problems.

GitHub Actions CI was added: the workflow builds GForth 0.7.9 from source and runs:

gforth fmix.4th -e version
gforth fmix.4th -e test

Later, a release pipeline was added: when a version tag is pushed, a GitHub Release is created automatically, and the release text is taken from .github/RELEASE_NOTES_X.Y.Z.md.

Path handling was also fixed. Previously, part of the logic depended on PWD. Now the project is detected from the directory where the command is started. This matters because FMIX_HOME points to the installed fmix, while commands should operate on the current project.

Several dependency installation problems were fixed:

  • git clone now checks its result;
  • failed shell commands make GForth exit with code 1;
  • parent directories are created before git clone;
  • git fetch and checkout are executed so that failures are not hidden;
  • Git commands use GIT_TERMINAL_PROMPT=0, so the command does not hang in a non-interactive environment.

Another important thing is input validation. fmix validates package names, versions, paths, Git URLs, and Git refs before calling cp, sed, or git. It is not a complex escaping system, but a simple whitelist. It rejects spaces, ;, $, backticks, pipes, and other shell metacharacters.

An Unexpected Terminal Problem

There was a separate story with the terminal. Before using the fmix command in the console, it had to be configured as an alias.

When running through a simple alias like this:

alias fmix='gforth "$FMIX_HOME/fmix.4th" -e'

the terminal sometimes ended up in a bad state after commands finished. Strange symbols appeared in the prompt, for example:

0c0c
[?2004l

This was especially visible in WSL.

In version 0.4.4, I switched to the bin/fmix launcher script. It restores the TTY after commands, resets bracketed paste mode, and briefly drains queued input from /dev/tty. To be honest, I would definitely not have solved all of that on my own. I patiently helped the neural network try hypothesis after hypothesis to fix the problem. Unlike the AI tools I had tried before, this time I only had to stop the AI once and create a new context because it seemed to get stuck in an endless correction loop.

But it worked, and now instead of an alias it is recommended to use:

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"

And then run it simply as:

fmix

Installation

Right now installation looks like this:

git clone https://github.com/VitaSound/fmix.git ~/fmix

Then add this to your shell config, for example .bashrc:

export FMIX_HOME="$HOME/fmix"
export PATH="$FMIX_HOME/bin:$PATH"

After that, the command should work directly in the console:

fmix version

Important: at the moment fmix expects Linux, Git, sed, cp, and GForth 0.7.9.

I do not recommend using earlier versions of GForth or the Snap version. Snap runs programs in a confined environment, so the current directory and paths may not match what the shell session expects. This breaks commands like new and packages.get.

It is better to use GForth from apt, a local build, or a tarball from the official GForth page.

Why This Matters

I think old languages really need tools like this to fit modern realities.

They do not have to be huge. They do not have to be perfect. But they should at least cover the basic scenarios. When a language has convenient tooling around projects, it becomes friendlier.

It is easier for a newcomer to try the language. It is easier for an experienced user to start a new project. Libraries are easier to reuse. Tests are easier to run.

This does not change the language itself, but it changes the experience of working with it. It allows an old tool to be used in new projects.

In the case of Forth, this is especially interesting. Forth is often seen as something old, strange, and niche. But maybe part of that “oldness” is not only in the language itself, but also in the absence of familiar modern tooling around it.

If we add convenient tools, the language can feel very different.

What Is Next

fmix is still small and experimental.

Right now it can create packages, fetch dependencies from Git and theforth.net, run tests, and publish releases through GitHub Actions.

In the future, I would like to improve compatibility, add documentation, and maybe add more commands around Forth package development.

But even now, it has become much more convenient for me to work on my Forth projects. But that is another story.

Links

🚀 ForgeZero v1.9.0 — The “Architect” Update: LSP Integration, Cross-Compilation & Industrial-Grade Reliability

If you’ve been sleeping on ForgeZero (fz) — the zero-config C build tool that actually respects your time — v1.9.0 just dropped and it’s a proper glow-up. We’re talking IDE superpowers, native cross-compilation, a critical bug squash, and enough test coverage to make your QA lead weep with joy. Let’s break it down. 🧵

1. LSP & IDE Support — Your Editor Finally Gets It

fz -compile-commands

One flag. That’s all it takes to spit out a compile_commands.json — the Compilation Database that every serious editor lives and breathes.

What this means in practice:

  • Neovim + clangd — full autocomplete, go-to-definition, find-references. Everything just works™
  • VSCode + clang — same story, zero manual config
  • Any LSP client — if it reads compile_commands.json, it’s happy

fz went from “fast build runner” to a legit professional dev environment with this single addition. Your IDE will finally stop lying to you about missing includes.

2. Cross-Compilation — Build for ARM from Your x86 Couch

Embedded devs, this one’s for you. fz now ships with full cross-compilation support out of the box:

fz -target arm-linux-gnuebihf

That’s it. No hunting for toolchain paths, no manual -CC= gymnastics. fz auto-detects the correct prefixed compilers and linkers for your target triple.

Build for ARM while sitting on your x86 machine. One command. Shipped. 📦

Supported target examples:

  • arm-linux-gnuebihf — ARMv7 hard-float (Raspberry Pi and friends)
  • Any standard GNU cross-compilation triple

3. Industrial Reliability — The Boring Part That Actually Matters

Linker Coverage: 48% → 60%+

Nobody likes writing linker tests. We did it anyway. The darkest corners of the linking pipeline are now covered, which means fewer “works on my machine” surprises in CI.

🐛 Critical Bug Fix: Object Name Collisions

This one was nasty. Multi-file projects were silently producing _.o collisions — meaning files from different directories could stomp each other’s object files. The result: corrupted builds that were sometimes correct, which is the worst kind of wrong.

The fix: every object file now gets a unique name derived from its full source path. No more _.o. No more collisions. Multi-file projects build reliably, every time.

Before: src/foo.c → _.o  (collision risk)
After:  src/foo.c → src_foo.o  (unique, always)

4. UX & Infrastructure — The QoL Stuff You’ll Actually Notice

Smart Self-Update with Rollback

fz -update

Before: blew away your old binary and hoped for the best.

Now: backs up the old version to /usr/local/bin/fz.old before installing the new one. Broke something? Roll back in one move. Safe as it gets. 💊

Shell Mode Now Has Tests

The interactive shell (fz REPL mode) now has full test coverage for:

  • SplitCommand parsing
  • CmdSet handling
  • CmdBuild execution

Your in-tool console is no longer running on vibes and prayers.

5. Strict Coding Standards — fz Teaches You to Write Clean C

ForgeZero now ships with sane-by-default compiler flags in all docs and JSON command configs:

-Wall -Wextra -Werror -Wpedantic -Wshadow -Wconversion

This isn’t gatekeeping — it’s a forcing function. If your code compiles cleanly with these flags, it’s genuinely clean code. fz has an opinion about code quality, and it’s the right opinion.

Treat warnings as errors. Your future self will thank you. Your coworkers will thank you. The universe will be in slightly better order.

TL;DR — What Shipped in v1.9.0

Feature What It Does
-compile-commands Generates compile_commands.json for LSP/IDE
Cross-compilation -target <triple> — auto-detects toolchain
Object name fix Unique .o names, no more multi-file collisions
Linker coverage 48% → 60%+ test coverage
Smart update fz -update backs up old binary to fz.old
Shell tests SplitCommand, CmdSet, CmdBuild are tested
Default flags -Wall -Wextra -Werror -Wpedantic -Wshadow -Wconversion

What’s Next?

The “Architect” name wasn’t chosen lightly. v1.9.0 lays the foundation for fz to become the go-to build tool for serious C projects — from single-file scripts to multi-target embedded systems.

Got feedback? Ran into an edge case? Drop it in the comments or open an issue. We read everything. 👇

Built with fz. Compiled cleanly. No warnings.

We shipped an on-device Japanese braille reader — here’s why offline-first was non-negotiable

The problem sitting in plain sight

There’s braille on train handrails, elevator buttons, and pill boxes all over Japan. Most people walk past it every day without a second thought — not because they don’t care, but because almost nobody can read braille.

TenjiScan is our attempt to close that gap. Point your iPhone camera at any Japanese braille, get plain text back. That’s the whole pitch.

Why we went fully offline

The first architectural decision we locked in was: no server. Braille appears on medication packaging, medical devices, banking equipment — contexts where sending someone’s photo to a remote endpoint feels like a bad trade. It’s not worth the privacy exposure.

Since v4.0.0, all recognition runs through a custom two-signal pipeline built on Vision. No network required, no image leaves the device. The trade-off is that we own the accuracy ceiling entirely — no external model updates — which pushes us to get the on-device pipeline right rather than lean on infrastructure.

What it actually supports

The app handles JIS Japanese braille (the national standard, JIS T 0921) and UEB Grade 1 English braille. Grade 2 contracted English braille isn’t supported yet — contracted braille compresses text using shorthand symbols, and it’s a meaningful gap for English-speaking users who depend on it in practice. We know. It’s on the list.

The honest limitation

Lighting and shooting angle matter. Braille bumps cast shadows that make recognition possible, which means flat, even light actually hurts accuracy. We added UI guidance to help users find better angles, but in poor lighting the recognition still struggles. This isn’t buried in fine print — it’s just physics.

What surprised us

The use case we didn’t fully anticipate: sighted people who are simply curious. Someone picks up a pill box they’ve owned for years and finally reads the braille on it. The app makes a small part of the tactile world readable without requiring expertise, and accessibility users have told us the on-device guarantee matters more to them than raw recognition speed.

TenjiScan is free on the App Store, with a one-time purchase to remove ads.
https://apps.apple.com/jp/app/id6759526188