Noders provides a shared DevNet sandbox for Season 3 teams so you build logic, not infrastructure. Status 2026-09-27: the public demo (https://100-30-125-235.nip.io) runs against the shared node with auto-refreshing offline tokens (EUR €70k→€30k + USD $312k→$40k settled through the public site, receipts + pain.001 verified).
Package note (2026-09-27): the repo now carries hardened contracts —
Approval is bound to a proposal-terms hash and fully-netted cycles settle with
zero transfers. A Daml change produces a new package id, and the shared node
only vets packages the Noders operator approves. The live demo therefore keeps
running the previously vetted package (d1b3b958…) until the new one is vetted.
Live state (2026-10-08): the demo runs the audited package
netsettle-v2-1.0.0 (5eccaf6d…), vetted, with CANTON_TERMS_BINDING=on.
The pre-audit package 0467433b… remains vetted for rollback. Verified on the
shared node after the flip: EUR 70k->30k and USD 312k->40k settled with 4
receipts, privacy proof green for all four subsidiaries, and the new
authorization guards return 403/404 as designed.
Historical upgrade sequence:
- Ask the Noders operator (Discord support / mentor channel) to vet package
netsettle-v2-1.0.0at its new hash — see the ask template below. - Upload
daml/.daml/dist/netsettle-v2-1.0.0.darvia the Console UI (same login). - On the demo host (
ssh -i ~/.ssh/netsettle-key.pem ec2-user@100.30.125.235):Then run the full flow once (ingest → review → propose → approve → settle) and checkcd /opt/netsettle && git pull pnpm install --frozen-lockfile (cd daml && dpm build) # Confirm the built package id equals what was vetted, then point the app at it: dpm inspect-dar daml/.daml/dist/netsettle-v2-1.0.0.dar | grep -oE 'netsettle-v2-1\.0\.0-[0-9a-f]{64}' | head -1 sed -i 's/^CANTON_PACKAGE_ID=.*/CANTON_PACKAGE_ID=<vetted package id>/' apps/web/.env.local sed -i 's/^CANTON_TERMS_BINDING=.*/CANTON_TERMS_BINDING=on/' apps/web/.env.local sudo systemctl restart netsettle-web.service curl -s localhost:3100/api/state | grep -o '"packageId":"[^"]*"'
/api/viewbefore announcing. The parties stay the same; only the package id changes.CANTON_TERMS_BINDING=offis what lets the app keep serving the older package today: leave it off until the new package is vetted, then flip it on with the id change. - Keep the old package vetted for rollback; pointing
CANTON_PACKAGE_IDback is the only rollback step.
Operator ask (copy-paste):
Hi Noders team — HackCanton team NetSettle (Track 1, package
netsettle, currently live and vetted onhackcanton-01asd1b3b958…).We hardened our Daml contracts: approvals now bind to a hash of the exact proposal terms, and fully-netted cycles settle with zero transfers. The new build is package id
5eccaf6d8061169762a7f091a3ee8f69ccf814b69b1dbb5d748b846fa87c8d24, same versionnetsettle-v2-1.0.0(592,681 bytes, sha25617f972f231d4f5d61d9386008c0934d4143846ee8467dcc7ece3320c58cb6670).It's not on the node yet — our app identity gets a 403 on
POST /v2/packages, so we can only upload through the Console UI. Two ways to unblock, whichever is easier: we upload the DAR ourselves and you vet it, or you upload and vet it. Happy to send the DAR however suits. Our existing operator + 4 subsidiary parties can stay exactly as they are — this is a package swap only.Not urgent: the current vetted package keeps the demo running. Thanks!
Source: Season 3 materials (official) + live endpoint probes.
| Resource | Endpoint |
|---|---|
| JSON Ledger API | https://ledger-api-json.participant.hackcanton-01.devnet.naas.noders.services |
| gRPC Ledger API | ledger-api-grpc.participant.hackcanton-01.devnet.naas.noders.services:443 |
| Wallet | https://wallet.validator.hackcanton-01.devnet.naas.noders.services |
| CNS | https://cns.validator.hackcanton-01.devnet.naas.noders.services |
| Console | https://console.participant.hackcanton-01.devnet.naas.noders.services |
| Validator / Scan API | https://validator-api-http.validator.hackcanton-01.devnet.naas.noders.services |
| Grafana logs | https://grafana.participant.hackcanton-01.devnet.naas.noders.services |
| OIDC token URL | https://keycloak.naas.noders.services/realms/noders-appsfactory/protocol/openid-connect/token |
| Token audience | https://hackcanton-01.devnet.naas.noders.services |
Shared node = shared ledger. No sensitive data. Our demo parties and contracts are visible to every team on the node.
Password grants are disabled on the wallet client, but the official guide
documents a working grant on the web-app client with offline_access:
curl -sS 'https://keycloak.naas.noders.services/realms/noders-appsfactory/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=web-app-ui-hackcanton-01-devnet' \
--data-urlencode 'username=<YOUR_PLATFORM_EMAIL>' \
--data-urlencode 'password=<YOUR_PLATFORM_PASSWORD>' \
--data-urlencode 'scope=openid daml_ledger_api offline_access' > tokens.jsonaccess_token: valid 3 hours, sent asAuthorization: Bearerto the node.refresh_token: offline token, no fixed expiry — valid until revoked or unused too long. This is what makes DevNet fully live: the app auto-refreshes the access token forever (see below). Guard it like a password.
For a quick browser-sourced token instead: wallet → DevTools → Local Storage. That access token works but dies in hours with no refresh path.
Wallet API tokens on the shared node are heavily restricted (probed 2026-09-23, all with a valid token):
| Action | Result |
|---|---|
| Read packages / ledger-end / vetting list / whoami | ✅ works |
Upload DAR (/v2/packages, /v2/dars) |
❌ 403 |
Vet package (/v2/package-vetting/update) |
❌ 403 |
| Allocate party (Daml Script over gRPC+TLS) | ❌ PERMISSION_DENIED |
Status (re-verified 2026-10-01): the demo package d1b3b958…dee64f is vetted
and live — /v2/packages returns it, and the running app settles against it. The
hardened package 33437385… is not on the node at all: POST /v2/package-vetting/list returns zero unvetted package ids, so an earlier note
saying it had been "uploaded via Console" was wrong. POST /v2/packages still
returns 403 for our app identity, so the DAR has to go up through the Console UI
before anyone can vet it.
Funding: ~900 CC obtained on our party (covers all demo traffic fees with orders of magnitude to spare — one future blocker pre-solved). Once the operator party exists, a small CC transfer to it covers submission fees.
Resolution path: one ask to Noders (Discord support/mentor channel) covers everything — vet the package AND allocate 5 parties (or grant our user party-allocation rights). Draft ask below.
- Open
https://console.participant.hackcanton-01.devnet.naas.noders.servicesand sign in. - Upload
daml/.daml/dist/netsettle-v2-1.0.0.dar. (0.1.0 done 2026-09-23; 1.0.0 uploaded and vetted 2026-10-08, live.) - Vet package
netsettle(uniquely named — no collisions with other teams). (Blocked — needs operator.) - Allocate 5 parties (operator + 4 subsidiaries) or equivalent rights. (Blocked — needs operator.)
UPLOADED=1 VETTED=1 TOKEN=<paste> bash scripts/devnet-deploy.shThe script allocates operator + 4 subsidiaries over gRPC and writes
apps/web/.env.devnet (git-ignored). If gRPC allocation also hits permission
errors, allocate via the Console UI and paste the party IDs into .env.devnet
by hand (format in apps/web/.env.local.example).
Note: our DAR builds under SDK 3.5.1; the node runs 3.5.17. Same 3.5 line —
expected compatible; the upload response will confirm or deny. If party
allocation over gRPC fights TLS, fall back to allocating via the Console UI
and paste the party IDs into .env.devnet by hand.
Point a local app run at .env.devnet and walk ingest → settle → payment
file. Do not repoint the public EC2 app — keep the judged demo on the
reproducible local ledger unless the DevNet run is fully green.
The gateway accepts a token provider backed by TokenManager
(packages/canton-gateway/src/tokenRefresh.ts): cached access token,
pre-emptive refresh 60s before expiry, rotation tracking, optional
persistence. The web app wires it when CANTON_REFRESH_TOKEN is set
(apps/web/lib/ledger.ts):
| Variable | Purpose |
|---|---|
CANTON_REFRESH_TOKEN |
Offline refresh token from Step 1. Enables indefinite access. |
CANTON_OIDC_TOKEN_URL |
Override (defaults to the DevNet realm). |
CANTON_OIDC_CLIENT_ID |
Override (defaults to web-app-ui-hackcanton-01-devnet). |
CANTON_REFRESH_TOKEN_FILE |
Optional path where rotated refresh tokens persist across restarts. |
CANTON_API_TOKEN |
Fallback: static access token for short sessions. |
Refresh tokens are password-equivalent: server-side only (SSM/env), never committed, never sent to browsers, never sent to the ledger — only to the token endpoint. If refresh fails with "Token is not active", re-run the Step 1 password grant.
- Buys: "settled on shared Canton DevNet" in the pitch + Q&A; real multi-participant topology instead of single-node sandbox.
- Doesn't buy: real assets (still test obligations), wallet UX (still server-mediated approvals), Featured status.
- Costs: shared-node flakiness, credential management, ~1 day.
Recommendation: attempt once the MVP freeze holds; timebox to one day. If the node fights back, the local-ledger demo plus this runbook is the fallback story — "DevNet-ready, reproducible locally."
The demo host's security group netsettle-sg (sg-01274cf91fbb413db) allows SSH
only from a single /32. A residential IP changes without warning, and the node
then silently drops port 22 — the box looks fine over HTTPS, so it reads as
"the host is down" when it is only your source address that stopped matching.
Diagnose in one line:
curl -s https://ifconfig.me # your current egress IP
nc -zv 100.30.125.235 443 # open -> host is alive
nc -zv 100.30.125.235 22 # timeout -> SG dropped youFix with the AWS CLI (no console needed):
aws ec2 authorize-security-group-ingress --group-id sg-01274cf91fbb413db \
--ip-permissions "IpProtocol=tcp,FromPort=22,ToPort=22,IpRanges=[{CidrIp=$(curl -s https://ifconfig.me)/32,Description='ssh from current egress IP'}]"
ssh -i ~/.ssh/netsettle-key.pem ec2-user@100.30.125.235Prefer a durable option if you get tired of this: EC2 Instance Connect
(push-based, needs no inbound SSH rule at all) or pin the SG to a range you
control. Do not leave 0.0.0.0/0 on port 22 for longer than the hackathon.