Denaro, "money" in Italian, is a decentralized cryptocurrency built entirely in Python and utilizes PostgreSQL for blockchain data. It offers a blockchain implementation that developers can understand and extend without the complexity often found in traditional cryptocurrency codebases. Additionally, it can serve as a foundation for developers that are interested in creating their own cryptocurrency.
Features:
-
-
Proof-of-Work blockchain using SHA256 hashing with dynamic difficulty adjustment every 512 blocks. Blocks are limited to 2MB and can process approximately 3,800 transactions.
-
Peer-to-peer network with cryptographic node identity, ECDSA-based request signing, and automatic blockchain synchronization. Includes reputation management, rate limiting, and security measures for network protection.
-
Transaction system supporting up to 6 decimal places with ECDSA signature verification. Transactions can include up to 255 inputs and outputs, with optimized signature schemes and optional messages.
-
PostgreSQL database backend with indexed queries, connection pooling, and integrated transaction validation for efficient blockchain storage and retrieval.
-
Consensus versioning system enabling clean protocol upgrades with support for both soft and hard forks through activation height scheduling.
-
RESTful API interface built on FastAPI providing comprehensive blockchain interaction, transaction submission, and network queries with background task processing and CORS support.
-
Monetary Policy:
-
Denaro's monetary policy has been chosen for its optimal balance of a scarce total supply, frequent halving events, and long-term emission lifespan.
- Initial Reward Per Block: 64 DNR
- Halving Interval: 262,144 blocks.
- Targets ~2.5 years per halving.
- Maximum halvings: 64
- Estimated Emission Lifespan: ~160 years.
- Maximum Total Supply: 33,554,432 DNR
- The-Sycorax / Denaro Wallet Client GUI
- StellarisChain / Quasar - Wallet Browser Extension
- connor33341 / Denaro Pool
- connor33341 / DenaroCudaMiner (Solo+Pool)
- Gamer000gaming / denaro-faucet
- witer33 / DenaroCudaMiner (Solo)
- 1460293896 / Denaro CUDA Miner (Pool)
- geiccobs / Denaro Solo Miner (CPU)
- geiccobs / Denaro Pool Miner (CPU)
- geiccobs / Denaro WASM Miner
- denaro-coin / DVM (Denaro Virtual Machine)
- The-Sycorax / Denaro Vanity Generator
- Avecci-Claussen / Denaro-Vanity-Gen
- New Website: https://denaro.mine.bz
- Denaro Node: https://node.denaro.network
- Block Explorer: https://denaro-explorer.aldgram-solutions.fr
- Discord Server: https://discord.gg/4Sq2dK4KMv
-
-
Automated configuration and deployment of a Denaro node can be achieved by using either the
setup.shscript orDocker. Both methods ensure that all prerequisites for operating a Denaro node are met and properly configured according to the user's preference.It is highly recommended to review the Environment Configuration section before setting up a Denaro node.
Setup via setup.sh:
-
The
setup.shscript is designed for traditional configuration and deployment of a single Denaro node. It automatically handles system package updates, manages environment variables, configures the PostgreSQL database, sets up a Python virtual environment, installs the required Python dependencies, and runs the Denaro node.Commands:
-
# Clone the Denaro repository to your local machine. git clone https://github.com/The-Sycorax/denaro.git # Change directory to the cloned repository. cd denaro # Make the setup script executable. chmod +x setup.sh # Execute the setup script with optional arguments if needed. ./setup.sh [--skip-prompts] [--setup-db] [--skip-package-install]
CLI Arguments:
-
-
--skip-prompts: Executes the setup script in an automated manner without requiring user input, bypassing all interactive prompts. -
--setup-db: Limits the setup script's actions to only configure the PostgreSQL database, excluding the execution of other operations such as virtual environment setup and dependency installation. -
--skip-package-install: Skipsaptpackage installation. This argument can be used for Linux distributions that do not utilizeaptas a package manager. However, it is important that the required system packages are installed prior to running the setup script (For more details refer to: Installation for Non-Debian Based Systems).
-
Installation for Non-Debian Based Systems:
-
The setup script is designed for Linux distributions that utilize
aptas their package manager (e.g. Debian/Ubuntu). If system package installation is unsuccessful, it is most likely due to the absence ofapton your system. This is generally the case for Non-Debian Linux distributions. Therefore, the required system packages must be installed manually.Required Packages:
-
Note: It is nessessary to ensure that the package names specified are adjusted to correspond with those recognized by your package manager.
gcclibgmp-devlibpq-devpostgresql-15python3python3-venvsudo
Once the required packages have been installed, the
--skip-package-installargument can be used with the setup script to bypass operations that requireapt. This should mitigate any unsucessful execution related to package installation, allowing the setup script to proceed. -
-
Setup via Docker:
-
The Docker setup provides a containerized deployment option for Denaro nodes. Unlike the
setup.shscript, it encapsulates everything needed to run a Denaro node in isolated Docker containers. This avoids installing dependencies on the host system and prevents conflicts with system packages. Additionally, the Docker setup allows for multi-node deployments, while thesetup.shscript does not.At the core of the Docker setup is the
docker-entrypoint.shscript, which automates the configuration and deployment of each node. When a node's container starts, this script automatically provisions the PostgreSQL database, generates the necessary environment configuration, handles bootstrap node selection, and starts the Denaro node. Docker coordinates the supporting services, shared resources, and startup order of each container.To test public node behavior over the Internet, the Docker setup includes optional support for exposing a node on the Internet by establishing an SSH reverse tunnel via Pinggy.io's free tunnleing service. For more information please refer to: 2025-09-18-refactor(docker).md: Optional Public Node Tunnleing.
Commands:
-
# Clone the Denaro repository to your local machine. git clone https://github.com/The-Sycorax/denaro.git # Change directory to the cloned repository. cd denaro # Create the PostgreSQL volume docker volume create denaro_postgres_volume # Run the Docker containers docker compose -f ./docker/docker-compose.yml up --build --force-recreate -d # Optionally show node logs docker logs -f <container-name>
Custom Node Configuration:
-
For documentation related to Denaro's Docker setup, please refer to: 2025-09-18-refactor(docker).md and 2025-10-14-refactor(docker).md. Please note that some information may be outdated.
To add or modify nodes in
docker-compose.yml, use the structure outlined in the examples below.Basic Node Example (Default):
-
denaro-node-3006: <<: *denaro-node-base image: denaro-node-3006 container_name: denaro-node-3006 hostname: denaro-node-3006 volumes: - denaro_node_3006_volume:/app - denaro_node_registry_volume:/shared/denaro-node-registry - denaro_node_topology_volume:/shared/denaro-node-topology:ro depends_on: denaro-node-topology: { condition: service_completed_successfully } postgres: { condition: service_started } ports: [ "3006:3006" ] environment: NODE_NAME: 'denaro-node-3006' DENARO_NODE_PORT: '3006' DENARO_BOOTSTRAP_NODE: 'https://node.denaro.network' #ENABLE_PINGGY_TUNNEL: 'true' #DENARO_SELF_URL: '' # Ensure the node's volume is added volumes: denaro_node_3006_volume: name: denaro_node_3006_volume
Multi-Node Example:
-
# First Node denaro-node-3006: <<: *denaro-node-base image: denaro-node-3006 container_name: denaro-node-3006 hostname: denaro-node-3006 volumes: - denaro_node_3006_volume:/app - denaro_node_registry_volume:/shared/denaro-node-registry - denaro_node_topology_volume:/shared/denaro-node-topology:ro depends_on: denaro-node-topology: { condition: service_completed_successfully } postgres: { condition: service_started } ports: [ "3006:3006" ] environment: NODE_NAME: 'denaro-node-3006' DENARO_NODE_PORT: '3006' DENARO_BOOTSTRAP_NODE: 'https://node.denaro.network' #ENABLE_PINGGY_TUNNEL: 'true' #DENARO_SELF_URL: '' # Second Node denaro-node-3007: <<: *denaro-node-base image: denaro-node-3007 container_name: denaro-node-3007 hostname: denaro-node-3007 volumes: - denaro_node_3007_volume:/app - denaro_node_registry_volume:/shared/denaro-node-registry - denaro_node_topology_volume:/shared/denaro-node-topology:ro depends_on: denaro-node-topology: { condition: service_completed_successfully } postgres: { condition: service_started } # This condition is meant for proper startup ordering, but is really only # nessessary if the DENARO_BOOTSTRAP_NODE variable is set to a node that # is already present in the compose file. denaro-node-3006: { condition: service_healthy } ports: [ "3007:3007" ] environment: NODE_NAME: 'denaro-node-3007' DENARO_NODE_PORT: '3007' DENARO_BOOTSTRAP_NODE: 'http://denaro-node-3006:3006' # Connects to first node # Third Node denaro-node-3008: <<: *denaro-node-base image: denaro-node-3008 container_name: denaro-node-3008 hostname: denaro-node-3008 volumes: - denaro_node_3008_volume:/app - denaro_node_registry_volume:/shared/denaro-node-registry - denaro_node_topology_volume:/shared/denaro-node-topology:ro depends_on: denaro-node-topology: { condition: service_completed_successfully } postgres: { condition: service_started } # This condition is meant for proper startup ordering, but is really only # nessessary if the DENARO_BOOTSTRAP_NODE variable is set to a node that # is already present in the compose file. denaro-node-3007: { condition: service_healthy } ports: [ "3008:3008" ] environment: NODE_NAME: 'denaro-node-3008' DENARO_NODE_PORT: '3008' DENARO_BOOTSTRAP_NODE: 'http://denaro-node-3007:3007' # Connects to second node # Ensure that the volumes of additional nodes are added volumes: denaro_node_3006_volume: name: denaro_node_3006_volume denaro_node_3007_volume: name: denaro_node_3007_volume denaro_node_3008_volume: name: denaro_node_3007_volume
Important Notes:
-
This information is meant to document the correct requirements for the Docker setup. This applies primarily to advanced setups and custom configurations. The default docker-compose.yml and examples above already satisfy these requirements.
-
Each node service must include the
<<: *denaro-node-basemerge. This ensures that Docker Compose applies the requireddenaro.node=truelabel, mounts the shared volumes, and establishes the baseline dependencies on services that are required by the entrypoint script. -
Each node service requires its own dedicated volume (for example,
denaro_node_3006_volume) mounted to/app. This volume preserves the node's configuration files, and application state across container restarts. Additionally, this volume should not be shared with other nodes, doing so may result in unexpected behavior. -
Each node service must be assigned a unique
NODE_NAMEandDENARO_NODE_PORTvalue. The entrypoint script uses these values to derive per-node database names and healthcheck targets. Duplicate values will cause database conflicts and prevent proper node identification. -
The shared
node-registryandnode-topologyvolumes must remain mounted on all node services. These volumes enable the entrypoint script to coordinate peer discovery through the shared registry and provide the dependency information required by the topology-aware healthcheck system. -
When configuring multi-node deployments, use
depends_onwith theservice_healthycondition to establish startup ordering. This ensures that Docker Compose waits for upstream peer nodes to become healthy before launching dependent nodes, preventing bootstrap connection failures during startup.
-
-
-
When running a publically facing node, the node's own port (e.g. 3006) should be exposed to the Internet in order to allow connections to it.
-
-
Denaro uses environment variables for node and database configuration. In a standard setup without Docker, these can be managed through a
.envfile in Denaro's root directory. All variable values should be enclosed in quotes.Environment Variables:
-
Node Configuration:
-
DENARO_NODE_HOST:-
-
<str>: The hostname or IP address the node binds to.
-
Required.
-
Default:
127.0.0.1
-
DENARO_NODE_PORT:-
-
<str>: The port the node listens on for incoming connections.
-
Required.
-
Default:
3006
-
NODE_NAME:-
-
<str>: A unique identifier for the node within the Docker Compose file. This variable is required since the entrypoint script uses its value to derive
DENARO_DATABASE_NAME, as well asDENARO_SELF_URLwhen it is not explicitly set. -
Should match the container name of the node service.
-
Required; Docker only.
-
DENARO_BOOTSTRAP_NODE:-
-
<str>: Specifies the bootstrap-node to connect to. This can be any Denaro node that is reachable via the Internet or internal network. It is used for joining Denaro's P2P network (mainnet or isolated), and discovering additional peers.
-
The accepted values are different based on the setup type:
-
Standard Setup:
<url>: Fixed HTTP(S) address of a Denaro Node.
Docker Setup:
-
In the Docker setup, this variable specifies either the selection criteria, a node name, or a fixed HTTP(S) address of the bootstrap-node.
-
'self': Bootstraps against the node's own address (DENARO_SELF_URL). The node starts with no peers and remains isolated unless others connect to it. -
<url>: Fixed HTTP(S) address of a Denaro Node. -
<node-name>: TheNODE_NAMEof another node in the compose file. The target node must be public (withENABLE_PINGGY_TUNNEL=true). The entrypoint script waits up to 120s for the target to publish its URL to the registry. Falls back to'self'if unavailable. -
'random': Randomly selects a bootstrap-node from all other public nodes (withENABLE_PINGGY_TUNNEL=true) in the compose file. The entrypoint script waits up to 120s for all public nodes to register before choosing. Falls back to'self'if no public nodes are available.
-
-
Optional.
-
Default:
https://node.denaro.network
-
DENARO_SELF_URL:-
-
<str>: Specifies the HTTP(S) address of the node itself, reachable via the Internet or internal network. Other peers use this address to connect back to the node, and is required for publicly facing nodes.
-
When left unset or is set to a local address, the node will operate as a private node.
-
Docker Setup:
-
DENARO_SELF_URLshould only be directly specified if the node is publicly facing. Otherwise, the entrypoint script will set it automatically in one of two ways:-
If left unset, its value is derived from
NODE_NAMEandDENARO_NODE_PORTashttp://{NODE_NAME}:{DENARO_NODE_PORT}. -
If
ENABLE_PINGGY_TUNNEL='true', its value is overridden with the public URL assigned to the node by Pinggy.io.
-
-
-
Optional; Docker only.
-
Default:
unset
-
ENABLE_PINGGY_TUNNEL:-
-
<str-bool>: Enables public tunneling via Pinggy.io for up to 60 minutes. This overrides
DENARO_SELF_URLwith the public URL assigned to the node by Pinggy. Useful for testing public node behavior over the Internet. -
Optional; Docker only.
-
Default:
'false'
-
-
Database Configuration:
-
POSTGRES_USER:-
-
<str>: The PostgreSQL username used to authenticate with the database.
-
Required.
-
Default:
'denaro'
-
POSTGRES_PASSWORD:-
-
<str>: The password for the PostgreSQL user.
-
Required.
-
Default:
'denaro'
-
DENARO_DATABASE_NAME:-
- <str>: The name of the node's PostgreSQL database.
-
Docker Setup:
- In the Docker setup,
DENARO_DATABASE_NAMEshould not be directly specified. It is automatically set fromNODE_NAMEby the entrypoint script, with hyphens replaced by underscores.
- In the Docker setup,
-
Required for the standard setup, but not required for the Docker setup.
-
Default:
'denaro'
DENARO_DATABASE_HOST:-
-
<str>: The hostname or IP address of the PostgreSQL server.
-
Required.
-
Default:
'127.0.0.1'
-
DOCKER_ENABLE_PGADMIN:-
-
<str-bool>: Toggles the pgAdmin container for browser-based database management.
-
Optional; Docker only.
-
Default:
'false'
-
-
Logging Configuration:
-
LOG_LEVEL:-
-
<str>: Specifies the logging verbosity level (e.g.,
DEBUG,INFO,WARNING,ERROR,CRITICAL). -
Optional.
-
Default:
'INFO'
-
LOG_FORMAT:-
-
<str>: Specifies the log message string using standard Python logging format specifiers.
-
Optional.
-
Default:
'%(asctime)s - %(levelname)s - %(name)s - %(message)s'
-
LOG_DATE_FORMAT:-
-
<str>: Specifies the date format for log timestamps using standard strftime directives.
-
Optional.
-
Default:
'%Y-%m-%dT%H:%M:%S'
-
LOG_CONSOLE_HIGHLIGHTING:-
-
<str-bool>: Toggles Rich console syntax highlighting for log outputs.
-
Optional.
-
Default:
'true'
-
LOG_INCLUDE_REQUEST_CONTENT:-
-
<str-bool>: Toggles HTTP request body content in the log.
-
Optional.
-
Default:
'false'
-
LOG_INCLUDE_RESPONSE_CONTENT:-
-
<str-bool>: Toggles HTTP response body content in the log.
-
Optional.
-
Default:
'false'
-
LOG_INCLUDE_BLOCK_SYNC_MESSAGES:-
-
<str-bool>: Toggles verbose blockchain synchronization messages in the log.
-
Optional.
-
Default:
'false'
-
-
-
Docker Setup Configuration:
-
When deploying a Denaro node with Docker, configuration is split between a globally shared
.envfile and per-container configuration via thedocker-compose.ymlfile. This is to prevent variable overriding conflicts.Global
.envVariables:-
These variables should only be included in the
.envfile, are shared across all containers, and are required for the Docker setup.POSTGRES_USERPOSTGRES_PASSWORDDENARO_DATABASE_HOSTDENARO_NODE_HOSTDOCKER_ENABLE_PGADMIN
Per-Node Variables (
docker-compose.yml):-
These variables should be configured per-node within the
docker-compose.ymlenvironment block. They should not be included in the.envfile.NODE_NAMEDENARO_NODE_PORTDENARO_BOOTSTRAP_NODEDENARO_SELF_URLENABLE_PINGGY_TUNNEL
Important Notes:
-
-
DENARO_DATABASE_NAMEshould not be directly specified. It is automatically set fromNODE_NAMEby the entrypoint script, with hyphens replaced by underscores. -
DENARO_SELF_URLshould only be directly specified if the node is publicly facing. Otherwise, the entrypoint script will set it automatically in one of two ways:-
If left unset, its value is derived from
NODE_NAMEandDENARO_NODE_PORTashttp://{NODE_NAME}:{DENARO_NODE_PORT}. -
If
ENABLE_PINGGY_TUNNEL='true', its value is overridden with the public URL assigned to the node by Pinggy.io.
-
-
Logging configuration variables can be set in either the
.envfile to apply globally, or per-node in thedocker-compose.ymlfile.
-
-
-
-
The PostgreSQL database used by Denaro can be managed through several methods.
Warning
Database management is intended for development and testing purposes only and should not be used in mainnet node environments.
Additionally, it is highly recommended that the PostgreSQL port (5432) and pgAdmin port (5050) are not publicly exposed on the Internet, especially if default credentials are in use, since that would go against the most basic of security standards.
Management Options:
-
psql (CLI):
-
psqlis the official PostgreSQL command-line client and provides direct access to the database. It can be used to run queries, inspect schemas, and perform administrative tasks.-
# Connect to the Denaro database directly PGPASSWORD='<POSTGRES_PASSWORD>' psql -h 127.0.0.1 -p 5432 -U <POSTGRES_USER> -d <DENARO_DATABASE_NAME>
Replace
<POSTGRES_PASSWORD>, and<POSTGRES_USER>with the credentials defined in the.envfile.Also replace
<DENARO_DATABASE_NAME>with the value defined for it. This will be different based on the setup type:- In the standard setup it's value is defined in the
.envfile. - In the Docker setup, it's value is automatically set from
NODE_NAMEby the entrypoint script, with hyphens replaced by underscores.
- In the standard setup it's value is defined in the
-
pgAdmin (Included in Docker Setup):
-
A pgAdmin container is included in the Docker setup and provides a browser-based GUI for managing the PostgreSQL database.
It can be enabled by setting the
DOCKER_ENABLE_PGADMINvariable totruein the.envfile, and is accessible from the host machine once the Docker containers are running.Access:
-
Property Value URL http://localhost:5050Default Email admin@admin.comDefault Password admin
Note: The default credentials are intended for local development only. If default credentials are in use, it is highly recommended that the pgAdmin port (
5050) and PostgreSQL port (5432) are not publicly exposed. -
Third-Party GUI Clients:
-
Any PostgreSQL-compatible GUI client can be used to connect to the database. Popular options include:
-
DBeaver — A free, cross-platform database tool that supports PostgreSQL and many other databases.
-
TablePlus — A modern, native GUI client for macOS, Windows, and Linux.
-
DataGrip — A JetBrains IDE with advanced SQL editing and database management features.
To connect, use the nessessary values and credentials defined in the
.envfile. The database should generally be accessible at127.0.0.1:5432from the host machine. -
-
-
-
Note: This section dose not apply to nodes deployed using Docker.
A Denaro node can be started manually if you have already executed the setup.sh script and chose not to start the node immediately, or if you need to start the node in a new terminal session. If the setup script was used with the --setup-db argument or manual installation was performed, it is reccomended that a Python virtual environment is created and that the required Python packages are installed prior to starting a node.
Commands to manually start a node:
-
# Navigate to the Denaro directory. cd path/to/denaro # Create a Python virtual environment (Optional). sudo apt install python3-venv python3 -m venv venv source venv/bin/activate # Install the required packages if needed. pip install -r requirements.txt # Start the Denaro Node python3 run_node.py # Manualy start the Denaro node via uvicorn (Optional). uvicorn denaro.node.main:app --host 127.0.0.1 --port 3006 # To stop the node, press Ctrl+C in the terminal.
To exit a Python virtual environment:
-
deactivate
To setup a nodeless wallet, use Denaro Wallet Client GUI.
Denaro adopts a Proof of Work (PoW) system for mining using SHA256 hashing, with dynamic difficulty adjustment every 512 blocks to maintain a target block time of 180 seconds (3 minutes).
Mining Details:
-
-
Block Hashing:
-
Utilizes the SHA256 algorithm for block hashing.
-
The hash of a block must begin with the last
difficultyhexadecimal characters of the hash from the previously mined block. -
difficultycan have decimal digits, which restricts thedifficulty + 1st character of the derived hash to have a limited set of values.from math import ceil difficulty = 6.3 decimal = difficulty % 1 charset = '0123456789abcdef' count = ceil(16 * (1 - decimal)) allowed_characters = charset[:count]
-
-
Difficulty Adjustment:
- Difficulty adjusts every 512 blocks based on the actual block time versus the target block time of 180 seconds (3 minutes).
- Starting difficulty is 6.0.
-
Block Size and Capacity:
- Maximum block size is 2MB (raw bytes), equivalent to 4MB in hexadecimal format.
- Transaction data is limited to approximately 1.9MB hex characters per block.
-
Rewards:
- Block rewards start at 64 DNR and decrease by half every 262,144 blocks until they reach zero.
-
Mining Software:
-
-
CPU Mining:
The CPU miner script (
./miner/cpu_miner.py) can be used to mine Denaro.Usage:
-
-
Syntax:
python3 miner/cpu_miner.py [-h] [-a ADDRESS] [-n NODE] [-w WORKERS] [-m MAX_BLOCKS]
-
Arguments:
-
--address,-a(Required): Your public Denaro wallet address where mining rewards will be sent. -
--node,-n(Optional): The URL or IP address of the Denaro node to connect to. Defaults tohttp://127.0.0.1:3006/. -
--workers,-w(Optional): The number of parallel processes to run. It's recommended to set this to the number of CPU cores you want to use for mining. Defaults to 1. -
--max-blocks,-m(Optional): Maximum number of blocks to mine before exiting. If not specified, the miner will continue indefinitely. -
--help,-h: Shows the help message.
-
Examples:
-
-
python3 miner/cpu_miner.py --address WALLET_ADDRESS
-
python3 miner/cpu_miner.py --address WALLET_ADDRESS --node http://a-public-node.com:3006
-
python3 miner/cpu_miner.py --address WALLET_ADDRESS --workers 8
(Replace
WALLET_ADDRESSwith your actual Denaro address) -
-
-
-
GPU Mining:
For GPU mining please refer to Denaro CUDA Miner Setup and Usage.
-
Denaro nodes maintain synchronization with the network through automatic peer discovery and chain validation mechanisms that ensure all nodes converge on the longest valid chain. Additionally nodes can also be manually synchronized.
Automatic Synchronization:
-
Nodes automatically detect and synchronize with longer chains through two mechanisms:
-
Handshake Synchronization: When connecting to a peer, nodes exchange chain state information. If the peer has a longer valid chain, synchronization is triggered immediately.
-
Periodic Chain Discovery: A background task polls 2 random peers every 60 seconds to check for longer chains, ensuring the node remains synchronized even without new connections.
-
Manual Synchronization:
-
To manually initiate blockchain synchronization, a request can be sent to a node's
/sync_blockchainendpoint:-
curl http://127.0.0.1:3006/sync_blockchain
-
-
The endpoint accepts an optional
node_idparameter to sync from a specific peer. The node ID of a peer can be found in the./denaro/node/nodes.jsonfile:-
curl "http://127.0.0.1:3006/sync_blockchain?node_id=NODE_ID"
-
- The endpoint returns an error if a sync operation is already in progress.
Denaro is released under the terms of the GNU Affero General Public License v3.0. See LICENSE for more information or goto https://www.gnu.org/licenses/agpl-3.0.en.html
