This document provides guidelines and instructions for contributing to the Rell project.
[[TOC]]
What are Postchain and Chromia?
- Postchain: Postchain is a container that runs blockchain modules. It handles incoming requests to a node, delegates operations and queries to Rell, and builds blocks.
- Chromia: A full blockchain network that consists of Postchain, Rell, and additional components.
- Rell: A programming language, allowing developers to write applications.
Rell's two key features are blockchain integration and SQL-like capabilities:
- Blockchain features: Operations, queries, and library APIs (
chain_context,op_context, etc.) - Query features: Entity and object definitions, at-expressions, and database manipulation operations
- IntelliJ IDEA - The recommended IDE
- JDK 21 - The project requires Java Development Kit 21 (can be managed by IDEA)
- PostgreSQL (
psql) - For setting up PostgreSQL for core module tests - Docker - For running PostgreSQL in an isolated container and for Testcontainers-based integration tests
The Rell project is organized into several modules:
- rell-base: Core language implementation, split into sub-modules:
utils: Shared utilities with no Rell-specific dependenciesrr-tree: Serializable RR_ tree data classes (types, definitions, IR, frames)rr-serialization: FlatBuffers serializer/deserializer for the RR_ treefrontend: Compiler frontend (AST, compilation layer, R_ model, type system, library framework) andR_ → RR_resolverruntime: Rt_ interpreter, runtime values, SQL generation, standard library implementationstest-utils: Shared test fixturestests: Unit/integration tests forrell-base
- rell-api-base: Base API definitions
- rell-api-gtx: Generic Transaction Protocol (GTX) API implementation
- rell-api-native: Native API
- rell-api-shell: Shell/REPL implementation
- rell-gtx: GTX integration
- rell-tools: Developer tools
- coverage-report-aggregate: Test coverage reporting
rell-toolbox/ — LSP server and code analysis tools:
- common: Shared utilities and Rell project model
- ast: ANTLR-based parser and AST
- indexer: Workspace file indexing
- code-quality: Formatter and linter
- language-server: LSP server (published as shadow JAR)
- seeder: Test data generation
rell-codegen/ — generates client stubs from Rell contracts:
-
codegen: Core code generation framework
-
codegen-kotlin, codegen-typescript, codegen-javascript, codegen-python, codegen-mermaid: Language-specific generators
-
rellgen: CLI application
-
rell-dokka-plugin: Dokka plugin for Rell system library documentation
Other directories:
- doc: Documentation, including language guide and release notes
- work: Scripts and configuration for running Rell
To build the project distribution without running tests:
./gradlew assembleThe highly recommended and simple way to provide a Postgres instance for running tests is to use Docker container:
./work/psql/psql-docker.shRequires local PostgreSQL to be running:
./gradlew check # all modules
./gradlew :rell-base:check # single module
./gradlew :rell-base:test --tests '*.MyTest' # single test classIn IntelliJ IDEA, use the run configuration All_tests (Gradle check).
Some integration tests use Testcontainers to spin up disposable Docker containers. Testcontainers requires a working Docker daemon. On Linux with Docker Engine installed natively this works out of the box.
On macOS, Colima is a lightweight alternative to Docker Desktop.
Because Colima exposes its Docker socket at a non-default path, you need to tell both Testcontainers and the Gradle build where to find it.
1. Configure Testcontainers globally — create or edit ~/.testcontainers.properties:
docker.host=unix://${HOME}/.colima/default/docker.sock
ryuk.disabled=trueryuk.disabled=true avoids a common issue where the Ryuk resource-reaper container fails to start under Colima.
2. Forward Docker config to Gradle test JVMs — add to local.properties in the project root:
DOCKER_HOST=unix://${HOME}/.colima/default/docker.sock
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
TESTCONTAINERS_RYUK_DISABLED=trueThe ANTLR grammar in :rell-toolbox:ast is checked against a corpus of Rell snippets recorded by the runtime test suites (rell-base, rell-gtx, rell-api-base, rell-api-gtx). The recorder writes each compiled snippet to {module}/build/rell-test-cases/ and :rell-toolbox:ast consumes them via a project dependency.
Grammar tests take ~6 minutes and are not part of check. Run them after editing the ANTLR grammar or the hand-written compiler parser:
./gradlew :rell-toolbox:ast:grammarTestThe task transitively depends on the four runtime test tasks, so the corpus is regenerated automatically.
Passing -PwithLocales to check runs the test suite under several non-default locales to verify that the implementation is locale-independent. This matters because Rell must be deterministic.
Some Java/Kotlin functions are locale-sensitive by default. For example, on a machine configured for Turkish (tr_TR), "i".toUpperCase() produces "İ" (dotted capital I) rather than 'I', and String.format uses locale-specific decimal and grouping separators. Despite minimal probability, these differences can cause divergence of nodes in a blockchain network.
./gradlew check -PwithLocalesTested locales: tr_TR (Turkish — the most common source of case-conversion bugs), ar_SA, ja_JP.
The build script loads local.properties from the project root (git-ignored) and forwards its entries as environment variables and system properties to every test JVM. This is the recommended way to set machine-specific configuration without touching tracked files.
The following variables are forwarded to test JVMs (via local.properties or the shell environment):
| Variable | Purpose |
|---|---|
DOCKER_HOST |
Docker daemon socket URL |
DOCKER_TLS_CERTDIR |
TLS certificate directory (if using TLS) |
TESTCONTAINERS_HOST_OVERRIDE |
Override the host Testcontainers connects to |
TESTCONTAINERS_RYUK_DISABLED |
Disable the Ryuk resource reaper (true/false) |
TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE |
Override the socket path inside the container |
The following project properties control resource allocation and test behavior. They can be passed on the command line or set in gradle.properties.
| Property | Default | Purpose |
|---|---|---|
testJvmMaxHeap |
2g |
Max heap for each forked test JVM |
junitParallelThreads |
availableProcessors() |
JUnit ForkJoinPool threads per test worker |
withLocales |
unset | Run tests under extra locales (tr_TR, ar_SA, ja_JP) |
gitlabAuthHeaderValue |
unset | GitLab Package Registry auth token (CI only) |
Example for a memory-constrained laptop:
./gradlew check -PtestJvmMaxHeap=1g -PjunitParallelThreads=2 --max-workers=2Shared run configurations are stored in work/ as .run.xml files. Import them into your IDE via File → Import Run Configuration (or copy into .idea/runConfigurations/):
| File | Purpose |
|---|---|
All_tests.run.xml |
Gradle check across all modules |
Kotlin_ABI_Dump.run.xml |
Gradle apiDump (binary compat) |
./work/rell.shExample usage:
Rell 0.15.0-SNAPSHOT
Type '\q' to quit or '\?' for help.
>>> 2+2
4
>>> range(10) @* {} (@sum $)
45
Create a file (e.g., hello.rell):
module;
function main() {
print('Hello, world!');
}
Run it:
work/rell.sh -d <parent-directory> hello main# You may avoid installing chr by using Docker for it, too
# https://docs.chromia.com/intro/getting-started/installation/cli-installation#start-the-docker-container-with-chromia-cli-pre-installed
chr create-rell-dapp
chr node start
chr query -brid <BRID> <QUERY_NAME> <ARGS_AS_GTV_DICT_STR>
chr tx -brid <BRID> <OP_NAME> ARG_AS_GTV_STR*The local-chr.sh script is a wrapper for the Chromia CLI (chr) that uses your local Rell build instead of the officially published release. It automatically patches CLI's POM to your local Rell version, making it ideal for testing language changes or new features before they're deployed.
./work/local-chr.sh [arguments]
# Print the software versions, for example
./work/local-chr.sh --versionIf you need to clone the local Chromia CLI repository and patch it again, then add the --rebuild flag:
./work/local-chr.sh --rebuild --versionGenerates Rell documentation locally. This is useful for previewing changes to the Rell system libraries documentation. Output goes to the libdoc directory.
./work/build-local-docs.shIn case your build fails on the apiCheck task with an error like this:
> Task :rell-api-base:apiCheck FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':rell-api-base:apiCheck'.
> API check failed for project rell-api-base.
Run configuration Kotlin_ABI_Dump in IntelliJ IDEA or the following command to dump the API:
./gradlew apiDumpAfterward, review the *.api files that were changed by this dump and ensure the changes are intended.
CI runs Qodana (Community edition). The SARIF report is saved as a pipeline artifact. Qodana produces a GitLab Code Quality report, so new findings appear inline in merge request. Locally, the same analysis can be performed by IntelliJ IDEA.
The project uses JaCoCo for code coverage reporting.
Local reports. Running tests for any module generates a per-module report at {module}/build/reports/jacoco/test/html/index.html. To generate a single aggregate report:
./gradlew testCodeCoverageReport # tCCRThe aggregate report is located at coverage-report-aggregate/build/reports/jacoco/testCodeCoverageReport/html/index.html.
GitLab MR coverage. CI pipelines upload per-module JaCoCo XML reports as a coverage_report artifact (format jacoco), so GitLab can display line-by-line coverage in merge request diffs. The pipeline also parses the aggregate CSV to extract an overall line-coverage percentage, which GitLab shows as the MR coverage badge.
Naming Prefixes
The project uses various prefixes for different types of classes:
S_- AST nodesG_- Helper grammar classesL_- Library frameworkM_- Type frameworkC_- CompilationR_- Compiled objectsRR_- Resolved runtime model (serializable IR consumed by the interpreter)Rt_- Runtime
Basic Conventions:
- The project has .editorconfig, but auto-formatting is not used strictly. Don't commit changes that only consist of applying formatting.
- All calculations in the implementation of Rell must be deterministic and reproducible. Expressions must produce the same results in both interpreted and database contexts.
- All dependency versions must be declared in
gradle/libs.versions.toml. Do not hardcode version strings inbuild.gradle.ktsfiles.
When adding new library functions, types, properties, or other declarations, always use RellVersions.SINCE_NOW for the since parameter:
function("my_new_function", result = "integer", since = RellVersions.SINCE_NOW) {
param("value", "text")
body { arg ->
// implementation
}
}The SINCE_NOW constant is defined as the current development version and will be replaced with the actual version during the release process.
-
The Language Guide
-
Release notes (starting with the oldest version, 0.6.1)
https://gitlab.com/chromaway/rell/-/tree/dev/doc/release-notes
-
Rell system library documentation
-
Chromia documentation
https://docs.chromia.com/ and https://docs.chromia.com/intro/getting-started/create-dapp/run-dapp-cli
See doc/release-guide.md.