Skip to content

Commit

Permalink
docs(frontend): edits
Browse files Browse the repository at this point in the history
  • Loading branch information
yuxizama authored and bcm-at-zama committed Aug 30, 2024
1 parent 2afc3d2 commit 5716912
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 49 deletions.
77 changes: 41 additions & 36 deletions docs/guides/deploy.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Deploy
This document explains how to deploy a circuit after the development. After developing your circuit, you may want to deploy it without sharing the circuit's details with every client or hosting computations on dedicated servers. In this scenario, you can use the `Client` and `Server` features of **Concrete**.

After developing your circuit, you may want to deploy it. However, sharing the details of your circuit with every client might not be desirable. As well as this, you might want to perform the computation on dedicated servers. In this case, you can use the `Client` and `Server` features of **Concrete**.
## Deployment process

In a typical **Concrete** deployment:

- The server hosts the compilation artifact, including client specifications and the FHE executable.
- The client requests circuit requirements, generates keys, sends an encrypted payload, and receives an encrypted result.

### Deployment

A typical Concrete deployment will host on a server the compilation artifact: Client specifications required by the compiled circuits and the fhe executable itself. Client will ask for the circuit requirements, generate keys accordingly, then it will send an encrypted payload and receive an encrypted result.

```mermaid
sequenceDiagram
Expand All @@ -17,9 +21,10 @@ sequenceDiagram
Client->>Client: Decrypt(result)
```

## Starting from an example
## Example
Follow these steps to deploy your circuit:

You can develop your circuit using the techniques discussed in previous chapters. Let's take the following example to describe deployment:
1. **Develop the circuit**: You can develop your own circuit using the techniques discussed in previous chapters. Here is an example.

<!--pytest-codeblocks:skip-->
```python
Expand All @@ -32,19 +37,19 @@ def function(x):
inputset = range(10)
circuit = function.compile(inputset)
```

Once you have your circuit, you can save everything the server needs:
2. **Save the server files**: Once you have your circuit, save the necessary server files.

<!--pytest-codeblocks:skip-->
```python
circuit.server.save("server.zip")
```

Then, send `server.zip` to your computation server.
3. **Send the server files**: Send `server.zip` to your computation server.

## Setting up a server
### Setting up a server

4. **Load the server files**: To set up the server, load the `server.zip` file received from the development machine.

You can load the `server.zip` you get from the development machine:

<!--pytest-codeblocks:skip-->
```python
Expand All @@ -53,97 +58,95 @@ from concrete import fhe
server = fhe.Server.load("server.zip")
```

You will need to wait for requests from clients. The first likely request is for `ClientSpecs`.
5. **Prepare for client requests**: The server needs to wait for the requests from clients.

Clients need `ClientSpecs` to generate keys and request computation. You can serialize `ClientSpecs`:
6. **Serialize `ClientSpecs`**: The requests typically starts with `ClientSpecs` as clients need `ClientSpecs` to generate keys and request computation.

<!--pytest-codeblocks:skip-->
```python
serialized_client_specs: str = server.client_specs.serialize()
```

Then, you can send it to the clients requesting it.
7. **Send serialized `ClientSpecs` to clients.**

## Setting up clients
### Setting up clients

After getting the serialized `ClientSpecs` from a server, you can create the client object:
8. **Create the client object**: After receiving the serialized `ClientSpecs` from a server, create the `Client` object.

<!--pytest-codeblocks:skip-->
```python
client_specs = fhe.ClientSpecs.deserialize(serialized_client_specs)
client = fhe.Client(client_specs)
```

## Generating keys (on the client)
### Generating keys (client-side)

Once you have the `Client` object, you can perform key generation:
9. **Generate keys**: Once you have the `Client` object, perform key generation. This method generates encryption/decryption keys and evaluation keys.

<!--pytest-codeblocks:skip-->
```python
client.keys.generate()
```

This method generates encryption/decryption keys and evaluation keys.

The server needs access to the evaluation keys that were just generated. You can serialize your evaluation keys as shown:
10. **Serialize the evaluation keys**: The server needs access to the evaluation keys. You can serialize your evaluation keys as below.

<!--pytest-codeblocks:skip-->
```python
serialized_evaluation_keys: bytes = client.evaluation_keys.serialize()
```

After serialization, send the evaluation keys to the server.
11. **Send the evaluation keys to the server**.

{% hint style="info" %}
Serialized evaluation keys are very large, so you may want to cache them on the server instead of sending them with each request.
Serialized evaluation keys are very large. Consider caching them on the server instead of sending them with each request.
{% endhint %}

## Encrypting inputs (on the client)
### Encrypting inputs (client-side)

The next step is to encrypt your inputs and request the server to perform some computation. This can be done in the following way:
12. **Encrypt inputs**: Encrypt your inputs and request the server to perform some computation. This can be done in the following way.

<!--pytest-codeblocks:skip-->
```python
arg: fhe.Value = client.encrypt(7)
serialized_arg: bytes = arg.serialize()
```

Then, send the serialized arguments to the server.
13. **Send the serialized arguments to the server**.

## Performing computation (on the server)
### Performing computation (server-side)

Once you have serialized evaluation keys and serialized arguments, you can deserialize them:
14. **Deserialize received data**: On the server, deserialize the received evaluation keys and arguments received from the client.

<!--pytest-codeblocks:skip-->
```python
deserialized_evaluation_keys = fhe.EvaluationKeys.deserialize(serialized_evaluation_keys)
deserialized_arg = fhe.Value.deserialize(serialized_arg)
```

You can perform the computation, as well:
15. **Run the computation**: Perform the computation and serialize the result.

<!--pytest-codeblocks:skip-->
```python
result: fhe.Value = server.run(deserialized_arg, evaluation_keys=deserialized_evaluation_keys)
serialized_result: bytes = result.serialize()
```

Then, send the serialized result back to the client. After this, the client can decrypt to receive the result of the computation.
16. **Send the serialized result to the client**:

{% hint style="info" %}
Clear arguments can directly be passed to `server.run` (e.g., `server.run(x, 10, z, evaluation_keys=...)`).
Clear arguments can directly be passed to `server.run` (For example, `server.run(x, 10, z, evaluation_keys=...)`).
{% endhint %}

## Decrypting the result (on the client)

Once you have received the serialized result of the computation from the server, you can deserialize it:
17. **Deserialize the result**: Once you receive the serialized result from the server, deserialize it.

<!--pytest-codeblocks:skip-->
```python
deserialized_result = fhe.Value.deserialize(serialized_result)
```

Then, decrypt the result:
18. **Decrypt the deserialized result**:

<!--pytest-codeblocks:skip-->
```python
Expand All @@ -153,7 +156,9 @@ assert decrypted_result == 49

# Deployment of modules

Deploying a [module](../compilation/composing_functions_with_modules.md) follows the same logic as the deployment of circuits. Assuming a module compiled in the following way:
Deploying a [module](../compilation/composing_functions_with_modules.md) follows the same logic as the deployment of circuits.

For example, consider a module compiled in the following way:

<!--pytest-codeblocks:skip-->
```python
Expand Down Expand Up @@ -183,23 +188,23 @@ my_module.server.save("server.zip")

The only noticeable difference between the deployment of modules and the deployment of circuits is that the methods `Client::encrypt`, `Client::decrypt` and `Server::run` must contain an extra `function_name` argument specifying the name of the targeted function.

The encryption of an argument for the `inc` function of the module would be:
For example, to encrypt an argument for the `inc` function of the module:

<!--pytest-codeblocks:skip-->
```python
arg = client.encrypt(7, function_name="inc")
serialized_arg = arg.serialize()
```

The execution of the `inc` function would be :
To execute the `inc` function:

<!--pytest-codeblocks:skip-->
```python
result = server.run(deserialized_arg, evaluation_keys=deserialized_evaluation_keys, function_name="inc")
serialized_result = result.serialize()
```

Finally, decrypting a result from the execution of `dec` would be:
To decrypt a result from the execution of `dec`:

<!--pytest-codeblocks:skip-->
```python
Expand Down
28 changes: 15 additions & 13 deletions docs/guides/manage_keys.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Manage Keys
This document explains how to manage keys when using **Concrete**, introducing the key management API for generating, reusing, and securely handling keys.

**Concrete** generates keys for you implicitly when they are needed and if they have not already been generated. This is useful for development, but it's not flexible **(or secure!)** for production. Explicit key management API is introduced to be used in such cases to easily generate and re-use keys.
**Concrete** generates keys implicitly when needed. While this is convenient for development, it's not ideal for the production environment. The explicit key management API is available for you to easily generate and reuse keys as needed.

## Definition

Let's start by defining a circuit:
Let's start by defining a circuit with the following example:

```python
from concrete import fhe
Expand All @@ -17,21 +18,21 @@ inputset = range(10)
circuit = f.compile(inputset)
```

Circuits have a property called `keys` of type `fhe.Keys`, which has several utility functions dedicated to key management!
Circuits have a `keys` property of type `fhe.Keys`, which includes several utilities for key management.

## Generation

To explicitly generate keys for a circuit, you can use:
To explicitly generate keys for a circuit, use:

```python
circuit.keys.generate()
```

{% hint style="info" %}
Generated keys are stored in memory upon generation, unencrypted.
Generated keys are stored in memory and remain unencrypted.
{% endhint %}

And it's possible to set a custom seed for reproducibility:
You can also set a custom seed for reproducibility:

```python
circuit.keys.generate(seed=420)
Expand All @@ -43,19 +44,20 @@ Do not specify the seed manually in a production environment!

## Serialization

To serialize keys, say to send it across the network:
To serialize keys, for tasks such as sending them across a network, use:


```python
serialized_keys: bytes = circuit.keys.serialize()
```

{% hint style="warning" %}
Keys are not serialized in encrypted form! Please make sure you keep them in a safe environment, or encrypt them manually after serialization.
Keys are not serialized in encrypted form. Please make sure you keep them in a safe environment, or encrypt them manually after serialization.
{% endhint %}

## Deserialization

To deserialize the keys back, after receiving serialized keys:
To deserialize the keys back after receiving serialized keys, use:

```python
keys: fhe.Keys = fhe.Keys.deserialize(serialized_keys)
Expand All @@ -75,27 +77,27 @@ If assigned keys are generated for a different circuit, an exception will be rai

## Saving

You can also use the filesystem to store the keys directly, without needing to deal with serialization and file management yourself:
You can also use the filesystem to store the keys directly, without managing serialization and file management manually:

```python
circuit.keys.save("/path/to/keys")
```

{% hint style="warning" %}
Keys are not saved encrypted! Please make sure you store them in a safe environment, or encrypt them manually after saving.
Keys are not saved in encrypted form. Please make sure you store them in a safe environment, or encrypt them manually after saving.
{% endhint %}

## Loading

After keys are saved to disk, you can load them back via:
After saving keys to disk, you can load them back using:

```python
circuit.keys.load("/path/to/keys")
```

## Automatic Management

If you want to generate keys in the first run and reuse the keys in consecutive runs:
If you want to generate keys in the first run and reuse the keys in consecutive runs, use:

```python
circuit.keys.load_if_exists_generate_and_save_otherwise("/path/to/keys")
Expand Down

0 comments on commit 5716912

Please sign in to comment.