Connect and authenticate¶
Point any HTTP client at a Narad node and authenticate each request with a username and password.
Before you start: the address of a Narad node or of the load balancer in front of the nodes, and a username and password from your operator. No cluster yet? Quickstart runs one on your machine in a minute; set AUTH to : for it.
Set the base URL and your credentials once per shell:
export NARAD="http://127.0.0.1:7942"
export AUTH="billing-service:your-password"
$NARADis the base URL of any Narad node, or of the load balancer in front of them, for examplehttp://127.0.0.1:7942.$AUTHisusername:passwordfor a user your operator created, herebilling-service. Quote it, so a password with shell characters survives. A local node with authentication off, such asnarad server start --dev, ignores credentials:export AUTH=":"(an empty user and password).
Then call the node:
curl -i -u "$AUTH" "$NARAD/v1/topics"
HTTP/1.1 200 OK
Content-Length: 288
Content-Type: application/json
Date: Mon, 28 Sep 2026 19:30:07 GMT
{
"next_page_token": "",
"topics": [
{
"name": "orders",
"id": "71f9b6869df02ef3",
"partitions": 3,
"retention_ms": 604800000,
"visibility_timeout_ms": 30000,
"max_in_flight_per_partition": 1024,
"max_acked_ahead_per_partition": 1024,
"created_at": 1790623535,
"owner": "billing-service",
"role": "standalone"
}
]
}
The list holds only the topics you may read, so an empty list can mean there are none or that you hold no grant on any.
Base URL¶
Every node serves the whole API, on port 7942 by default. A node serves what it holds and forwards the rest: a produce is accepted by whichever node you reach, a consume or an ack goes on to the node that owns the partition, and a topic change goes on to the cluster leader. So any node, or a load balancer over all of them, is a valid base URL.
- API paths start with
/v1. The probes/healthzand/readyzsit outside it and need no credentials. - Narad speaks plain HTTP. Anywhere off your own machine, terminate TLS in front of it and use an
https://base URL, so passwords never cross the network in the clear. narad server start --devbindshttp://127.0.0.1:7942and turns authentication off.
Credentials¶
Narad uses HTTP Basic auth on every /v1 request. Your operator creates one user per service and gives it grants: an action (produce, consume, create or admin) on topic names or prefixes such as orders-*. What each action allows is in Access model and grants; how an admin creates users is in Manage users and grants.
Pass the credentials the way your client expects them:
curl -u "$AUTH" "$NARAD/v1/topics"
client, err := narad.New(os.Getenv("NARAD_ADDR"),
narad.WithAuth("billing-service", os.Getenv("NARAD_PASS")))
export NARAD_ADDR="$NARAD"
export NARAD_USER="billing-service"
export NARAD_PASS="your-password"
narad topic ls
Missing or wrong credentials get 401 with a Basic challenge:
HTTP/1.1 401 Unauthorized
Content-Length: 36
Content-Type: application/json
Www-Authenticate: Basic realm="narad"
Date: Mon, 28 Sep 2026 19:18:44 GMT
{"error":"authentication required"}
- Valid credentials without the right grant get
403, for example{"error":"produce not allowed on this topic"}. - After five wrong passwords for one username, the node answers
429withtoo many failed authentication attemptsand allows one more attempt every 12 seconds. Fix the password rather than retrying in a loop.
Required headers¶
A POST, PUT or PATCH must carry Content-Type: application/json or application/octet-stream, or a non-empty X-Narad-Client header. Anything else gets 415. curl -d on its own sends a form content type, so it is refused:
curl -i -u "$AUTH" -X POST \
"$NARAD/v1/topics/orders/produce?key=customer-42" \
-d '{"order_id": "ord_123"}'
HTTP/1.1 415 Unsupported Media Type
Content-Length: 134
Content-Type: application/json
Date: Mon, 28 Sep 2026 19:18:45 GMT
{"error":"state-changing requests must send Content-Type: application/json or application/octet-stream, or an X-Narad-Client header"}
- The rule is a guard against cross-site requests from a browser, not a format check: a message body is stored as the same bytes whichever of the two types you send. Networking and security explains the attack it stops.
GETandDELETErequests are not checked, with one exception: a batch consume (GET .../consume?max=N) withoutX-Narad-Clientgets400. A consume reserves messages, so a batch consume needs the header that forces a preflight. Withcurl, add-H 'X-Narad-Client: curl'.- The Go SDK and the CLI set
X-Narad-Clienton every request, so they never see this415.
Limits¶
| Limit | Default | Over the limit |
|---|---|---|
| Request body | 1 MiB (1,048,576 bytes) | 413 |
| Batch produce body (v3.2.0) | 16 MiB, each payload at most 1 MiB | 413 |
| Batch produce bodies over 1 MiB being read at once, per node (v3.2.0) | 256 MiB, http.max_batch_body_bytes_in_flight |
503 with Retry-After: 1 |
| Request headers | 64 KiB, http.max_header_bytes |
431, with a plain-text body from Go's HTTP server |
| Concurrent consume requests per identity, per node | 1024, http.max_consume_in_flight_per_identity |
429 |
| Concurrent produce requests per identity, per node (v3.1.0) | off, http.max_produce_in_flight_per_identity |
429 |
Long-poll wait on a consume |
10 s, http.max_consume_wait |
clamped, with an X-Narad-Wait-Clamped response header |
| Open connections per node | 4096, http.max_connections |
extra connections wait to be accepted |
- An identity is the authenticated user, or the client IP address when authentication is off.
- A long poll counts against the consume cap for as long as it waits, so a consumer with many workers can reach it.
- Operators change these values in the configuration reference.
Next steps¶
- Manage topics: create the topic your service will use.
- Produce messages: send your first message with these credentials.
- Go SDK: produce and consume from Go: let the client handle credentials, headers and retries.