v2.31.0-dev17 bd898f0

rate-limiting.md RawHistoryFolder

Rate Limiting

Protect your Gordon instance from abuse with configurable rate limiting.

Overview

Rate limiting prevents:

  • Brute force attacks on authentication endpoints
  • Denial of Service (DoS) from excessive requests
  • Resource exhaustion from misbehaving clients
  • Registry abuse (e.g., automated scraping)

Rate limiting applies to:

  • Registry API (/v2/*) — Global + per-IP limits
  • Auth endpoints (/auth/*) — Same limiter as registry (Global + per-IP)
  • Admin API (/admin/*) — Global + per-IP limits (separate limiter instances)

Quick Start

Rate limiting is enabled by default with sensible defaults:

[api.rate_limit]
enabled = true
global_rps = 500
per_ip_rps = 50
burst = 100
trusted_proxies = []

Important: If you run Gordon behind a front proxy (Cloudflare, nginx, etc.), configure trusted_proxies so rate limiting sees real client IPs instead of your proxy's IP.

Configuration

[api.rate_limit]
enabled = true
global_rps = 500
per_ip_rps = 50
burst = 100
trusted_proxies = ["127.0.0.1", "10.0.0.0/8"]

Options

Option Type Default Description
enabled bool true Enable or disable rate limiting
global_rps float 500 Maximum requests per second across all clients combined
per_ip_rps float 50 Maximum requests per second per client IP address
burst int 100 Maximum burst size (requests allowed to exceed the rate temporarily)
trusted_proxies []string [] IP addresses or CIDR ranges trusted to set X-Forwarded-For

Architecture

Gordon uses two separate rate limiters:

┌─────────────────────────────────────────────────────────┐
│                     Incoming Request                     │
└─────────────────────────────────────────────────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│  /v2/* routes │   │ /auth/* routes│   │ /admin/* routes│
└───────────────┘   └───────────────┘   └───────────────┘
        │                   │                   │
        ▼                   ▼                   ▼
┌─────────────────────────────────┐   ┌─────────────────────────┐
│   Registry Rate Limiter          │   │   Admin Rate Limiter    │
│  ┌───────────────────┐           │   │  ┌───────────────────┐  │
│  │   Global Limiter   │          │   │  │   Global Limiter   │  │
│  │   (global_rps)     │          │   │  │   (global_rps)     │  │
│  └───────────────────┘           │   │  └───────────────────┘  │
│  ┌───────────────────┐           │   │  ┌───────────────────┐  │
│  │  Per-IP Limiters   │          │   │  │  Per-IP Limiters   │  │
│  │   (per_ip_rps)     │          │   │  │   (per_ip_rps)     │  │
│  └───────────────────┘           │   │  └───────────────────┘  │
└─────────────────────────────────┘   └─────────────────────────┘

Registry Limiter (for /v2/* and /auth/*):

  • Checks global limit first (all clients combined)
  • Then checks per-IP limit
  • Both must pass for the request to proceed
  • Auth endpoints share this limiter to prevent brute force attacks

Admin Limiter (for /admin/*):

  • Separate global + per-IP limiters (independent instances)
  • Isolates admin traffic from registry traffic
  • Prevents CI/CD bursts from affecting admin access

Option Details

enabled

Master switch for rate limiting. When false, all requests are allowed without throttling.

[api.rate_limit]
enabled = false  # Disable rate limiting (not recommended for production)

global_rps

The maximum number of requests per second that Gordon will accept from all clients combined. This is a hard cap that protects against distributed attacks where many IPs each send moderate traffic.

[api.rate_limit]
global_rps = 500  # 500 requests/second total

Sizing guidance:

  • Small deployments (1-10 apps): 100-500 RPS
  • Medium deployments (10-50 apps): 500-2000 RPS
  • Large deployments (50+ apps): 2000-10000 RPS

Consider your CI/CD patterns—parallel builds pushing multiple images can generate bursts of traffic.

per_ip_rps

The maximum requests per second allowed from a single client IP. This prevents any single client from monopolizing Gordon's resources.

[api.rate_limit]
per_ip_rps = 50  # 50 requests/second per IP

Sizing guidance:

  • Normal Docker operations (pull, push) rarely exceed 10-20 RPS
  • CI/CD pipelines with parallel jobs may need 50-100 RPS
  • Automated tooling (vulnerability scanners, etc.) may need higher limits

burst

The token bucket burst size. Allows clients to temporarily exceed the rate limit for short bursts, which is useful for legitimate traffic patterns like:

  • Initial connection setup (multiple manifest/blob requests)
  • Parallel layer downloads
  • Health check bursts
[api.rate_limit]
burst = 100  # Allow bursts of up to 100 requests

A burst of 100 with per_ip_rps = 50 means a client can send 100 requests instantly, then must wait ~2 seconds for the bucket to refill before another burst.

trusted_proxies

Critical for correct IP detection in production.

Gordon is a reverse proxy that routes requests to your containers. In production you can either terminate TLS directly in Gordon (via its internal CA on server.tls_port) or place it behind a TLS-terminating proxy like Cloudflare, nginx, or a load balancer.

Internet → [Cloudflare/nginx] → Gordon → Containers
              (HTTPS)          (HTTP)

In this setup, Gordon sees all connections coming from your front proxy's IP, not the real clients. The proxy sets X-Forwarded-For to communicate the original client IP.

The problem: X-Forwarded-For can be spoofed. If Gordon trusted this header unconditionally, attackers could:

  • Bypass per-IP rate limits by sending fake IPs
  • Create unlimited rate limiter entries (memory exhaustion)

The solution: Gordon only honors X-Forwarded-For from IPs listed in trusted_proxies. For all other connections, it uses the direct connection IP.

[api.rate_limit]
trusted_proxies = ["127.0.0.1", "10.0.0.0/8"]

Supported formats:

  • Single IP: "192.168.1.1"
  • CIDR range: "10.0.0.0/8"
  • IPv6: "::1", "fd00::/8"

If trusted_proxies is empty or misconfigured, all requests appear to come from your proxy's IP, making per-IP rate limiting ineffective (all clients share one limit).

Deployment Examples

Cloudflare provides free TLS termination and DDoS protection. This is the recommended setup for production.

Cloudflare publishes their IP ranges at https://www.cloudflare.com/ips/

[api.rate_limit]
# Cloudflare IPs (verified 2026-01-18 - check cloudflare.com/ips for updates)
trusted_proxies = [
    # IPv4
    "173.245.48.0/20",
    "103.21.244.0/22",
    "103.22.200.0/22",
    "103.31.4.0/22",
    "141.101.64.0/18",
    "108.162.192.0/18",
    "190.93.240.0/20",
    "188.114.96.0/20",
    "197.234.240.0/22",
    "198.41.128.0/17",
    "162.158.0.0/15",
    "104.16.0.0/13",
    "104.24.0.0/14",
    "172.64.0.0/13",
    "131.0.72.0/22",
    # IPv6
    "2400:cb00::/32",
    "2606:4700::/32",
    "2803:f800::/32",
    "2405:b500::/32",
    "2405:8100::/32",
    "2a06:98c0::/29",
    "2c0f:f248::/32",
]

Note: Cloudflare IPs may change. Verify at https://www.cloudflare.com/ips/ periodically.

Local Development (No TLS)

For local development only, Gordon can be accessed directly without a proxy:

[api.rate_limit]
trusted_proxies = []  # Empty = use RemoteAddr directly

Warning: Never expose Gordon directly to the internet without TLS. Use this configuration only for local development.

Behind nginx (Same Host)

When nginx runs on the same machine as Gordon:

[api.rate_limit]
trusted_proxies = ["127.0.0.1", "::1"]

Behind nginx (Separate Host)

When nginx runs on a different server:

[api.rate_limit]
trusted_proxies = ["10.0.1.5"]  # nginx server IP

Behind AWS Application Load Balancer (ALB)

ALB terminates TLS and forwards requests to your instances. Unlike Cloudflare, ALB IPs are dynamic and change as AWS scales the load balancer. You need to trust your VPC's private IP range (CIDR) since ALB connects from within your VPC.

[api.rate_limit]
trusted_proxies = ["10.0.0.0/16"]  # Your VPC CIDR

For detailed setup instructions, see the AWS ALB Guide.

Behind Kubernetes Ingress

When running Gordon in Kubernetes with an ingress controller (nginx-ingress, Traefik, etc.), you need to trust the pod network CIDR. The ingress controller must also be configured to forward client IPs via X-Forwarded-For.

[api.rate_limit]
trusted_proxies = ["10.244.0.0/16"]  # Your pod network CIDR

For detailed setup instructions, see the Kubernetes Ingress Guide.

Rate Limit Response

When a client exceeds the rate limit, Gordon returns:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Docker-Distribution-API-Version: registry/2.0
Retry-After: 1

{
  "errors": [
    {
      "code": "TOOMANYREQUESTS",
      "message": "rate limit exceeded"
    }
  ]
}

The Retry-After header indicates when the client can retry (in seconds).

Monitoring

Rate-limited requests are logged at info level:

level=info msg="rate limit exceeded" client_ip=203.0.113.50 path=/v2/myapp/manifests/latest

To see rate limiting in action, enable debug logging:

[logging]
level = "debug"

Security Considerations

IP Spoofing Prevention

If trusted_proxies is misconfigured, attackers can:

  1. Send requests with fake X-Forwarded-For: 1.2.3.4
  2. Each request appears to come from a different IP
  3. Per-IP rate limiting becomes ineffective
  4. Attackers create unbounded limiter entries (memory exhaustion)

Always verify your proxy configuration by checking logs to ensure client IPs are detected correctly.

Endpoint Coverage

All authenticated endpoints are protected:

Endpoint Rate Limiter Limits Applied
/v2/* (registry) Registry limiter Global RPS + Per-IP RPS
/auth/* (authentication) Registry limiter Global RPS + Per-IP RPS
/admin/* (management) Admin limiter Global RPS + Per-IP RPS

The auth endpoints share the registry limiter, preventing brute force attacks on credentials. The admin API uses separate limiter instances to avoid CI/CD traffic affecting admin access.

[api.rate_limit]
enabled = true
global_rps = 1000      # Adjust based on your traffic
per_ip_rps = 30        # Stricter per-IP limit
burst = 50             # Smaller burst for tighter control
trusted_proxies = []   # Configure based on your proxy setup

Troubleshooting

"I'm getting rate limited but traffic is low"

  1. Check if trusted_proxies is configured correctly
  2. All traffic may appear to come from your proxy's IP
  3. Add your proxy to trusted_proxies to see real client IPs

"Rate limiting doesn't seem to work"

  1. Verify enabled = true
  2. Check that requests go through Gordon (not cached by CDN)
  3. Ensure global_rps and per_ip_rps are set appropriately

"I need to whitelist certain IPs"

Rate limiting doesn't support IP whitelisting. Consider:

  • Increasing per_ip_rps for legitimate high-traffic clients
  • Using a reverse proxy with its own rate limiting rules
  • Implementing IP-based access control at the network level
source
1# Rate Limiting
2
3Protect your Gordon instance from abuse with configurable rate limiting.
4
5## Overview
6
7Rate limiting prevents:
8- **Brute force attacks** on authentication endpoints
9- **Denial of Service (DoS)** from excessive requests
10- **Resource exhaustion** from misbehaving clients
11- **Registry abuse** (e.g., automated scraping)
12
13Rate limiting applies to:
14- **Registry API** (`/v2/*`) — Global + per-IP limits
15- **Auth endpoints** (`/auth/*`) — Same limiter as registry (Global + per-IP)
16- **Admin API** (`/admin/*`) — Global + per-IP limits (separate limiter instances)
17
18## Quick Start
19
20Rate limiting is **enabled by default** with sensible defaults:
21
22```toml
23[api.rate_limit]
24enabled = true
25global_rps = 500
26per_ip_rps = 50
27burst = 100
28trusted_proxies = []
29```
30
31> **Important:** If you run Gordon behind a front proxy (Cloudflare, nginx, etc.), configure `trusted_proxies` so rate limiting sees real client IPs instead of your proxy's IP.
32
33## Configuration
34
35```toml
36[api.rate_limit]
37enabled = true
38global_rps = 500
39per_ip_rps = 50
40burst = 100
41trusted_proxies = ["127.0.0.1", "10.0.0.0/8"]
42```
43
44## Options
45
46| Option | Type | Default | Description |
47|--------|------|---------|-------------|
48| `enabled` | bool | `true` | Enable or disable rate limiting |
49| `global_rps` | float | `500` | Maximum requests per second across all clients combined |
50| `per_ip_rps` | float | `50` | Maximum requests per second per client IP address |
51| `burst` | int | `100` | Maximum burst size (requests allowed to exceed the rate temporarily) |
52| `trusted_proxies` | []string | `[]` | IP addresses or CIDR ranges trusted to set `X-Forwarded-For` |
53
54## Architecture
55
56Gordon uses two separate rate limiters:
57
58```
59┌─────────────────────────────────────────────────────────┐
60│                     Incoming Request                     │
61└─────────────────────────────────────────────────────────┘
62                            │
63        ┌───────────────────┼───────────────────┐
64        ▼                   ▼                   ▼
65┌───────────────┐   ┌───────────────┐   ┌───────────────┐
66│  /v2/* routes │   │ /auth/* routes│   │ /admin/* routes│
67└───────────────┘   └───────────────┘   └───────────────┘
68        │                   │                   │
69        ▼                   ▼                   ▼
70┌─────────────────────────────────┐   ┌─────────────────────────┐
71│   Registry Rate Limiter          │   │   Admin Rate Limiter    │
72│  ┌───────────────────┐           │   │  ┌───────────────────┐  │
73│  │   Global Limiter   │          │   │  │   Global Limiter   │  │
74│  │   (global_rps)     │          │   │  │   (global_rps)     │  │
75│  └───────────────────┘           │   │  └───────────────────┘  │
76│  ┌───────────────────┐           │   │  ┌───────────────────┐  │
77│  │  Per-IP Limiters   │          │   │  │  Per-IP Limiters   │  │
78│  │   (per_ip_rps)     │          │   │  │   (per_ip_rps)     │  │
79│  └───────────────────┘           │   │  └───────────────────┘  │
80└─────────────────────────────────┘   └─────────────────────────┘
81```
82
83**Registry Limiter** (for `/v2/*` and `/auth/*`):
84- Checks global limit first (all clients combined)
85- Then checks per-IP limit
86- Both must pass for the request to proceed
87- Auth endpoints share this limiter to prevent brute force attacks
88
89**Admin Limiter** (for `/admin/*`):
90- Separate global + per-IP limiters (independent instances)
91- Isolates admin traffic from registry traffic
92- Prevents CI/CD bursts from affecting admin access
93
94## Option Details
95
96### `enabled`
97
98Master switch for rate limiting. When `false`, all requests are allowed without throttling.
99
100```toml
101[api.rate_limit]
102enabled = false  # Disable rate limiting (not recommended for production)
103```
104
105### `global_rps`
106
107The maximum number of requests per second that Gordon will accept from **all clients combined**. This is a hard cap that protects against distributed attacks where many IPs each send moderate traffic.
108
109```toml
110[api.rate_limit]
111global_rps = 500  # 500 requests/second total
112```
113
114**Sizing guidance:**
115- **Small deployments** (1-10 apps): `100-500` RPS
116- **Medium deployments** (10-50 apps): `500-2000` RPS
117- **Large deployments** (50+ apps): `2000-10000` RPS
118
119Consider your CI/CD patterns—parallel builds pushing multiple images can generate bursts of traffic.
120
121### `per_ip_rps`
122
123The maximum requests per second allowed from a **single client IP**. This prevents any single client from monopolizing Gordon's resources.
124
125```toml
126[api.rate_limit]
127per_ip_rps = 50  # 50 requests/second per IP
128```
129
130**Sizing guidance:**
131- Normal Docker operations (pull, push) rarely exceed 10-20 RPS
132- CI/CD pipelines with parallel jobs may need 50-100 RPS
133- Automated tooling (vulnerability scanners, etc.) may need higher limits
134
135### `burst`
136
137The token bucket burst size. Allows clients to temporarily exceed the rate limit for short bursts, which is useful for legitimate traffic patterns like:
138- Initial connection setup (multiple manifest/blob requests)
139- Parallel layer downloads
140- Health check bursts
141
142```toml
143[api.rate_limit]
144burst = 100  # Allow bursts of up to 100 requests
145```
146
147A burst of `100` with `per_ip_rps = 50` means a client can send 100 requests instantly, then must wait ~2 seconds for the bucket to refill before another burst.
148
149### `trusted_proxies`
150
151**Critical for correct IP detection in production.**
152
153Gordon is a reverse proxy that routes requests to your containers. In production you can either terminate TLS directly in Gordon (via its internal CA on `server.tls_port`) or place it behind a TLS-terminating proxy like Cloudflare, nginx, or a load balancer.
154
155```
156Internet → [Cloudflare/nginx] → Gordon → Containers
157              (HTTPS)          (HTTP)
158```
159
160In this setup, Gordon sees all connections coming from your front proxy's IP, not the real clients. The proxy sets `X-Forwarded-For` to communicate the original client IP.
161
162**The problem:** `X-Forwarded-For` can be spoofed. If Gordon trusted this header unconditionally, attackers could:
163- Bypass per-IP rate limits by sending fake IPs
164- Create unlimited rate limiter entries (memory exhaustion)
165
166**The solution:** Gordon only honors `X-Forwarded-For` from IPs listed in `trusted_proxies`. For all other connections, it uses the direct connection IP.
167
168```toml
169[api.rate_limit]
170trusted_proxies = ["127.0.0.1", "10.0.0.0/8"]
171```
172
173**Supported formats:**
174- Single IP: `"192.168.1.1"`
175- CIDR range: `"10.0.0.0/8"`
176- IPv6: `"::1"`, `"fd00::/8"`
177
178> **If `trusted_proxies` is empty or misconfigured**, all requests appear to come from your proxy's IP, making per-IP rate limiting ineffective (all clients share one limit).
179
180## Deployment Examples
181
182### Cloudflare (Recommended)
183
184Cloudflare provides free TLS termination and DDoS protection. This is the recommended setup for production.
185
186Cloudflare publishes their IP ranges at https://www.cloudflare.com/ips/
187
188```toml
189[api.rate_limit]
190# Cloudflare IPs (verified 2026-01-18 - check cloudflare.com/ips for updates)
191trusted_proxies = [
192    # IPv4
193    "173.245.48.0/20",
194    "103.21.244.0/22",
195    "103.22.200.0/22",
196    "103.31.4.0/22",
197    "141.101.64.0/18",
198    "108.162.192.0/18",
199    "190.93.240.0/20",
200    "188.114.96.0/20",
201    "197.234.240.0/22",
202    "198.41.128.0/17",
203    "162.158.0.0/15",
204    "104.16.0.0/13",
205    "104.24.0.0/14",
206    "172.64.0.0/13",
207    "131.0.72.0/22",
208    # IPv6
209    "2400:cb00::/32",
210    "2606:4700::/32",
211    "2803:f800::/32",
212    "2405:b500::/32",
213    "2405:8100::/32",
214    "2a06:98c0::/29",
215    "2c0f:f248::/32",
216]
217```
218
219> **Note:** Cloudflare IPs may change. Verify at https://www.cloudflare.com/ips/ periodically.
220
221### Local Development (No TLS)
222
223For local development only, Gordon can be accessed directly without a proxy:
224
225```toml
226[api.rate_limit]
227trusted_proxies = []  # Empty = use RemoteAddr directly
228```
229
230> **Warning:** Never expose Gordon directly to the internet without TLS. Use this configuration only for local development.
231
232### Behind nginx (Same Host)
233
234When nginx runs on the same machine as Gordon:
235
236```toml
237[api.rate_limit]
238trusted_proxies = ["127.0.0.1", "::1"]
239```
240
241### Behind nginx (Separate Host)
242
243When nginx runs on a different server:
244
245```toml
246[api.rate_limit]
247trusted_proxies = ["10.0.1.5"]  # nginx server IP
248```
249
250### Behind AWS Application Load Balancer (ALB)
251
252ALB terminates TLS and forwards requests to your instances. Unlike Cloudflare, ALB IPs are dynamic and change as AWS scales the load balancer. You need to trust your VPC's private IP range (CIDR) since ALB connects from within your VPC.
253
254```toml
255[api.rate_limit]
256trusted_proxies = ["10.0.0.0/16"]  # Your VPC CIDR
257```
258
259For detailed setup instructions, see the [AWS ALB Guide](../../wiki/guides/aws-alb.md).
260
261### Behind Kubernetes Ingress
262
263When running Gordon in Kubernetes with an ingress controller (nginx-ingress, Traefik, etc.), you need to trust the pod network CIDR. The ingress controller must also be configured to forward client IPs via `X-Forwarded-For`.
264
265```toml
266[api.rate_limit]
267trusted_proxies = ["10.244.0.0/16"]  # Your pod network CIDR
268```
269
270For detailed setup instructions, see the [Kubernetes Ingress Guide](../../wiki/guides/kubernetes-ingress.md).
271
272## Rate Limit Response
273
274When a client exceeds the rate limit, Gordon returns:
275
276```
277HTTP/1.1 429 Too Many Requests
278Content-Type: application/json
279Docker-Distribution-API-Version: registry/2.0
280Retry-After: 1
281
282{
283  "errors": [
284    {
285      "code": "TOOMANYREQUESTS",
286      "message": "rate limit exceeded"
287    }
288  ]
289}
290```
291
292The `Retry-After` header indicates when the client can retry (in seconds).
293
294## Monitoring
295
296Rate-limited requests are logged at `info` level:
297
298```
299level=info msg="rate limit exceeded" client_ip=203.0.113.50 path=/v2/myapp/manifests/latest
300```
301
302To see rate limiting in action, enable debug logging:
303
304```toml
305[logging]
306level = "debug"
307```
308
309## Security Considerations
310
311### IP Spoofing Prevention
312
313If `trusted_proxies` is misconfigured, attackers can:
3141. Send requests with fake `X-Forwarded-For: 1.2.3.4`
3152. Each request appears to come from a different IP
3163. Per-IP rate limiting becomes ineffective
3174. Attackers create unbounded limiter entries (memory exhaustion)
318
319**Always verify your proxy configuration** by checking logs to ensure client IPs are detected correctly.
320
321### Endpoint Coverage
322
323All authenticated endpoints are protected:
324
325| Endpoint | Rate Limiter | Limits Applied |
326|----------|--------------|----------------|
327| `/v2/*` (registry) | Registry limiter | Global RPS + Per-IP RPS |
328| `/auth/*` (authentication) | Registry limiter | Global RPS + Per-IP RPS |
329| `/admin/*` (management) | Admin limiter | Global RPS + Per-IP RPS |
330
331The auth endpoints share the registry limiter, preventing brute force attacks on credentials. The admin API uses separate limiter instances to avoid CI/CD traffic affecting admin access.
332
333### Recommended Production Settings
334
335```toml
336[api.rate_limit]
337enabled = true
338global_rps = 1000      # Adjust based on your traffic
339per_ip_rps = 30        # Stricter per-IP limit
340burst = 50             # Smaller burst for tighter control
341trusted_proxies = []   # Configure based on your proxy setup
342```
343
344## Troubleshooting
345
346### "I'm getting rate limited but traffic is low"
347
3481. Check if `trusted_proxies` is configured correctly
3492. All traffic may appear to come from your proxy's IP
3503. Add your proxy to `trusted_proxies` to see real client IPs
351
352### "Rate limiting doesn't seem to work"
353
3541. Verify `enabled = true`
3552. Check that requests go through Gordon (not cached by CDN)
3563. Ensure `global_rps` and `per_ip_rps` are set appropriately
357
358### "I need to whitelist certain IPs"
359
360Rate limiting doesn't support IP whitelisting. Consider:
361- Increasing `per_ip_rps` for legitimate high-traffic clients
362- Using a reverse proxy with its own rate limiting rules
363- Implementing IP-based access control at the network level
364
365## Related
366
367- [Authentication](./auth.md)
368- [Configuration Reference](./reference.md)
369- [Logging](./logging.md)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close