Compare commits

...

18 Commits

Author SHA1 Message Date
vinhtc27
df73e89dfc chore: trigger CI 2026-01-10 00:03:02 +07:00
vinhtc27
8854180f9e doc(zerokit-api): add error handling section for each platform 2026-01-09 23:52:42 +07:00
vinhtc27
6157167a75 doc(zerokit-api): address PR comments 2026-01-06 18:02:22 +07:00
vinhtc27
376b60efcd doc(zerokit-api): add contributors and editor 2026-01-06 17:28:42 +07:00
vinhtc27
444eea0876 doc(zerokit-api): remove message_id from Important Note 2026-01-05 22:29:43 +07:00
vinhtc27
fd9f767fcc doc(zerokit-api): mention message_id in the Important Note section 2026-01-05 22:24:02 +07:00
seugu
99a11e7e08 sembr update 2026-01-05 11:33:11 +03:00
vinhtc27
3cd37b4538 doc(zerokit-api): initial draft for Zerokit API RFC 2026-01-05 13:15:48 +07:00
seugu
b9a08305bb lint 2025-12-15 12:32:18 +03:00
seugu
bf198face6 initial commit 2025-12-15 12:28:44 +03:00
Prem Chaitanya Prathi
dabc31786b fixing format errors in mix rfc (#229)
<img width="1158" height="635" alt="image"
src="https://github.com/user-attachments/assets/3f3582b4-77b2-4eb5-a7ea-12b60951303c"
/>
2025-12-15 13:26:43 +05:30
Cofson
b2f35644a4 Improved codex/raw/codex-block-exchange.md file (#215)
Improved codex-block-exchange.md file in codex/raw folder
2025-12-12 12:10:02 +01:00
Prem Chaitanya Prathi
4f54254706 fix format errors in math sections for mix rfc (#225) 2025-12-12 14:59:14 +05:30
Prem Chaitanya Prathi
7f1df32779 chore: use sembreaks for easy review and edits (#223)
Modified the mix spec to use sembreaks and not break line at charater limits
as per
https://github.com/vacp2p/rfc-index/pull/194#pullrequestreview-3562274262
2025-12-11 21:02:15 +05:30
AkshayaMani
e742cd5192 RFC Addition: Section 9 Security Considerations (#194)
This PR continues work from PR #158 and PR #173, and introduces a new
**Section 9: Security Considerations** to the Mix Protocol RFC. It
formalizes the protocol’s core guarantees, trust assumptions, and known
limitations.

### New Section Added

Structured Section 9 with the following subsections:

- [x] **9.1 Security Guarantees of the Core Mix Protocol**
Defines sender anonymity, metadata protection, and statelessness
guarantees.

- [x] **9.2 Exit Node Trust Model**
  Trust assumptions at the final hop:

  - [x] `9.2.1 Message Delivery and Origin Trust`
  - [x] `9.2.2 Origin Protocol Trust and Client Role Abuse`

- [x] **9.3 Destination as Final Hop**
Optional deployment model where the destination operates its own Mix
instance to eliminate exit-level trust.

- [x] **9.4 Known Protocol Limitations**
  Clearly outlines out-of-scope threats:
  - Undetectable node misbehavior
  - Lack of built-in retries or acknowledgments
  - No Sybil resistance
  - Vulnerability to DoS attacks

### Key Improvements
- Clearly delineates what the Mix Protocol guarantees and what it leaves
to external systems.
- Formalizes the exit trust boundary, a key concept for downstream
applications.
- Introduces an alternative destination participation model.
- Enables future discussions around accountability, reliability, and
Sybil resistance.

---------

Co-authored-by: Prem Chaitanya Prathi <chaitanyaprem@gmail.com>
2025-12-10 20:15:32 +05:30
AkshayaMani
9d11a22901 docs: finalize Section 8 Sphinx Packet Construction and Handling (#202)
This PR builds on PR #173 and completes the remaining construction and
runtime processing logic in `Section 8` of the Mix Protocol RFC. It
finalizes the last steps of packet construction (`Section 8.5.2 step 3.
e–f`) and introduces the complete mix node handler logic in `Section
8.6`, including intermediary and exit processing.
It clearly separates construction, role determination, and processing
logic.

### Changes Introduced in This PR

- **8.5.2 Construction Steps (Final Steps Added)**
  - Sphinx packet construction
    - [x] Assemble Final Packet
    - [x] Transmit Packet
    
- **8.6 Sphinx Packet Handling**
  - [x] **8.6.1 Shared Preprocessing**
- Derives session key, validates replay tag and MAC, decrypts
header/payload
  - [x] **8.6.2 Node Role Determination**
- Inspects decrypted header prefix and padding to classify node as
intermediary or exit
  - [x] **8.6.3 Intermediary Processing**
    - Parses next hop address and mean delay
    - Updates ephemeral key and routing fields
    - Samples actual forwarding delay and transmits packet
    - Erases all temporary state.
  - [x] **8.6.4 Exit Processing**
    - Verifies payload padding and extracts destination address
    - Parses and validates application-layer message
- Hands off to Exit Layer along with origin protocol codec and
destination address

### Highlights
  - Explicit role determination via zero-delay and padding inspection
  - Fully decoupled construction and handling logic
  - Forwarding delay behavior updated:
    - Sender selects per-hop mean delay
    - Mix node samples actual delay using pluggable distribution

---------

Co-authored-by: kaiserd <1684595+kaiserd@users.noreply.github.com>
2025-12-10 12:24:23 +00:00
Arunima Chaudhuri
aaf158aa59 VAC/RAW/LOGOS-DISCOVERY-CAPABILITY RFC (#212)
This RFC defines the Logos discovery capability, a DISC-NG-inspired
discovery mechanism built on top of Kad-dht.

---------

Co-authored-by: Hanno Cornelius <68783915+jm-clius@users.noreply.github.com>
Co-authored-by: seugu <99656002+seugu@users.noreply.github.com>
2025-12-09 14:52:01 +05:30
seugu
e39d2884fe VAC/RAW/ ETH-MLS-OFFCHAIN RFC multi-steward support (#193)
adding done: 
- multi steward support sections such as: consensus types, big and small
type flows
- new two de-mls term epoch and backup steward
- violation list section
- who can initiate the consensuses
- deterministically creation of the steward list (against biased list)
- order of the consensus messages
- adding member-id as 160 bit id
- adding flexible committing for multiple steward
- unifying the commit among multiple committing for specific epoch
- clarifying minimum number of steward list s_min and the scenario that
group member number getting below this

next (in a separate PR)
- introducing peer scoring (currently we only kicked off malicious
members)
- anti deadlock mechanism in case of non-active steward (even if the
committing is flexible now)
- identifying the format of dishonesty evidence

---------

Co-authored-by: Ekaterina Broslavskaya <seemenkina@gmail.com>
Co-authored-by: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com>
2025-11-26 19:12:01 +03:00
5 changed files with 3988 additions and 895 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -54,11 +54,15 @@ are to be interpreted as described in [2119](https://www.ietf.org/rfc/rfc2119.tx
The three roles used in de-MLS is as follows:
- `node`: Nodes are members of network without being in any secure group messaging.
- `node`: Nodes are participants in the network that are not currently members
of any secure group messaging session but remain available as potential candidates for group membership.
- `member`: Members are special nodes in the secure group messaging who
obtains current group key of secure group messaging.
- `steward`: Stewards are special and transparent members in secure group
messaging who organizes the changes upon the voted-proposals.
Each node is assigned a unique identity represented as a 20-byte value named `member id`.
- `steward`: Stewards are special and transparent members in the secure group
messaging who organize the changes by releasing commit messages upon the voted proposals.
There are two special subsets of steward as epoch and backup steward,
which are defined in the section de-MLS Objects.
## MLS Background
@@ -78,7 +82,7 @@ manage the `keyPackage` of the users where the `keyPackage` is the objects
Following section presents the MLS objects and components that used in this RFC:
`Epoch`: Fixed time intervals that changes the state that is defined by members,
`Epoch`: Time intervals that changes the state that is defined by members,
section 3.4 in [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/).
`MLS proposal message:` Members MUST receive the proposal message prior to the
@@ -90,6 +94,7 @@ Here, the add and remove proposals are used.
This is restricted by [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) as if there is pending proposal,
the application message should be cut.
Note that: Since the MLS is based on servers, this delay between proposal and commit messages are very small.
`Commit message:` After members receive the proposals regarding group changes,
the committer, who may be any member of the group, as specified in [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/),
generates the necessary key material for the next epoch, including the appropriate welcome messages
@@ -97,21 +102,48 @@ for new joiners and new entropy for removed members. In this RFC, the committers
### de-MLS Objects
`Voting proposal` Similar to MLS proposals, but processed only if approved through a voting process.
This section presents the de-MLS objects:
`Voting proposal`: Similar to MLS proposals, but processed only if approved through a voting process.
They function as application messages in the MLS group,
allowing the steward to collect them without halting the protocol.
There are three types of `voting proposal` according to the type of consensus as in shown Consensus Types section,
these are, `commit proposal`, `steward election proposal` and `emergency criteria proposal`.
`Epoch steward`: The steward assigned to commit in `epoch E` according to the steward list.
Holds the primary responsibility for creating commit in that epoch.
`Backup steward`: The steward next in line after the `epoch steward` on the `steward list` in `epoch E`.
Only becomes active if the `epoch steward` is malicious or fails,
in which case it completes the commitment phase.
If unused in `epoch E`, it automatically becomes the `epoch steward` in `epoch E+1`.
`Steward list`: It is an ordered list that contains the `member id`s of authorized stewards.
Each steward in the list becomes main responsible for creating the commit message when its turn arrives,
according to this order for each epoch.
For example, suppose there are two stewards in the list `steward A` first and `steward B` last in the list.
`steward A` is responsible for creating the commit message for first epoch.
Similarly, `steward B` is for the last epoch.
Since the `epoch steward` is the primary committer for an epoch,
it holds the main responsibility for producing the commit.
However, other stewards MAY also generate a commit within the same epoch to preserve liveness
in case the epoch steward is inactive or slow.
Duplicate commits are not re-applied and only the single valid commit for the epoch is accepted by the group,
as in described in section filtering proposals against the multiple comitting.
Therefore, if a malicious steward occurred, the `backup steward` will be charged with committing.
Lastly, the size of the list named as `sn`, which also shows the epoch interval for steward list determination.
## Flow
General flow is as follows:
- A steward initializes a group just once, and then sends out Group Announcements (GA) periodically.
- Meanwhile, each`node`creates and sends their`credential` includes `keyPackage`.
- Each `member`creates `voting proposals` sends them to from MLS group during epoch E.
- Meanwhile, each `node` creates and sends their `credential` includes `keyPackage`.
- Each `member` creates `voting proposals` sends them to from MLS group during `epoch E`.
- Meanwhile, the `steward` collects finalized `voting proposals` from MLS group and converts them into
`MLS proposals` then sends them with correspondng `commit messages`
- Evantually, with the commit messages, all members starts the next epoch E+1.
`MLS proposals` then sends them with corresponding `commit messages`
- Evantually, with the commit messages, all members starts the next `epoch E+1`.
## Creating Voting Proposal
@@ -179,57 +211,220 @@ This is mostly similar with the general flow and specified in voting proposal an
as in section 8.1. Group Context in [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/).
2. `steward` creates a group anouncement (GA) according to the previous step and
broadcast it to the all network periodically. GA message is visible in network to all `nodes`.
3. The each `node` who wants to be a member needs to obtain this anouncement and create `credential`
3. The each `node` who wants to be a `member` needs to obtain this anouncement and create `credential`
includes `keyPackage` that is specified in [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) section 10.
4. The `steward` aggregates all `KeyPackages` utilizes them to provision group additions for new members,
4. The `node` send the `KeyPackages` in plaintext with its signature with current `steward` public key which
anounced in welcome topic. This step is crucial for security, ensuring that malicious nodes/stewards
cannot use others' `KeyPackages`.
It also provides flexibility for liveness in multi-steward settings,
allowing more than one steward to obtain `KeyPackages` to commit.
5. The `steward` aggregates all `KeyPackages` utilizes them to provision group additions for new members,
based on the outcome of the voting process.
5. Any `member` start to create `voting proposals` for adding or removing users,
6. Any `member` start to create `voting proposals` for adding or removing users,
and present them to the voting in the MLS group as an application message.
However, unlimited use of `voting proposals` within the group may be misused by
malicious or overly active members.
Therefore, an application-level constraint can be introduced to limit the number
or frequency of proposals initiated by each member to prevent spam or abuse.
6. Meanwhile, the `steward` collects finalized `voting proposals` with in epoch `E`,
7. Meanwhile, the `steward` collects finalized `voting proposals` with in epoch `E`,
that have received affirmative votes from members via application messages.
Otherwise, the `steward` discards proposals that did not receive a majority of "YES" votes.
Since voting proposals are transmitted as application messages, omitting them does not affect
the protocols correctness or consistency.
7. The `steward` converts all approved `voting proposals` into
8. The `steward` converts all approved `voting proposals` into
corresponding `MLS proposals` and `commit message`, and
transmits both in a single operation as in [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) section 12.4,
including welcome messages for the new members. Therefore, the `commit message` ends the previous epoch and create new ones.
including welcome messages for the new members.
Therefore, the `commit message` ends the previous epoch and create new ones.
9. The `members` applied the incoming `commit message` by checking the signatures and `voting proposals`
and synchronized with the upcoming epoch.
## Multi stewards
Decentralization has already been achieved in the previous section.
However, to improve availability and ensure censorship resistance,
the single-steward protocol is extended to a multi-steward architecture.
the single steward protocol is extended to a multi steward architecture.
In this design, each epoch is coordinated by a designated steward,
operating under the same protocol as the single-steward model.
Thus, the multi-steward approach primarily defines how steward roles
operating under the same protocol as the single steward model.
Thus, the multi steward approach primarily defines how steward roles
rotate across epochs while preserving the underlying structure and logic of the original protocol.
Two variants of the multi-steward design are introduced to address different system requirements.
Two variants of the multi steward design are introduced to address different system requirements.
### Multi steward with single consensus
### Consensus Types
Consensus is agnostic with its payload; therefore, it can be used for various purposes.
Note that each message for the consensus of proposals is an `application message` in the MLS object section.
It is used in three ways as follows:
1. `Commit proposal`: It is the proposal instance that is specified in Creating Voting Proposal section
with `Proposal.payload` MUST show the commit request from `members`.
Any member MAY create this proposal in any epoch and `epoch steward` MUST collect and commit YES voted proposals.
This is the only proposal type common to both single steward and multi steward designs.
2. `Steward election proposal`: This is the process that finalizes the `steward list`,
which sets and orders stewards responsible for creating commits over a predefined number of range in (`sn_min`,`sn_max`).
The validity of the choosen `steward list` ends when the last steward in the list (the one at the final index) completes its commit.
At that point, a new `steward election proposal` MUST be initiated again by any member during the corresponding epoch.
The `Proposal.payload` field MUST represent the ordered identities of the proposed stewards.
Each steward election proposal MUST be verified and finalized through the consensus process
so that members can identify which steward will be responsible in each epoch
and detect any unauthorized steward commits.
3. `Emergency criteria proposal`: If there is a malicious member or steward,
this event MUST be voted on to finalize it.
If this returns YES, the next epoch MUST include the removal of the member or steward.
In a specific case where a steward is removed from the group, causing the total number of stewards to fall below `sn_min`,
it is required to repeat the `steward election proposal`.
`Proposal.payload` MUST consist of the evidence of the dishonesty as described in the Steward violation list,
and the identifier of the malicious member or steward.
This proposal can be created by any member in any epoch.
The order of consensus proposal messages is important to achieving a consistent result.
Therefore, messages MUST be prioritized by type in the following order, from highest to lowest priority:
- `Emergency criteria proposal`
- `Steward election proposal`
- `Commit proposal`
This means that if a higher-priority consensus proposal is present in the network,
lower-priority messages MUST be withheld from transmission until the higher-priority proposals have been finalized.
### Steward list creation
The `steward list` consists of steward nominees who will become actual stewards if the `steward election proposal` is finalized with YES,
is arbitrarily chosen from `member` and OPTIONALLY adjusted depending on the needs of the implementation.
The `steward list` size, defined by the minimum `sn_min` and maximum `sn_max` bounds,
is determined at the time of group creation.
The `sn_min` requirement is applied only when the total number of members exceeds `sn_min`;
if the number of available members falls below this threshold,
the list size automatically adjusts to include all existing members.
The actual size of the list MAY vary within this range as `sn`, with the minimum value being at least 1.
The index of the slots shows epoch info and value of index shows `member id`s.
The next in line steward for the `epoch E` is named as `epoch steward`, which has index E.
And the subsequent steward in the `epoch E` is named as the `backup steward`.
For example, let's assume steward list is (S3, S2, S1) if in the previous epoch the roles were
(`backup steward`: S2, `epoch steward`: S1), then in the next epoch they become
(`backup steward`: S3, `epoch steward`: S2) by shifting.
If the `epoch steward` is honest, the `backup steward` does not involve the process in epoch,
and the `backup steward` will be the `epoch steward` within the `epoch E+1`.
If the `epoch steward` is malicious, the `backup steward` is involved in the commitment phase in `epoch E`
and the former steward becomes the `backup steward` in `epoch E`.
Liveness criteria:
Once the active `steward list` has completed its assigned epochs,
members MUST proceed to elect the next set of stewards
(which MAY include some or all of the previous members).
This election is conducted through a type 2 consensus procedure, `steward election proposal`.
A `Steward election proposal` is considered valid only if the resulting `steward list`
is produced through a deterministic process that ensures an unbiased distribution of steward assignments,
since allowing bias could enable a malicious participant to manipulate the list
and retain control within a favored group for multiple epochs.
The list MUST consist of at least `sn_min` members, including retained previous stewards,
sorted according to the ascending value of `SHA256(epoch E || member id || group id)`,
where `epoch E` is the epoch in which the election proposal is initiated,
and `group id` for shuffling the list across the different groups.
Any proposal with a list that does not adhere to this generation method MUST be rejected by all members.
We assume that there are no recurring entries in `SHA256(epoch E || member id || group id)`, since the SHA256 outputs are unique
when there is no repetition in the `member id` values, against the conflicts on sorting issues.
### Multi steward with big consensuses
In this model, all group modifications, such as adding or removing members,
must be approved through consensus by all participants,
including the steward assigned for epoch `E`.
including the steward assigned for `epoch E`.
A configuration with multiple stewards operating under a shared consensus protocol offers
increased decentralization and stronger protection against censorship.
However, this benefit comes with reduced operational efficiency.
The model is therefore best suited for small groups that value
decentralization and censorship resistance more than performance.
### Multi steward with two consensuses
To create a multi steward with a big consensus,
the group is initialized with a single steward as specified as follows:
The two-consensus model offers improved efficiency with a trade-off in decentralization.
1. The steward initialized the group with the config file.
This config file MUST contain (`sn_min`,`sn_max`) as the `steward list` size range.
2. The steward adds the members as a centralized way till the number of members reaches the `sn_min`.
Then, members propose lists by voting proposal with size `sn`
as a consensus among all members, as mentioned in the consensus section 2, according to the checks:
the size of the proposed list `sn` is in the interval (`sn_min`,`sn_max`).
Note that if the total number of members is below `sn_min`,
then the steward list size MUST be equal to the total member count.
3. After the voting proposal ends up with a `steward list`,
and group changes are ready to be committed as specified in single steward section
with a difference which is members also check the committed steward is `epoch steward` or `backup steward`,
otherwise anyone can create `emergency criteria proposal`.
4. If the `epoch steward` violates the changing process as mentioned in the section Steward violation list,
one of the members MUST initialize the `emergency criteria proposal` to remove the malicious Steward.
Then `backup steward` fulfills the epoch by committing again correctly.
A large consensus group provides better decentralization, but it requires significant coordination,
which MAY not be suitable for groups with more than 1000 members.
### Multi steward with small consensuses
The small consensus model offers improved efficiency with a trade-off in decentralization.
In this design, group changes require consensus only among the stewards, rather than all members.
Regular members participate by periodically selecting the stewards but do not take part in each decision.
Regular members participate by periodically selecting the stewards by `steward election proposal`
but do not take part in commit decision by `commit proposal`.
This structure enables faster coordination since consensus is achieved within a smaller group of stewards.
It is particularly suitable for large user groups, where involving every member in each decision would be impractical.
The flow is similar to the big consensus including the `steward list` finalization with all members consensus
only the difference here, the commit messages requires `commit proposal` only among the stewards.
## Filtering proposals against the multiple comitting
Since stewards are allowed to produce a commit even when they are not the designated `epoch steward`,
multiple commits may appear within the same epoch, often reflecting recurring versions of the same proposal.
To ensure a consistent outcome, the valid commit for the epoch SHOULD be selected as the one derived
from the longest proposal chain, ordered by the ascending value of each proposal as `SHA256(proposal)`.
All other cases, such as invalid commits or commits based on proposals that were not approved through voting,
can be easily detected and discarded by the members.
## Steward violation list
A stewards activity is called a violation if the action is one or more of the following:
1. Broken commit: The steward releases a different commit message from the voted `commit proposal`.
This activity is identified by the `members` since the [MLS RFC 9420](https://datatracker.ietf.org/doc/rfc9420/) provides the methods
that members can use to identify the broken commit messages that are possible in a few situations,
such as commit and proposal incompatibility. Specifically, the broken commit can arise as follows:
1. The commit belongs to the earlier epoch.
2. The commit message should equal the latest epoch
3. The commit needs to be compatible with the previous epochs `MLS proposal`.
2. Broken MLS proposal: The steward prepares a different `MLS proposal` for the corresponding `voting proposal`.
This activity is identified by the `members` since both `MLS proposal` and `voting proposal` are visible
and can be identified by checking the hash of `Proposal.payload` and `MLSProposal.payload` is the same as RFC9240 section 12.1. Proposals.
3. Censorship and inactivity: The situation where there is a voting proposal that is visible for every member,
and the Steward does not provide an MLS proposal and commit.
This activity is again identified by the `members`since `voting proposals` are visible to every member in the group,
therefore each member can verify that there is no `MLS proposal` corresponding to `voting proposal`.
## Security Considerations
In this section, the security considerations are shown as de-MLS assurance.
1. Malicious Steward: A Malicious steward can act maliciously,
as in the Steward violation list section.
Therefore, de-MLS enforces that any steward only follows the protocol under the consensus order
and commits without emergency criteria application.
2. Malicious Member: A member is only marked as malicious
when the member acts by releasing a commit message.
3. Steward list election bias: Although SHA256 is used together with two global variables
to shuffle stewards in a deterministic and verifiable manner,
this approach only minimizes election bias; it does not completely eliminate it.
This design choice is intentional, in order to preserve the efficiency advantages provided by the MLS mechanism.
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

429
vac/raw/zerokit-api.md Normal file
View File

@@ -0,0 +1,429 @@
---
title: Zerokit-API
name: Zerokit API
status: raw
category: Standards Track
tags: [zerokit, rln, api]
editor: Vac Team
contributors:
- Vinh Trinh <vinh@status.im>
---
## Abstract
This document specifies the Zerokit API, an implementation of the RLN-V2 protocol.
The specification covers the unified interface exposed through native Rust,
C-compatible Foreign Function Interface (FFI) bindings,
and WebAssembly (WASM) bindings.
## Motivation
The main goal of this RFC is to define the API contract,
serialization formats,
and architectural guidance for integrating the Zerokit library
across all supported platforms.
Zerokit is the reference implementation of the RLN-V2 protocol.
## Format Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”,
“SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document
are to be interpreted as described in [2119](https://www.ietf.org/rfc/rfc2119.txt).
### Important Note
All terms and parameters used remain the same as in [RLN-V2](./rln-v2.md) and [RLN-V1](../32/rln-v1.md#technical-overview).
### Architecture Overview
Zerokit follows a layered architecture where
the core RLN logic is implemented once in Rust and
exposed through platform-specific bindings.
The protocol layer handles zero-knowledge proof generation and verification,
Merkle tree operations, and cryptographic primitives.
This core is wrapped by three interface layers:
native Rust for direct library integration,
FFI for C-compatible bindings consumed by languages (such as C and Nim),
and WASM for browser and Node.js environments.
All three interfaces maintain functional parity and
share identical serialization formats for inputs and outputs.
```text
┌─────────────────────────────────────────────────────┐
│ Application Layer │
└──────────┬───────────────┬───────────────┬──────────┘
│ │ │
┌──────▼───────┐ ┌─────▼─────┐ ┌───────▼─────┐
│ FFI API │ │ WASM API │ │ Rust API │
│ (C/Nim/..) │ │ (Browser) │ │ (Native) │
└──────┬───────┘ └─────┬─────┘ └───────┬─────┘
└───────────────┼───────────────┘
┌─────────▼─────────┐
│ RLN Protocol │
│ (Rust Core) │
└───────────────────┘
```
### Supported Features
Zerokit provides compile-time feature flags that
control Merkle tree storage backends,
operational modes,
and parallelization.
#### Merkle Tree Backends
`fullmerkletree` allocates the complete tree structure in memory.
This backend provides the fastest performance but consumes the most memory.
`optimalmerkletree` uses sparse HashMap storage that only allocates nodes as needed.
This backend balances performance and memory efficiency.
`pmtree` persists the tree to disk using a sled database.
This backend enables state durability across process restarts.
#### Operational Modes
`stateless` disables the internal Merkle tree.
Applications MUST provide the Merkle root and
membership proof externally when generating proofs.
When `stateless` is not enabled,
the library operates in stateful mode and
requires one of the Merkle tree backends.
#### Parallelization
`parallel` enables rayon-based parallel computation for
proof generation and tree operations.
This flag SHOULD be enabled for end-user clients where
fastest individual proof generation time is required.
For server-side proof services handling multiple concurrent requests,
this flag SHOULD be disabled and
applications SHOULD use dedicated worker threads per proof instead.
The worker thread approach provides significantly higher throughput for
concurrent proof generation.
## The API
### Overview
The API exposes functional interfaces with strongly-typed parameters.
All three platform bindings share the same function signatures,
differing only in language-specific conventions.
Function signatures documented below are from the Rust perspective.
- Rust: <https://github.com/vacp2p/zerokit/blob/master/rln/src/public.rs>
- FFI: <https://github.com/vacp2p/zerokit/tree/master/rln/src/ffi>
- WASM: <https://github.com/vacp2p/zerokit/tree/master/rln-wasm>
### Error Handling
Error handling differs across platform bindings.
For native Rust,
functions return `Result<T, RLNError>` where `RLNError` is an enum
representing specific error conditions.
The enum variants provide type-safe error handling and
pattern matching capabilities.
For WASM and FFI bindings,
errors are returned as human-readable string messages.
This simplifies cross-language error propagation at
the cost of type safety.
Applications consuming these bindings SHOULD parse error strings or
use error message prefixes to distinguish error types when needed.
### Initialization
Functions with the same name but different signatures are conditional compilation variants.
Only one variant exists at compile time based on enabled feature flags.
`RLN::new(tree_depth, tree_config)` creates a new RLN instance by loading circuit resources from the default folder.
The `tree_config` parameter accepts multiple types via the `TreeConfigInput` trait: a JSON string,
a direct config object (with pmtree feature), or an empty string for defaults.
Not available in WASM. Not available when `stateless` feature is enabled.
`RLN::new()` creates a new stateless RLN instance by loading circuit resources from the default folder.
Only available when `stateless` feature is enabled. Not available in WASM.
`RLN::new_with_params(tree_depth, zkey_data, graph_data, tree_config)` creates a new RLN instance
with pre-loaded circuit parameters passed as byte vectors.
The `tree_config` parameter accepts multiple types via the `TreeConfigInput` trait.
Not available in WASM. Not available when `stateless` feature is enabled.
`RLN::new_with_params(zkey_data, graph_data)` creates a new stateless RLN instance with pre-loaded circuit parameters.
Only available when `stateless` feature is enabled. Not available in WASM.
`RLN::new_with_params(zkey_data)` creates a new stateless RLN instance for WASM with pre-loaded zkey data.
Graph data is not required as witness calculation is handled externally in WASM environments (e.g., using [witness_calculator.js](https://github.com/vacp2p/zerokit/blob/master/rln-wasm/resources/witness_calculator.js)).
Only available in WASM with `stateless` feature enabled.
### Key Generation
`keygen()` generates a random identity keypair returning `(identity_secret, id_commitment)`.
`seeded_keygen(seed)` generates a deterministic identity keypair
from a seed returning `(identity_secret, id_commitment)`.
`extended_keygen()` generates a random extended identity keypair
returning `(identity_trapdoor, identity_nullifier, identity_secret, id_commitment)`.
`extended_seeded_keygen(seed)` generates a deterministic extended identity keypair
from a seed returning `(identity_trapdoor, identity_nullifier, identity_secret, id_commitment)`.
### Merkle Tree Management
All tree management functions are only available when
`stateless` feature is NOT enabled.
`set_tree(tree_depth)` initializes the internal Merkle tree with the specified depth.
Leaves are set to the default zero value.
`set_leaf(index, leaf)` sets a leaf value at the specified index.
`get_leaf(index)` returns the leaf value at the specified index.
`set_leaves_from(index, leaves)` sets multiple leaves starting from the specified index.
Updates `next_index` to `max(next_index, index + n)`.
If n leaves are passed, they will be set at positions `index`, `index+1`, ..., `index+n-1`.
`init_tree_with_leaves(leaves)` resets the tree state to default and initializes it
with the provided leaves starting from index 0.
This resets the internal `next_index` to 0 before setting the leaves.
`atomic_operation(index, leaves, indices)` atomically inserts leaves starting from index
and removes leaves at the specified indices.
Updates `next_index` to `max(next_index, index + n)` where n is the number of leaves inserted.
`set_next_leaf(leaf)` sets a leaf at the next available index and increments `next_index`.
The leaf is set at the current `next_index` value, then `next_index` is incremented.
`delete_leaf(index)` sets the leaf at the specified index to the default zero value.
Does not change the internal `next_index` value.
`leaves_set()` returns the number of leaves that have been set in the tree.
`get_root()` returns the current Merkle tree root.
`get_subtree_root(level, index)` returns the root of a subtree at the specified level and index.
`get_merkle_proof(index)` returns the Merkle proof for the leaf at the specified index as `(path_elements, identity_path_index)`.
`get_empty_leaves_indices()` returns indices of leaves set to zero up to the final leaf that was set.
`set_metadata(metadata)` stores arbitrary metadata in the RLN object for application use.
This metadata is not used by the RLN module.
`get_metadata()` returns the metadata stored in the RLN object.
`flush()` closes the connection to the Merkle tree database.
Should be called before dropping the RLN object when using persistent storage.
### Witness Construction
`RLNWitnessInput::new(identity_secret, user_message_limit, message_id, path_elements, identity_path_index, x, external_nullifier)` constructs
a witness input for proof generation. Validates that `message_id < user_message_limit`.
### Witness Calculation
For native (non-WASM) environments, witness calculation is handled internally by the proof generation functions.
The circuit witness is computed from the `RLNWitnessInput` and passed to the zero-knowledge proof system.
For WASM environments, witness calculation must be performed externally using a JavaScript witness calculator.
The workflow is:
1. Create a `WasmRLNWitnessInput` with the required parameters
2. Export to JSON format using `toBigIntJson()` method
3. Pass the JSON to an external JavaScript witness calculator
4. Use the calculated witness with `generate_rln_proof_with_witness`
The witness calculator computes all intermediate values required by the RLN circuit.
### Proof Generation
`generate_zk_proof(witness)` generates a Groth16 zkSNARK proof from a witness.
Extract proof values separately using `proof_values_from_witness`.
Not available in WASM.
`generate_rln_proof(witness)` generates a complete RLN proof returning both the zkSNARK proof and proof values as `(proof, proof_values)`.
This combines proof generation and proof values extraction.
Not available in WASM.
`generate_rln_proof_with_witness(calculated_witness, witness)` generates an RLN proof using
a pre-calculated witness from an external witness calculator.
The `calculated_witness` should be a `Vec<BigInt>` obtained from the external witness calculator.
Returns `(proof, proof_values)`.
This is the primary proof generation method for WASM where witness calculation is handled by JavaScript.
### Proof Verification
`verify_zk_proof(proof, proof_values)` verifies only the zkSNARK proof without root or signal validation.
Returns `true` if the proof is valid.
`verify_rln_proof(proof, proof_values, x)` verifies the proof against the internal Merkle tree root and
validates that `x` matches the proof signal.
Returns an error if verification fails (invalid proof, invalid root, or invalid signal).
Only available when `stateless` feature is NOT enabled.
`verify_with_roots(proof, proof_values, x, roots)` verifies the proof against a set of acceptable roots and
validates the signal.
If the roots slice is empty, root verification is skipped. Returns an error if verification fails.
### Slashing
`recover_id_secret(proof_values_1, proof_values_2)` recovers the identity secret from two proof values
that share the same external nullifier.
Used to detect and penalize rate limit violations.
### Hash Utilities
`poseidon_hash(inputs)` computes the Poseidon hash of the input field elements.
`hash_to_field_le(input)` hashes arbitrary bytes to a field element using little-endian byte order.
`hash_to_field_be(input)` hashes arbitrary bytes to a field element using big-endian byte order.
### Serialization Utilities
`rln_witness_to_bytes_le` / `rln_witness_to_bytes_be` serializes an RLN witness to bytes.
`bytes_le_to_rln_witness` / `bytes_be_to_rln_witness` deserializes bytes to an RLN witness.
`rln_proof_to_bytes_le` / `rln_proof_to_bytes_be` serializes an RLN proof to bytes.
`bytes_le_to_rln_proof` / `bytes_be_to_rln_proof` deserializes bytes to an RLN proof.
`rln_proof_values_to_bytes_le` / `rln_proof_values_to_bytes_be` serializes proof values to bytes.
`bytes_le_to_rln_proof_values` / `bytes_be_to_rln_proof_values` deserializes bytes to proof values.
`fr_to_bytes_le` / `fr_to_bytes_be` serializes a field element to 32 bytes.
`bytes_le_to_fr` / `bytes_be_to_fr` deserializes 32 bytes to a field element.
`vec_fr_to_bytes_le` / `vec_fr_to_bytes_be` serializes a vector of field elements to bytes.
`bytes_le_to_vec_fr` / `bytes_be_to_vec_fr` deserializes bytes to a vector of field elements.
### WASM-Specific Notes
WASM bindings wrap the Rust API with JavaScript-compatible types. Key differences:
- Field elements are wrapped as `WasmFr` with `fromBytesLE`, `fromBytesBE`, `toBytesLE`, `toBytesBE` methods.
- Vectors of field elements use `VecWasmFr` with `push`, `get`, `length` methods.
- Identity generation uses `Identity.generate()` and `Identity.generateSeeded(seed)` static methods.
- Extended identity uses `ExtendedIdentity.generate()` and `ExtendedIdentity.generateSeeded(seed)`.
- Witness input uses `WasmRLNWitnessInput` constructor and `toBigIntJson()` for witness calculator integration.
- Proof generation requires external witness calculation via `generateRLNProofWithWitness(calculatedWitness, witness)`.
- When `parallel` feature is enabled, call `initThreadPool()` to initialize the thread pool.
- Errors are returned as JavaScript strings that can be caught via try-catch blocks.
### FFI-Specific Notes
FFI bindings use C-compatible types with the `ffi_` prefix. Key differences:
- Field elements are wrapped as `CFr` with corresponding conversion functions.
- Results use `CResult` or `CBoolResult` structs with `ok` and `err` fields.
- Errors are returned as C-compatible strings in the `err` field of result structs.
- Memory must be explicitly freed using `ffi_*_free` functions.
- Vectors use `repr_c::Vec` with `ffi_vec_*` helper functions.
- Configuration is passed via file path to a JSON configuration file.
## Usage Patterns
This section describes common deployment scenarios and
the recommended API combinations for each.
### Stateful with Changing Root
Applies when membership changes over time with members joining and slashing continuously.
Applications MUST maintain a sliding window of recent roots externally.
When members are added or removed via `set_leaf`, `delete_leaf`, or `atomic_operation`,
capture the new root using `get_root` and append it to the history buffer.
Verify incoming proofs using `verify_with_roots` with the root history buffer,
accepting proofs valid against any recent root.
The window size depends on network propagation delays and epoch duration.
### Stateful with Fixed Root
Applies when membership is established once and remains static during an operation period.
Initialize the tree using `init_tree_with_leaves` with the complete membership set.
No root history is required.
Verify proofs using `verify_rln_proof` which checks against the internal tree root directly.
### Stateless
Applies when membership state is managed externally,
such as by a smart contract or relay network.
Enable the `stateless` feature flag.
Obtain Merkle proofs and valid roots from the external source.
Pass externally provided `path_elements` and `identity_path_index` to `RLNWitnessInput::new`.
Verify using `verify_with_roots` with externally provided roots.
### WASM Browser Integration
WASM environments require external witness calculation.
Use `WasmRLNWitnessInput::toBigIntJson()` to export the witness for
JavaScript witness calculators,
then pass the result to `generateRLNProofWithWitness`.
When `parallel` feature is enabled,
call `initThreadPool()` before proof operations.
This requires COOP/COEP headers for SharedArrayBuffer support.
#### Epoch and Rate Limit Configuration
The external nullifier is computed as `poseidon_hash([epoch, rln_identifier])`.
The `rln_identifier` is a field element that uniquely identifies your application (e.g., a hash of your app name).
Each application SHOULD use a unique `rln_identifier` to
prevent cross-application nullifier collisions.
The `user_message_limit` in the rate commitment determines messages allowed per epoch.
The `message_id` must be less than `user_message_limit` and
should increment with each message.
Applications MUST persist the `message_id` counter to avoid violations after restarts.
## Security/Privacy Considerations
The security of Zerokit depends on the correct implementation of the RLN-V2 protocol
and the underlying zero-knowledge proof system.
Applications MUST ensure that:
- Identity secrets are kept confidential and never transmitted or logged
- The `message_id` counter is properly persisted to prevent accidental rate limit violations
- External nullifiers are constructed correctly to prevent cross-application attacks
- Merkle tree roots are validated when using stateless mode
- Circuit parameters (zkey and graph data) are obtained from trusted sources
When using the `parallel` feature in WASM,
applications MUST serve content with appropriate COOP/COEP headers to
enable SharedArrayBuffer support securely.
The slashing mechanism exposes identity secrets when rate limits are violated.
Applications SHOULD educate users about this risk and
implement safeguards to prevent accidental violations.
## References
### Normative
- [RLN-V1 Specification](../32/rln-v1.md) - Rate Limit Nullifier V1 protocol
### Informative
- [Zerokit GitHub Repository](https://github.com/vacp2p/zerokit) - Reference implementation
- [RLN-V2 Specification](./rln-v2.md) - Rate Limit Nullifier V2 protocol
- [Sled Database](https://sled.rs) - Embedded database for persistent Merkle tree storage
- [Witness Calculator](https://github.com/vacp2p/zerokit/blob/master/rln-wasm/resources/witness_calculator.js) - JavaScript witness calculator for WASM environments
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).