diff --git a/.gitignore b/.gitignore
index dff6696..8d47fb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,9 @@ lefthook-local.yml
# Local Netlify folder
.netlify
+
+# Internal docs-rework working files (not for publication)
+DOCS_IMPROVEMENTS_SPEC.md
+FACT_CHECK.md
+DOCS_NOTES.md
+.DS_Store
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 8896622..390b6c4 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -5,7 +5,7 @@ import { availableSinceMarkdownPlugin } from './availableSinceMarkdownPlugin'
export default withMermaid(defineConfig({
title: "AnyCable",
- description: "A real-time server for Rails, Laravel, Node.js, and Hotwire applications",
+ description: "AnyCable: a realtime server with delivery guarantees for Rails, Laravel, Node.js, Python, and any backend.",
cleanUrls: true,
ignoreDeadLinks: true,
@@ -34,10 +34,10 @@ export default withMermaid(defineConfig({
['link', { rel: 'apple-touch-icon', href: '/assets/images/apple-touch-icon.png' }],
['meta', { name: 'theme-color', content: '#ff5e5e' }],
['meta', { property: 'og:title', content: 'AnyCable Documentation' }],
- ['meta', { property: 'og:description', content: 'A real-time server for Rails, Laravel, Node.js, and Hotwire applications' }],
+ ['meta', { property: 'og:description', content: 'A realtime server with delivery guarantees for Rails, Laravel, Node.js, Python, and any backend.' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'twitter:site', content: '@any_cable' }],
- ['meta', { name: 'keywords', content: 'ruby, rails, websockets, real-time, action-cable, anycable, hotwire, laravel' }],
+ ['meta', { name: 'keywords', content: 'anycable, websockets, real-time, realtime server, delivery guarantees, reliable streams, presence, action-cable, ruby, rails, hotwire, laravel, nodejs, python, go' }],
],
themeConfig: {
@@ -47,40 +47,43 @@ export default withMermaid(defineConfig({
},
nav: [
- { text: 'Guide', link: '/getting_started', activeMatch: '/' },
+ { text: 'Guide', link: '/overview', activeMatch: '/' },
{ text: 'anycable.io', link: 'https://anycable.io' },
{ text: 'LLMs', link: '/llms-full.txt' },
],
sidebar: [
{
- text: 'Guides',
+ text: 'Start here',
items: [
- { text: 'Getting started', link: '/getting_started' },
- { text: 'Using with Rails', link: '/rails/getting_started' },
- { text: 'Client-side usage', link: '/guides/client-side' },
- { text: 'Using with JavaScript (serverless)', link: '/guides/serverless' },
- { text: 'Using with Hotwire', link: '/guides/hotwire' },
- { text: 'Using with Laravel', link: '/guides/laravel' },
- { text: 'Broadcasting', link: '/anycable-go/broadcasting' },
- { text: 'Signed streams', link: '/anycable-go/signed_streams' },
- { text: 'Reliable streams', link: '/anycable-go/reliable_streams' },
- { text: 'Presence', link: '/anycable-go/presence' },
- { text: 'REST API', link: '/anycable-go/api' },
- { text: 'JWT authentication', link: '/anycable-go/jwt_identification' },
+ { text: 'What is AnyCable', link: '/overview' },
+ { text: 'Quick start', link: '/quickstart' },
+ { text: 'Capabilities', link: '/capabilities' },
{ text: 'π₯ Troubleshooting', link: '/troubleshooting' },
]
},
{
- text: 'AnyCable PRO',
+ text: 'By backend',
items: [
- { text: 'Going PRO', link: '/pro' },
- { text: 'Install PRO', link: '/pro/install' },
- { text: 'AnyCable RPC', link: '/anycable-go/rpc' },
- { text: 'Apollo GraphQL', link: '/anycable-go/apollo' },
- { text: 'Binary formats', link: '/anycable-go/binary_formats' },
- { text: 'Long polling', link: '/anycable-go/long_polling' },
- { text: 'OCPP support', link: '/anycable-go/ocpp' },
+ { text: 'Rails', link: '/rails/getting_started' },
+ { text: 'Laravel', link: '/guides/laravel' },
+ { text: 'Node.js (serverless)', link: '/guides/serverless' },
+ { text: 'Python & any HTTP backend', link: '/guides/python' },
+ { text: 'Hotwire', link: '/guides/hotwire' },
+ { text: 'Client-side usage', link: '/guides/client-side' },
+ ]
+ },
+ {
+ text: 'Capabilities',
+ items: [
+ { text: 'Reliable streams & recovery', link: '/anycable-go/reliable_streams' },
+ { text: 'Presence', link: '/anycable-go/presence' },
+ { text: 'Broadcasting', link: '/anycable-go/broadcasting' },
+ { text: 'Signed streams', link: '/anycable-go/signed_streams' },
+ { text: 'JWT authentication', link: '/anycable-go/jwt_identification' },
+ { text: 'Broker deep dive', link: '/anycable-go/broker' },
+ { text: 'Pub/sub (node-node)', link: '/anycable-go/pubsub' },
+ { text: 'REST API', link: '/anycable-go/api' },
]
},
{
@@ -88,13 +91,23 @@ export default withMermaid(defineConfig({
items: [
{ text: 'Server-sent events', link: '/anycable-go/sse' },
{ text: 'Pusher', link: '/anycable-go/pusher' },
+ { text: 'Apollo GraphQL', link: '/anycable-go/apollo' },
{ text: 'Durable Streams', link: '/anycable-go/durable_streams' },
{ text: 'Long polling', link: '/anycable-go/long_polling' },
{ text: 'OCPP support', link: '/anycable-go/ocpp' },
+ { text: 'Binary formats', link: '/anycable-go/binary_formats' },
]
},
{
- text: 'Deployment',
+ text: 'Editions & Pro',
+ items: [
+ { text: 'Editions', link: '/editions' },
+ { text: 'Going Pro', link: '/pro' },
+ { text: 'Install Pro', link: '/pro/install' },
+ ]
+ },
+ {
+ text: 'Deploy & operate',
items: [
{ text: 'Heroku', link: '/deployment/heroku' },
{ text: 'Fly.io', link: '/deployment/fly' },
@@ -110,6 +123,11 @@ export default withMermaid(defineConfig({
{ text: 'AWS ECS', link: 'https://medium.com/expsoftwareengineering/deploying-ruby-on-rails-with-anycable-using-docker-ecs-80f0da2051ba' },
{ text: 'Load Balancing', link: '/deployment/load_balancing' },
{ text: 'Load Testing', link: '/deployment/load_testing' },
+ { text: 'Instrumentation', link: '/anycable-go/instrumentation' },
+ { text: 'Health checking', link: '/anycable-go/health_checking' },
+ { text: 'Tracing', link: '/anycable-go/tracing' },
+ { text: 'OS Tuning', link: '/anycable-go/os_tuning' },
+ { text: 'Telemetry', link: '/anycable-go/telemetry' },
]
},
{
@@ -132,20 +150,13 @@ export default withMermaid(defineConfig({
]
},
{
- text: 'AnyCable-Go',
+ text: 'AnyCable-Go (server)',
items: [
{ text: 'Configuration', link: '/anycable-go/configuration' },
+ { text: 'All server options', link: '/anycable-go/options' },
{ text: 'AnyCable RPC', link: '/anycable-go/rpc' },
- { text: 'Broker deep dive', link: '/anycable-go/broker' },
- { text: 'Pub/sub (node-node)', link: '/anycable-go/pubsub' },
- { text: 'Instrumentation', link: '/anycable-go/instrumentation' },
- { text: 'Health checking', link: '/anycable-go/health_checking' },
- { text: 'Tracing', link: '/anycable-go/tracing' },
- { text: 'OS Tuning', link: '/anycable-go/os_tuning' },
- { text: 'Signed streams', link: '/anycable-go/signed_streams' },
{ text: 'Embedded NATS', link: '/anycable-go/embedded_nats' },
{ text: 'Using as a library', link: '/anycable-go/library' },
- { text: 'Telemetry', link: '/anycable-go/telemetry' },
]
},
{
@@ -156,7 +167,7 @@ export default withMermaid(defineConfig({
]
},
{
- text: 'Misc',
+ text: 'Reference',
items: [
{ text: 'Benchmarks', link: '/benchmarks' },
{ text: 'Action Cable protocols', link: '/misc/action_cable_protocol' },
diff --git a/docs/.vitepress/theme/layouts/LandingLayout.vue b/docs/.vitepress/theme/layouts/LandingLayout.vue
index 9068b02..4e284ad 100644
--- a/docs/.vitepress/theme/layouts/LandingLayout.vue
+++ b/docs/.vitepress/theme/layouts/LandingLayout.vue
@@ -19,11 +19,12 @@
/>
AnyCable Documentation
-
Realtime server for reliable two-way communication
+
Realtime server with delivery guarantees for Rails, Laravel, Node.js, Python, and any backend
+
+ Explore
+
@@ -288,6 +301,10 @@
margin: 0 0 32px;
}
+.getting-started-inner .explore-heading {
+ margin-top: 48px;
+}
+
.path-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
diff --git a/docs/anycable-go/broadcasting.md b/docs/anycable-go/broadcasting.md
index faf8a5b..0ab0607 100644
--- a/docs/anycable-go/broadcasting.md
+++ b/docs/anycable-go/broadcasting.md
@@ -64,6 +64,8 @@ echo -n 'broadcast-cable' | openssl dgst -sha256 -hmac '' | awk '{p
## Redis Pub/Sub
> Enable via `--broadcast_adapter=redis` (or `ANYCABLE_BROADCAST_ADAPTER=redis`).
+>
+> [Valkey](https://valkey.io), the open-source Redis fork, works as a drop-in alternative for this and the Redis X adapter below.
This broadcaster uses Redis [Pub/Sub](https://redis.io/topics/pubsub) feature under the hood, and, thus, publications are delivered to all subscribed AnyCable servers simultaneously.
diff --git a/docs/anycable-go/configuration.md b/docs/anycable-go/configuration.md
index 2a06632..23bb76b 100644
--- a/docs/anycable-go/configuration.md
+++ b/docs/anycable-go/configuration.md
@@ -1,5 +1,7 @@
# AnyCable server configuration
+> Looking for a specific flag? See the [complete server options reference](./options.md) for every option and environment variable.
+
You can configure AnyCable server via CLI options, e.g.:
```sh
@@ -109,6 +111,8 @@ You can specify on which port to receive broadcasting requests (NOTE: it could b
## Redis configuration
+> [Valkey](https://valkey.io), the open-source fork of Redis, works as a drop-in alternative everywhere AnyCable uses Redis. Some features require recent versions: Redis-backed presence needs **Redis 7.4+** or **Valkey 9.0+**.
+
**--redis_url** (`ANYCABLE_REDIS_URL` or `REDIS_URL`)
Redis URL to connect to (default: `"redis://localhost:6379/5"`). Used by the corresponding pub/sub, broadcasting, and broker adapters.
diff --git a/docs/anycable-go/jwt_identification.md b/docs/anycable-go/jwt_identification.md
index d4608d4..bcdb532 100644
--- a/docs/anycable-go/jwt_identification.md
+++ b/docs/anycable-go/jwt_identification.md
@@ -18,7 +18,7 @@ By default, the `--secret` configuration parameter is used as a JWT secret key.
Other configuration options are:
-- (_Optional_) **--jwt_param** (`ANYCABLE_ID_PARAM`, default: "jid"): the name of a query string param or an HTTP header, which carries a token. The header name is prefixed with `X-`.
+- (_Optional_) **--jwt_param** (`ANYCABLE_JWT_PARAM`, default: "jid"): the name of a query string param or an HTTP header, which carries a token. The header name is prefixed with `X-`.
- (_Optional_) **--enforce_jwt** (`ANYCABLE_ENFORCE_JWT`, default: false): whether to require all connection requests to contain a token. Connections without a token would be rejected right away. If not set, the servers fallbacks to the RPC call (if RPC is configured) or would be accepted if authentication is disabled (`--noauth`).
A client must provide an identification token either via a query param or via an HTTP header (if possible). For example:
diff --git a/docs/anycable-go/ocpp.md b/docs/anycable-go/ocpp.md
index 920ed19..4bf4386 100644
--- a/docs/anycable-go/ocpp.md
+++ b/docs/anycable-go/ocpp.md
@@ -14,9 +14,9 @@ AnyCable-Go Pro supports OCPP and allows you to _connect_ your charging stations
- The station sends a `BootNotification` request to initialize the connection
- AnyCable transforms this request into several AnyCable RPC calls to match the Action Cable interface:
1) `Authenticate -> Connection#connect` to authenticate the station.
- 2) `Command{subscribe} -> OCCPChannel#subscribed` to initialize a channel entity to association with this station.
- 3) `Command{perform} -> OCCPChannel#boot_notification` to handle the `BootNotification` request.
-- Subsequent requests from the station are converted into `OCCPChannel` action calls (e.g., `Authorize -> OCCPChannel#authorize`, `StartTransaction -> OCCPChannel#start_transaction`).
+ 2) `Command{subscribe} -> OCPPChannel#subscribed` to initialize a channel entity to association with this station.
+ 3) `Command{perform} -> OCPPChannel#boot_notification` to handle the `BootNotification` request.
+- Subsequent requests from the station are converted into `OCPPChannel` action calls (e.g., `Authorize -> OCPPChannel#authorize`, `StartTransaction -> OCPPChannel#start_transaction`).
AnyCable also takes care of heartbeats and acknowledgment messages (unless you send them manually, see below).
@@ -120,7 +120,7 @@ end
### Single-action variant
-It's possible to handle all OCCP commands with a single `#receive` method at the channel class. For that, you must configure `anycable-go` to not use granular actions for OCPP:
+It's possible to handle all OCPP commands with a single `#receive` method at the channel class. For that, you must configure `anycable-go` to not use granular actions for OCPP:
```sh
anycable-go --ocpp_granular_actions=false
@@ -152,7 +152,7 @@ end
You can send remote commands to stations via Action Cable broadcasts:
```ruby
-OCCPChannel.broadcast_to(
+OCPPChannel.broadcast_to(
"ev/#{serial_number}",
{
command: "TriggerMessage",
diff --git a/docs/anycable-go/options.md b/docs/anycable-go/options.md
new file mode 100644
index 0000000..783bd7f
--- /dev/null
+++ b/docs/anycable-go/options.md
@@ -0,0 +1,254 @@
+# Server options reference (anycable-go)
+
+Every command-line option and environment variable for the `anycable-go` server, grouped by area. Generated from `anycable-go --help` (OSS v1.6.14), with Pro-only options listed separately.
+
+For prose explanations of the most common options, see [Configuration](./configuration.md). This page is the complete, flat list, handy as a lookup and for agents.
+
+> Every option can be set three ways: a command-line flag (`--port 8080`), an environment variable (the **Env** column, e.g. `ANYCABLE_PORT=8080`), or a key in a TOML config file (`--config-path`). Options marked **Value** take an argument. Run `anycable-go --help` for the list matching your installed version.
+
+## Global
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--config-path` | yes | β | β | Path to the TOML configuration file |
+| `--help, -h` | β | β | β | show help |
+| `--ignore-config-path` | β | β | `false` | Ignore configuration files |
+| `--print-config` | β | β | `false` | Print configuration and exit |
+| `--version, -v` | β | β | β | print the version |
+| `--ws_max_pending_size` | yes | β | `1048576` | Maximum size (in bytes) of the write queue for a session before it's considered slow and disconnected (0 = unlimited) |
+
+## AnyCable-Go Server
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--allowed_origins` | yes | `ANYCABLE_ALLOWED_ORIGINS` | β | Accept requests only from specified origins, e.g., "www.example.com,*example.io". No check is performed if empty |
+| `--broadcast_key` | yes | `ANYCABLE_BROADCAST_KEY` | β | An authentication key for broadcast requests |
+| `--health-path` | yes | `ANYCABLE_HEALTH_PATH` | `"/health"` | HTTP health endpoint path |
+| `--host` | yes | `ANYCABLE_HOST` | `"localhost"` | Server host |
+| `--max-conn` | yes | `ANYCABLE_MAX_CONN` | `0` | Limit simultaneous server connections (0 β without limit) |
+| `--noauth` | β | `ANYCABLE_NOAUTH` | `false` | [DANGER ZONE] Disable client authentication over RPC |
+| `--path` | yes | `ANYCABLE_PATH` | `"/cable"` | WebSocket endpoint path (you can specify multiple paths using comma as separator) |
+| `--port` | yes | `ANYCABLE_PORT, $PORT` | `8080` | Server port |
+| `--public` | β | `ANYCABLE_PUBLIC` | `false` | [DANGER ZONE] Run server in the public mode allowing all connections and stream subscriptions |
+| `--secret` | yes | `ANYCABLE_SECRET` | β | A common secret key used by all features by default |
+| `--shutdown_delay` | yes | `ANYCABLE_SHUTDOWN_DELAY` | `0` | Sleep time before shutting down (in seconds) |
+| `--shutdown_timeout` | yes | `ANYCABLE_SHUTDOWN_TIMEOUT` | `30` | Graceful shutdown timeout (in seconds) |
+
+## API
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--api_path` | yes | `ANYCABLE_API_PATH` | `"/api"` | API endpoint base path |
+| `--api_port` | yes | `ANYCABLE_API_PORT` | `0` | API server port (0 = use main server port) |
+| `--api_secret` | yes | `ANYCABLE_API_SECRET` | β | Secret token to authenticate API requests |
+
+## Broadcasting
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--broadcast_adapter` | yes | `ANYCABLE_BROADCAST_ADAPTER` | β | Broadcasting adapter to use (http, redisx, redis or nats). You can specify multiple at once via a comma-separated list |
+| `--broker` | yes | `ANYCABLE_BROKER` | β | Broker engine to use (memory) |
+| `--nats_channel` | yes | `ANYCABLE_NATS_CHANNEL` | `"__anycable__"` | NATS channel for broadcasts |
+| `--pubsub` | yes | `ANYCABLE_PUBSUB` | β | Pub/Sub adapter to use (redis or nats) |
+| `--redis_channel` | yes | `ANYCABLE_REDIS_CHANNEL` | `"__anycable__"` | Redis channel for broadcasts |
+
+## Broker
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--history_limit` | yes | `ANYCABLE_HISTORY_LIMIT` | `100` | Max number of messages to keep in the stream's history |
+| `--history_ttl` | yes | `ANYCABLE_HISTORY_TTL` | `300` | TTL for messages in streams history (seconds) |
+| `--presence_ttl` | yes | `ANYCABLE_PRESENCE_TTL` | `15` | TTL for presence information (seconds) |
+| `--sessions_ttl` | yes | `ANYCABLE_SESSIONS_TTL` | `300` | TTL for expired/disconnected sessions (seconds) |
+
+## Disconnector
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--disconnect_mode` | yes | `ANYCABLE_DISCONNECT_MODE` | `"auto"` | Define when to call Disconnect callback (always, never, auto) |
+
+## Durable Streams
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--ds` | β | `ANYCABLE_DS` | `false` | Enable Durable Streams endpoint |
+| `--ds_path` | yes | `ANYCABLE_DS_PATH` | `"/ds"` | Durable Streams endpoint path |
+| `--ds_poll_interval` | yes | `ANYCABLE_DS_POLL_INTERVAL` | `10` | Durable Streams long polling interval (seconds) |
+| `--ds_skip_auth` | β | `ANYCABLE_DS_SKIP_AUTH` | `false` | Disable client authentication for Durable Streams (only authorize stream access) |
+| `--ds_sse_ttl` | yes | `ANYCABLE_DS_SSE_TTL` | `60` | Durable Streams SSE connections time-to-live (seconds) |
+
+## Embedded NATS
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--embed_nats` | β | `ANYCABLE_EMBED_NATS` | `false` | Enable embedded NATS server and use it for pub/sub |
+| `--enats_cluster_name` | yes | `ANYCABLE_ENATS_CLUSTER_NAME` | `"anycable-cluster"` | NATS cluster name |
+| `--enats_cluster_routes` | yes | `ANYCABLE_ENATS_CLUSTER_ROUTES` | β | Comma-separated list of known cluster addresses |
+| `--enats_gateway_advertise` | yes | `ANYCABLE_ENATS_GATEWAY_ADVERTISE` | β | NATS gateway advertise address |
+| `--enats_gateways` | yes | `ANYCABLE_ENATS_GATEWAYS` | β | Semicolon-separated list of known gateway configurations: name_a:gateway_1,gateway_2;name_b:gateway_4 |
+| `--enats_max_payload` | yes | `ANYCABLE_ENATS_MAX_PAYLOAD` | `0` | Maximum message payload size in bytes (0 uses the NATS default of 1MB) |
+| `--enats_server_name` | yes | `ANYCABLE_ENATS_SERVER_NAME` | β | Embedded NATS unique server name (required for JetStream), auto-generated by default |
+
+## HTTP Broadcast
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--http_broadcast_path` | yes | `ANYCABLE_HTTP_BROADCAST_PATH` | `"/_broadcast"` | HTTP pub/sub endpoint path |
+| `--http_broadcast_port` | yes | `ANYCABLE_HTTP_BROADCAST_PORT` | `0` | HTTP pub/sub server port |
+
+## JWT
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--enforce_jwt` | β | `ANYCABLE_ENFORCE_JWT` | `false` | Whether to enforce token presence for all connections |
+| `--jwt_param` | yes | `ANYCABLE_JWT_PARAM` | `"jid"` | The name of a query string param or an HTTP header carrying a token |
+| `--jwt_secret` | yes | `ANYCABLE_JWT_SECRET` | β | The encryption key used to verify JWT tokens |
+
+## Log
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--debug` | β | `ANYCABLE_DEBUG` | `false` | Enable debug mode (more verbose logging) |
+| `--log_format` | yes | `ANYCABLE_LOG_FORMAT` | `"text"` | Set logging format (text/json) |
+| `--log_level` | yes | `ANYCABLE_LOG_LEVEL` | `"info"` | Set logging level (debug/info/warn/error) |
+
+## Metrics
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--metrics_host` | yes | `ANYCABLE_METRICS_HOST` | β | Server host for metrics endpoint |
+| `--metrics_http` | yes | `ANYCABLE_METRICS_HTTP` | β | Enable HTTP metrics endpoint at the specified path |
+| `--metrics_log` | β | `ANYCABLE_METRICS_LOG` | `false` | Enable metrics logging (with info level) |
+| `--metrics_log_filter` | yes | `ANYCABLE_METRICS_LOG_FILTER` | β | Specify list of metrics to print to log (to reduce the output) |
+| `--metrics_port` | yes | `ANYCABLE_METRICS_PORT` | `0` | Server port for metrics endpoint, the same as for main server by default |
+| `--metrics_rotate_interval` | yes | `ANYCABLE_METRICS_ROTATE_INTERVAL` | `15` | Specify how often flush metrics to writers (logs, statsd) (in seconds) |
+| `--metrics_tags` | yes | `ANYCABLE_METRICS_TAGS` | β | Comma-separated list of default (global) tags to add to every metric |
+| `--stats_refresh_interval` | yes | `ANYCABLE_STATS_REFRESH_INTERVAL` | `5` | How often to refresh the server stats (in seconds) |
+
+## Misc
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--presets` | yes | `ANYCABLE_PRESETS` | β | Configuration presets, comma-separated (none, fly, heroku, broker). Inferred automatically |
+
+## NATS
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--nats_servers` | yes | `ANYCABLE_NATS_SERVERS` | `"nats://127.0.0.1:4222"` | Comma separated list of NATS cluster servers |
+
+## Ping
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--enable_native_pings` | β | `ANYCABLE_ENABLE_NATIVE_PINGS` | `false` | Send native pings (e.g., WebSocket ping frames) along with application-level pings to keepalive clients using custom protocols |
+| `--ping_interval` | yes | `ANYCABLE_PING_INTERVAL` | `3` | Action Cable ping interval (in seconds) |
+| `--pong_timeout` | yes | `ANYCABLE_PONG_TIMEOUT` | `0` | How long to wait for a pong response before disconnecting the client (in seconds). Zero means no pongs required |
+
+## Pusher
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--pusher_api_port` | yes | `ANYCABLE_PUSHER_API_PORT` | `0` | Port for Pusher HTTP API (0 = use the main server port) |
+| `--pusher_app_id` | yes | `ANYCABLE_PUSHER_APP_ID` | β | Pusher application ID |
+| `--pusher_app_key` | yes | `ANYCABLE_PUSHER_APP_KEY` | β | Pusher application key |
+| `--pusher_secret` | yes | `ANYCABLE_PUSHER_SECRET` | β | Pusher secret |
+
+## Redis X Broadcast
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--redisx_stream` | yes | `ANYCABLE_REDISX_STREAM` | `"__anycable__"` | Redis X broadcaster stream name |
+
+## Redis
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--redis_sentinels` | yes | `ANYCABLE_REDIS_SENTINELS` | β | Comma separated list of sentinel hosts, format: 'hostname:port,..' |
+| `--redis_tls_ca_cert_path` | yes | `ANYCABLE_REDIS_TLS_CA_CERT_PATH` | β | Path to the CA certificate file to verify the Redis server certificate |
+| `--redis_tls_client_cert_path` | yes | `ANYCABLE_REDIS_TLS_CLIENT_CERT_PATH` | β | Path to the client TLS certificate file for mutual TLS with Redis |
+| `--redis_tls_client_key_path` | yes | `ANYCABLE_REDIS_TLS_CLIENT_KEY_PATH` | β | Path to the client TLS private key file for mutual TLS with Redis |
+| `--redis_url` | yes | `ANYCABLE_REDIS_URL, $REDIS_URL` | `"redis://localhost:6379"` | Redis url |
+
+## RPC
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--headers` | yes | `ANYCABLE_HEADERS` | `"cookie"` | List of headers to proxy to RPC |
+| `--http_rpc_secret` | yes | `ANYCABLE_HTTP_RPC_SECRET` | β | Authentication secret for RPC over HTTP |
+| `--norpc` | β | `ANYCABLE_NORPC` | `false` | Disable RPC component and run server in the standalone mode |
+| `--proxy-cookies` | yes | `ANYCABLE_PROXY_COOKIES` | β | Cookie keys to send to RPC, default is all |
+| `--rpc_concurrency` | yes | `ANYCABLE_RPC_CONCURRENCY` | `28` | Max number of concurrent RPC request; should be slightly less than the RPC server concurrency |
+| `--rpc_host` | yes | `ANYCABLE_RPC_HOST` | `"localhost:50051"` | RPC service address (full URL in case of HTTP RPC) |
+| `--rpc_request_timeout` | yes | `ANYCABLE_RPC_REQUEST_TIMEOUT` | `0` | RPC requests timeout (in ms) |
+
+## Server-Sent Events
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--sse` | β | `ANYCABLE_SSE` | `false` | Enable SSE endpoint |
+| `--sse_path` | yes | `ANYCABLE_SSE_PATH` | `"/events"` | SSE endpoint path |
+
+## Signed Streams
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--cable_ready` | β | `ANYCABLE_CABLE_READY` | `false` | Enable Cable Ready support |
+| `--cable_ready_secret` | yes | `ANYCABLE_CABLE_READY_SECRET` | β | A custom secret to verify CableReady streams |
+| `--public_streams` | β | `ANYCABLE_PUBLIC_STREAMS` | `false` | Enable public (unsigned) streams |
+| `--streams_presence` | β | `ANYCABLE_STREAMS_PRESENCE` | `true` | Enable presence for signed pub/sub streams |
+| `--streams_secret` | yes | `ANYCABLE_STREAMS_SECRET` | β | Secret you use to sign stream names |
+| `--streams_whisper` | β | `ANYCABLE_STREAMS_WHISPER` | `false` | Enable whispering for signed pub/sub streams |
+| `--turbo_streams` | β | `ANYCABLE_TURBO_STREAMS` | `false` | Enable Turbo Streams support |
+| `--turbo_streams_secret` | yes | `ANYCABLE_TURBO_STREAMS_SECRET` | β | A custom secret to verify Turbo Streams |
+
+## SSL
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--ssl_cert` | yes | `ANYCABLE_SSL_CERT` | β | SSL certificate path |
+| `--ssl_key` | yes | `ANYCABLE_SSL_KEY` | β | SSL private key path |
+
+## StatsD
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--statsd_host` | yes | `ANYCABLE_STATSD_HOST` | β | Server host for metrics sent to statsd server in the format <host>:<port> |
+| `--statsd_prefix` | yes | `ANYCABLE_STATSD_PREFIX` | `"anycable_go."` | Statsd metrics prefix |
+| `--statsd_tags_format` | yes | `ANYCABLE_STATSD_TAGS_FORMAT` | `"datadog"` | One of "datadog", "influxdb", or "graphite" |
+
+## WebSockets
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--ws_write_timeout` | yes | `ANYCABLE_WS_WRITE_TIMEOUT` | `2` | Maximum time to wait for a write operation to complete |
+
+## Pro-only options
+
+These options are available in [AnyCable Pro](../pro.md) and AnyCable+, on top of everything above. Generated from a Pro build (`1.6.14-pro`).
+
+| Option | Value | Env | Default | Description |
+|---|---|---|---|---|
+| `--admin` | β | `ANYCABLE_ADMIN` | `false` | Enable admin console |
+| `--admin_path` | yes | `ANYCABLE_ADMIN_PATH` | `"/_high_voltage_"` | Admin console HTTP root |
+| `--admin_port` | yes | `ANYCABLE_ADMIN_PORT` | `0` | Admin console server port (set to 0 to run on the same port as the main server) |
+| `--admin_secret` | yes | `ANYCABLE_ADMIN_SECRET` | β | Authentication secret for admin actions |
+| `--graphql_action` | yes | `ANYCABLE_GRAPHQL_ACTION` | `"execute"` | GraphQL Ruby channel action (method) name |
+| `--graphql_channel` | yes | `ANYCABLE_GRAPHQL_CHANNEL` | `"GraphqlChannel"` | GraphQL Ruby channel class name |
+| `--graphql_idle_timeout` | yes | `ANYCABLE_GRAPHQL_IDLE_TIMEOUT` | `2` | Defines for how long to wait for connection_init (in seconds) |
+| `--graphql_path` | yes | `ANYCABLE_GRAPHQL_PATH` | β | Enable GraphQL proxy and mount at the specified path |
+| `--poll` | β | `ANYCABLE_POLL` | `false` | Enable long polling support |
+| `--poll_flush_interval` | yes | `ANYCABLE_POLL_FLUSH_INTERVAL` | `500` | Long polling flush interval (in milliseconds) |
+| `--poll_interval` | yes | `ANYCABLE_POLL_INTERVAL` | `15` | Long polling interval (in seconds) |
+| `--poll_keepalive_timeout` | yes | `ANYCABLE_POLL_KEEPALIVE_TIMEOUT` | `5` | Long polling keepalive timeout (in seconds) |
+| `--poll_max_request_size` | yes | `ANYCABLE_POLL_MAX_REQUEST_SIZE` | `65536` | Long polling maximum request body size (in bytes) |
+| `--poll_path` | yes | `ANYCABLE_POLL_PATH` | `"/lp"` | Long polling endpoint path |
+| `--ocpp_channel` | yes | `ANYCABLE_OCPP_CHANNEL` | `"OCPPChannel"` | OCPP Action Cable channel class name |
+| `--ocpp_granular_actions` | β | `ANYCABLE_OCPP_GRANULAR_ACTIONS` | `true` | Translate each OCPP command into its own action |
+| `--ocpp_heartbeat_interval` | yes | `ANYCABLE_OCPP_HEARTBEAT_INTERVAL` | `30` | Default heartbeat interval in seconds. **Note:** current Pro builds (β€ `1.6.14-pro`) spell this `--ocpp_hearbeat_interval` / `ANYCABLE_OCPP_HEARBEAT_INTERVAL` (a known typo); use that spelling until the fix ships. |
+| `--ocpp_path` | yes | `ANYCABLE_OCPP_PATH` | β | WebSocket endpoint path prefix to accept OCPP connections |
+| `--rpc_concurrency_initial` | yes | `ANYCABLE_RPC_CONCURRENCY_INITIAL` | `25` | Initial concurrency (adaptive) |
+| `--rpc_concurrency_max` | yes | `ANYCABLE_RPC_CONCURRENCY_MAX` | `100` | Max concurrency (adaptive) |
+| `--rpc_concurrency_min` | yes | `ANYCABLE_RPC_CONCURRENCY_MIN` | `5` | Min concurrency (adaptive) |
+| `--shutdown_slowdrain` | β | `ANYCABLE_SHUTDOWN_SLOWDRAIN` | `false` | Enable slow draining of connections during shutdown |
+| `--netpoll_enabled` | β | `ANYCABLE_NETPOLL_ENABLED` | `true` | Whether to use net polling (epoll, kqueue) |
diff --git a/docs/anycable-go/reliable_streams.md b/docs/anycable-go/reliable_streams.md
index 588ebd0..32cab1f 100644
--- a/docs/anycable-go/reliable_streams.md
+++ b/docs/anycable-go/reliable_streams.md
@@ -149,7 +149,7 @@ $ anycable-go --embed_nats --broker=nats
-AnyCable Pro comes with a Redis-based broker adapter. It stores all data in Redis and, thus, can be used in multi-node installations.
+AnyCable Pro comes with a Redis-based broker adapter. It stores all data in Redis and, thus, can be used in multi-node installations. [Valkey](https://valkey.io), the open-source Redis fork, works as a drop-in alternative (note that Redis-backed presence requires **Redis 7.4+** or **Valkey 9.0+**).
To use Redis broker, you need to provide the `--broker` option with the `redis` adapter name:
diff --git a/docs/anycable-go/rpc.md b/docs/anycable-go/rpc.md
index 60161ad..073562f 100644
--- a/docs/anycable-go/rpc.md
+++ b/docs/anycable-go/rpc.md
@@ -2,7 +2,7 @@
AnyCable allows you to control all the real-time communication logic from your backend application. For that, AnyCable uses a _remote procedure call_ (RPC) mechanism to delegate handling of connection lifecycle events and processing of incoming messages (subscriptions, arbitrary actions).
-Using RPC is required if you design your real-time logic using _Channels_ (like in Rails Action Cable). For primitive pub/sub, you can run AnyCable in a [standalone mode](./getting_started.md#standalone-mode-pubsub-only), i.e., without RPC.
+Using RPC is required if you design your real-time logic using _Channels_ (like in Rails Action Cable). For primitive pub/sub, you can run AnyCable in a [standalone mode](../quickstart.md#any-backend), i.e., without RPC.
## RPC over gRPC
diff --git a/docs/architecture.md b/docs/architecture.md
index edd2f0e..a4cc429 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -13,7 +13,7 @@ AnyCable **real-time server** (WS, or WebSocket, since it's a primary transport)
AnyCable can be used in a standalone mode as a typical pub/sub server. However, it was primarily designed to act as a _business-logic proxy_ allowing you to avoid duplicating real-time logic between multiple apps. For that, we use an [RPC protocol](/anycable-go/rpc) to delegate subscriptions, authentication and authorization logic to your backend.
-The application publish broadcast messages to the WebSocket server (directly via HTTP or via some **queuing service**, see [broadcast adapters](/ruby/broadcast_adapters.md)). In case of running a WebSocket cluster (multiple nodes), there is also can be a **Pub/Sub service** responsible for re-transmitting broadcast messages between nodes. You can use [embedded NATS](/anycable-go/embedded_nats.md) as a pub/sub service to miminalize the number of infrastructure dependencies. See [Pub/Sub documentation](/anycable-go/pubsub.md) for other options.
+The application publish broadcast messages to the WebSocket server (directly via HTTP or via some **queuing service**, see [broadcast adapters](/ruby/broadcast_adapters.md)). In case of running a WebSocket cluster (multiple nodes), there is also can be a **Pub/Sub service** responsible for re-transmitting broadcast messages between nodes. You can use [embedded NATS](/anycable-go/embedded_nats.md) as a pub/sub service to minimize the number of infrastructure dependencies. See [Pub/Sub documentation](/anycable-go/pubsub.md) for other options.
## State management
diff --git a/docs/capabilities.md b/docs/capabilities.md
new file mode 100644
index 0000000..3ec4941
--- /dev/null
+++ b/docs/capabilities.md
@@ -0,0 +1,154 @@
+# Capabilities
+
+Socket.IO and uWebSockets give you a transport. AnyCable gives you a realtime
+framework: the delivery, presence, and deploy guarantees that production
+realtime features actually need. This page maps each capability to where it is
+documented.
+
+Every claim below is exercised against the running server; numbers come from the
+public [Node.js WebSocket benchmark](https://anycable.io/compare/nodejs-websocket/).
+
+## Delivery guarantees & recovery {#delivery-guarantees}
+
+A user's network blinks. With an at-most-once transport, every message sent
+during that window is gone. AnyCable keeps a short-lived history of each stream,
+so a reconnecting client catches up on exactly what it missed.
+
+How it works: with [reliable streams](./anycable-go/reliable_streams.md) enabled
+(the `broker` preset), every broadcast is tagged with a sequential `offset` and
+an `epoch`. The [client SDK](https://github.com/anycable/anycable-client) tracks
+the last offset it saw and, on reconnect, requests history from that point. The
+server replays the missed messages and acknowledges with `confirm_history`. If
+history is no longer available, the client gets `reject_history` and can fall
+back, so loss is always signaled.
+
+This takes AnyCable from **at-most-once** to **at-least-once**, and to
+**exactly-once** when the client de-duplicates by offset.
+
+In the benchmark, under repeated two-second network drops, AnyCable delivered
+**100%** of messages where default Socket.IO delivered 84.6% and uWebSockets
+87.0%.
+
+```sh
+anycable-go --presets=broker
+```
+
+β [Reliable streams and resumable sessions](./anycable-go/reliable_streams.md)
+
+## Message ordering {#ordering}
+
+Messages in a stream are assigned monotonically increasing offsets and delivered
+in that order. Combined with offset tracking on the client, this gives ordered,
+gap-aware delivery: a client can always tell whether it has every message up to
+its current position. Ordering is part of the same
+[broker](./anycable-go/broker.md) machinery as recovery.
+
+β [Broker deep dive](./anycable-go/broker.md)
+
+## Resumable sessions {#sessions}
+
+When the broker is enabled, each connection gets a session id. If a client
+reconnects within the session TTL, it restores its state (identifiers and
+subscriptions) without re-authenticating or re-subscribing. This makes
+reconnects cheap for both the client and your backend.
+
+Note: restored sessions do not currently re-fire connect/disconnect callbacks in
+your application. See the
+[reliable streams notes](./anycable-go/reliable_streams.md#resumed-sessions-vs-disconnect-callbacks)
+before relying on those callbacks.
+
+β [Resumable sessions](./anycable-go/reliable_streams.md)
+
+## Presence {#presence}
+
+Knowing who is online in a channel usually means writing custom storage and
+heartbeat code. AnyCable has [presence](./anycable-go/presence.md) built in.
+Clients join a stream's presence set with an id and arbitrary info; all
+subscribers receive `join` and `leave` events, and can read the current set.
+
+Presence is part of the broker, so the same `broker` preset enables it.
+Disconnected clients linger in the set for a short, configurable window
+(`--presence_ttl`, default 15s) to avoid churn on flaky connections.
+
+```js
+channel.presence.join(user.id, { name: user.name })
+channel.on('presence', ({ type, id, info }) => { /* join / leave */ })
+```
+
+β [Presence tracking](./anycable-go/presence.md)
+
+## Zero-downtime deploys {#deploys}
+
+Because AnyCable runs as a process separate from your application, deploying your
+app does not touch live WebSocket connections. Your app restarts; AnyCable stays
+up; users stay connected.
+
+In the benchmark's rolling-deploy test, an embedded Socket.IO server dropped
+every connection on each deploy and, at 20K clients, only 33% reconnected within
+the measurement window. AnyCable lost **zero** connections, because the deploy
+never restarted it.
+
+For RPC-backed (Rails) setups, you roll your application and RPC servers while
+the WebSocket server keeps running. Pro adds a
+[slow drain mode](./anycable-go/configuration.md#slow-drain-mode) to spread
+reconnects when you do restart the WebSocket layer.
+
+β [Load balancing](./deployment/load_balancing.md) Β·
+[Kamal deployment](./deployment/kamal.md)
+
+## Efficiency at scale {#efficiency}
+
+AnyCable is written in Go, so a single instance holds a large number of
+long-lived connections with a small per-connection footprint. In the 1M-target
+idle benchmark on a single 32 GB box:
+
+- **822,037** idle connections held (Pro), **821,877** (OSS).
+- **~18 KB** per connection (Pro), **~34 KB** (OSS).
+- **3 ms** median / **11 ms** p99 latency at 10,000 subscribers (Pro).
+
+Single-threaded JS servers hit a CPU ceiling far earlier: Socket.IO topped out
+around **120,000** connections on the same hardware, with one core saturated.
+
+We report this honestly: a bare transport like uWebSockets holds more
+connections per gigabyte (around 5 KB each) because it does less. AnyCable
+trades some of that density for delivery guarantees, presence, and deploy
+resilience.
+
+β [Benchmarks](./benchmarks.md) Β·
+[OS tuning](./anycable-go/os_tuning.md) Β·
+[Going Pro](./pro.md)
+
+## Binary formats {#binary}
+
+For high-volume or bandwidth-sensitive workloads, AnyCable Pro can serialize
+messages with **Msgpack** or **Protobuf** instead of JSON. Both reduce the bytes
+on the wire and speed up encoding and decoding; the client SDK supports them out
+of the box by setting an encoder and protocol.
+
+In the documented comparison, the same broadcast workload sent 502 MB as JSON,
+340 MB as Msgpack, and 315 MB as Protobuf, with Protobuf also decoding roughly
+3x faster than JSON. Binary formats help most for structured (object-like)
+payloads; they help less when you broadcast long strings such as HTML fragments.
+
+```js
+import { createCable } from '@anycable/web'
+import { ProtobufEncoder } from '@anycable/protobuf-encoder'
+
+export default createCable({ protocol: 'actioncable-v1-protobuf', encoder: new ProtobufEncoder() })
+```
+
+β [Binary messaging formats](./anycable-go/binary_formats.md)
+
+## Authentication & authorization {#auth}
+
+AnyCable authenticates connections and authorizes streams without requiring a
+persistent link to your backend:
+
+- [JWT authentication](./anycable-go/jwt_identification.md): your backend issues
+ a token, the client presents it on connect.
+- [Signed streams](./anycable-go/signed_streams.md): your backend signs stream
+ names so clients can only subscribe to what you allow. The signing algorithm
+ is plain HMAC-SHA256, identical across Ruby, Node, Python, and PHP.
+
+β [JWT authentication](./anycable-go/jwt_identification.md) Β·
+[Signed streams](./anycable-go/signed_streams.md)
diff --git a/docs/editions.md b/docs/editions.md
new file mode 100644
index 0000000..7c949fa
--- /dev/null
+++ b/docs/editions.md
@@ -0,0 +1,60 @@
+# Editions
+
+AnyCable comes in three editions. They share the same protocol, the same
+configuration, and the same client SDKs, so your application code does not change
+between them. You change which server you run or connect to, not how you
+integrate.
+
+- **Open source** β the full Go server, free and self-hosted. MIT licensed.
+- **[Pro](./pro.md)** β the same server with a denser memory model, cluster
+ features, and extra protocols. A drop-in replacement for the open-source binary.
+- **[AnyCable+](https://plus.anycable.io)** β Pro as a managed service, so you do
+ not operate the server yourself. Free tier available.
+
+## Which one
+
+- Start on **open source** if you are self-hosting and a single node (or a
+ NATS-based cluster) covers your scale. It already includes reliable streams,
+ presence, signed streams, JWT, SSE, and the Pusher protocol.
+- Move to **Pro** when you want the lower per-connection memory at scale, a
+ Redis- or Valkey-backed cluster with shared history and presence, or the Pro-only
+ protocols (Apollo GraphQL, long polling, OCPP).
+- Choose **AnyCable+** when you would rather not run and scale the server
+ yourself. It is the fastest way to start; you can self-host later without
+ changing your application.
+
+## Feature comparison
+
+| | Open source | Pro | AnyCable+ |
+|---|---|---|---|
+| License / model | MIT, self-hosted | Commercial, self-hosted | Managed service |
+| Action Cable / RPC, standalone pub/sub | β | β | β |
+| Signed streams, JWT auth | β | β | β |
+| Reliable streams & resumable sessions | β (memory, NATS) | β (+ Redis/Valkey, multi-node) | β |
+| Presence | β (single node) | β (+ cluster via Redis/Valkey) | β |
+| SSE, Pusher protocol | β | β | β |
+| Memory per connection (benchmark) | ~34 KB | ~18 KB | ~18 KB |
+| Adaptive RPC concurrency | β | β | β |
+| Binary formats (Msgpack, Protobuf) | β | β | β |
+| Apollo GraphQL protocol | β | β | β |
+| Long polling | β | β | β |
+| OCPP (EV charging) | β | β | β |
+| Slow drain on shutdown | β | β | β |
+| Hosting & scaling | you | you | AnyCable |
+| Support | community | commercial | included |
+
+For the full Pro feature details, see [Going Pro](./pro.md). Memory figures come
+from the published [Node.js WebSocket
+benchmark](https://anycable.io/compare/nodejs-websocket/).
+
+## Moving between editions
+
+Because the protocol and configuration are shared:
+
+- **OSS to Pro**: swap the binary and add a license. Your channels, JWT issuing,
+ stream signing, broadcasting, and client code are unchanged. Pro reads the same
+ configuration and enables its extra features through additional options.
+- **Self-hosted to AnyCable+**: point your client connection and broadcasts at
+ the managed endpoint. Your application integration stays the same.
+
+β [Install Pro](./pro/install.md) Β· [AnyCable+](https://plus.anycable.io)
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 9f6522c..b7af5d0 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -1,127 +1,12 @@
# Getting Started with AnyCable
-AnyCable is a language-agnostic real-time server focused on performance and reliability written in Go.
+> This page has moved. See the **[Quick Start](./quickstart.md)** to install
+> AnyCable and get a client connected, with a path for your stack (Rails,
+> Laravel, Node.js, Python, or any backend).
-> The quickest way to get AnyCable is to use our managed (and free) solution: [plus.anycable.io](https://plus.anycable.io)
+Looking for something specific?
-## Installation
-
-The easiest way to install AnyCable-Go is to [download](https://github.com/anycable/anycable/releases) a pre-compiled binary (for versions < 1.6.0 use our [legacy repository](https://github.com/anycable/anycable-go/releases)).
-
-MacOS users could install it with [Homebrew](https://brew.sh/)
-
-```sh
-brew install anycable-go
-```
-
-Arch Linux users can install [anycable-go package from AUR](https://aur.archlinux.org/packages/anycable-go/).
-
-### Via NPM
-
-For JavaScript projects, there is also an option to install AnyCable-Go via NPM:
-
-```sh
-npm install --save-dev @anycable/anycable-go
-pnpm install --save-dev @anycable/anycable-go
-yarn add --dev @anycable/anycable-go
-
-# and run as follows
-npx anycable-go
-```
-
-**NOTE:** The version of the NPM package is the same as the version of the AnyCable server binary (which is downloaded automatically on the first run).
-
-## Usage
-
-After installation, you can run AnyCable as follows:
-
-```sh
-$ anycable-go
-
-2024-03-06 13:38:07.545 INF Starting AnyCable 1.6.0-4f16b99 (pid: 8289, open file limit: 122880, gomaxprocs: 8) nodeid=hj2mXN
-...
-2024-03-06 13:38:56.490 INF RPC controller initialized: localhost:50051 (concurrency: 28, impl: grpc, enable_tls: false, proto_versions: v1) nodeid=FlCtwf context=rpc
-```
-
-By default, AnyCable tries to connect to a gRPC server listening at `localhost:50051` (the default host for the Ruby gem).
-
-AnyCable is designed as a logic-less proxy for your real-time features relying on a backend server to authenticate connections, authorize subscriptions and process incoming messages. That's why our default configuration assumes having an RPC server to handle all this logic.
-
-You can read more about AnyCable RPC in the [corresponding documentation](./rpc.md).
-
-### Standalone mode (pub/sub only)
-
-For pure pub/sub functionality, you can use AnyCable in a standalone mode, without any RPC servers. For that, you must configure the following features:
-
-- [JWT authentication](./jwt_identification.md) or disable authentication completely (`--noauth`). **NOTE:** You can still add minimal protection via the `--allowed_origins` option (see [configuration](./configuration.md#primary-settings)).
-
-- Enable [signed streams](./signed_streams.md) or allow public streams via the `--public_streams` option.
-
-There is also a shortcut option `--public` to enable both `--noauth` and `--public_streams` options. **Use it with caution**.
-
-You can also explicitly disable the RPC component by specifying the `--norpc` option.
-
-Thus, to run AnyCable real-time server in an insecure standalone mode, use the following command:
-
-```sh
-$ anycable-go --public
-
-2024-03-06 14:00:12.549 INF Starting AnyCable 1.6.0-4f16b99 (pid: 17817, open file limit: 122880, gomaxprocs: 8) nodeid=wAhWDB
-2024-03-06 14:00:12.549 WRN Server is running in the public mode nodeid=wAhWDB
-...
-```
-
-To secure access to AnyCable server, specify either the `--jwt_secret` or `--streams_secret` option. There is also the `--secret` shortcut:
-
-```sh
-anycable-go --secret=VERY_SECRET_VALUE --norpc
-```
-
-Read more about pub/sub mode in the [signed streams documentation](./signed_streams.md).
-
-### Connecting to AnyCable
-
-AnyCable uses the [Action Cable protocol][protocol] for client-server communication. We recommend using our official [JavaScript client library][anycable-client] for all JavaScript/TypeScript runtimes:
-
-```js
-import { createCable } from '@anycable/web'
-
-const cable = createCable(CABLE_URL)
-
-const subscription = cable.subscribeTo('ChatChannel', { roomId: '42' })
-
-const _ = await subscription.perform('speak', { msg: 'Hello' })
-
-subscription.on('message', msg => {
- if (msg.type === 'typing') {
- console.log(`User ${msg.name} is typing`)
- } else {
- console.log(`${msg.name}: ${msg.text}`)
- }
-})
-```
-
-**Note**: The snippet above assumes having a "ChatChannel" defined in your application (which is connected to AnyCable via RPC).
-
-You can also use:
-
-- Third-party Action Cable-compatible clients.
-
-- EventSource (Server-Sent Events) connections ([more info](./sse.md)).
-
-- Custom WebSocket clients following the [Action Cable protocol][protocol].
-
-AnyCable Pro also supports:
-
-- Apollo GraphQL WebSocket clients ([more info](./apollo.md))
-
-- HTTP streaming (long-polling) ([more info](./long_polling.md))
-
-- OCPP WebSocket clients ([more info](./ocpp.md))
-
-### Broadcasting messages
-
-Finally, to broadcast messages to connected clients via the name pub/sub streams, you can use one of the provided [broadcast adapters](./broadcasting.md).
-
-[anycable-client]: https://github.com/anycable/anycable-client
-[protocol]: ../misc/action_cable_protocol.md
+- [Install AnyCable](./quickstart.md#install)
+- [Standalone pub/sub (any backend)](./quickstart.md#any-backend)
+- [What is AnyCable?](./overview.md)
+- [Capabilities](./capabilities.md)
diff --git a/docs/guides/laravel.md b/docs/guides/laravel.md
index f4292c7..b4fbcaa 100644
--- a/docs/guides/laravel.md
+++ b/docs/guides/laravel.md
@@ -101,7 +101,7 @@ You can also create an `anycable.toml` configuration file to fine-tune your AnyC
**NOTE:** The Artisan command automatically configures [AnyCable broadcasting adapter](/anycable-go/broadcasting.md) to HTTP and enables [the "broker" preset](/anycable-go/reliable_streams.md) (streams history).
-Alternatively, you can install AnyCable using [other available options](/anycable-go/getting_started?id=installation).
+Alternatively, you can install AnyCable using [other available options](../quickstart.md#install).
That's it! Run your Laravel application, launch AnyCable server, and you should see your Echo client connecting to it and receiving updates.
diff --git a/docs/guides/python.md b/docs/guides/python.md
new file mode 100644
index 0000000..4a82921
--- /dev/null
+++ b/docs/guides/python.md
@@ -0,0 +1,128 @@
+# Using AnyCable with Python (and any HTTP backend)
+
+AnyCable is language-agnostic. Your Python application, on Django, FastAPI,
+Flask, or anything else, does not need a special SDK or a persistent connection
+to AnyCable. It authorizes clients by issuing tokens and signing stream names,
+and publishes messages with plain HTTP POSTs. AnyCable owns the WebSocket
+connections; your app stays the source of truth.
+
+This is the same [standalone pub/sub](../quickstart.md#any-backend) model used by
+any HTTP backend. The examples below are Python, but the three moving parts (a
+JWT, a signed stream name, an HTTP broadcast) are identical in every language.
+
+## Architecture
+
+```
+ Browser βββ WebSocket ββ AnyCable βββ POST /_broadcast ββ Your Python app
+ β² (Django / FastAPI /
+ β JWT (?jid=...) on connect Flask / ...)
+ β signed stream names
+```
+
+## 1. Run the server
+
+```sh
+export ANYCABLE_SECRET=$(openssl rand -hex 32) # use a stable value in production
+anycable-go \
+ --jwt_secret=$ANYCABLE_SECRET \
+ --streams_secret=$ANYCABLE_SECRET \
+ --broadcast_adapter=http \
+ --enforce_jwt
+```
+
+- `--jwt_secret` authenticates connections.
+- `--streams_secret` authorizes stream subscriptions via signed names.
+- `--enforce_jwt` rejects any connection without a valid token.
+
+Use a secret of at least 32 bytes (HMAC-SHA256 warns on shorter keys).
+
+## 2. Authenticate connections with JWT
+
+Issue a token in your Python backend when a user loads the page. The payload
+carries an `ext` claim with your connection identifiers (the equivalent of
+Action Cable's `identified_by`):
+
+```python
+import json, jwt, time # pip install pyjwt
+
+def anycable_token(user_id, secret, ttl=300):
+ payload = {"ext": json.dumps({"user_id": user_id}), "exp": int(time.time()) + ttl}
+ return jwt.encode(payload, secret, algorithm="HS256")
+```
+
+Hand the token to the client, which passes it on connect:
+
+```js
+import { createCable } from '@anycable/web'
+const cable = createCable(`ws://localhost:8080/cable?jid=${token}`)
+```
+
+A connection with no token (or a bad one) is rejected with
+`disconnect: unauthorized`. A valid token is accepted with `welcome`, and the
+identifiers from `ext` are available to AnyCable.
+
+## 3. Authorize streams with signed names
+
+So a client can only subscribe to streams you allow, sign the stream name in
+Python and give the signed value to the client. The algorithm is HMAC-SHA256,
+the same one used across Ruby, Node, and PHP:
+
+```python
+import base64, json, hmac, hashlib
+
+def sign_stream(name, secret):
+ encoded = base64.b64encode(json.dumps(name).encode()).decode()
+ digest = hmac.new(secret.encode(), encoded.encode(), hashlib.sha256).hexdigest()
+ return f"{encoded}--{digest}"
+```
+
+```js
+const channel = cable.streamFromSigned(signedStreamName)
+channel.on('message', (msg) => render(msg))
+```
+
+## 4. Broadcast from Python
+
+Publishing is an HTTP POST. The `data` field is a string; clients receive it
+parsed:
+
+```python
+import json, httpx # or requests
+
+BROADCAST_URL = "http://localhost:8090/_broadcast"
+
+def broadcast(stream, payload):
+ httpx.post(BROADCAST_URL, json={"stream": stream, "data": json.dumps(payload)})
+
+broadcast("chat/1", {"text": "Hello from Python"})
+```
+
+> The broadcast endpoint runs on port `8090` by default. To serve it on the main
+> port and require an auth key, see [securing the broadcast
+> endpoint](../anycable-go/broadcasting.md#securing-http-endpoint).
+
+## Framework notes
+
+The integration is the same regardless of framework, because it is just token
+issuing, stream signing, and HTTP POSTs:
+
+- **Django**: issue tokens in a view or context processor; broadcast from views,
+ signals, or Celery tasks.
+- **FastAPI / Flask**: issue tokens in a route; broadcast from request handlers
+ or background workers.
+- **Any other backend**: replicate the three snippets above in your language.
+
+## Verified behavior
+
+Every step here is exercised against a running server: a Python-generated JWT is
+accepted (and a missing one rejected with `unauthorized`), a Python-signed stream
+name is accepted for subscription, and an HTTP broadcast is delivered to the
+subscribed client.
+
+## Related
+
+- [Quick Start: any backend](../quickstart.md#any-backend)
+- [JWT authentication](../anycable-go/jwt_identification.md)
+- [Signed streams](../anycable-go/signed_streams.md)
+- [Broadcasting](../anycable-go/broadcasting.md)
+- [HTTP RPC](../ruby/http_rpc.md) for the RPC-style integration (delegating channel logic to your app)
diff --git a/docs/guides/serverless.md b/docs/guides/serverless.md
index e799bcc..107e512 100644
--- a/docs/guides/serverless.md
+++ b/docs/guides/serverless.md
@@ -267,7 +267,7 @@ fly launch --image anycable/anycable-go:1.5 --generate-name --ha=false \
## Running AnyCable locally
-There are plenty of ways of installing `anycable-go` binary on your machine (see [../anycable-go/getting_started.md]). For your convenience, we also provide an NPM package that can be used to install and run `anycable-go`:
+There are plenty of ways of installing `anycable-go` binary on your machine (see [Quick Start](../quickstart.md#install)). For your convenience, we also provide an NPM package that can be used to install and run `anycable-go`:
```sh
npm install --save-dev @anycable/anycable-go
diff --git a/docs/overview.md b/docs/overview.md
new file mode 100644
index 0000000..6e42671
--- /dev/null
+++ b/docs/overview.md
@@ -0,0 +1,95 @@
+# What is AnyCable
+
+AnyCable is a realtime server with **delivery guarantees** for Rails, Laravel,
+Node.js, Python, and any backend that can speak HTTP. It handles the hard parts
+of production WebSockets so your application does not have to: recovering missed
+messages after a connection blip, keeping users connected through deploys,
+tracking presence, and preserving message order.
+
+It has been in production since 2017 and powers realtime features across
+healthtech, fintech, field services and IoT, collaboration tools, and SaaS
+products.
+
+> **In a hurry?** Jump to the [Quick Start](./quickstart.md) and pick your stack.
+
+## Why a separate realtime server
+
+Most frameworks let you add WebSockets inside your application process. That is
+fine until production reality arrives:
+
+- A user's network blinks for two seconds and the messages sent during that
+ window are gone for good.
+- You deploy, the process restarts, and every connected user is dropped at once.
+- Traffic grows and a single application process becomes the bottleneck for
+ every open connection.
+
+AnyCable runs as a dedicated process next to your app. Your application stays
+the source of truth for authentication and business logic; AnyCable owns the
+connections. Because it is a separate process, **a deploy of your app does not
+touch live connections**, and because it is written in Go, a single instance
+holds **over 820,000 idle connections** at roughly 18 KB of RAM each (Pro), all
+while keeping latency low.
+
+We measured these properties against popular Node.js setups (Socket.IO,
+uWebSockets.js). The full methodology and raw numbers are public; see the
+[Node.js WebSocket comparison](https://anycable.io/compare/nodejs-websocket/).
+The highlights, on a single 32 GB box:
+
+- **Capacity:** 822,037 idle connections (Pro), where single-threaded Socket.IO
+ topped out near 120,000.
+- **Memory:** ~18 KB per connection (Pro), ~34 KB (OSS).
+- **Latency:** 3 ms median, 11 ms p99 at 10,000 subscribers (Pro).
+- **Reliability:** 100% of messages delivered under repeated network drops,
+ where at-most-once setups lost 13-15%.
+- **Deploys:** zero connections lost across application deploys that otherwise
+ disconnected every user.
+
+## What you get
+
+| Capability | What it means |
+|---|---|
+| [Delivery guarantees & recovery](./capabilities.md#delivery-guarantees) | Clients catch up on messages missed during a disconnect |
+| [Message ordering](./capabilities.md#ordering) | Messages arrive in the order they were published |
+| [Presence](./capabilities.md#presence) | Track who is online in a stream, with join/leave events |
+| [Zero-downtime deploys](./capabilities.md#deploys) | Connections survive application restarts |
+| [Efficiency at scale](./capabilities.md#efficiency) | Over 800,000 connections per instance in Go |
+| [Binary formats](./capabilities.md#binary) | Msgpack and Protobuf encoding to cut bandwidth (Pro) |
+
+See the [Capabilities](./capabilities.md) overview for how each one works and
+where it is documented.
+
+## How it fits your application
+
+AnyCable supports two integration styles. You can mix them in one application.
+
+**1. Pub/sub (standalone).** AnyCable handles connections and streams on its
+own. Your app authorizes clients with [JWT](./anycable-go/jwt_identification.md)
+and publishes messages over HTTP or to [signed
+streams](./anycable-go/signed_streams.md). No persistent connection between
+AnyCable and your backend is required. This is the quickest path for Node.js,
+Python, Laravel, and any HTTP backend.
+
+**2. RPC-backed.** AnyCable delegates connection and subscription logic to your
+application over [RPC](./anycable-go/rpc.md). This is the default for Rails,
+where AnyCable is a drop-in replacement for Action Cable and reuses your
+existing channels.
+
+## Editions
+
+AnyCable comes in three editions that share the same protocol and
+configuration, so you can move between them as your needs change.
+
+- **Open source.** The full-featured Go server, free and
+ [self-hosted](./quickstart.md). MIT licensed.
+- **[Pro](./pro.md).** The same server with a denser memory model (further
+ RAM reduction at scale), multi-node stream history, adaptive RPC concurrency,
+ binary formats, and additional protocols (Apollo GraphQL, long polling, OCPP).
+ It is a drop-in replacement for the open-source binary.
+- **[AnyCable+](https://plus.anycable.io).** A managed service, so you do not
+ run the server yourself. Free tier available.
+
+## Next steps
+
+- [Quick Start](./quickstart.md): get a server running and a client connected, by stack.
+- [Capabilities](./capabilities.md): the features that make AnyCable a realtime framework, not just a transport.
+- [Using with Rails](./rails/getting_started.md): the Action Cable drop-in path.
diff --git a/docs/pro.md b/docs/pro.md
index e335389..ccb1eed 100644
--- a/docs/pro.md
+++ b/docs/pro.md
@@ -1,16 +1,20 @@
-# AnyCable-Go
+# AnyCable Pro
-AnyCable-Go Pro aims to bring AnyCable to the next level of efficient resources usage and developer ~~experience~~ happiness.
+AnyCable Pro aims to bring AnyCable to the next level of efficient resources usage and developer ~~experience~~ happiness.
+> Not sure which edition you need? See [Editions](./editions.md) for a comparison of open source, Pro, and AnyCable+.
+>
> Read also AnyCable Goes Pro: Fast WebSockets for Ruby, at scale.
## Memory usage
Pro version uses a different memory model under the hood, which gives you yet another **30-50% RAM usage reduction**.
-Here is the results of running [websocket-bench][] `broadcast` and `connect` benchmarks and measuring RAM used:
+> In the current [Node.js WebSocket benchmark](https://anycable.io/compare/nodejs-websocket/), Pro held connections at ~18 KB each versus ~34 KB for the open-source build on the same hardware.
+
+Here is the results of running [websocket-bench][] `broadcast` and `connect` benchmarks and measuring RAM used (from an earlier release):
| versionΒ | broadcast 5k | connect 10k | connect 15k |
|---|----|---|---|
@@ -32,7 +36,7 @@ incoming messages, you might want to fallback to the _actor-per-connection_ mode
- [Binary messaging formats](anycable-go/binary_formats.md)
- [Apollo GraphQL protocol support](anycable-go/apollo.md)
- [Long polling support](anycable-go/long_polling.md)
-- [OCCP support](anycable-go/occp.md)
+- [OCPP support](anycable-go/ocpp.md)
## Installation
diff --git a/docs/public/_redirects b/docs/public/_redirects
index 63cf0af..a3fddba 100644
--- a/docs/public/_redirects
+++ b/docs/public/_redirects
@@ -25,8 +25,9 @@
/configuration /anycable-go/configuration 301
/health_checking /anycable-go/health_checking 301
-# The standalone anycable-go landing page was merged into the top-level guide.
-/anycable-go/getting_started /getting_started 301
+# The old standalone anycable-go landing page now points to the Quick Start.
+# (Top-level /getting_started keeps a pointer page, so no redirect for it.)
+/anycable-go/getting_started /quickstart 301
# Older path schemes still linked around the web.
/guides/anycable-go/* /anycable-go/:splat 301
diff --git a/docs/quickstart.md b/docs/quickstart.md
new file mode 100644
index 0000000..647d107
--- /dev/null
+++ b/docs/quickstart.md
@@ -0,0 +1,199 @@
+# Quick Start
+
+Get an AnyCable server running and a client receiving messages in a few minutes.
+Pick your stack:
+
+- [Any backend (standalone pub/sub)](#any-backend) β Node.js, Python, Go, PHP, or any HTTP server
+- [Rails](#rails) β drop-in Action Cable replacement
+- [Laravel](#laravel) β drop-in Reverb / Pusher replacement
+- [Node.js](#nodejs) β with the serverless SDK
+
+If you do not want to run a server at all, start with the managed
+[AnyCable+](https://plus.anycable.io) (free tier) and skip installation.
+
+## Install AnyCable {#install}
+
+The server is a single Go binary.
+
+```sh
+# macOS (Homebrew)
+brew install anycable-go
+
+# Any platform: download a release binary
+# https://github.com/anycable/anycable/releases
+
+# JavaScript projects (installs the matching binary on first run)
+npm install --save-dev @anycable/anycable-go
+npx anycable-go
+```
+
+Check it runs:
+
+```sh
+anycable-go --version
+# AnyCable 1.6.7 ...
+```
+
+## Any backend (standalone pub/sub) {#any-backend}
+
+In standalone mode AnyCable handles connections and message delivery on its own.
+Your backend, in any language, publishes messages over plain HTTP. This is the
+fastest way to add realtime to a Node.js, Python, Go, or PHP application.
+
+> **Try it without writing code:** the [browser
+> playground](https://github.com/anycable/anycable-browser-playground) lets you
+> connect and publish to a live AnyCable server from the browser. For a minimal
+> code example, see [anycable-pubsub-nextjs](https://github.com/anycable/anycable-pubsub-nextjs).
+
+### 1. Run the server (development)
+
+```sh
+anycable-go --public --broadcast_adapter=http
+```
+
+`--public` disables authentication and allows any stream name, which is handy
+for local development. The HTTP broadcast endpoint starts on port `8090` (it
+moves off the main port when no authorization is configured). You will see:
+
+```
+INF Starting AnyCable 1.6.7 ...
+WRN Server is running in the public mode
+INF Accept broadcast requests at http://localhost:8090/_broadcast (no authorization)
+INF Handle WebSocket connections at http://localhost:8080/cable
+```
+
+> **Do not run `--public` in production.** See [Secure it](#secure-it) below.
+
+### 2. Connect a client
+
+Subscribe to a stream named `chat/1` using the
+[AnyCable client SDK](https://github.com/anycable/anycable-client):
+
+```js
+import { createCable } from '@anycable/web'
+
+const cable = createCable('ws://localhost:8080/cable')
+
+const channel = cable.streamFrom('chat/1')
+channel.on('message', (msg) => {
+ console.log('received', msg)
+})
+```
+
+Any Action Cable-compatible client works too. The raw subscribe command is:
+
+```json
+{"command":"subscribe","identifier":"{\"channel\":\"$pubsub\",\"stream_name\":\"chat/1\"}"}
+```
+
+### 3. Broadcast a message
+
+From your backend (or just `curl`), POST to the broadcast endpoint. The `data`
+field is a string; clients receive it parsed:
+
+```sh
+curl -X POST http://localhost:8090/_broadcast \
+ -H "Content-Type: application/json" \
+ -d '{"stream":"chat/1","data":"{\"text\":\"Hello, world!\"}"}'
+# 201 Created
+```
+
+The connected client logs `received { text: 'Hello, world!' }`. That is a
+complete realtime round trip with no application-server connection involved.
+
+### 4. Secure it {#secure-it}
+
+For production, replace `--public` with real authorization. Two pieces:
+
+**Authorize streams with signed names.** Set a secret and sign stream names in
+your backend so clients cannot subscribe to arbitrary streams:
+
+```sh
+export ANYCABLE_SECRET=$(openssl rand -hex 32) # use a stable value in production
+anycable-go --streams_secret=$ANYCABLE_SECRET --broadcast_adapter=http
+```
+
+Generate a signed name in your backend and hand it to the client. The algorithm
+is HMAC-SHA256, identical across languages. Node.js:
+
+```js
+import { createHmac } from 'crypto'
+
+const encoded = Buffer.from(JSON.stringify('chat/1')).toString('base64')
+const digest = createHmac('sha256', process.env.ANYCABLE_SECRET).update(encoded).digest('hex')
+const signedStreamName = `${encoded}--${digest}`
+```
+
+Python:
+
+```python
+import base64, json, hmac, hashlib
+
+encoded = base64.b64encode(json.dumps("chat/1").encode()).decode()
+digest = hmac.new(secret.encode(), encoded.encode(), hashlib.sha256).hexdigest()
+signed_stream_name = f"{encoded}--{digest}"
+```
+
+The client subscribes with the signed name:
+
+```js
+const channel = cable.streamFromSigned(signedStreamName)
+```
+
+**Authenticate connections with JWT.** Issue a token in your backend and have
+clients present it on connect. See [JWT
+authentication](./anycable-go/jwt_identification.md).
+
+Full reference: [Signed streams](./anycable-go/signed_streams.md) and
+[Broadcasting](./anycable-go/broadcasting.md) (including securing the broadcast
+endpoint with a key).
+
+## Rails {#rails}
+
+For Rails, AnyCable is a drop-in replacement for Action Cable. It runs your
+existing channels over [RPC](./anycable-go/rpc.md), so your `app/channels` code
+stays the same.
+
+```sh
+bundle add anycable-rails
+```
+
+Follow the [Rails getting started guide](./rails/getting_started.md) for the
+full setup, then run the server alongside your app:
+
+```sh
+anycable-go
+```
+
+You can also use Rails in [standalone mode](./anycable-go/signed_streams.md)
+(no RPC) for Hotwire and Turbo Streams.
+
+## Laravel {#laravel}
+
+AnyCable is a drop-in replacement for Laravel Reverb or Pusher. The quickest
+path is [Pusher mode](./anycable-go/pusher.md):
+
+```sh
+ANYCABLE_PUSHER_APP_ID=app-id \
+ANYCABLE_PUSHER_APP_KEY=app-key \
+ANYCABLE_SECRET=app-secret \
+anycable-go
+```
+
+AnyCable listens on port `8080` and recognizes your existing Reverb environment
+variables. See the [Laravel guide](./guides/laravel.md) for native mode with
+reliable streams and presence.
+
+## Node.js {#nodejs}
+
+Beyond the [standalone path](#any-backend) above, the
+[AnyCable Serverless SDK](https://github.com/anycable/anycable-serverless-js)
+lets your Node.js backend handle channels, authentication, and broadcasting with
+familiar abstractions. See [Using AnyCable with serverless
+JavaScript](./guides/serverless.md).
+
+## Next steps
+
+- [Capabilities](./capabilities.md): delivery guarantees, presence, deploy resilience.
+- [Deployment](./deployment/heroku.md): ship the server to production.
+- [Configuration](./anycable-go/configuration.md): every server option.
diff --git a/docs/release_notes.md b/docs/release_notes.md
index 314b9fe..3a1040d 100644
--- a/docs/release_notes.md
+++ b/docs/release_notes.md
@@ -42,7 +42,7 @@ This page contains combined release notes for major and minor releases of all An
Now a single secret is enough to secure all AnyCable features; we call it an **application secret**. You can provide it via the `--secret` flag or the `ANYCABLE_SECRET=` env var. It's used as is for JWT and signed streams (unless specific secrets specified) and as a secret base for HTTP RPC and HTTP broadcasting (again, unless specific keys specified).
- There is also new `--broadast_key` (`ANYCABLE_BROADCAST_KEY`) that is meant to be used to authenticate broadcast actions. Currently, it's only used by HTTP broadcasting (as a replacement for `http_broadcast_secret`).
+ There is also new `--broadcast_key` (`ANYCABLE_BROADCAST_KEY`) that is meant to be used to authenticate broadcast actions. Currently, it's only used by HTTP broadcasting (as a replacement for `http_broadcast_secret`).
### Features
@@ -130,7 +130,7 @@ This page contains combined release notes for major and minor releases of all An
- Broadcasted messages are now delivered in the order they were received by the server.
- Previously, we used an executor pool internally to deliver broadcasted messages concurrently (to reduce the latency). That led to nonderterministic order of messages within a single stream delivered in a short period of time. Now, we preserve the order of messages within a streamβthe delivered as they were accepted by the server.
+ Previously, we used an executor pool internally to deliver broadcasted messages concurrently (to reduce the latency). That led to nondeterministic order of messages within a single stream delivered in a short period of time. Now, we preserve the order of messages within a streamβthe delivered as they were accepted by the server.
That means, with a single AnyCable-Go server, the following snippet will result in clients receiving the messages in the same order they were broadcasted:
@@ -185,7 +185,7 @@ This page contains combined release notes for major and minor releases of all An
See [documentation](./anycable-go/embedded_nats.md).
-- StatsD and metric tags are now generally available (dowstreamed from PRO).
+- StatsD and metric tags are now generally available (downstreamed from PRO).
See [documentation](./anycable-go/instrumentation.md#statsd).
@@ -223,7 +223,7 @@ This page contains combined release notes for major and minor releases of all An
#### AnyCable Ruby
-- A new configuration paramter, `rpc_max_connection_age`, has been added to replace the previous `rpc_server_args.max_connection_age_ms` (or `ANYCABLE_RPC_SERVER_ARGS__MAX_CONNECTION_AGE_MS`).
+- A new configuration parameter, `rpc_max_connection_age`, has been added to replace the previous `rpc_server_args.max_connection_age_ms` (or `ANYCABLE_RPC_SERVER_ARGS__MAX_CONNECTION_AGE_MS`).
It comes with the **default value of 300 (5 minutes)**.
@@ -249,7 +249,7 @@ For full list of changes see the corresponding change logs:
- Add JWT authentication/identification support.
- You can pass a properly structured token along the connection request to authorize the connection and set up _identifiers_ without peforming an RPC call.
+ You can pass a properly structured token along the connection request to authorize the connection and set up _identifiers_ without performing an RPC call.
See [documentation](./anycable-go/jwt_identification.md).
diff --git a/docs/ruby/broadcast_adapters.md b/docs/ruby/broadcast_adapters.md
index df271a1..2c8b3b9 100644
--- a/docs/ruby/broadcast_adapters.md
+++ b/docs/ruby/broadcast_adapters.md
@@ -25,6 +25,8 @@ If your HTTP broadcasting endpoint is secured, use the `broadcast_key` option to
### Redis Pub/Sub
> Enable via `broadcast_adapter: redis` in `anycable.yml` or `ANYCABLE_BROADCAST_ADAPTER=redis`.
+>
+> [Valkey](https://valkey.io), the open-source Redis fork, works as a drop-in alternative here and for all Redis-based adapters.
**NOTE:** To use Redis adapters, you MUST add the `redis` gem to your Gemfile yourself.
diff --git a/docs/ruby/non_rails.md b/docs/ruby/non_rails.md
index 8fda99b..4934828 100644
--- a/docs/ruby/non_rails.md
+++ b/docs/ruby/non_rails.md
@@ -20,7 +20,7 @@ If you don't plan to use _channels_ (RPC), you can go with the `anycable-core` g
## Pub/Sub only mode
-To use AnyCable in a standalone (pub/sub only) mode (see [docs](../anycable-go/getting_started.md)), you need to use the following APIs:
+To use AnyCable in a standalone (pub/sub only) mode (see [docs](../quickstart.md#any-backend)), you need to use the following APIs:
- Broadcasting.
diff --git a/forspell.dict b/forspell.dict
index 955f338..b441647 100644
--- a/forspell.dict
+++ b/forspell.dict
@@ -83,5 +83,22 @@ norpc
reconnection
resumable
subprotocol
+subprotocols
virtualized
webhooks
+realtime
+healthtech
+fintech
+telehealth
+replayable
+leaderboards
+analytics
+httpx
+cron
+redisx
+nats
+statsd
+Disconnector
+Hatchbox
+fastlane
+downstreamed