Enqueue a URL and a JSON body. Hopper stores the job in MongoDB, puts { "job_id" } on RabbitMQ, and POSTs until the target accepts — or the job is dead and you replay it. At-least-once HTTP only (a crash after 2xx and before Mongo success can duplicate). MVP type: http_post.
docker compose -f deploy/compose.yaml up -d
go build -o hopper ./cmd/hopper
./hopper api # ingress + outbox relay
./hopper worker # claim, POST, relayConfig example: deploy/hopper.example.yaml.
| Method | Path |
|---|---|
POST |
/v1/jobs |
GET |
/v1/jobs/{id} |
GET |
/v1/jobs?status=dead |
POST |
/v1/jobs/{id}/replay |
GET |
/healthz |
POST /v1/jobs
Authorization: Bearer <token>
Idempotency-Key: order-42
Content-Type: application/json
{
"type": "http_post",
"target": "https://hooks.example.com/notify",
"payload": { "order_id": 42 },
"max_attempts": 8
}Outbound: Idempotency-Key: hopper/{job_id}/{cycle}/{attempt}.
| Role | Package |
|---|---|
| Language | Go 1.27 |
| Router | chi |
| DI | fx |
| Config | config |
| Log | zap |
| Documents | mongo-driver |
| Broker | amqp091-go |
| Egress | net/http |
flowchart LR
C[Client] -->|Bearer| A[hopper api]
A -->|job doc| M[(MongoDB)]
A -->|job_id| R[[RabbitMQ]]
R --> W[hopper worker]
W --> M
W -->|HTTP POST| T[Target]
W -->|delay / DLQ| R
