busybee 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c005e2f714f3f1ea1b3eedf3d8e7b6ce72ad88fe27cf2a8e84637ca4fe3de281
4
- data.tar.gz: cbefb422a412d32143493b8903886e6429f59bd639a3a05de052f89a94be9edb
3
+ metadata.gz: eeb297a403676c22e7055dbb550d541c6f12fbc29f16caa8550bc625b6265857
4
+ data.tar.gz: 2a0930162e3b589cf65c85ded384b4b6e2c8100a45da3213c40dac324b5b340d
5
5
  SHA512:
6
- metadata.gz: e812d00b20c0ff7395b72db6225d57a2adcdee6ad9ecf7381215cd5352b5b034458df6bb3728a3cd25429effee83bc1f85c941dc2f4a7937f86cfb3d64f21688
7
- data.tar.gz: 61a813477c650a801a04d6d5ed50dc2a5bbef5686de5690599943a61898d2b77a3d66c52109c037c4e99fcf0cfb02c2393cb9f369cac9a65d3ef9da20a8d9da2
6
+ metadata.gz: 7ab1f433f8c1266d9a45c966ec4819f6eb1565c00e3013f44f96d3072dc173f50cd41e8c725f588cc9b19026995e0820700bea4e2adb18fb006181ae6a809e27
7
+ data.tar.gz: b92c2655751b7bf4070c5fa3bd55ea70a0d687f5725786763c55bdb7b470622c5d567b6c74362831f93963c2e639ecae9878a588164a552605e7c16209a314d7
data/CHANGELOG.md CHANGED
@@ -1,10 +1,36 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
- ## [0.1.0] - 2025-12-29
3
+ ## v0.2.0 (2026-02-05)
4
+
5
+ Production-ready Client API with Rails integration.
6
+
7
+ ### New Features:
8
+
9
+ - **Client class** (`Busybee::Client`) - a Ruby-idiomatic wrapper around the Zeebe gRPC API:
10
+ - Pluggable authentication with four credential types: `Insecure`, `TLS`, `OAuth`, and `CamundaCloud`
11
+ - Automatic credential type detection from parameters
12
+ - Almost all GRPC operations, including `deploy_process`, `start_instance`, `cancel_instance`, `publish_message`, `broadcast_signal` `set_variables`, `resolve_incident`, `complete_job`, `fail_job`, `throw_bpmn_error`, `update_job_retries`, `update_job_timeout`
13
+ - Two job activation methods: `with_each_job` (long-polling), and `open_job_stream` (continuous stream) with Enumerable wrapper class
14
+ - Rich wrapper class (`Busybee::Job`) for activated jobs:
15
+ - `variables` and `headers` with indifferent access (string or symbol keys)
16
+ - Status tracking (`:ready`, `:complete`, `:failed`) to prevent double-completion
17
+ - Action methods: `complete!`, `fail!`, `throw_bpmn_error!`
18
+ - Variable serialization handles ActiveRecord or other custom objects
19
+
20
+ - **Rails integration** - Automatic configuration from `config.x.busybee.*`, works seamlessly with Rails secrets
21
+ - Defaults to using Rails logger
22
+ - Structured logs with a `[busybee]` prefix, supporting text or JSON modes
23
+
24
+ ### Breaking Changes:
25
+
26
+ - **Testing helpers** now use `Busybee.cluster_address` instead of `Busybee::Testing.address`, which has been removed
27
+ - **Testing::Helpers** was refactored for namespace safety and some methods are no longer accessible within specs
28
+
29
+ ## v0.1.0 (2025-12-29)
4
30
 
5
31
  Initial public release with foundational components for testing BPMN workflows.
6
32
 
7
- ### Added
33
+ ### New Features:
8
34
 
9
35
  - **Testing module** (`Busybee::Testing`) - RSpec helpers and matchers for testing BPMN workflows against Zeebe:
10
36
  - `deploy_process` - Deploy BPMN files with optional unique IDs for test isolation
@@ -18,6 +44,8 @@ Initial public release with foundational components for testing BPMN workflows.
18
44
 
19
45
  - **GRPC layer** (`Busybee::GRPC`) - Generated protocol buffer classes from the Zeebe 8.8 proto definition for direct Zeebe API access
20
46
 
21
- ## [0.0.1] - 2025-12-03
47
+ ## v0.0.1 (2025-12-03)
48
+
49
+ - Initial development, not released
50
+
22
51
 
23
- - Initial development, not for release
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  **A complete Ruby toolkit for workflow-based orchestration with BPMN, running on Camunda Platform.**
4
4
 
5
- If you're a Ruby shop that needs workflow orchestration, you've probably noticed the gap: Camunda Platform is powerful, but the Ruby ecosystem support is thin. Busybee fills that gap. One gem, one Camunda Cloud account, and you're ready to start building. And when you're ready to scale further, Busybee is ready to grow with you, with battle-proven patterns for large distributed systems.
5
+ If you're a Ruby shop considering workflow orchestration, you might have noticed: Camunda Platform is powerful, but the Ruby ecosystem support is thin. Busybee fills that gap. One gem, one Camunda Cloud account, and you're ready to start building. And when you're ready to scale further, Busybee is ready to grow with you, with battle-proven patterns for large distributed systems.
6
6
 
7
- Busybee provides everything you need to work with Camunda Platform or self-hosted Zeebe from Ruby:
7
+ Busybee provides everything you need to work with Camunda Platform or self-hosted Zeebe in a Ruby world:
8
8
 
9
9
  - **Worker Pattern Framework** - Define job handlers as classes with a clean DSL. Busybee handles polling, execution, and lifecycle.
10
10
  - **Idiomatic Zeebe Client** - Ruby-native interface with keyword arguments, sensible defaults, and proper exception handling.
@@ -17,10 +17,10 @@ Busybee provides everything you need to work with Camunda Platform or self-hoste
17
17
  | Version | Features | Status |
18
18
  |---------|---------|--------|
19
19
  | v0.1 | BPMN Testing Tools, GRPC Layer | Available now! |
20
- | v0.2 | Client, Rails Integration | January 2026 |
20
+ | v0.2 | Client, Rails Integration | Available now! |
21
21
  | v0.3 | Worker Pattern & CLI | Early 2026 |
22
- | v0.4 | Instrumentation Hooks, Deployment Tools | Planned for Early 2026 |
23
- | v1.0 | Production Polish | Planned for Mid 2026 |
22
+ | v0.4 | Instrumentation Hooks, Deployment Tools | Mid 2026 |
23
+ | v1.0 | Production Polish | Late 2026 |
24
24
 
25
25
  ## Installation
26
26
 
@@ -74,20 +74,20 @@ Planned capabilities:
74
74
  - Graceful shutdown on SIGTERM
75
75
  - CLI for running workers: `bundle exec busybee work` or similar
76
76
 
77
- ### Idiomatic Zeebe Client (coming in January 2026)
77
+ ### Idiomatic Zeebe Client (available now!)
78
78
 
79
79
  A Ruby-native client for Zeebe with keyword arguments, sensible defaults, and proper exception handling.
80
80
 
81
- > This feature is still being designed. The example shown here is only representative and will change before implementation.
82
-
83
81
  ```ruby
82
+ # Connect to Camunda Cloud with environment variables
83
+ # (CAMUNDA_CLIENT_ID, CAMUNDA_CLIENT_SECRET, CAMUNDA_CLUSTER_ID, CAMUNDA_CLUSTER_REGION)
84
84
  client = Busybee::Client.new
85
85
 
86
86
  # Deploy a workflow
87
87
  client.deploy_process("workflows/order-fulfillment.bpmn")
88
88
 
89
89
  # Start a process instance
90
- instance_key = client.start_process("order-fulfillment",
90
+ instance_key = client.start_instance("order-fulfillment",
91
91
  vars: { order_id: "123", items: ["widget", "gadget"] }
92
92
  )
93
93
 
@@ -96,19 +96,27 @@ client.publish_message("payment-received",
96
96
  correlation_key: "order-123",
97
97
  vars: { amount: 99.99 }
98
98
  )
99
+
100
+ # Process jobs
101
+ client.with_each_job("send-confirmation") do |job|
102
+ EmailService.send(job.variables.customer_email)
103
+ job.complete!(sent_at: Time.now.iso8601)
104
+ end
99
105
  ```
100
106
 
101
- Planned capabilities:
107
+ Capabilities:
108
+
109
+ - Multiple credential types (Insecure, TLS, OAuth, Camunda Cloud) with automatic detection
110
+ - Complete process lifecycle: deploy, start, cancel, set variables, resolve incidents
111
+ - Job operations: activate, complete, fail, throw BPMN errors, streaming
112
+ - Rails integration via Railtie with `config.x.busybee.*` configuration
113
+ - GRPC error wrapping with configurable retry
102
114
 
103
- - Connection management with automatic reconnection
104
- - Multiple credential types (insecure, TLS, OAuth, Camunda Cloud)
105
- - GRPC error wrapping with preserved cause chains
106
- - Rails integration via Railtie and `config/busybee.yml`
107
- - Duration support for timeouts (works with ActiveSupport if present)
115
+ **[Full client documentation →](docs/client.md)**
108
116
 
109
117
  ### RSpec Testing Integration (available now!)
110
118
 
111
- Deploy processes, create instances, activate jobs, and verify workflow behavior against a real Zeebe instance. A full replacement for the [zeebe_bpmn_rspec](https://github.com/ezcater/zeebe_bpmn_rspec) gem.
119
+ Allows you to unit test your BPMN files. Deploy processes, create instances, activate jobs, and verify workflow behavior against a real Zeebe instance.
112
120
 
113
121
  #### Setup
114
122
 
@@ -117,8 +125,9 @@ Deploy processes, create instances, activate jobs, and verify workflow behavior
117
125
  require "rspec"
118
126
  require "busybee/testing"
119
127
 
120
- Busybee::Testing.configure do |config|
121
- config.address = "localhost:26500" # or use ZEEBE_ADDRESS env var
128
+ # Optional: defaults to localhost:26500, or set CLUSTER_ADDRESS env var
129
+ Busybee.configure do |config|
130
+ config.cluster_address = "localhost:26500"
122
131
  end
123
132
  ```
124
133
 
@@ -156,7 +165,7 @@ end
156
165
  - `assert_process_completed!` - Verify workflow reached an end event
157
166
  - `have_activated`, `have_received_variables`, `have_received_headers` - RSpec matchers
158
167
 
159
- **[Full testing documentation](docs/testing.md)**
168
+ **For more info, see our [full testing documentation here](docs/testing.md).**
160
169
 
161
170
  ### Deployment Tools (coming in early 2026)
162
171
 
@@ -166,6 +175,8 @@ CI/CD tooling for deploying BPMN processes to your Zeebe clusters. Version track
166
175
 
167
176
  For edge cases where the higher-level abstractions don't cover what you need, busybee exposes the raw GRPC interface to Zeebe. This is a complete drop-in replacement for the now-discontinued [zeebe-client](https://github.com/zeebe-io/zeebe-client-ruby) gem.
168
177
 
178
+ > Most users won't need this, as the Testing module, Client class, and Worker pattern cover most common use cases.
179
+
169
180
  ```ruby
170
181
  require "busybee/grpc"
171
182
 
@@ -179,17 +190,17 @@ response = stub.topology(request)
179
190
  puts response.brokers.map(&:host)
180
191
  ```
181
192
 
182
- Most users won't need this—the Testing module and future Client cover common workflows. See **[GRPC documentation](docs/grpc.md)** for details.
193
+ **For more info, see the [full GRPC documentation here](docs/grpc.md).**
183
194
 
184
195
  ## Ruby Implementation Support
185
196
 
186
- Busybee currently only supports MRI (CRuby). JRuby is not supported because it cannot run C extensions (it would require `grpc-java` with a Ruby wrapper). TruffleRuby's C extension support is experimental and the `grpc` gem does not currently build on it.
197
+ Busybee currently only supports MRI (CRuby). This is due to the state of `grpc` support on other implementations. JRuby is not supported because it cannot run C extensions (it would require `grpc-java` with a Ruby wrapper). TruffleRuby's C extension support is experimental and the `grpc` gem does not currently build on it.
187
198
 
188
- If you successfully run busybee on an alternative Ruby implementation, please open an issue. We'd welcome contributions to expand platform support!
199
+ If you successfully run busybee on an alternative Ruby implementation, please open a GitHub issue to let us know! We'd welcome contributions to expand platform support.
189
200
 
190
201
  ## Development
191
202
 
192
- Busybee includes a Docker Compose setup for running Zeebe locally, plus rake tasks for common development workflows.
203
+ Busybee includes a Docker Compose setup for running Zeebe locally, plus rake tasks for common development workflows:
193
204
 
194
205
  ```bash
195
206
  bin/setup # Install dependencies
@@ -199,7 +210,7 @@ bundle exec rspec # Run unit tests
199
210
  RUN_INTEGRATION_TESTS=1 bundle exec rspec # Run all tests including integration
200
211
  ```
201
212
 
202
- **[Full development guide](docs/development.md)** Local environment setup, running tests, regenerating GRPC classes, and release procedures.
213
+ **The full development guide for contributors [is available here](docs/development.md),** including local environment setup, running tests, regenerating GRPC classes, and release procedures.
203
214
 
204
215
  ## Contributing
205
216
 
@@ -0,0 +1,279 @@
1
+ # Busybee::Client Quick Start
2
+
3
+ This tutorial gets you from zero to a deployed process and running instance in about 10 minutes. By the end, you'll have:
4
+
5
+ 1. A Zeebe environment (local or Camunda Cloud)
6
+ 2. Busybee configured with appropriate credentials
7
+ 3. A BPMN process deployed
8
+ 4. A running process instance
9
+
10
+ ## Prerequisites
11
+
12
+ Before starting, you'll need:
13
+
14
+ - Ruby 3.2+
15
+ - A Zeebe environment (see "Choose Your Environment" below)
16
+ - A BPMN process file (we'll create a simple one, or you can use an existing one)
17
+
18
+ ## Step 1: Install Busybee
19
+
20
+ Add to your Gemfile:
21
+
22
+ ```ruby
23
+ gem "busybee"
24
+ ```
25
+
26
+ Then run:
27
+
28
+ ```bash
29
+ bundle install
30
+ ```
31
+
32
+ ## Step 2: Choose Your Environment
33
+
34
+ Busybee supports both local Zeebe (typical for development) and Camunda Cloud (recommended for production).
35
+
36
+ ### Option A: Local Zeebe
37
+
38
+ For local development, you can run Zeebe using Docker. The simplest setup is:
39
+
40
+ ```bash
41
+ docker run -d --name zeebe -p 26500:26500 camunda/zeebe:latest
42
+ ```
43
+
44
+ For a more complete setup with Operate (the web UI for monitoring workflows), see the [Camunda Docker Compose documentation](https://docs.camunda.io/docs/self-managed/setup/deploy/local/docker-compose/).
45
+
46
+ When using local Zeebe, you'll use insecure (non-TLS) connections.
47
+
48
+ ### Option B: Camunda Cloud
49
+
50
+ Camunda Cloud is Camunda's managed SaaS offering. To get started:
51
+
52
+ 1. Create a free account at [camunda.io](https://camunda.io/)
53
+ 2. Create a cluster (the free tier includes a development cluster)
54
+ 3. Create API credentials:
55
+ - Go to your cluster's "API" tab
56
+ - Click "Create new client"
57
+ - Select "Zeebe" scope
58
+ - Save the credentials (you'll need: Client ID, Client Secret, Cluster ID, and Region)
59
+
60
+ For detailed setup instructions, see [Camunda Cloud Getting Started](https://docs.camunda.io/docs/guides/getting-started/).
61
+
62
+ ## Step 3: Configure Credentials
63
+
64
+ A Busybee::Client needs to know where to find the Zeebe cluster and how to authenticate to it. There are many ways to provide or configure this information, which you can find in the [Client reference](../client.md). For the purpose of this tutorial, we'll pass this information directly to `new`:
65
+
66
+ ### For Local Zeebe
67
+
68
+ ```ruby
69
+ require "busybee"
70
+
71
+ client = Busybee::Client.new(
72
+ cluster_address: "localhost:26500",
73
+ insecure: true
74
+ )
75
+ ```
76
+
77
+ ### For Camunda Cloud
78
+
79
+ ```ruby
80
+ require "busybee"
81
+
82
+ client = Busybee::Client.new(
83
+ client_id: "your client ID",
84
+ client_secret: "your client secret",
85
+ cluster_id: "your cluster ID (typically a UUID)",
86
+ region: "your cluster region (e.g. 'bru-2')"
87
+ )
88
+ ```
89
+
90
+ ### Rails Configuration
91
+
92
+ In a Rails app, configure via `config/application.rb` or an initializer:
93
+
94
+ ```ruby
95
+ # config/application.rb or config/initializers/busybee.rb
96
+ Rails.application.configure do
97
+ config.x.busybee.credential_type = :camunda_cloud
98
+ config.x.busybee.cluster_address = ENV["CLUSTER_ADDRESS"] # for local/TLS
99
+ # OAuth credentials are passed to Client.new, not configured globally
100
+ end
101
+ ```
102
+
103
+ ## Step 4: Create a BPMN Process
104
+
105
+ BPMN (Business Process Model and Notation) files define your workflows. You can create them using:
106
+
107
+ - **Camunda Modeler** (desktop app) - Download from [camunda.com/download/modeler](https://camunda.com/download/modeler/)
108
+ - **Camunda Cloud Web Modeler** - Available in your Camunda Cloud console
109
+ - **Any BPMN 2.0 editor** - Busybee works with standard BPMN files
110
+
111
+ Here's a minimal example you can save as `simple_process.bpmn`:
112
+
113
+ ```xml
114
+ <?xml version="1.0" encoding="UTF-8"?>
115
+ <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
116
+ xmlns:zeebe="http://camunda.org/schema/zeebe/1.0"
117
+ id="Definitions_1"
118
+ targetNamespace="http://bpmn.io/schema/bpmn">
119
+ <bpmn:process id="hello-world" name="Hello World" isExecutable="true">
120
+ <bpmn:startEvent id="Start">
121
+ <bpmn:outgoing>Flow1</bpmn:outgoing>
122
+ </bpmn:startEvent>
123
+ <bpmn:endEvent id="End">
124
+ <bpmn:incoming>Flow1</bpmn:incoming>
125
+ </bpmn:endEvent>
126
+ <bpmn:sequenceFlow id="Flow1" sourceRef="Start" targetRef="End"/>
127
+ </bpmn:process>
128
+ </bpmn:definitions>
129
+ ```
130
+
131
+ This creates a process that starts and immediately ends. Real processes would include service tasks, gateways, and other BPMN elements.
132
+
133
+ For more about BPMN modeling, see the [Camunda BPMN documentation](https://docs.camunda.io/docs/components/modeler/bpmn/).
134
+
135
+ ## Step 5: Deploy the Process
136
+
137
+ Deploy your BPMN file to Zeebe:
138
+
139
+ ```ruby
140
+ # Deploy a single file
141
+ result = client.deploy_process("simple_process.bpmn")
142
+ # => { "hello-world" => 2251799813685249 }
143
+
144
+ # The result maps process IDs to definition keys
145
+ process_id = result.keys.first # => "hello-world"
146
+ definition_key = result.values.first # => 2251799813685249
147
+
148
+ # Deploy multiple files at once
149
+ result = client.deploy_process("order.bpmn", "payment.bpmn")
150
+ # => { "order-process" => 123, "payment-process" => 456 }
151
+ ```
152
+
153
+ The returned hash maps BPMN process IDs (the `id` attribute on the `<bpmn:process>` element) to process definition keys (unique identifiers assigned by Zeebe).
154
+
155
+ ## Step 6: Start a Process Instance
156
+
157
+ Now start an instance of your deployed process:
158
+
159
+ ```ruby
160
+ # Start a basic instance
161
+ instance_key = client.start_instance("hello-world")
162
+ # => 2251799813685300
163
+
164
+ # Start with variables
165
+ instance_key = client.start_instance("order-process",
166
+ vars: { order_id: "ORD-123", customer: "Alice", total: 99.99 }
167
+ )
168
+
169
+ # Start a specific version (not the latest)
170
+ instance_key = client.start_instance("order-process",
171
+ vars: { order_id: "ORD-456" },
172
+ version: 2
173
+ )
174
+ ```
175
+
176
+ The returned `instance_key` is the unique identifier for this process instance.
177
+
178
+ ## Putting It All Together
179
+
180
+ Here's a complete example:
181
+
182
+ ```ruby
183
+ require "busybee"
184
+
185
+ # Configure for local development
186
+ client = Busybee::Client.new(insecure: true)
187
+
188
+ # Deploy the process
189
+ deployments = client.deploy_process("workflows/order-fulfillment.bpmn")
190
+ puts "Deployed: #{deployments.keys.join(', ')}"
191
+
192
+ # Start an instance
193
+ instance_key = client.start_instance("order-fulfillment",
194
+ vars: {
195
+ order_id: "ORD-2024-001",
196
+ items: ["widget", "gadget"],
197
+ total: 149.99
198
+ }
199
+ )
200
+ puts "Started instance: #{instance_key}"
201
+
202
+ # You can now monitor this instance in Operate (web UI)
203
+ # or interact with it via messages and signals
204
+ ```
205
+
206
+ ## What's Next?
207
+
208
+ Now that you have a running process instance, you might want to:
209
+
210
+ - **Cancel an instance**: `client.cancel_instance(instance_key)`
211
+ - **Publish a message** to trigger a message catch event: `client.publish_message("payment-received", correlation_key: "ORD-2024-001", vars: { amount: 149.99 })`
212
+ - **Broadcast a signal** to all waiting instances: `client.broadcast_signal("system-shutdown")`
213
+ - **Set variables** on a running instance: `client.set_variables(instance_key, vars: { status: "approved" })`
214
+
215
+ See the API Reference section below for complete documentation of all available methods.
216
+
217
+ # Credential Types
218
+
219
+ Busybee supports four credential types for different environments:
220
+
221
+ | Type | Use Case | TLS | Authentication |
222
+ |------|----------|-----|----------------|
223
+ | `:insecure` | Local development, Docker, CI | No | None |
224
+ | `:tls` | Self-hosted with TLS | Yes | Server cert only |
225
+ | `:oauth` | Self-hosted with OAuth | Yes | OAuth2 client credentials |
226
+ | `:camunda_cloud` | Camunda Cloud SaaS | Yes | OAuth2 (auto-configured) |
227
+
228
+ ## Insecure Credentials
229
+
230
+ For local development with no TLS or authentication:
231
+
232
+ ```ruby
233
+ client = Busybee::Client.new(insecure: true)
234
+
235
+ # Or with explicit address
236
+ client = Busybee::Client.new(insecure: true, cluster_address: "zeebe:26500")
237
+ ```
238
+
239
+ ## TLS Credentials
240
+
241
+ For self-hosted Zeebe with TLS but no client authentication:
242
+
243
+ ```ruby
244
+ client = Busybee::Client.new(
245
+ cluster_address: "zeebe.example.com:443",
246
+ certificate_file: "/path/to/ca.crt" # Optional: custom CA certificate
247
+ )
248
+ ```
249
+
250
+ ## OAuth Credentials
251
+
252
+ For self-hosted Zeebe with OAuth2 authentication:
253
+
254
+ ```ruby
255
+ client = Busybee::Client.new(
256
+ cluster_address: "zeebe.example.com:443",
257
+ token_url: "https://auth.example.com/oauth/token",
258
+ client_id: "my-client-id",
259
+ client_secret: "my-client-secret",
260
+ audience: "zeebe.example.com"
261
+ )
262
+ ```
263
+
264
+ ## Camunda Cloud Credentials
265
+
266
+ For Camunda Cloud, the cluster address and OAuth configuration are derived automatically:
267
+
268
+ ```ruby
269
+ client = Busybee::Client.new(
270
+ client_id: ENV["CAMUNDA_CLIENT_ID"],
271
+ client_secret: ENV["CAMUNDA_CLIENT_SECRET"],
272
+ cluster_id: ENV["CAMUNDA_CLUSTER_ID"],
273
+ region: ENV["CAMUNDA_CLUSTER_REGION"] # e.g., "bru-2", "us-east-1"
274
+ )
275
+ ```
276
+
277
+ # Next Steps
278
+
279
+ For complete API documentation, error handling details, and advanced usage, see the [Client documentation](../client.md).