Skip to content

Commit

Permalink
feat: new signed streams
Browse files Browse the repository at this point in the history
- Add $pubsub channel for direct pub/sub support

- Unify signed stream controllers implementations
  • Loading branch information
palkan committed Mar 1, 2024
1 parent 4a3ee3a commit dfdc26a
Show file tree
Hide file tree
Showing 17 changed files with 677 additions and 466 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## master

- Added direct stream subscribing via a dedicated channel. ([@palkan][])

Added a `$pubsub` reserver channel to allow subscribing to streams (signed or unsigned) without relying on channels (similar to Turbo Streams).

Enable public (unsigned) streams support via the `--public_streams` (or `ANYCABLE_PUBLIC_STREAMS=1`) option or provide a secret key to verify signed streams via the `--streams_secret=<val>` (or `ANYCABLE_STREAMS_SECRET=<val>`) option.

- The `--turbo_rails_key` and `--cable_ready_key` options are deprecated in favor of the new `--streams_secret` option. The `--turbo_rails_key` and `--cable_ready_cleartext` are no longer supported (use `--public_streams` and the `$pubsub` channel instead).

- Allowing embedding AnyCable into existing web applications. ([@palkan][])

You can now set up an AnyCable instance without an HTTP server and mount AnyCable WebSocket/SSE handlers wherever you like.
Expand Down
15 changes: 10 additions & 5 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import (
"github.com/anycable/anycable-go/mrb"
"github.com/anycable/anycable-go/node"
"github.com/anycable/anycable-go/pubsub"
"github.com/anycable/anycable-go/rails"
"github.com/anycable/anycable-go/router"
"github.com/anycable/anycable-go/server"
"github.com/anycable/anycable-go/sse"
"github.com/anycable/anycable-go/streams"
"github.com/anycable/anycable-go/telemetry"
"github.com/anycable/anycable-go/utils"
"github.com/anycable/anycable-go/version"
Expand Down Expand Up @@ -480,13 +480,18 @@ func (r *Runner) Instrumenter() metricspkg.Instrumenter {
func (r *Runner) defaultRouter() *router.RouterController {
router := router.NewRouterController(nil)

if r.config.Rails.TurboRailsKey != "" || r.config.Rails.TurboRailsClearText {
turboController := rails.NewTurboController(r.config.Rails.TurboRailsKey, r.log)
if r.config.Streams.PubSubChannel != "" {
streamController := streams.NewStreamsController(&r.config.Streams, r.log)
router.Route(r.config.Streams.PubSubChannel, streamController) // nolint:errcheck
}

if r.config.Streams.Turbo && r.config.Streams.Secret != "" {
turboController := streams.NewTurboController(r.config.Streams.Secret, r.log)
router.Route("Turbo::StreamsChannel", turboController) // nolint:errcheck
}

if r.config.Rails.CableReadyKey != "" || r.config.Rails.CableReadyClearText {
crController := rails.NewCableReadyController(r.config.Rails.CableReadyKey, r.log)
if r.config.Streams.CableReady && r.config.Streams.Secret != "" {
crController := streams.NewCableReadyController(r.config.Streams.Secret, r.log)
router.Route("CableReady::Stream", crController) // nolint:errcheck
}

Expand Down
73 changes: 69 additions & 4 deletions cli/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,42 @@ Use shutdown_timeout instead.`)

c.SSE.AllowedOrigins = c.WS.AllowedOrigins

if c.Rails.TurboRailsKey != "" {
fmt.Println(`DEPRECATION WARNING: turbo_rails_key option is deprecated
and will be removed in the next major release of anycable-go.
Use streams_secret instead.`)

if c.Streams.Secret == "" {
c.Streams.Secret = c.Rails.TurboRailsKey
}

c.Streams.Turbo = true
}

if c.Rails.TurboRailsClearText {
fmt.Println(`DEPRECATION WARNING: turbo_rails_cleartext option is deprecated
and will be removed in the next major release of anycable-go.
It has no effect anymore, use public streams instead.`)
}

if c.Rails.CableReadyKey != "" {
fmt.Println(`DEPRECATION WARNING: cable_ready_key option is deprecated
and will be removed in the next major release of anycable-go.
Use streams_secret instead.`)

if c.Streams.Secret == "" {
c.Streams.Secret = c.Rails.CableReadyKey
}

c.Streams.CableReady = true
}

if c.Rails.CableReadyClearText {
fmt.Println(`DEPRECATION WARNING: cable_ready_cleartext option is deprecated
and will be removed in the next major release of anycable-go.
It has no effect anymore, use public streams instead.`)
}

return &c, nil, false
}

Expand Down Expand Up @@ -721,6 +757,7 @@ func metricsCLIFlags(c *config.Config, filter *string, mtags *string) []cli.Flag
Usage: "DEPRECATED. Specify how often flush metrics logs (in seconds)",
Value: c.Metrics.LogInterval,
Destination: &c.Metrics.LogInterval,
Hidden: true,
},

&cli.StringFlag{
Expand Down Expand Up @@ -859,28 +896,56 @@ func jwtCLIFlags(c *config.Config) []cli.Flag {
// signedStreamsCLIFlags returns misc CLI flags
func signedStreamsCLIFlags(c *config.Config) []cli.Flag {
return withDefaults(signedStreamsCategoryDescription, []cli.Flag{
&cli.StringFlag{
Name: "streams_secret",
Usage: "Secret you use to sign stream names",
Destination: &c.Streams.Secret,
},

&cli.BoolFlag{
Name: "public_streams",
Usage: "Enable public (unsigned) streams",
Destination: &c.Streams.Public,
},

&cli.BoolFlag{
Name: "turbo_streams",
Usage: "Enable Turbo Streams support",
Destination: &c.Streams.Turbo,
},

&cli.BoolFlag{
Name: "cable_ready",
Usage: "Enable Cable Ready support",
Destination: &c.Streams.CableReady,
},

&cli.StringFlag{
Name: "turbo_rails_key",
Usage: "Enable Turbo Streams fastlane with the specified signing key",
Usage: "[DEPRECATED] Enable Turbo Streams fastlane with the specified signing key",
Destination: &c.Rails.TurboRailsKey,
Hidden: true,
},

&cli.BoolFlag{
Name: "turbo_rails_cleartext",
Usage: "Enable Turbo Streams fastlane without stream names signing",
Usage: "[DEPRECATED] Enable Turbo Streams fastlane without stream names signing",
Destination: &c.Rails.TurboRailsClearText,
Hidden: true,
},

&cli.StringFlag{
Name: "cable_ready_key",
Usage: "Enable CableReady fastlane with the specified signing key",
Usage: "[DEPRECATED] Enable CableReady fastlane with the specified signing key",
Destination: &c.Rails.CableReadyKey,
Hidden: true,
},

&cli.BoolFlag{
Name: "cable_ready_cleartext",
Usage: "Enable Cable Ready fastlane without stream names signing",
Usage: "[DEPRECATED] Enable Cable Ready fastlane without stream names signing",
Destination: &c.Rails.CableReadyClearText,
Hidden: true,
},
})
}
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/anycable/anycable-go/rpc"
"github.com/anycable/anycable-go/server"
"github.com/anycable/anycable-go/sse"
"github.com/anycable/anycable-go/streams"
"github.com/anycable/anycable-go/ws"

nanoid "github.com/matoous/go-nanoid"
Expand Down Expand Up @@ -51,6 +52,7 @@ type Config struct {
EmbedNats bool
EmbeddedNats enats.Config
SSE sse.Config
Streams streams.Config
UserPresets []string
}

Expand Down Expand Up @@ -82,6 +84,7 @@ func NewConfig() Config {
Rails: rails.NewConfig(),
EmbeddedNats: enats.NewConfig(),
SSE: sse.NewConfig(),
Streams: streams.NewConfig(),
}

return config
Expand Down
33 changes: 31 additions & 2 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,38 @@ Run server:
```sh
$ anycable-go

=> INFO time context=main Starting AnyCable v1.2.1 (pid: 12902, open files limit: 524288, gomaxprocs: 4)
=> INFO time context=main Starting AnyCable v1.4.8 (pid: 12902, open files limit: 524288, gomaxprocs: 4)
```

By default, `anycable-go` tries to connect to an RPC server listening at `localhost:50051` (the default host for the Ruby gem). You can change this setting by providing `--rpc_host` option or `ANYCABLE_RPC_HOST` env variable (read more about [configuration](./configuration.md)).
By default, `anycable-go` tries to connect to a gRPC server listening at `localhost:50051` (the default host for the Ruby gem).

You can change this setting by providing `--rpc_host` option or `ANYCABLE_RPC_HOST` env variable (read more about [configuration](./configuration.md)).

All other configuration parameters have the same default values as the corresponding parameters for the AnyCable RPC server, so you don't need to change them usually.

### Standalone mode (pub/sub only)

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.

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**.

Thus, to run AnyCable real-time server in an insecure standalone mode, use the following command:

```sh
$ anycable-go --public

...
```

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

```
Loading

0 comments on commit dfdc26a

Please sign in to comment.