LensMint is a Raspberry Pi-oriented proof of concept that combines:
- deterministic Ed25519 device key generation,
- an Axum API server for mint-related operations,
- and a Slint touch UI for a 3.5-inch Raspberry Pi display (default window 320×240 in
ui/main.slint, matching common SPI / small panels).
This README starts with how to run the project, then explains the implementation in this order:
- key generation mechanism,
- server and mint pipeline,
- Slint UI flow on Raspberry Pi.
git clone https://github.com/anurag-p6/lensmint-poc
cd lensmint-pocCreate a .env file in the project root. Minimum required values are chain/operator values:
cp .env.example .envThen edit .env:
LENS_MINT_RPC_URL=<rpc_url>
LENS_MINT_CHAIN_ID=<chain_id>
LENS_MINT_DEVICE_REGISTRY_ADDRESS=<device_registry_contract>
LENS_MINT_ERC1155_ADDRESS=<erc1155_contract>
LENS_MINT_OPERATOR_KEY=<operator_private_key>Optional but commonly used:
LENS_MINT_API_PORT=3000
LENS_MINT_API_BIND_HOST=127.0.0.1
LENS_MINT_UI_LIVE_PREVIEW=1
# On Raspberry Pi with hwrng enabled:
# LENS_MINT_ENTROPY_DEVICE=/dev/hwrngcargo run- API health:
curl http://127.0.0.1:3000/health
- UI:
- Slint camera interface should open.
- On Pi + camera, live preview should update when
LENS_MINT_UI_LIVE_PREVIEW=1.
The device needs a stable cryptographic identity so each minted frame can be signed by the same Ed25519 keypair across reboots.
- Startup resolves the key path using
ed25519_secret_path(data_dir). - Default persisted key path:
~/.lensmint/data/ed25519_secret_key.bin- or
<LENS_MINT_DATA_DIR>/data/ed25519_secret_key.binwhen overridden.
- If the key file exists:
- it must be exactly 32 bytes,
- secret key is loaded,
- public key is deterministically re-derived.
- If the key file does not exist:
- a new signing key is generated from configured entropy,
- secret key bytes are persisted,
- public key is derived and stored in runtime state.
- Default entropy source:
/dev/urandom - Pi hardware RNG option:
LENS_MINT_ENTROPY_DEVICE=/dev/hwrng
This keeps device identity persistent while allowing Pi-specific entropy configuration.
main.rs performs:
- load
.env(if present), - read config from environment,
- load/create Ed25519 keypair,
- initialize chain clients,
- start Axum API server thread,
- optionally run API-only mode (
LENS_MINT_API_ONLY=1), - otherwise start preview thread and Slint UI.
-
GET /health
simple readiness check. -
POST /mint
executes the main pipeline:- capture frame bytes,
- compute
SHA-256frame hash, - sign hash with device Ed25519 secret key,
- derive deterministic demo proof and tx hash values,
- return
MintResponsewith hashes/signature/public key metadata.
Additional endpoints:
POST /v1/nft/preparePOST /v1/chain/register-devicePOST /v1/chain/mint-originalPOST /v1/chain/mint-edition
For /mint, capture tries in this order:
- webcam via
nokhwa, - Raspberry Pi
rpicam-still/libcamera-still, - synthetic JPEG frame,
- final deterministic byte fallback.
This layered fallback keeps the server functional even when camera hardware is missing or temporarily unavailable.
- Prefer running directly on Raspberry Pi OS for reliable camera access.
- Ensure
rpicam-stillorlibcamera-stillexists. - For LAN access:
- set
LENS_MINT_API_BIND_HOST=0.0.0.0, - consume API via
http://<pi-ip>:<port>.
- set
- 3.5-inch touch display. The Slint root
LensMintAppuses a fixed 320×240 window (preferred-/min-/max-width and height) so the layout fits compact panels (seeui/main.slint). If your framebuffer differs, change those values and adjust rail widths if needed. - Camera-style UX:
- left control rail,
- center viewfinder,
- right capture rail.
- Preview loop pushes RGBA frames to the UI.
- Preview source order:
- webcam frame stream,
- low-latency libcamera JPEG preview,
- synthetic animated preview (if enabled and no camera source).
- UI modes:
- hero-image mode (default when live preview is disabled),
- live viewfinder mode (
LENS_MINT_UI_LIVE_PREVIEW=1).
- Shutter action updates UI hash state based on latest frame.
- Mint action triggers backend call to
/mint. - Status text and hash metadata are updated from API responses.
- Settings sheet allows editing
camera_idand NFT title used in requests.
- Prefer landscape fullscreen or kiosk mode on the 3.5-inch panel. Match the 320×240 logical size in
ui/main.slint, or scale at the OS if the hardware reports another mode. - Run fullscreen/kiosk so Slint fills the display cleanly.
- Use large touch targets (already reflected in the UI icon rails and capture controls).
LENS_MINT_API_PORT(default:3000)LENS_MINT_API_BIND_HOST(default:127.0.0.1)LENS_MINT_API_BASE_URL(default:http://127.0.0.1:<port>)LENS_MINT_API_ONLY(1=> run server only)LENS_MINT_DATA_DIR(override base data dir for persisted key material)LENS_MINT_ENTROPY_DEVICE(default/dev/urandom, Pi option/dev/hwrng)LENS_MINT_UI_LIVE_PREVIEW(1=> live preview in Slint viewfinder)LENS_MINT_SYNTHETIC_PREVIEW(0=> disable synthetic fallback animation)LENS_MINT_WEBCAM_INDEX(prioritized webcam index in dev environments)

