DecentraLabs Gateway provides a complete blockchain-based authentication system for laboratory access. It includes all components needed for a decentralized lab access solution with advanced features, wallet management, billing and service-credit operations, and remote FMU access through generated proxy.fmu artifacts.
This repository corresponds to the provider+consumer deployment mode. In that mode, the institution can publish labs, expose authentication endpoints, and also fund reservation/access costs for its own users.
If you need only a funding and cost-management backend for your own users, without publishing labs or exposing provider auth endpoints, use blockchain-services in standalone consumer-only mode instead.
flowchart LR
User["User wallet / JWT / FMI tool"]
OpenResty["OpenResty"]
Blockchain["blockchain-services"]
Guacamole["Guacamole"]
FmuFacade["fmu-runner (public FMU facade)"]
Ops["ops-worker"]
Mysql[("MySQL")]
Contracts["Smart contracts"]
Station["Lab Station"]
User --> OpenResty
OpenResty --> Blockchain
OpenResty --> Guacamole
OpenResty --> FmuFacade
OpenResty --> Ops
Blockchain --> Contracts
Blockchain --> Mysql
Guacamole --> Mysql
FmuFacade -. target internal backend .-> Station
sequenceDiagram
participant Tool as FMI Tool
participant Proxy as proxy.fmu
participant Gateway as Lab Gateway
participant Auth as blockchain-services
participant Station as Lab Station
participant Model as real .fmu
Tool->>Proxy: FMI 2 calls
Proxy->>Gateway: WSS /fmu/api/v1/fmu/sessions
Gateway->>Auth: issue/redeem session ticket
Gateway->>Station: internal FMU describe/run/stream/session calls
Station->>Model: load / initialize / step / outputs
Station-->>Gateway: state and outputs
Gateway-->>Proxy: model.description / sim.state / sim.outputs
Proxy-->>Tool: FMI 2 responses
FMU target model:
- The real
.fmuremains on Lab Station. - The Gateway keeps the public REST/WSS surface, proxy generation, auth and ticketing.
- The generated
proxy.fmucontains interface metadata, runtime binaries and reservation-scoped config, never the real model. - This repository keeps a local FMU execution path in
fmu-runneras a permanent dev/test mode. That local path is not the intended production topology.
- Flexible Signature Verification: Users authenticate using their crypto wallet or SSO credentials in an external trusted system that emits a signed JWT
- Smart Contract Integration: Validates users' lab reservations on-chain
- JWT Token Generation: Issues secure access tokens for lab sessions (to be consumed by Guacamole)
- RESTful API: Comprehensive authentication endpoints
- Blockchain Integration: Web3j for smart contract interaction
- JWT Management: Token validation and generation
- Wallet Operations: Create, import, and manage Ethereum wallets
- Service-Credit Billing: Managed credit issuance with spending limits and period controls
- Health Monitoring: Built-in health checks and metrics
- Apache Guacamole Integration: Clientless RDP/VNC/SSH access through the browser
- Session Cookie Management: JTI-based session validation with automatic expiration
- Header Propagation: Authenticated username forwarded to Guacamole for auto-login
- Ops Worker: Remote power management for lab stations (Wake-on-LAN, shutdown)
- Generated
proxy.fmudelivery: Reservation-scoped download with signed metadata and one-shot session tickets - Public WSS facade: Stable
WSS /fmu/api/v1/fmu/sessionscontract for generated runtimes - Station-based execution target: Real FMUs are meant to live and execute on Lab Station, with Gateway acting as facade and router across internal REST/WSS channels
- Permanent dev/test backend: Local FMU execution in
fmu-runnerremains available for development, smoke tests and automated tests
Use one of these modes depending on your target:
-
Setup Scripts (
setup.sh/setup.bat)
Best for first-time installs. It prepares env files, secrets, and can start the full stack. -
Manual Docker Compose
Best if you want full control over compose commands and deployment flow. -
NixOS Compose-managed Host (
nixos-rebuild --flake ...#gateway)
Best for dedicated NixOS hosts where you want declarative system + service management.
The setup scripts will automatically:
- β Check Docker, Docker Compose, and Git prerequisites
- β
Initialize/refresh the
blockchain-servicessubmodule and env files - β Configure environment variables (database, domain, blockchain, CORS)
- β Generate database passwords
- β
Create the
blockchain-data/directory for wallet persistence - β
Optionally start every container with
docker compose up -d - β Ask if you want to enable a Cloudflare Tunnel so the gateway is reachable without a public IP/DNS
- β Configure Guacamole admin credentials
- β Generate OPS worker secret for lab power operations
- βοΈ Remind you to create/import the institutional wallet later from the blockchain-services web console
Windows:
setup.batLinux/macOS:
chmod +x setup.sh
./setup.shThat's it! The script will guide you through the setup and start all services automatically.
This repository also includes a flake.nix with:
nixosModules.default: NixOS module to manage the stack through systemdnixosModules.gateway-host: host defaults for a dedicated NixOS gateway machinenixosConfigurations.gateway: complete host config ready fornixos-rebuild
This mode is only for NixOS machines.
Use the module directly (example):
{
inputs.lab-gateway.url = "path:/srv/lab-gateway";
outputs = { nixpkgs, lab-gateway, ... }: {
nixosConfigurations.gateway = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
lab-gateway.nixosModules.default
{
services.lab-gateway = {
enable = true;
projectDir = "/srv/lab-gateway";
envFile = "/srv/lab-gateway/.env";
# profiles = [ "cloudflare" ];
};
}
];
};
};
}Then apply it:
sudo nixos-rebuild switch --flake /srv/lab-gateway#gatewayComplete host flow (real machine):
# 1) Put this repo on the target NixOS host
sudo mkdir -p /srv
sudo git clone https://github.com/DecentraLabsCom/lite-lab-gateway.git /srv/lab-gateway
cd /srv/lab-gateway
# 2) Prepare env files
sudo cp .env.example .env
sudo cp blockchain-services/.env.example blockchain-services/.env
# 3) Edit values (passwords, domain, tokens, RPC, contract address)
sudo nano .env
sudo nano blockchain-services/.env
# 4) Apply the full NixOS configuration shipped by this flake
sudo nixos-rebuild switch --flake /srv/lab-gateway#gateway
# 5) Validate the service
systemctl status lab-gateway.serviceblockchain-services/.env must still exist under projectDir, because docker-compose.yml references it directly.
nixosConfigurations.gateway imports your existing /etc/nixos/configuration.nix and layers the gateway module on top, so host-specific settings (bootloader, users, disks, hardware) are preserved.
Host-level values (hostname, timezone, firewall, profiles, SSH hardening) are installation-specific and should be overridden per environment.
If you prefer manual configuration:
-
Copy environment template:
cp .env.example .env cp blockchain-services/.env.example blockchain-services/.env
-
Edit
.envandblockchain-services/.envwith your configuration (see Configuration section below)
- Configure the two gateway access tokens for production:
ADMIN_ACCESS_TOKEN: protects wallet/billing routes (/wallet,/billing,/wallet-dashboard,/billing/admin/**)LAB_MANAGER_TOKEN: protects/lab-managerand/opsfrom public networks
- For this repository's normal
provider+consumerdeployment, also set these inblockchain-services/.env:FEATURES_PROVIDERS_ENABLED=true FEATURES_PROVIDERS_REGISTRATION_ENABLED=true
-
Set host UID/GID for bind mounts (Linux/macOS) so containers can write to
certs/andblockchain-data/:# Choose the user that will own the folders id -u id -gThen set in
.env(use0/0if you run everything as root):HOST_UID=1000 HOST_GID=1000
Ensure the folders are owned by that user:
chown -R 1000:1000 certs blockchain-data
-
Add SSL certificates to
certs/folder:certs/ βββ fullchain.pem # SSL certificate chain βββ privkey.pem # SSL private key βββ public_key.pem # JWT public key (optional if blockchain-services generates it)public_key.pemis generated automatically byblockchain-serviceson first start when missing. You only need to provide it manually if you use an external auth signer.Database schema: When
blockchain-serviceshas a MySQL datasource configured, it runs Flyway migrations on startup to create the auth, WebAuthn, and intents tables automatically. -
Start the services:
docker compose up -d --build
The gateway uses modular configuration with separate .env files:
.env- Gateway-specific configuration (server, database, Guacamole)blockchain-services/.env- Blockchain service configuration (contracts, wallets, RPC)
This separation keeps concerns isolated and makes the blockchain service independently configurable.
# Basic Configuration
SERVER_NAME=yourdomain.com
HTTPS_PORT=443
HTTP_PORT=80
# OpenResty bind address (127.0.0.1 for local-only, 0.0.0.0 for public)
OPENRESTY_BIND_ADDRESS=0.0.0.0
# OpenResty bind ports (local ports on the host)
OPENRESTY_BIND_HTTPS_PORT=443
OPENRESTY_BIND_HTTP_PORT=80
# Host UID/GID for bind mounts (Linux/macOS)
HOST_UID=1000
HOST_GID=1000
# Database Configuration
MYSQL_ROOT_PASSWORD=secure_password
MYSQL_DATABASE=guacamole_db
MYSQL_USER=guacamole_user
MYSQL_PASSWORD=db_password
BLOCKCHAIN_MYSQL_DATABASE=blockchain_services
# Guacamole
GUAC_ADMIN_USER=guacadmin
GUAC_ADMIN_PASS=secure_admin_password
AUTO_LOGOUT_ON_DISCONNECT=true
# OpenResty CORS allowlist (comma-separated, optional)
CORS_ALLOWED_ORIGINS=https://your-frontend.com,https://marketplace.com
# Lab Manager + Ops Worker
LAB_MANAGER_TOKEN=your_lab_manager_token
LAB_MANAGER_TOKEN_HEADER=X-Lab-Manager-Token
LAB_MANAGER_TOKEN_COOKIE=lab_manager_token
# Blockchain Services remote access
ADMIN_ACCESS_TOKEN=your_admin_access_token
ADMIN_ACCESS_TOKEN_HEADER=X-Access-Token
ADMIN_ACCESS_TOKEN_COOKIE=access_token
ADMIN_ACCESS_TOKEN_REQUIRED=true
ADMIN_DASHBOARD_LOCAL_ONLY=true
ADMIN_ALLOWED_CIDRS=
SECURITY_ALLOW_PRIVATE_NETWORKS=false
ADMIN_DASHBOARD_ALLOW_PRIVATE=false
# Certbot / ACME (optional - for Let's Encrypt automation)
CERTBOT_DOMAINS=yourdomain.com,www.yourdomain.com
CERTBOT_EMAIL=you@example.com
CERTBOT_STAGING=0Use a strong GUAC_ADMIN_PASS. Common defaults are rejected at startup to avoid insecure deployments. The same check applies to MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD (defaults like CHANGE_ME will stop MySQL from initializing). Set a strong LAB_MANAGER_TOKEN (or leave it empty to keep /ops disabled and /lab-manager private-network-only). Set ADMIN_ACCESS_TOKEN to protect wallet/billing endpoints exposed through OpenResty for remote access.
blockchain-services uses a dedicated schema named blockchain_services by default. If you want a different name, set BLOCKCHAIN_MYSQL_DATABASE in .env.
OpenResty and blockchain-services derive public URLs (issuer, OpenID metadata, etc.) from SERVER_NAME and HTTPS_PORT. If you ever need to override that computed value, set BASE_DOMAIN inside blockchain-services/.env or export it in the container's
environment. All authentication endpoints live under the fixed /auth base path to match both services.
- Full mode: leave
ISSUERempty. This gateway exposes its own auth endpoints and validates its own locally generated JWT signing keys. - Lite mode: set
ISSUER=https://<full-gateway-or-external-issuer>/auth. This gateway no longer acts as the JWT issuer. Instead, it trusts JWTs issued elsewhere and synchronizes the remote public key automatically.
-
Direct (default): Gateway has a public IP or you're testing locally.
- Local-only access:
OPENRESTY_BIND_ADDRESS=127.0.0.1 - Public access:
OPENRESTY_BIND_ADDRESS=0.0.0.0 - If you change
HTTPS_PORT/HTTP_PORT, also setOPENRESTY_BIND_HTTPS_PORT/OPENRESTY_BIND_HTTP_PORTto the same values.
docker compose up -d
- Local-only access:
-
Behind a router/NAT: External traffic arrives via port forwarding (e.g., router:8043 -> host:443). Set
OPENRESTY_BIND_ADDRESS=0.0.0.0.- Public port (what clients use):
HTTPS_PORT=8043 - Local bind port (what the host listens on):
OPENRESTY_BIND_HTTPS_PORT=443
docker compose up -d
- Public port (what clients use):
Optional Cloudflare Tunnel settings (filled automatically if you opt in during setup):
CLOUDFLARE_TUNNEL_TOKEN=your_cloudflare_tunnel_token_or_empty_for_quick_tunnelRuntime activation requires Compose profiles (--profile cloudflare or --profile cloudflare-token).
# Smart Contract
CONTRACT_ADDRESS=0xYourSmartContractAddress
# Network RPC URLs (with failover support)
ETHEREUM_MAINNET_RPC_URL=https://eth.public-rpc.com
ETHEREUM_SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com,https://0xrpc.io/sep,https://ethereum-sepolia-public.nodies.app
# Institutional Wallet (for automated transactions)
INSTITUTIONAL_WALLET_ADDRESS=0xYourWalletAddress
INSTITUTIONAL_WALLET_PASSWORD=YourSecurePassword
# Security
ALLOWED_ORIGINS=https://your-frontend.com,https://marketplace.com
MARKETPLACE_PUBLIC_KEY_URL=https://marketplace.com/.well-known/public-key.pem/wallet-dashboard,/wallet,/billing: requireADMIN_ACCESS_TOKENfor non-private clients. If the token is unset, access is limited to loopback/Docker networks./billing/admin/**: usesADMIN_ACCESS_TOKENonly (header/cookie). If the token is unset, access is limited to loopback/Docker ranges./billing/admin/execute: additionally requires an EIP-712 signature from the institutional wallet, including a fresh timestamp.- Initial setup: Click "Wallet & Treasuryβ" from the homepage, enter your
ADMIN_ACCESS_TOKENwhen prompted. The token will be stored in your browser and automatically included in all requests. - Strict localhost-only mode for the wallet dashboard and related wallet/billing routes:
ADMIN_DASHBOARD_LOCAL_ONLY=true,ADMIN_DASHBOARD_ALLOW_PRIVATE=false,SECURITY_ALLOW_PRIVATE_NETWORKS=false - Private-network mode for those routes:
ADMIN_DASHBOARD_LOCAL_ONLY=true,ADMIN_DASHBOARD_ALLOW_PRIVATE=true,SECURITY_ALLOW_PRIVATE_NETWORKS=true, and keepADMIN_ACCESS_TOKEN_REQUIRED=true - To limit private-network mode to specific subnets, set
ADMIN_ALLOWED_CIDRS:ADMIN_ALLOWED_CIDRS=10.20.0.0/16,192.168.50.0/24 /lab-manager: allows loopback and RFC1918 private networks by default; requiresLAB_MANAGER_TOKENfor non-private clients. Click "Lab Managerβ" from the homepage and enter your token when prompted. The bootstrap?token=...is stripped from the browser URL after the cookie is set./ops: network-restricted to loopback plus RFC1918 private networks (10.0.0.0/8,172.16.0.0/12,192.168.0.0/16), and also requiresLAB_MANAGER_TOKEN./aas-admin/**: always requiresLAB_MANAGER_TOKENvia header/cookie, even from private networks. This keeps AAS write operations aligned with explicit admin auth instead of LAN-only trust.- If wallet actions return
JSON.parseerrors in the browser, ensureCORS_ALLOWED_ORIGINSincludes your gateway origin.
The institutional wallet is managed automatically by blockchain-services:
-
First-time setup: Create or import the wallet via:
- Web console:
https://localhost:8443/wallet-dashboard(orhttps://your-domain/wallet-dashboard) - Or API: Call
/wallet/createor/wallet/importendpoints
- Web console:
-
Automatic configuration: After creation/import, blockchain-services automatically:
- Stores the encrypted wallet in
blockchain-data/wallets.json - Writes credentials to
blockchain-data/wallet-config.properties - Loads the wallet on every restart using the stored configuration
- Stores the encrypted wallet in
-
Manual override (optional): Only needed if using external secret management:
# In blockchain-services/.env - leave empty for automatic configuration INSTITUTIONAL_WALLET_ADDRESS= # Auto-configured from wallet-config.properties INSTITUTIONAL_WALLET_PASSWORD= # Auto-configured from wallet-config.properties
The encrypted wallet and configuration files are stored in blockchain-data/ which is mounted as a Docker volume and excluded from git.
Operating System:
- Linux (recommended) - Ubuntu 20.04+, Debian 11+, CentOS 8+
- Unix-like systems (BSD, macOS) - supported
- Windows - via WSL2 or Docker Desktop
Hardware (Minimum):
- 2 CPU cores
- 4GB RAM
- 20GB disk space (including Docker images and logs)
- Network interface with internet connectivity
Software:
- Docker Engine 20.10+ (Linux) or Docker Desktop (Windows/macOS)
- Docker Compose 2.0+ (included with Docker Desktop)
The Lab Gateway requires network connectivity to:
- External Users - To accept incoming HTTP(s) connections
- Internal Laboratory Servers - To proxy RDP/VNC/SSH connections
This can be achieved through various network topologies:
Internet ββ> [NIC1: Public IP] Lab Gateway [NIC2: Private IP] ββ> Lab Computers
- β Two physical or virtual Network Interface Cards (NICs)
- β Physical network isolation between public and lab networks
- β Highest security level
- β Requires specific hardware/VM configuration
Internet ββ> Router/Firewall ββ> [NIC: Private IP] Lab Gateway ββ> Lab Computers
- β Single NIC with routing configuration
- β Works with cloud providers (AWS, Azure, GCP, DigitalOcean, etc.)
- β Works with CDN/proxies (CloudFlare, CloudFront, etc.)
- β Works with VPS/dedicated servers
- β Labs accessed via private IPs or VPN tunnels
- β Most flexible and commonly deployed
Internet ββ> [NIC with VLAN tagging] Lab Gateway ββ> VLAN 10 / VLAN 20
- β Single NIC with 802.1Q VLAN tagging
- β Logical separation of public and lab traffic
- β Common in enterprise/datacenter environments
- Enable the Cloudflare Tunnel option during
setup.sh/setup.batto spin up thecloudflaredsidecar (Compose profilecloudflare) and expose the gateway without opening inbound ports. - Works behind campus/corporate NAT as long as outbound HTTPS (443) is allowed; WebSockets for Guacamole are supported through the tunnel.
- Token mode: paste a Cloudflare Tunnel token from your Zero Trust dashboard and Cloudflare will publish the hostname in your zone.
- Quick Tunnel mode: leave the token empty and a random
*.cfargotunnel.comhostname will appear indocker compose --profile cloudflare logs cloudflared. - Start/stop with the profile when needed:
docker compose --profile cloudflare up -d/docker compose --profile cloudflare down.
Development:
- Self-signed certificates (auto-generated)
- Valid for localhost testing
Production:
- Valid SSL certificate from trusted CA
- Let's Encrypt (free, automated renewal)
- Commercial certificate providers
- Wildcard certificates for multiple subdomains
- OpenResty - Reverse proxy and load balancer with Lua scripting
- Apache Guacamole - Clientless remote desktop gateway (RDP/VNC/SSH)
- MySQL 8.0 - Primary database for configuration and user data
- Docker - Containerization platform with Compose orchestration
- Blockchain Services (Spring Boot 4.x) - Authentication and wallet operations microservice
- Web3j - Ethereum blockchain integration library
- JWT - Generates authentication tokens with blockchain claims
- Smart Contract Events - Real-time blockchain monitoring
lab-gateway/
βββ π flake.nix # Nix flake outputs (NixOS config/module)
βββ π docker-compose.yml # Main service orchestration
βββ π .env.example # Gateway configuration template
βββ π setup.sh / setup.bat # Guided setup scripts
βββ π selfsigned-refresh.sh # Self-signed cert helper
βββ π nix/
β βββ nixos-module.nix # services.lab-gateway (compose-managed) module
β βββ hosts/gateway.nix # Host defaults for nixosConfigurations.gateway
βββ π blockchain-services/ # Blockchain auth/wallet service (submodule)
βββ π openresty/ # Reverse proxy (Nginx + Lua)
β βββ nginx.conf
β βββ lab_access.conf
β βββ lua/
β βββ tests/ # Lua unit test runner/specs
βββ π guacamole/ # Guacamole image customizations
βββ π mysql/ # DB bootstrap and schema scripts
βββ π ops-worker/ # Lab station operations API worker
βββ π web/ # Static frontend assets/pages
βββ π certbot/ # ACME webroot/support files
βββ π tests/
β βββ smoke/ # End-to-end smoke tests
β βββ integration/ # Integration tests with mocks
βββ π docs/ # Install guides, eduGAIN integration, and provider tutorials
βββ π certs/ # Runtime certificates/keys (not in git)
βββ π blockchain-data/ # Runtime wallet/provider data (not in git)
βββ π configuring-lab-connections/ # Guacamole connection setup docs
certs/ and blockchain-data/ are runtime directories and may not exist until first setup.
blockchain-services/ is a Git submodule and must be initialized/updated before running the stack.
- Fork the project
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request