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

- Get Started + Get Started + What is AnyCable?

Laravel

-

Integrate real-time capabilities into your Laravel app

+

Drop-in replacement for Reverb or Pusher in your Laravel app

+
+ +

Node.js & TypeScript

+

Use AnyCable as a real-time server for (serverless) JS apps

+
+ +

Python

+

Power realtime in Django, FastAPI, or any Python backend

Hotwire

Build reactive frontends with Turbo Streams and AnyCable

- -

TS/JS

-

Use AnyCable as a real-time server for (serverless) JavaScript apps

+
+

Any backend

+

Pub/sub over a plain HTTP API, from any language

- -

Pusher

-

Drop-in Pusher-compatible real-time server

+
+ +

Explore

+
+ +

Capabilities

+

Delivery guarantees, recovery, presence, and zero-downtime deploys

- -

Durable Streams

-

Reliable message delivery with automatic catch-up

+
+

Editions

+

Open source, Pro, and managed AnyCable+

@@ -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