{"id":50404558,"url":"https://github.com/layer-3/nitrolite-store-example","last_synced_at":"2026-05-31T01:02:55.091Z","repository":{"id":354630988,"uuid":"1210239856","full_name":"layer-3/nitrolite-store-example","owner":"layer-3","description":"Go reference app for the Nitrolite SDK","archived":false,"fork":false,"pushed_at":"2026-05-25T13:35:44.000Z","size":2638,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T15:28:12.248Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/layer-3.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-14T08:08:17.000Z","updated_at":"2026-04-28T11:29:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/layer-3/nitrolite-store-example","commit_stats":null,"previous_names":["layer-3/nitrolite-store-example"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/layer-3/nitrolite-store-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/layer-3%2Fnitrolite-store-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/layer-3%2Fnitrolite-store-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/layer-3%2Fnitrolite-store-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/layer-3%2Fnitrolite-store-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/layer-3","download_url":"https://codeload.github.com/layer-3/nitrolite-store-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/layer-3%2Fnitrolite-store-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33715211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T01:02:51.288Z","updated_at":"2026-05-31T01:02:55.079Z","avatar_url":"https://github.com/layer-3.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nitrolite-go-example\n\nGo backend reference app for a Nitrolite-backed **content store** with a **TypeScript + MetaMask** frontend.\n\nOne Go service serves the built frontend and the store API from the same origin. The shopper experience is intentionally small:\n\n- connect MetaMask\n- add funds\n- buy content\n- read purchased content\n- withdraw remaining balance\n\nProtocol details stay behind the implementation boundary. The main UI does not expose raw Nitrolite or channel-management surfaces.\n\n## Surfaces\n\n- `/` shopper-facing store UI\n- `/healthz` process health\n- `/readyz` Clearnode readiness\n\n## What This Demonstrates\n\n- a Go backend that validates, app-signs, and submits frontend-constructed app-session updates\n- a React + Vite frontend that uses MetaMask as the real shopper identity\n- automatic app-session bootstrap after wallet connect\n- exact-payload forwarding: the update the user signs is the update the backend submits\n- instant content gating after successful purchase\n- YUSD and YELLOW catalog pricing from one seeded catalog\n\n## Trust model\n\nThis refresh is wallet-first.\n\n- MetaMask is the shopper identity\n- the frontend constructs the full app-session update\n- the shopper signs the update in MetaMask\n- the backend validates the same payload, app-signs it, and submits it\n- the backend does **not** sign as the user\n- channel management is assumed to exist already and is out of scope for this app\n\nSession keys are intentionally deferred to a later phase.\n\n## Quickstart\n\n```bash\ncp .env.example .env\ngo run ./cmd/server\n```\n\nOpen [http://localhost:8080/](http://localhost:8080/).\n\nIf you were previously running an older build of this app, stop that server first. Web assets are embedded into the Go binary, so an older process will keep serving stale UI until restarted.\n\n## How To Use The Store\n\nThe main product surface is `/`.\n\nNormal flow:\n\n1. Open `/`\n2. Connect MetaMask on Sepolia\n3. Wait for the store session to bootstrap automatically\n4. Add funds to the store balance\n5. Buy an item from the catalog\n6. Open it from `Library`\n7. Withdraw any remaining balance\n\nThe main panels mean:\n\n- `Wallet` shows the connected shopper identity\n- `Available balance` is what can still be committed into the store\n- `Store balance` is the shopper allocation already inside the store session\n- `Catalog` lists seeded content and prices for the selected asset\n- `Library` shows wallet-owned content\n- `Reader` displays purchased content after ownership is confirmed\n- `Browser activity` is a client-side trace for debugging\n\n## Frontend development\n\nThe product UI now lives in `frontend/` and is built with React + Vite + TypeScript.\n\nDevelopment shape:\n\n- run the Go API separately\n- run Vite in `frontend/`\n- proxy `/api/*` to the Go server\n\nProduction shape:\n\n- build the frontend into `internal/webui/dist`\n- run the Go server\n- the Go server serves the built frontend and API from the same origin\n\n## Railway deployment\n\nThe recommended production-like deployment for this repo is a single Railway service backed by the included `Dockerfile`.\n\nExpected Railway shape:\n\n- one web service for the whole app\n- repo-linked to `main`\n- one attached volume mounted at `/app/data`\n- `SQLITE_PATH=/app/data/nitrolite-go-example.db`\n\nRecommended first-time flow:\n\n1. Link the repo to the target project:\n\n```bash\nrailway link --workspace \u003cyour-workspace\u003e --project \u003cyour-project\u003e\n```\n\n2. Add or link the service that will run this repo.\n3. Add a volume and mount it at `/app/data`.\n4. Set the required variables on the service.\n5. Deploy from the repo or run `railway up` from the repo root.\n\nIf you are deploying inside the Yellow org, use the appropriate internal workspace/project instead of the placeholders above.\n\nUseful Railway CLI commands:\n\n```bash\nrailway volume add --mount-path /app/data\nrailway variable set CLEARNODE_WS_URL=... DEMO_PRIVATE_KEY=... CONSOLE_API_KEY=... BLOCKCHAIN_RPC_URLS='{\"11155111\":\"https://...\"}' HOME_BLOCKCHAINS='{\"yusd\":11155111,\"yellow\":11155111}' SQLITE_PATH=/app/data/nitrolite-go-example.db STORE_NAME=\"Nitrolite App Session Store\" STORE_APP_ID=default\nrailway up --service \u003cservice-name\u003e\nrailway service status --service \u003cservice-name\u003e\n```\n\nIf Railway cannot fetch the GitHub repo initially, the fallback is a first deploy from the local checkout with `railway up`, then converting the service to repo-linked afterward.\n\n## Required environment\n\nRequired:\n\n- `CLEARNODE_WS_URL`\n- `DEMO_PRIVATE_KEY`\n- `CONSOLE_API_KEY`\n- `BLOCKCHAIN_RPC_URLS`\n- `HOME_BLOCKCHAINS`\n\nOptional with defaults:\n\n- `PORT=8080` locally; Railway injects `PORT` automatically\n- `LOG_LEVEL=info`\n- `SQLITE_PATH=./data/nitrolite-go-example.db`\n- `STORE_NAME=Nitrolite App Session Store`\n- `STORE_APP_ID=default`\n- `STORE_APP_PRIVATE_KEY=` to override the derived store-app signer\n\nRuntime assumptions:\n\n- the demo signer wallet must already have sandbox funds\n- for a shared deployment, the signer should be a team-owned funded sandbox key\n- the configured home-channel assets must exist on the node\n- the RPC URLs must support the configured home chains\n- `/healthz` reports process health even while the SDK is reconnecting; `/readyz` stays `503` until the Clearnode connection is live\n\n## API shape\n\nPublic store routes:\n\n- `POST /api/store/connect/challenge`\n- `POST /api/store/connect/verify`\n- `GET /api/store/bootstrap?asset=...`\n- `POST /api/store/update`\n- `GET /api/store/content/{id}?asset=...`\n- `GET /healthz`\n- `GET /readyz`\n\nThe backend receives the full frontend-constructed update payload, validates it, app-signs it, and submits the same payload to Clearnode.\n\n## Persistence\n\nSQLite stores:\n\n- browser-scoped store sessions\n- purchases\n- legacy unused tables that are no longer part of the active product path\n\nDefault path:\n\n- `./data/nitrolite-go-example.db`\n\nRecommended Railway path:\n\n- `/app/data/nitrolite-go-example.db`\n\nIf you are switching from an older build, delete the old local DB first if you want a clean store state:\n\n```bash\nrm -f ./data/nitrolite-go-example.db\n```\n\n## Running tests\n\nStandard:\n\n```bash\ngo test ./...\n```\n\nIf your machine blocks cgo builds because of the local Xcode license state, use:\n\n```bash\nCGO_ENABLED=0 GOCACHE=/tmp/nitrolite-go-example-gocache go test ./...\n```\n\n## Troubleshooting\n\nIf you still see an older UI or the old “Go SDK guided demo” UI:\n\n- stop the existing process on `:8080`\n- restart with `go run ./cmd/server`\n- hard refresh the browser\n\nIf store actions fail:\n\n- check `/healthz`\n- confirm the demo wallet has balance in the selected asset\n- on Railway, confirm the volume is attached and `SQLITE_PATH` points at `/app/data/nitrolite-go-example.db`\n- inspect `/advanced` for raw SDK state\n- use `/reference` to inspect the live request/response shapes\n\n## Where to look\n\n- [`AGENTS.md`](./AGENTS.md)\n- [`CLAUDE.md`](./CLAUDE.md)\n- [`docs/api.md`](./docs/api.md)\n- [`docs/architecture.md`](./docs/architecture.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flayer-3%2Fnitrolite-store-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flayer-3%2Fnitrolite-store-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flayer-3%2Fnitrolite-store-example/lists"}