iii 0.18.0 is out, and the team shipped our first tutorial for you.
You do not spin up Redis, a job runner, a separate API gateway, and a streaming layer, then integrate them together, it happens naturally with iii. You run one iii engine and walk through seven chapters that each add a real shippable feature of production: HTTP short links, observability, SQLite persistence, queued click writes, pub/sub fan-out (including a Python analytics worker), live click streams, bulk CSV import over channels, and a browser UI on RBAC-gated WebSockets.
It is not any traditional hello-world demo.
You register functions, bind triggers, call state:: and database::, queue work, publish events, and stream data: the same thing you would use for a serious backend, with copy-paste steps and expected curl output at each stage.
If you have been curious what “one engine instead of fifty or hundered or even thousand tools” looks like in practice, this is the walkthrough.
Check out today!
A first-time contributor added native GO support to iii from start to finish in three days.
That means you can now turn any GO service into an iii worker. A gRPC API, a Kubernetes controller, a data pipeline. Register your functions and triggers, and the service is on the iii engine with everything else. Yes, a "kagent" can be just a worker with iii.
You can now compose GO services with workers written in Node, Python, and Rust. They share the same engine, the same triggers, the same function calls. An agent invoking a Go function doesn't know or care what language is on the other end. You can now get real-time discovery, extensibility, composability, and observability across your GO services without writing the integration work yourself. You can now expose GO functions to agents as tools. The function id is the tool. The schema is the function signature. Agents call GO the same way they call anything else on the engine.
This was possible in three days because of an early architectural decision by us. The small surface area of the SDK is the primitives: Worker, Trigger, and Function. That is the entire thing an SDK has to express. Porting iii to a new language is porting three concepts, not a framework. There is no orchestration logic to translate. There are no opinions about state machines or agent loops to argue with.
That small surface area is what lets the ecosystem expand without fragmenting. Every new language lands speaking the same three primitives, so a team running Node today gets GO interop the day it ships. The mental model of the system stays small even as the ecosystem grows.
For agents, the benefit is bigger. An agent just needs to understand the engine, regardless of how many services run on it or what languages they use.
Java and .NET are coming soon. The path is the same one this contributor walked. Want to add a new language? Create a GitHub issue today!
Marcus Elwin(@MarqKwesi), thank you. An entire GO SDK in three days is an amazing job.
Link to PR: https://t.co/GOWv7bfVVv
I learned to build a Linkly, a URL shortener that grows into a multi-tenant link platform on iii.
This tutorial builds Linkly, a link shortener, one worker at a time. You start with a single function that turns /s/abc into a redirect, and finish with a multi-tenant link platform that has durable execution, storage, analytics, a live click stream, bulk imports, and a browser worker on the same bus.
Each chapter adds one capability by adding a worker to your existing system. The point is not the link shortener itself, but the pattern: a real system on iii is a set of small workers that invoke each other’s functions through the engine. You add capability without rewriting what came before.
You do not spin up Redis, a job runner, a separate API gateway, and a streaming layer, then integrate them together, it happens naturally with iii. You run one iii engine and walk through seven chapters that each add a real shippable feature of production: HTTP short links, observability, SQLite persistence, queued click writes, pub/sub fan-out (including a Python analytics worker), live click streams, bulk CSV import over channels, and a browser UI on RBAC-gated WebSockets.
It is not any traditional hello-world demo.
You register functions, bind triggers, call state:: and database::, queue work, publish events, and stream data: the same thing you would use for a serious backend, with copy-paste steps and expected curl output at each stage.
If you have been curious what “one engine instead of fifty or hundered or even thousand tools” looks like in practice, this is the walkthrough.
Loved it!
https://t.co/JvOh3au5UY
iii 0.18.0 is out, and the team shipped our first tutorial for you.
You do not spin up Redis, a job runner, a separate API gateway, and a streaming layer, then integrate them together, it happens naturally with iii. You run one iii engine and walk through seven chapters that each add a real shippable feature of production: HTTP short links, observability, SQLite persistence, queued click writes, pub/sub fan-out (including a Python analytics worker), live click streams, bulk CSV import over channels, and a browser UI on RBAC-gated WebSockets.
It is not any traditional hello-world demo.
You register functions, bind triggers, call state:: and database::, queue work, publish events, and stream data: the same thing you would use for a serious backend, with copy-paste steps and expected curl output at each stage.
If you have been curious what “one engine instead of fifty or hundered or even thousand tools” looks like in practice, this is the walkthrough.
Check out today!
iii 0.18.0 is out, and the team shipped our first tutorial for you.
You do not spin up Redis, a job runner, a separate API gateway, and a streaming layer, then integrate them together, it happens naturally with iii. You run one iii engine and walk through seven chapters that each add a real shippable feature of production: HTTP short links, observability, SQLite persistence, queued click writes, pub/sub fan-out (including a Python analytics worker), live click streams, bulk CSV import over channels, and a browser UI on RBAC-gated WebSockets.
It is not any traditional hello-world demo.
You register functions, bind triggers, call state:: and database::, queue work, publish events, and stream data: the same thing you would use for a serious backend, with copy-paste steps and expected curl output at each stage.
If you have been curious what “one engine instead of fifty or hundered or even thousand tools” looks like in practice, this is the walkthrough.
Check out today!
Most observability for agents is the wrong shape.
You install a debugger. You connect it to your harness. You hope the spans the framework chose to emit cover the failure mode you're chasing. When they don't, you patch instrumentation into the framework code, which means forking it, which means owning a fork forever. Every team I've talked to running agents in production has this story. The harness is opaque by default, and the path to making it transparent runs through the framework's source code.
That shape exists because the harness is one thing. A monolith. A framework you imported. There's no surface to attach observability to except the one the framework chose to expose. If the framework didn't trace it, you don't see it. If the framework traced it badly, you read it badly.
Our engineering cooked with iii harness, below image shows one agent run on iii. 219 traces. Grouped by function. Every worker tagged with its name. Every turn step nested under its parent. Every tool call resolves all the way down to "provider::anthropic::stream" with the actual HTTP post to the model API visible at the leaf. The trace tree is not a feature we shipped. It is what falls out when the harness is decomposed into workers on a shared bus and the SDK auto-instruments every function call at the runtime layer.
The trace tree is not a feature we shipped. It is what falls out when there is no harness category in the first place.
This is the hardest part to communicate. The harness is not a layer. There is no boundary between the harness and the rest of the system. The turn orchestrator is a worker. The provider router is a worker. The queue your agent reads from is a worker. The key-value store holding session state is a worker. The HTTP endpoint serving traffic is a worker. The sandbox running the tool call is a worker. They all connect to the engine over the same WebSocket protocol. They all register functions on the same bus. They are wrapped by the same Proxy in the SDK that emits an OpenTelemetry span every time one of their functions runs. The span carries the session id, the message id, and the function id as baggage, which propagates across every nested call regardless of which worker handled it.
There is no inside or outside of the harness. There is no tier where the trace ends and the infrastructure begins. The harness is just the name for whatever set of workers you composed to run a turn. The sandbox is on the same bus. The queue is on the same bus. The model provider is on the same bus. The trace tree on the other side is one connected graph because there is nowhere for it to break.
Most of what is hard about running agents in production is not which model you picked or what prompt you wrote. It is what happens between the model call and the tool result and the next model call. The state transitions. The approval gates. The retries. The hook fanout. The compaction. The places where one of fifteen things can go wrong on a turn that took 2.76 seconds and burned 3068 spans of work. If you cannot see what happened in those 2.76 seconds at the granularity of the function call, you are debugging by vibes. The framework era of harnesses normalized debugging by vibes. The worker era ends it.
Pick the workers. Write the missing ones. Read every span.
https://t.co/HOXKjjRyW4
Kaffu's "rich man's toy" line is the one of the sharp thing I've read on harnesses this year. He's right about the symptom. I'd push back on one part of the diagnosis.
The bloat drift he names, agent engineering quietly turning into software engineering, is real. Every harness team I've talked to hits it around month nine. The framework you started with grows features it didn't need, the system prompt swells, the retrieval layer doubles, the cost-per-task triples. Codex and Claude Code keep getting better, and you start to wonder what you're building.
My extension: the drift is structural. It happens because the unit of work in a framework-shaped harness is the whole framework. To add a capability you grow the framework. To change a behaviour you fork the framework. The bloat has nowhere else to go.
When the unit shrinks to one narrow worker, one typed function, one job, drift loses its surface area. A retrieval worker that's wrong gets replaced, not extended. The math-based reranker kaffu is right to advocate for becomes a worker that registers rerank::score. The fine-tuned RoBERTa becomes a worker that registers embed::generate. They sit next to the LLM provider worker on the same bus. The system stays cheap by being composable.
Simply, everything become a worker.
This doesn't make harnesses economically valuable on its own. Kaffu's deeper point stands. Most of what teams ship is fancy on paper and useless in production. The framework era encouraged that because the unit it sold was always too big.
I don't know what the economically valuable harness looks like at steady state. I think it looks small. Small enough that every part is replaceable, every part is debuggable, every part is benchmarkable, and observable with observability worker against a 100-line fine-tuned alternative. The harness as a slider, not a monument.
For the love of the game.
276K lines of tests guarding 262K lines of app code. Garry's calling that a Foxconn factory for agents, and the ratio holds well past agents. It's what every backend turns into once you count the integration problem between the queue, the workflow engine, the retry loops, the observability layer.
The skill pack is the right primitive for the harness. Reusable capability with its own tests, pulled in when relevant, instead of behavior frozen into code the day you shipped it.
Push the same logic down to the infrastructure. The integration problem between systems is the cage. Make each piece a worker you install and compose through one protocol bus, and the integration cost stops compounding with every component you add. Agents stop being something you wrap in control code and start being peers that install their own capabilities at runtime.
https://t.co/UIrJrkHuDz
Harness post lands the architecture, Check this iii console 🤩
3,068 spans, 3 services, one agent turn. Every worker auto-instrumented at the SDK. Group by session, message, or function, the tree reconstructs
Observability as a worker
iii worker add iii-observability
백엔드 프로젝트 만들 때마다 큐 세팅하고 크론 맞추고 모니터링 붙이느라 며칠씩 날리는 노가다를 하나로 합친 오픈소스임. iii-hq/iii라는 물건인데 worker, function, trigger라는 3가지 개념만 가지고 이 과정을 통째로 단순화함. iii worker add 명령어로 기능을 추가하면 라이브 카탈로그에 실시간 등록돼서 다른 프로세스나 AI 에이전트가 중간 레이어 없이 즉시 호출하는 구조임. Rust 기반 엔진에 Node, Python SDK도 지원해서 마이크로서비스나 에이전트용 아키텍처 짤 때 요긴하겠네.