Cross-language: Python → Go over Redis

The whole point of BabelQueue: a message produced in one language is consumed natively in another, over the broker you already run, using one strict JSON envelope. This demo proves it end-to-end — a Python or PHP producer and a Go consumer sharing a Redis queue. The consumer never knows which language wrote the message; it only sees the canonical envelope and meta.lang.

Every SDK uses the identical reliable-queue pattern (RPUSH to produce, BLMOVE to a :processing list to reserve, LREM to ack), so they interoperate on the plain orders Redis list. Full runnable sources: github.com/BabelQueue/babelqueue-examples.

Producer — Python

# pip install "babelqueue[redis]"
from babelqueue import BabelQueue

app = BabelQueue("redis://localhost:6379/0", queue="orders")
app.publish("urn:babel:orders:created", {"order_id": 1042, "amount": 99.90, "currency": "USD"})
app.publish("urn:babel:catalog:item.indexed", {"sku": "WIDGET-1", "title": "Café Widget ☕"})

Producer — PHP (same queue, no framework)

The framework-less core builds the envelope and the bundled RedisTransport does a plain RPUSH onto the identical orders list — so the Go consumer below reads PHP-produced messages without changing a line.

// composer require babelqueue/php-sdk predis/predis
use BabelQueue\Codec\EnvelopeCodec;
use BabelQueue\Transport\RedisTransport;

$transport = new RedisTransport(new Predis\Client("redis://localhost:6379/0"), "orders");

$envelope = EnvelopeCodec::fromJob($job, "orders"); // $job: a PolyglotJob (URN + toPayload())
$transport->publish(EnvelopeCodec::encode($envelope), "orders");

Consumer — Go

// go get github.com/babelqueue/babelqueue-go/redis
import (
    babelqueue "github.com/babelqueue/babelqueue-go"
    bqredis "github.com/babelqueue/babelqueue-go/redis"
)

tr, _ := bqredis.New("redis://localhost:6379/0")
app := babelqueue.NewApp(tr, babelqueue.WithDefaultQueue("orders"))

app.Handle("urn:babel:orders:created", func(ctx context.Context, env babelqueue.Envelope) error {
    fmt.Printf("order %v (produced by %q)\n", env.Data["order_id"], env.Meta.Lang)
    return nil
})
app.Consume(context.Background()) // routes by URN, forever

The proof

Run either producer, then the Go consumer. The Go program prints messages it never produced — produced by "php" (or "python") — with the data, trace_id and even unicode intact:

[go] order created  id=1042 amount=99.9 USD  trace=1d3b…  (produced by "php")
[go] order created  id=1043 amount=12.5 EUR  trace=79c3…  (produced by "php")
[go] item indexed   sku=WIDGET-1 title="Café Widget ☕"  (produced by "php")
[go] processed 3 message(s) — same envelope, different language.

The envelope on the wire is the canonical shape every SDK emits:

{
  "job": "urn:babel:orders:created",
  "trace_id": "…",
  "data": { "order_id": 1042, "amount": 99.9, "currency": "USD" },
  "meta": { "id": "…", "queue": "orders", "lang": "php", "schema_version": 1, "created_at": 1749132727000 },
  "attempts": 0
}

Swap the ends

The queue carries the canonical envelope, so any SDK can be on either side — a Go producer (app.Publish(...)) and a Python consumer (@app.handler(...) + app.run()) work the same way. PHP (framework-less RedisTransport, or Laravel/Symfony on a babelqueue-* connection), Node, Java and .NET read and write the identical envelope on their own transports.