feat(localnet): role-scoped nginx vhosts for wallet and ledger APIs - #280
Conversation
Serve each Splice UI/API behind an instance-scoped virtual host of the
form <service>.<instance>.localhost (e.g. wallet.localnet-2.localhost)
instead of the flat *.localhost names, so URLs stay unambiguous across
concurrently running localnets.
- assets/nginx/{app-provider,app-user,sv}.conf: server_name is now a
${VHOST_*} placeholder (wallet, ans, scan, sv, json-ledger-api,
grpc-ledger-api); the flat Splice names are dropped. The deprecated
canton.localhost block is left flat.
- overlay.go: add instanceVHost() + VHostService* consts; thread the
instance name into WriteNginxVhostOverlay and inject the per-instance
VHOST_* values as nginx container env (envsubst expands them at boot).
- up.go / status.go: advertise wallet UIs at wallet.<instance>.localhost
in the welcome screen and status endpoints.
- env.go: instance-scope CANTON_SCAN_UI_URL and emit per-role
CANTON_<ROLE>_{JSON,GRPC}_LEDGER_API_URL plus unqualified
CANTON_{JSON,GRPC}_LEDGER_API_URL aliases (app-provider), each behind
the matching ledger-api vhost.
- token/registry_url.go + registry/client.go + registry/doc.go +
canton integration test: thread the instance-scoped scan Host header
through DevKit's own scan-registry client.
- ui_reachability.go: dial loopback (multi-label *.localhost does not
resolve via the OS/Go resolver on macOS) but carry the wallet vhost as
the Host header so the probe validates the real route.
Note the resolution caveat documented throughout: *.localhost resolves
to 127.0.0.1 in browsers, curl, and Go, but not in JVM/Node/Python
resolvers, which must send an explicit Host header (HTTP) / :authority
pseudo-header (gRPC) or add an /etc/hosts entry.
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Serve role-specific Splice services at role-scoped instance vhosts of the form <service>.<role>.<instance>.localhost (wallet, json-ledger-api, grpc-ledger-api, ans) so per-role UIs/APIs are unambiguous. Single-per- instance services (scan, sv) keep the shorter <service>.<instance> shape. The longer role-scoped names overflow nginx's default 64-byte server-name hash bucket, so add a DevKit-owned http-context tuning snippet (00-devkit-tuning.conf) that bumps server_names_hash_bucket_size to 128 and mount it straight into conf.d; without it nginx aborts on boot with "could not build server_names_hash".
|
Note: this is an intermediate migration step. Right now each localnet instance still exposes its services across several distinct UI ports (e.g. app-user :52342, app-provider :52343, sv :52344), and we disambiguate per role/service with hostnames layered on top of those ports. The end goal is to serve each localnet instance behind a single port, using multiple hostnames to route to the individual services/roles — i.e. one listen port per instance, with nginx |
Summary
Builds on #279. Serves each nginx-fronted Splice UI/API behind an instance-scoped virtual host so URLs stay unambiguous when multiple localnets run concurrently, dropping the flat
*.localhostnames entirely.Vhosts come in two shapes:
<service>.<role>.<instance>.localhost) for services that exist per role —wallet,ans,json-ledger-api,grpc-ledger-api— e.g.wallet.app-user.localnet-2.localhost,json-ledger-api.app-provider.localnet-2.localhost. This disambiguates the app-user / app-provider / sv instances of each service, which all listen on distinct UI ports.<service>.<instance>.localhost) for the SV node's own UIs —scan,sv.Changes
assets/nginx/{app-provider,app-user,sv}.conf:server_nameis now a role-suffixed${VHOST_*_<ROLE>}placeholder for role-scoped services (e.g.${VHOST_WALLET_APP_USER}), and a plain${VHOST_SCAN}/${VHOST_SV}for the single ones; flat Splice names removed. The deprecatedcanton.localhostblock is left flat.assets/nginx/00-devkit-tuning.conf(new): http-context snippet mounted straight intoconf.d(ahead of the role blocks via the00-sort prefix). The longer role-scoped names overflow nginx's default 64-byte server-name hash bucket, so this bumpsserver_names_hash_bucket_sizeto 128 — without it nginx aborts on boot withcould not build server_names_hash.overlay.go:instanceVHost()(single) + newinstanceVHostRole()(role-scoped) +VHostService*consts;WriteNginxVhostOverlayinjects the per-instance/per-roleVHOST_*values as nginx container env (envsubst expands them at boot) and materializes + mounts the tuning snippet (always overwritten — it's a boot invariant, not edit-preserving).up.go/status.go: welcome screen + status endpoints advertise each wallet UI at its role-scopedwallet.<role>.<instance>.localhostvia a sharedwalletUIRoleByKeymap /walletVHostForKeyhelper.env.go: single-scopesCANTON_SCAN_UI_URL; per-roleCANTON_<ROLE>_{JSON,GRPC}_LEDGER_API_URLand unqualifiedCANTON_{JSON,GRPC}_LEDGER_API_URLaliases (default app-provider) now carry the role-scoped ledger-api vhost.ui_reachability.go: dials loopback (multi-label*.localhostdoesn't resolve via the OS/Go resolver on macOS) but carries the role-scoped wallet vhost as theHostheader so the probe validates the real route.Resolution caveat (documented throughout)
*.localhostresolves to 127.0.0.1 in browsers, curl, and Go, but not in JVM/Node/Python resolvers (and some Rust HTTP clients). Those clients must send an explicitHost:header (HTTP) /:authority:pseudo-header (gRPC) or add an/etc/hostsentry. No auto/etc/hostsmanagement is performed.Verification
go build ./...,go vet,golangci-lintclean; fullgo test ./...green (1538 passed).localnet-2(Splice 0.6.12) and curl-verified against the live nginx:Host: wallet.app-user.localnet-2.localhost→ wallet bundle (index-V6UCL5Qo.js) on :52342; app-provider :52343; sv :52344.Host: ans.app-user.localnet-2.localhost→ ANS bundle (index-BFLvwVCQ.js) — confirms wallet and ANS route to distinct upstreams on the same port (the bug this fixes: the wallet URL was previously falling through to the ANS server block).Host: json-ledger-api.app-provider.localnet-2.localhost/v2/version→ 200.Host: scan.localnet-2.localhost→ 200.localnet statusshows role-scoped wallet URLs withreachability: ok;localnet envemits the role-scoped ledger-api URL vars.