Skip to content

Commit ff3de2f

Browse files
docs: add Redis ACL configuration guide (#408)
1 parent e8d4aae commit ff3de2f

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ node_modules/
2525
.dccache
2626
.DS_Store
2727

28+
# Local Setup
29+
redis.conf
30+
users.acl
31+
postgresql.local.conf
32+
2833
# generate output
2934
dist
3035

docs/REDIS.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Redis
2+
3+
Nostream uses Redis as a cache layer, currently for rate limiting incoming requests. This document covers how to configure Redis to work with Nostream, including the recommended ACL-based setup for production environments.
4+
5+
## Overview
6+
7+
Nostream uses Redis 7.0.5 (Alpine 3.16) and connects to it via the `redis` npm package (v4.5.1). Currently, Redis is used exclusively for rate limiting — throttling incoming requests from clients to prevent abuse.
8+
9+
The Redis client is initialized as a singleton instance in `src/cache/client.ts`, meaning a single connection is shared across the entire application. This connection is wrapped by a `RedisAdapter` which exposes only the specific Redis operations
10+
Nostream needs.
11+
12+
Rate limiting is implemented using a sliding window strategy, which uses Redis sorted sets to track request timestamps. This allows Nostream to accurately enforce rate limits over a rolling time window rather than a fixed one, preventing clients from bursting requests at window boundaries.
13+
14+
## Requirements
15+
16+
- Redis 6.0 or higher (for ACL support)
17+
- Nostream ships with Redis 7.0.5 (Alpine) by default via Docker Compose
18+
19+
If you are using your own Redis instance instead of the one provided by Docker Compose, ensure it is running Redis 6.0 or higher to take advantage of the ACL configuration described in this document.
20+
21+
## Configuration
22+
23+
### Default Setup
24+
25+
By default, Nostream connects to Redis using a single password on the default user via the `--requirepass` flag. This is configured in `docker-compose.yml`:
26+
27+
```yaml
28+
command: redis-server --loglevel warning --requirepass nostr_ts_relay
29+
```
30+
31+
While this works, it grants the connecting user full access to all Redis commands which is not recommended for production environments.
32+
33+
Nostream reads the Redis connection details from the following environment variables:
34+
35+
```
36+
REDIS_URI=redis://default:nostr_ts_relay@localhost:6379
37+
38+
# or individually:
39+
REDIS_HOST=localhost
40+
REDIS_PORT=6379
41+
REDIS_USER=default
42+
REDIS_PASSWORD=nostr_ts_relay
43+
```
44+
45+
### ACL Setup (Recommended)
46+
47+
Redis ACL (Access Control List), introduced in Redis 6.0, allows you to create restricted users that can only execute specific commands. This is recommended for production environments as it follows the principle of least privilege — Nostream only gets access to the commands it actually needs.
48+
49+
#### Required Commands
50+
51+
Nostream uses the following Redis commands internally:
52+
53+
| Command | Used For |
54+
|---|---|
55+
| `EXISTS` | Checking if a rate limit key exists |
56+
| `GET` | Retrieving a cached value |
57+
| `SET` | Storing a cached value |
58+
| `ZADD` | Adding a request timestamp to the sliding window |
59+
| `ZRANGE` | Reading request timestamps from the sliding window |
60+
| `ZREMRANGEBYSCORE` | Removing expired timestamps from the sliding window |
61+
| `EXPIRE` | Setting expiry on rate limit keys |
62+
63+
#### Example Configuration
64+
65+
**Using redis.conf:**
66+
67+
Add the following to your `redis.conf`:
68+
69+
```conf
70+
aclfile /etc/redis/users.acl
71+
```
72+
73+
Then create `/etc/redis/users.acl` with the following:
74+
75+
```
76+
user nostream on >your_password ~* &* +EXISTS +GET +SET +ZADD +ZRANGE +ZREMRANGEBYSCORE +EXPIRE
77+
```
78+
79+
**Using redis-cli:**
80+
81+
You can also set the ACL rule directly via `redis-cli`:
82+
83+
```bash
84+
ACL SETUSER nostream on >your_password ~* &* +EXISTS +GET +SET +ZADD +ZRANGE +ZREMRANGEBYSCORE +EXPIRE
85+
```
86+
87+
Verify the user was created correctly:
88+
89+
```bash
90+
ACL GETUSER nostream
91+
```
92+
93+
**Updating docker-compose.yml:**
94+
95+
Replace the default `--requirepass` flag with the ACL file approach:
96+
97+
```yaml
98+
nostream-cache:
99+
image: redis:7.0.5-alpine3.16
100+
container_name: nostream-cache
101+
volumes:
102+
- cache:/data
103+
- ./redis.conf:/usr/local/etc/redis/redis.conf
104+
- ./users.acl:/etc/redis/users.acl
105+
command: redis-server /usr/local/etc/redis/redis.conf
106+
networks:
107+
default:
108+
restart: always
109+
healthcheck:
110+
test: [ "CMD", "redis-cli", "-u", "redis://nostream:your_password@localhost:6379", "ping" ]
111+
interval: 1s
112+
timeout: 5s
113+
retries: 5
114+
```
115+
116+
Then update your `.env` file:
117+
118+
```
119+
REDIS_URI=redis://nostream:your_password@localhost:6379
120+
```
121+
122+
## Troubleshooting
123+
124+
**NOAUTH Authentication required**
125+
126+
Redis is requiring authentication but no password was provided. Ensure your `REDIS_URI` or `REDIS_PASSWORD` environment variables are set correctly.
127+
128+
**WRONGPASS invalid username-password pair**
129+
130+
The username or password provided is incorrect. Double check your `REDIS_USER` and `REDIS_PASSWORD` environment variables match what was configured in your ACL setup.
131+
132+
**NOPERM this user has no permissions to run the command**
133+
134+
The Redis user does not have permission to run a specific command. Ensure all 7 required commands are granted in your ACL rule:
135+
136+
```
137+
+EXISTS +GET +SET +ZADD +ZRANGE +ZREMRANGEBYSCORE +EXPIRE
138+
```
139+
140+
**Connection refused (ECONNREFUSED)**
141+
142+
Redis is not running or is not reachable at the configured host and port. Verify:
143+
- Redis is running (`docker ps` if using Docker)
144+
- `REDIS_HOST` and `REDIS_PORT` are correct
145+
- No firewall is blocking the connection

0 commit comments

Comments
 (0)