Skip to content

Commit

Permalink
Migrate from GitHub Wiki
Browse files Browse the repository at this point in the history
  • Loading branch information
petehayes102 committed Feb 14, 2024
1 parent ea7ea40 commit 4b3db68
Show file tree
Hide file tree
Showing 17 changed files with 1,635 additions and 2 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Deploy book to Pages

on:
push:
branches: ["main"]

workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
env:
MDBOOK_VERSION: 0.4
steps:
- uses: actions/checkout@v4
- name: Install mdBook
run: |
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh
rustup update
cargo install --version ${MDBOOK_VERSION} mdbook
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
- name: Build with mdBook
run: mdbook build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./book

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# guide
Selium user guide
# Selium User Guide

Selium's user guide, built by [`mdBook`](https://github.com/rust-lang/mdBook).

## Editing

To render the book whilst making changes, mdBook has a built in web server to help:

```bash
mdbook serve --open
```

## Deployment

The rendered book is deployed to GitHub Pages using the `pages.yml` workflow. Any push to `main`
will trigger this deployment.
12 changes: 12 additions & 0 deletions book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[book]
authors = ["Selium Developers"]
language = "en"
multilingual = false
src = "src"
title = "Selium User Guide"

[output.html]
default-theme = "ayu"
preferred-dark-theme = "ayu"
git-repository-url = "https://github.com/seliumlabs/selium"
cname = "guide.selium.com"
16 changes: 16 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Summary
![Selium.com](selium.png)

[Introduction](./introduction.md)

# Getting Started

- [First Steps](./getting_started/first_steps.md)
- [Request/Reply](./getting_started/request_reply.md)
- [Reusing Connections](./getting_started/reusing_connections.md)
- [`Stream` and `Sink` traits](./getting_started/streams_and_sinks.md)
- [Codecs](./getting_started/codecs.md)

---

- [Selium Cloud](./cloud.md)
77 changes: 77 additions & 0 deletions src/cloud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Selium Cloud

Selium Cloud (Beta) consists of a managed Selium Server, authentication, certificate
management, dashboard and technical support. If you want a hands-free, production-ready
solution for all of your software comms, backed by the people that made Selium, this is
for you.

## Getting Started

#### 1. Sign up
To get started with Selium Cloud, you'll [need an account!](https://www.selium.com/#CTA)
Selium Cloud is free for life, and super affordable as you grow with us.

#### 2. Create your first certificate
Once you've got your account, login to the Selium Cloud dashboard at
[cloud.selium.io](https://cloud.selium.io). From here, you can create TLS certificates
for each client on your account.

After logging in, you should see a screen like this:

<img width="800" alt="Dashboard" src="https://github.com/seliumlabs/selium/assets/3638076/3ad412cf-7d8b-4f02-b632-e6728c7c9ce6">

To create a certificate, fill in a name for your client, e.g. "web1.example.com", then
click the submit button. After a moment you should see a dialog box like this:

<img width="800" alt="Add client" src="https://github.com/seliumlabs/selium/assets/3638076/cf2ab47c-86d3-4f4f-a94c-8b26ca425303">

**Make sure you download the private key as you cannot download it again!**

Add the public and private keys to your project and you're ready to start using Selium.

## Basic Usage

Selium Cloud's API feels just like running your own Selium server. The main difference is
when establishing a connection to the server. Instead of using `selium::custom()` to
connect to your own server, use `selium::cloud()` to connect to the cloud:

```rust
let connection = selium::cloud()
.with_cert_and_key(
"./web1.example.com.der",
"./web1.example.com.key.der",
)?
.connect()
.await?;
```

Note that when using `selium::cloud()`, you won't need to specify a CA path or endpoint.
These details are baked into the Selium client for your convenience.

#### Namespaces

Each Selium Cloud account is linked to a unique _namespace_. This is used to distinguish
your data from other accounts, and is linked to every certificate you create. You can find
your namespace on the Selium Cloud dashboard:

<img width="800" alt="Namespace" src="https://github.com/seliumlabs/selium/assets/3638076/f5b9761b-da92-4d5c-9611-471e92ddd20c">

To use your namespace, simply prepend it to every topic name. For example, to publish the
topic "retail-transactions", you would code the following:

```rust
let mut publisher = connection
.publisher("/example/retail-transactions")
...
.await?;
```

### IMPORTANT NOTE!

**You must compile your code with the `--release` flag for both testing and production
use. This is so the Selium client knows to use the _production_ Certificate Authority!**

If you don't do this, you will not be able to connect to Selium.

> N.B. We know this isn't ideal, and likely we'll be removing this restriction in future
> versions. We'd love your feedback on this too!
87 changes: 87 additions & 0 deletions src/getting_started/codecs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Codecs
Looking back at the previous chapters, you probably noticed lines like these ones:

```rust
let mut publisher = connection
...
.with_encoder(StringCodec) // allows you to exchange string messages between clients

let mut subscriber = connection
...
.with_decoder(StringCodec) // use the same codec as the publisher
```

If you've not worked with data streaming before, codecs might seem like a very strange
concept indeed. However the answer is quite simple. The term _codecs_ is a portmanteau of
_encoder_ and _decoder_. Thus a codec is simply a structure that can _encode_ frames of
data on one side of a connection, and _decode_ them on the other.

In the example above, we use `StringCodec`, which does exactly what it says on the tin -
it _encodes_ strings to bytes and then _decodes_ those bytes back to strings.

## Why do we need codecs?

We need codecs because at the network level, computers only support sending raw bytes.
However most data we work with are not raw bytes - they're strings, integers, booleans,
enumerators, structures etc. In order to abstract away the pain of converting these types
to bytes and back again, we use a codec that knows how to do it for us.

## Can I send more than just strings?

Yep! With the help of the `serde` crate, we support sending all manner of Rust types.
In the example below, we use the `BincodeCodec` to send a stream of `StockEvent`s.

You'll need to install `serde` with the _derive_ feature to follow this example:
```bash
$ cargo add -F derive serde
```

```rust
use futures::SinkExt;
use selium::{prelude::*, std::codecs::BincodeCodec};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct StockEvent {
ticker: String,
change: f64,
}

impl StockEvent {
pub fn new(ticker: &str, change: f64) -> Self {
Self {
ticker: ticker.to_owned(),
change,
}
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let connection = selium::custom()
.endpoint("127.0.0.1:7001")
.with_certificate_authority("certs/client/ca.der")?
.with_cert_and_key(
"certs/client/localhost.der",
"certs/client/localhost.key.der",
)?
.connect()
.await?;

let mut publisher = connection
.publisher("/some/topic")
.with_encoder(BincodeCodec::default())
.open()
.await?;

publisher.send(StockEvent::new("INTC", -9.0)).await?;
publisher.finish().await?;

Ok(())
}
```

## Can I build my own codec?

Yes, and you can use third party codecs like protocol buffers, SBE etc. We'll have a chapter on
this coming soon.
129 changes: 129 additions & 0 deletions src/getting_started/first_steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# First Steps

Selium comprises a client library and a server binary. In order to use Selium, there are 3 basic
steps:
1. Create TLS certificates for your client and server
2. Run the server binary
3. Integrate the client library into your project

## Step 1 - TLS Certificates

First we need some certs to validate the client and server. Selium uses mutual TLS (_mTLS_) to
validate both parties cryptographically, making things nice and secure.

We've built a tool to make this easy, so let's install that, then mint our certs:

```bash
# Install the selium-tools CLI
$ cargo install selium-tools
# Use this CLI to create our certificates
$ selium-tools gen-certs
```

You should now have a directory in the current path called `certs/`. Inside we have certs for
the client and server, which you can move to a more convenient location if you like - the paths are
configurable in code. Both the `client/` and `server/` directories include a copy of the
certificate authority, which you'll need if you want to create more client certificates later.

## Step 2 - Start the Selium Server

The Selium server allows us to exchange messages between clients. For this example we'll grab a
copy from [crates.io](https://crates.io/crates/selium-server).

> For production deployments you can also download prebuilt binaries from
> [GitHub](https://github.com/orgs/seliumlabs/packages?repo_name=selium).
Let's start a new server with our freshly minted certs. In the same directory as your `certs/`
folder, open a new terminal and run the following commands:

```bash
# Install Selium server
$ cargo install selium-server
# Run the server
$ selium-server --bind-addr=127.0.0.1:7001
```

The `selium-server` command will not produce any output by default, but that doesn't mean it
isn't working! You can increase logging using the verbosity flag:

```bash
$ selium-server -v # Warnings only
$ selium-server -vv # Info
$ selium-server -vvv # Debug
$ selium-server -vvvv # Trace
```

## Step 3 - Implement the Selium Client

Selium Client is a composable library API for the Selium Server. Let's have a look at a minimal
example:

```rust
use futures::{SinkExt, StreamExt};
use selium::{prelude::*, std::codecs::StringCodec};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let connection = selium::custom() // connect to your own Selium server
.endpoint("127.0.0.1:7001") // your Selium server's address
.with_certificate_authority("certs/client/ca.der")? // your Selium cert authority
.with_cert_and_key(
"certs/client/localhost.der",
"certs/client/localhost.key.der",
)? // your client certificates
.connect()
.await?;

let mut publisher = connection
.publisher("/some/topic") // choose a topic to group similar messages together
.with_encoder(StringCodec) // allows you to exchange string messages between clients
.open() // opens a new stream for sending data
.await?;

let mut subscriber = connection
.subscriber("/some/topic") // subscribe to the publisher's topic
.with_decoder(StringCodec) // use the same codec as the publisher
.open() // opens a new stream for receiving data
.await?;

// Send a message and close the publisher
publisher.send("Hello, world!".into()).await?;
publisher.finish().await?;

// Receive the message
if let Some(Ok(message)) = subscriber.next().await {
println!("Received message: {message}");
}

Ok(())
}
```

There's a lot to take in here, but for the moment let's just get this baby running!

```bash
# Create a new Cargo project
$ cargo new --bin hello-selium

# Move into our project
$ cd hello-selium

# Add the crate dependencies
$ cargo add futures
$ cargo add -F std selium
$ cargo add -F macros,rt tokio
```

Now copy and paste the code above into `hello-selium/src/main.rs`.

Now let's execute the project and start exchanging some messages! Make sure your server is still
running from the previous step.

```bash
$ cargo run
Received message: Hello, world!
```

## Next Steps

We've just setup a working Selium publish/subscribe project, but [you can also use RPC too.](./request_reply.md)
Loading

0 comments on commit 4b3db68

Please sign in to comment.