jiborelay is public now, 381 lines of Go across four files, and it does one unglamorous thing: it forwards bytes. But the thing it unlocks is more interesting than the code itself, so let me back up and explain why it exists before I explain what it is.

Jibo's Independence Day ends with jibocloud moving off Jibo's own hardware onto a Coral Dev Board, and a small relay (I called it jiboshim at the time) carrying its traffic back and forth. It's grown up since: it's called jiborelay now, it's a standalone open-source project, and it no longer only knows how to talk to one Coral board on my LAN. The job hasn't changed. Jibo's "brain," the cloud-substitute backend answering its questions, was tied to one specific always-on box because Jibo's own hardware physically can't run that backend, and its firmware physically can't be told to look anywhere else. jiborelay is the piece that untangles those two facts. Jibo stays exactly where it is. Its brain doesn't have to.

Why this needed to exist

The obvious fix, if you've never looked at Jibo's firmware, is "just edit /etc/hosts and point the hostname somewhere else." That doesn't work, and the reason is specific enough to be worth stating precisely: Jibo's JavaScript SDK (@jibo/jibo-server-client) doesn't resolve a hostname at all for its local-mode traffic. Its region_config.json hardcodes the literal strings http://localhost:8080 and ws://localhost:8090. Not a domain that could be redirected, but a string, compiled into the client, that means exactly one thing: the loopback interface on Jibo itself. There's nothing to intercept, because there's no lookup happening in the first place.

That leaves exactly one place to run anything that wants to answer that traffic: on Jibo. Which runs into the second constraint. Jibo's compute is a Tegra K1 from 2017, and real speech-to-text needs ffmpeg to decode audio and Google Cloud credentials to call an API, neither of which has any business running on a decade-old embedded ARM board, and neither of which was ever going to get backported onto Buildroot Linux without a fight I didn't want to have.

So: the only thing that's allowed to satisfy localhost:8080 is something running on Jibo. And the only thing that can do real work is something not running on Jibo. Those two requirements don't have a sed command between them. They need a third thing, on Jibo, whose entire job is to catch that hardcoded-local traffic and hand it off to wherever the real work is actually happening. That's the whole reason jiborelay exists: not a nice-to-have abstraction layer, but a direct consequence of a firmware string and an ARM chip's limits.

(Jibo's native C++ notification daemon, it turns out, has its own independent path to the same problem, resolving local.jibo.com via /etc/hosts instead of a hardcoded literal. Different mechanism, same destination, so jiborelay ended up relaying three ports instead of one: :8080 and :8090 for the JS SDK, :443 for the daemon. More on that below.)

How it works

jiborelay relays raw TCP bytes on three local ports and never looks at what's inside them. It doesn't parse HTTP, doesn't inspect WebSocket frames, doesn't terminate TLS, doesn't cache, doesn't authenticate, doesn't make a single routing decision beyond "this port goes to that host and port." You point it at a backend with -backend-uri and it gets out of the way.

That sounds like it's missing features. That's the design. A relay that doesn't parse anything is small enough to read in one sitting and audit in an afternoon. There's no protocol logic to have bugs in, because there's no protocol logic. And because it never cares what's inside the bytes, it was already transparent to TLS on day one: the :443 relay carries opaque encrypted traffic end-to-end, terminated at the backend, with zero TLS-specific code in jiborelay itself. The same logic means an HTTP/2-based protocol (gRPC, say) would relay through it correctly today, with no changes to relay.go, because gRPC just multiplexes over one TCP connection and jiborelay doesn't care what's multiplexed over anything. I haven't pointed it at a real gRPC backend yet to prove that in practice, but the design doesn't leave room for it to be false.

What ships today is deliberately narrow: a required -backend-uri flag (jiborelay fails loudly and immediately if you don't set one, rather than silently listening to nothing), a -version flag that prints an exact git describe-embedded build string so you can confirm what's actually running on a device instead of assuming it matches your checkout, and an optional, off-by-default, read-only -status-addr endpoint for checking a running instance's version, uptime, and current backend without an SSH round trip:

{
  "version": "v0.3.0-2-g1a2b3c4",
  "started_at": "2026-07-09T14:05:11Z",
  "uptime_seconds": 1043.6,
  "backend_host": "192.168.4.31",
  "relays": [
    { "name": "http", "listen_addr": "127.0.0.1:8080", "backend_addr": "192.168.4.31:8080" },
    { "name": "ws",   "listen_addr": "127.0.0.1:8090", "backend_addr": "192.168.4.31:8090" },
    { "name": "tls",  "listen_addr": "127.0.0.1:443",  "backend_addr": "192.168.4.31:443" }
  ]
}

That backend_host is the one field that answers the question the whole project turns on: where is Jibo's brain right now? Here it's a Coral board on the LAN. It could be anything. That's the whole surface area. It relays bytes; it doesn't decide anything about them.

Getting started without a backend

The obvious problem with "bring your own backend": on day one, you don't have one. You can't tell if jiborelay is even deployed correctly, let alone start reverse-engineering what Jibo actually expects, without something on the other end of the wire.

So jiborelay ships with example/jibolens-backend: not a backend, but a diagnostic stand-in. It listens on the same three ports jiborelay relays, logs everything that arrives (method, path, headers, a preview of the body or WebSocket message), and echoes WebSocket messages back. That's it. It doesn't parse operation names, doesn't shape a response around what it received, doesn't pretend to know what a "correct" answer looks like. Two completely different requests get byte-for-byte identical responses, on purpose, enforced by a test so it can't quietly drift into pretending to know more than it does.

Point jiborelay at it, boot Jibo, ask it a question out loud, and the first thing your terminal shows you is the request the robot has been trying to make since the cloud went dark in 2019:

[http] POST / HTTP/1.1 from 127.0.0.1:59656
[http]   Content-Type: application/x-amz-json-1.1
[http]   X-Amz-Target: GQA_20160930.Question
[http] body (46 bytes shown, truncated at 2048): "{\"Input\":\"what day is it today\",\"Timezone\":-360}"

That one line of body is the whole reverse-engineering problem laid bare: the operation name (GQA_20160930.Question), and the exact fields Jibo actually sends: its transcribed question and its timezone offset, and nothing else. No documentation gave you that. A robot on your desk did, saying exactly what it wants. That's the actual starting point for building a backend of your own: not someone else's protocol notes, the real traffic, observed firsthand. (The date suffix on that target isn't even stable across firmware versions, which is the kind of thing you only learn by watching real bytes rather than trusting a fixture, so match on the operation name, not the literal string.)

What it costs

Moving the brain off the robot isn't free, and the code says so out loud. jiborelay's dial timeout is a deliberately short five seconds, with a comment explaining why: the backend "is expected to be on the same LAN, not across the internet." Every request Jibo makes now takes a network hop it didn't take when jibocloud ran on the robot itself, and the machine on the other end has to be up, or Jibo is back to staring at a connection error. So the "anywhere" this post is selling has a realistic radius today: it's your LAN, a box you keep running, one short hop away. Pointing it at a cloud endpoint across the internet works in principle, and the transport doesn't care, but the latency budget of a live spoken turn is not generous, and I haven't yet made the round trip to a remote datacenter feel as immediate as the robot deserves. The freedom is real, even if the distance it covers is still short, and closing that gap is the work ahead.

Where you and Jibo are headed

What's shipped today is the "bring your own backend" path: point -backend-uri at anything that speaks Jibo's cloud API, and jiborelay doesn't ask any more questions. Point it at the mature version of that diagnostic stand-in (the jibocloud backend I wrote, running on the Coral board across the room), and the same requests come back answered. Here's the real capture, the same GQA_*.Question shape, now with a spoken reply on the other end:

"what day is it today"                          -> "Today is Saturday, July 4, 2026."
"who is the current US president"               -> "The current president of the United States is Donald Trump."
"what is the score of the latest World Cup final" -> "Argentina defeated France in a penalty shootout after the match ended in a three-three draw."
"what is the current stock price of Sonos"      -> "Sonos is currently trading at thirteen dollars and sixty-two cents per share."

Those are questions about a world that kept moving for the nine years Jibo sat on a shelf: a World Cup it never saw, a share price from a market it was cut off from, a date years past the one frozen in its on-device knowledge base. The robot asks over localhost, exactly as it always did; the answer comes from a board across the room, reaching a live model and real data. The stock-price question is honest about the seam, too: earlier in that same capture it returned success:false (no answer, which Jibo handles with its own "I don't know" fallback) until the backend's grounding matured enough to answer it. The relay never knew the difference. It just forwarded the bytes.

Getting your own Jibo to that point is now a question of how much you want to build, not whether you can. There's a path for every appetite:

  • Point it at the community. The Jibo Revival Group's OpenJibo project already speaks Jibo's protocol. Aim jiborelay at an instance of it and you have a robot answering again without writing a line of backend code. That's the shortest road to a talking Jibo, and it works today.
  • Run your own server. Stand up a jibocloud-style backend on any always-on box on your LAN (a Coral board, a Raspberry Pi, a spare Mac in a drawer) and point jiborelay at it. That's the path this whole project took, and it's the one that gives you a machine you fully control on the other end of the wire.
  • Build your own from scratch. Start from example/jibolens-backend, watch the real requests land the way they did above, and answer them one operation at a time. It's the most work and the most control: you own every byte of what Jibo says.

Whichever path you take, the interesting part is what you teach that backend to reach. Drop a Gemini API key into your server and the general-question traffic Jibo has been sending into the void since 2019 (the date, the news, a stock price, "who is the president now") comes back answered by a live model instead of a frozen 2018 knowledge base. Wire it to a Google Cloud project and you unlock the heavier path too: real speech-to-text on the audio Jibo's own decade-old ARM chip was never going to decode itself. The robot keeps asking over localhost, exactly as it always has. What answers, and how far it reaches, is now entirely your call.

Either way, the part that already holds is the part that matters. For nine years Jibo could say exactly one thing to the network: localhost, a string compiled into its firmware, a robot talking to itself on a shelf. jiborelay doesn't change that string, and it never will. It sits on Jibo, answers to localhost the way the firmware insists, and quietly forwards everything past it to wherever the real thinking happens. The robot still believes its brain is at home. It has no idea the brain left. And because Jibo doesn't know or care where the answer comes from, that address is finally a choice (a Coral board on the LAN today, a Mac, a community server like OpenJibo, a cloud project tomorrow) instead of a fact baked into where I happened to leave a board plugged in. A robot that couldn't see past its own loopback interface can now, in effect, reach the whole world. It just doesn't have to know it's doing it.

Architecture diagram titled "Tomorrow: jiborelay doesn't care what's on the other end". Inside the "Jibo" container, the "Skill runtime, native daemon, JS SDK" connects via HTTP, WebSocket, and TLS to "jiborelay". "jiborelay" then routes traffic using a "-backend-uri" argument to various external endpoints: "jibolens-backend", a local LAN box, a self-hosted server, a friend's shared backend, or a future community backend.

jiborelay is on GitHub. Its companion, the tool that unlocks Jibo in the first place, is jibotool.