Skip to content
This repository was archived by the owner on Mar 20, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
ab4339b
Implement sliding window ratelimiting
davfsa Jun 21, 2025
74368fa
Raise ctx error when acquiring
davfsa Jun 22, 2025
be1b21b
Cleanup logs
davfsa Jun 22, 2025
8ab0a97
Add config option to disable concurrent requests
davfsa Jun 22, 2025
61da1cd
Improve bucket logging
davfsa Jun 22, 2025
6ee19fe
Improve timings
davfsa Jun 22, 2025
9ea4369
Fix handling of global ratelimits
davfsa Jun 22, 2025
a7b162f
Finish off
davfsa Jun 22, 2025
bea4001
Improve err log
davfsa Jun 22, 2025
b17e798
Update documentation on new option
davfsa Jun 22, 2025
bde8d18
Use mutex instead of RWMutex for queue channels
davfsa Jun 22, 2025
e597d93
Adjust lock when doing a bucket update
davfsa Jun 22, 2025
570b8c2
Revert incorrect log change
davfsa Jun 22, 2025
f899d38
Remove healing log
davfsa Jun 22, 2025
9c6bc9b
Improve parsing of headers
davfsa Jun 23, 2025
896c297
Add some logging information
davfsa Jun 24, 2025
6f7f69b
Fix incorrect negation
davfsa Jun 24, 2025
98faf76
Use identifier instead of user ID
davfsa Jun 24, 2025
d544584
Remove extra logging
davfsa Jun 24, 2025
4624c7e
Get scope from correct header
davfsa Jun 24, 2025
04bf222
Lower remaining too if limit is lowered
davfsa Jun 24, 2025
e50b39d
Remove unused argument
davfsa Jun 25, 2025
0699f16
Improve ratelimit handling when starting already hitting ratelimits o…
davfsa Jul 9, 2025
7e4310f
Re-add case for slide period differing too much with adjusted limits
davfsa Jul 9, 2025
45afabd
Update logging wording
davfsa Jul 9, 2025
944c2d6
fix: add support for routes that have a fixed window
davfsa Jul 10, 2025
869bbc3
feat: improve detection of fixed window ratelimits
davfsa Aug 24, 2025
1cf99a6
feat: improve detection of fixed buckets
davfsa Aug 25, 2025
1471b8f
fix: typo
davfsa Aug 25, 2025
4b3a716
feat: greatly improve ratelimit avoidance by tracking in transit requ…
davfsa Sep 3, 2025
e5207cc
chore: drop logging level
davfsa Sep 3, 2025
8babac1
chore: readd self-healing logic
davfsa Sep 3, 2025
287eab3
chore: bump go version
davfsa Sep 3, 2025
2ca5d87
chore: update bucket name when updating ratelimiter
davfsa Sep 4, 2025
04fb1e4
chore: improve struct alignment
davfsa Sep 15, 2025
6d64c9d
fix: fix issues from review
davfsa Sep 16, 2025
69cc2d2
feat: add anti-deadlock measures
davfsa Sep 16, 2025
75e7600
fix: add missing return
davfsa Sep 16, 2025
721b393
chore: do not shadow err variable
davfsa Sep 16, 2025
5c3c0bb
feat: improve global locked until sleep
davfsa Sep 16, 2025
63c7f4e
chore: cleanup locks and add some documentation
davfsa Sep 16, 2025
9ac55a5
fix: add missing unlock to prevent a deadlock
davfsa Sep 19, 2025
5a33b04
fix: release bucket before erroring when context is done
davfsa Sep 19, 2025
e55351c
fix: add more precise lock section
davfsa Sep 19, 2025
49cb455
chore: make ratelimiter never nil
davfsa Oct 1, 2025
2eca7d1
chore: microoptimization
davfsa Oct 1, 2025
7aac3a6
Apply suggestion from @davfsa
davfsa Oct 7, 2025
62e4029
fix: improve logic to detect fixed buckets
davfsa Dec 13, 2025
de5eca4
fix: decrease sliding precision
davfsa Dec 13, 2025
d453f4d
fix: proper order of logic
davfsa Dec 13, 2025
649bfb0
feat: improve detection of fixed buckets
davfsa Dec 13, 2025
4b66b81
Revert "feat: improve detection of fixed buckets"
davfsa Dec 14, 2025
af4b41e
chore: treat bucket as fixed by default
davfsa Dec 15, 2025
415794a
chore: only update bucket type if not first ratelimit information
davfsa Dec 15, 2025
2485b95
feat: improve 429 handling
davfsa Jan 2, 2026
4aa5601
chore: drop logging level
davfsa Jan 2, 2026
e764551
chore: readd math.Ceil call
davfsa Jan 3, 2026
6dec0b6
fix: only re-initialize bucket if limit changed
davfsa Jan 3, 2026
10a68af
chore: remove redundant parenthesis
davfsa Jan 3, 2026
81887d5
chore: cleanup logging
davfsa Jan 3, 2026
10a554b
chore: re-add self healing logic
davfsa Jan 3, 2026
f6cd40d
fix: make bucket following less strict for insanely fast connections
davfsa Jan 3, 2026
d79fade
chore: remove random spaces
davfsa Jan 3, 2026
0d7c14e
chore: remove incorrect optimistic bucket path
davfsa Jan 3, 2026
cadbd6f
chore: remove sliding prevention if out of sync
davfsa Jan 3, 2026
b4da566
chore: re-add after or equal check
davfsa Jan 3, 2026
c6b4f8a
chore: speedup arm docker builds
davfsa Jan 4, 2026
2e461d5
chore: also build docker images on pushes to branches
davfsa Jan 4, 2026
6a1571d
fix: go build instead of go install
davfsa Jan 4, 2026
7e94515
chore: re-disable ci in testing branches
davfsa Jan 4, 2026
70e506a
fix: properly calculate message delete bucket path
davfsa Jan 9, 2026
d27c34b
feat: add more logging to buckets
davfsa Jan 9, 2026
0ee3b4f
fix: properly handle route scoped ratelimit hits
davfsa Jan 9, 2026
0ab9d20
fix: drop logger back to debug
davfsa Jan 9, 2026
928030d
feat: multi-bucket handling (#1)
davfsa Jan 9, 2026
59a9452
test: allow sliding window even if out of sync
davfsa Jan 9, 2026
32a510c
fix: use proper parameter for buckets sweep
davfsa Jan 9, 2026
99ece9e
fix: remove test code
davfsa Jan 10, 2026
99a3109
chore: account for network delay
davfsa Jan 10, 2026
28fe761
chore: cleanup
davfsa Jan 10, 2026
f4eb642
chore: remove unused field
davfsa Jan 10, 2026
0ebe4e1
fix: remove incorrect assumption of major channels bucket
davfsa Jan 10, 2026
e45bbdb
feat: improve logging
davfsa Jan 10, 2026
6cd4471
feat: enable concurrent requests by default
davfsa Jan 10, 2026
5fa606b
feat: improve handling of buckets with top level resources and multip…
davfsa Jan 11, 2026
f3b61b9
fix: properly group up CF per route ban
davfsa Jan 11, 2026
2e58221
chore: group up all interaction callbacks into a single queue
davfsa Jan 11, 2026
522a240
feat: improve logging
davfsa Jan 11, 2026
b31cd61
fix: avoid spamming metrics with activity-instance IDs
davfsa Jan 11, 2026
7e9e4f0
fix: do not distribute interaction callbacks over cluster
davfsa Jan 11, 2026
6bae75f
fix: do not merge callback endpoints
davfsa Jan 12, 2026
f25cb13
chore: bump deps
davfsa Jan 12, 2026
dcce6bc
fix: detection of fixed buckets
davfsa Jan 13, 2026
48a6f9d
chore: small optimization
davfsa Jan 14, 2026
8491aba
chore: revert some old behavior and prevent period increase
davfsa Jan 14, 2026
057489c
chore: prevent resetAt and increaseAt from sliding backwards
davfsa Jan 15, 2026
229af11
fix: prevent path traversal at proxy level
davfsa Jan 29, 2026
25ae3ef
chore: use 422 instead of 400 for path traversal
davfsa Jan 29, 2026
6603673
fix: increase inactive bucket sweep time + properly calculate bucket …
davfsa Feb 1, 2026
ed21f1c
fix: do not drop old ratelimit information
davfsa Feb 1, 2026
bbe33df
chore: cleanup bucket last updated at
davfsa Feb 1, 2026
48a08fd
feat: add nanosecond precision to log messages
davfsa Feb 1, 2026
5467daa
fix: avoid immediately collecting newly created buckets
davfsa Feb 1, 2026
62a101d
fix: reduce number of 429s
davfsa Feb 8, 2026
1a5ae4c
fix: revert incorrect change
davfsa Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ Default: false

In the future, this will be the only possible behavior.

##### ALLOW_CONCURRENT_REQUESTS
Allows nirn to perform concurrent requests to Discord endpoints, instead of one at a time per queue. This might have the unintended side effect of a small increase in 429's if Discord updates buckets on the fly.

If you do not care about throughput or do not make a lot of requests to the same endpoint that might take Discord a while to answer, then it would be fine to keep this off.

Default: true

## Unstable env vars
Collection of env vars that may be removed at any time, mainly used for Discord introducing new behaviour on their edge api versions

Expand Down
12 changes: 8 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
FROM golang:alpine as app-builder
FROM --platform=$BUILDPLATFORM golang:alpine as app-builder
WORKDIR /go/src/app
COPY . .
RUN CGO_ENABLED=0 go install -ldflags '-extldflags "-static"' -tags timetzdata -buildvcs=false

ARG TARGETOS
ARG TARGETARCH

RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -ldflags '-extldflags "-static"' -tags timetzdata -buildvcs=false

FROM scratch
COPY --from=app-builder /go/bin/nirn-proxy /nirn-proxy
COPY --from=app-builder /go/src/app/nirn-proxy /nirn-proxy
# the tls certificates:
# NB: this pulls directly from the upstream image, which already has ca-certificates:
COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 9000
EXPOSE 8080
ENTRYPOINT ["/nirn-proxy"]
ENTRYPOINT ["/nirn-proxy"]
57 changes: 29 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,27 @@ The proxy sits between the client and discord. Instead of pointing to discord.co

Configuration options are

| Variable | Value | Default |
|-----------------|---------------------------------------------|---------|
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
| PORT | number | 8080 |
| METRICS_PORT | number | 9000 |
| ENABLE_METRICS | boolean | true |
| ENABLE_PPROF | boolean | false |
| BUFFER_SIZE | number | 50 |
| OUTBOUND_IP | string | "" |
| BIND_IP | string | 0.0.0.0 |
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
| CLUSTER_PORT | number | 7946 |
| CLUSTER_MEMBERS | string list (comma separated) | "" |
| CLUSTER_DNS | string | "" |
| MAX_BEARER_COUNT| number | 1024 |
| DISABLE_HTTP_2 | bool | true |
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
| DISABLE_GLOBAL_RATELIMIT_DETECTION | boolean | false |

Information on each config var can be found [here](https://github.com/germanoeich/nirn-proxy/blob/main/CONFIG.md)
| Variable | Value | Default |
|------------------------------------|-----------------------------------------------|---------|
| LOG_LEVEL | panic, fatal, error, warn, info, debug, trace | info |
| PORT | number | 8080 |
| METRICS_PORT | number | 9000 |
| ENABLE_METRICS | boolean | true |
| ENABLE_PPROF | boolean | false |
| BUFFER_SIZE | number | 50 |
| OUTBOUND_IP | string | "" |
| BIND_IP | string | 0.0.0.0 |
| REQUEST_TIMEOUT | number (milliseconds) | 5000 |
| CLUSTER_PORT | number | 7946 |
| CLUSTER_MEMBERS | string list (comma separated) | "" |
| CLUSTER_DNS | string | "" |
| MAX_BEARER_COUNT | number | 1024 |
| DISABLE_HTTP_2 | bool | true |
| BOT_RATELIMIT_OVERRIDES | string list (comma separated) | "" |
| DISABLE_GLOBAL_RATELIMIT_DETECTION | boolean | false |
| ALLOW_CONCURRENT_REQUESTS | boolean | true |

Information on each config var can be found [here](CONFIG.md)

.env files are loaded if present

Expand Down Expand Up @@ -101,14 +102,14 @@ This will vary depending on your usage, how many unique routes you see, etc. For

### Metrics / Health

| Key | Labels | Description |
|------------------------------------|----------------------------------------|------------------------------------------------------------|
|nirn_proxy_error | none | Counter for errors |
|nirn_proxy_requests | method, status, route, clientId | Histogram that keeps track of all request metrics |
|nirn_proxy_open_connections | route, method | Gauge for open client connections with the proxy |
|nirn_proxy_requests_routed_sent | none | Counter for requests routed to other nodes |
|nirn_proxy_requests_routed_received | none | Counter for requests received from other nodes |
|nirn_proxy_requests_routed_error | none | Counter for requests routed that failed |
| Key | Labels | Description |
|-------------------------------------|---------------------------------|---------------------------------------------------|
| nirn_proxy_error | none | Counter for errors |
| nirn_proxy_requests | method, status, route, clientId | Histogram that keeps track of all request metrics |
| nirn_proxy_open_connections | route, method | Gauge for open client connections with the proxy |
| nirn_proxy_requests_routed_sent | none | Counter for requests routed to other nodes |
| nirn_proxy_requests_routed_received | none | Counter for requests received from other nodes |
| nirn_proxy_requests_routed_error | none | Counter for requests routed that failed |

Note: 429s can produce two status: 429 Too Many Requests or 429 Shared. The latter is only produced for requests that return with the x-ratelimit-scope header set to "shared", which means they don't count towards the cloudflare firewall limit and thus should not be used for alerts, etc.

Expand Down
54 changes: 28 additions & 26 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
module github.com/germanoeich/nirn-proxy

go 1.18
go 1.25

require (
github.com/Clever/leakybucket v1.2.0
github.com/hashicorp/golang-lru v0.5.4
github.com/hashicorp/memberlist v0.3.1
github.com/joho/godotenv v1.4.0
github.com/prometheus/client_golang v1.11.0
github.com/sirupsen/logrus v1.8.1
github.com/Clever/leakybucket v1.3.0
github.com/hashicorp/golang-lru v1.0.2
github.com/hashicorp/memberlist v0.5.4
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.23.2
github.com/sirupsen/logrus v1.9.3
)

require (
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-msgpack v0.5.3 // indirect
github.com/hashicorp/go-multierror v1.0.0 // indirect
github.com/hashicorp/go-sockaddr v1.0.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.26 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
github.com/miekg/dns v1.1.70 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.26.0-rc.1 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/mod v0.32.0 // indirect
golang.org/x/net v0.49.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/tools v0.41.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
)
Loading