Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a489a91
test: bootstrap bats-core harness with isolated CANTON_DEVREL_DIR
lmcorbalan May 14, 2026
06e9f43
feat(lib): add validate_validator_name with regex + reserved-name checks
lmcorbalan May 14, 2026
86cda27
feat(lib): add atomic JSON registry with flock/mkdir lock fallback
lmcorbalan May 14, 2026
777f1d2
feat(lib): allocate_port_base probes 5900-step-1000 with 10 retries
lmcorbalan May 14, 2026
97a3962
feat(lib): resolve_active_set implements §6 precedence rules
lmcorbalan May 14, 2026
c6827f6
feat(lib): render per-custom .env file with load-bearing bundle keys
lmcorbalan May 14, 2026
a029994
feat(overlays): add customs (localnet :5000 nginx) and attach-localne…
lmcorbalan May 14, 2026
848dbc3
feat(lib): render per-custom nginx conf and provide reload helper
lmcorbalan May 14, 2026
c7640e4
feat(lib): centralize infra and per-custom docker compose argv builders
lmcorbalan May 14, 2026
e23f357
chore(lib): export REPO_DIR/OVERLAYS_DIR/VALIDATOR_BUNDLE_DIR/CANTON_…
lmcorbalan May 14, 2026
f4a4091
feat(validator): list and info verbs read registry and probe health
lmcorbalan May 14, 2026
30fd664
feat(validator): add verb with multi-step rollback on failure
lmcorbalan May 14, 2026
6252595
feat(validator): start/stop/rm verbs for builtin and custom validators
lmcorbalan May 14, 2026
33c8aab
feat(scripts): add validator subcommand dispatcher
lmcorbalan May 14, 2026
0ce66fc
feat(cli): route 'canton devrel validator' to validator dispatcher
lmcorbalan May 14, 2026
b355c1d
feat(start): parse --validators/--with/--without and boot custom proj…
lmcorbalan May 14, 2026
860f548
feat(stop): snapshot intent in registry and stop customs before localnet
lmcorbalan May 14, 2026
5ed5468
feat(status): drive rows from the registry and include customs
lmcorbalan May 14, 2026
3e031c9
feat(reset): drive teardown from registry and add --purge for state-d…
lmcorbalan May 14, 2026
8eca305
feat(logs): add --validator <name> to target a custom project
lmcorbalan May 14, 2026
87338ed
feat(deploy): add --validator <name> for targeted DAR upload
lmcorbalan May 14, 2026
7cd517d
test: docker-gated lifecycle integration suite (skipped unless CI_INT…
lmcorbalan May 14, 2026
245d444
docs: document --with/--without flags and validator subcommand
lmcorbalan May 14, 2026
9fc4f4f
fix(canton): forward args from wrapper to start/stop/status/reset
lmcorbalan May 14, 2026
e5dbdb2
fix(compose): re-emit --profile flags lost in validator-projects refa…
lmcorbalan May 14, 2026
06b5f44
fix(splice): override party hints to work around bundle v0.5.18 valid…
lmcorbalan May 14, 2026
f9d5901
fix(reset): wipe runtime state only and brace CANTON_DEVREL_DIR
lmcorbalan May 14, 2026
4ee13c4
fix(status): blank wallet URL for DOWN validators
lmcorbalan May 14, 2026
30bc860
fix(start): only print URLs for active profiles in the final banner
lmcorbalan May 14, 2026
8da2872
fix(customenv): use ghcr.io image repo, derive IMAGE_TAG, ship UI def…
lmcorbalan May 14, 2026
d60e35d
fix(overlay): bind validator and participant host ports for custom pr…
lmcorbalan May 14, 2026
4a17460
fix(customenv): default PARTICIPANT_IDENTIFIER to party_hint
lmcorbalan May 14, 2026
3ce5d49
fix(splice): patch SV scan public-url and internal-url for peer reach…
lmcorbalan May 14, 2026
d150e4f
feat(validator): fetch a fresh onboarding secret from the SV DevNet e…
lmcorbalan May 14, 2026
2425d0f
fix(nginx): publish customs on :5500 and include the customs subdir glob
lmcorbalan May 14, 2026
f092a79
chore(validator): longer readyz timeout, progress lines, failure-log …
lmcorbalan May 14, 2026
80c95f4
fix(deploy): target running validators from the registry
lmcorbalan May 14, 2026
e46dcd8
Merge branch 'main' into feat/validator-projects-v2
lmcorbalan May 14, 2026
40f0b4e
docs(readme): correct custom wallet port to :5500
lmcorbalan May 14, 2026
cddb92e
docs(canton): correct custom wallet port to :5500 in help output
lmcorbalan May 14, 2026
9daa048
chore(gitignore): ignore runtime state files created in repo root by
lmcorbalan May 15, 2026
ab85e12
fix(compose): include bundle .env so custom_compose_argv loads NGINX_…
lmcorbalan May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Pinned Splice LocalNet version
IMAGE_TAG=0.5.18
BUNDLE_DIR=$HOME/.canton-devrel/bundle
BUNDLE_DIR=$HOME/.canton-builder/bundle
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.env
bundle/
*.tar.gz
*.tar.gz
docs/
tests/.bats/
validators.json
.registry.lock
nginx-customs/
validators/
last-validator-add-failure-*.log
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,58 @@ canton builder reset # wipe everything, start clean
| SV Ledger API (gRPC) | localhost:4901 | - |
| PostgreSQL | localhost:5432 | - |

## Validators

The default `canton builder start` brings up SV + `app-provider`. You can boot a subset, a superset, or add custom validators on top.

### Boot-time flags

```bash
canton builder start # SV + app-provider
canton builder start --validators app-provider # absolute set
canton builder start --only app-provider # alias for --validators
canton builder start --with app-user # additive
canton builder start --without app-provider # subtractive
canton builder start --with app-user --without app-provider
```

`sv` is infrastructure and is always on — passing it explicitly is an error.

### Manage validators at runtime

```bash
canton builder validator list # show all validators + health
canton builder validator info acme # ports, wallet URL, party hint
canton builder validator add acme # register + start a custom validator
canton builder validator add bob --port-base 7900
canton builder validator stop acme # stop, keep data
canton builder validator start acme # bring back with existing ledger
canton builder validator rm acme # full delete (data + recipe)
```

Each custom validator joins the same local SV. Wallet UI is served via the localnet nginx on `:5500`:

- `http://wallet.acme.localhost:5500` — wallet UI
- `http://localhost:5975` — JSON ledger API (port_base + 75)
- `http://localhost:5903/api/validator/readyz` — health probe

### Default validators

Persist your preferred default set at `~/.canton-builder/.env`:
```bash
DEFAULT_VALIDATORS=app-provider,app-user,acme
```
Used by `canton builder start` when no flags are passed and no validators are currently registered as running.

### Reset

```bash
canton builder reset # wipe ledger data, keep validator recipes
canton builder reset --purge # also wipe ~/.canton-builder (factory reset)
```

---

## First Run

On `canton builder start`, the tool:
Expand Down
18 changes: 14 additions & 4 deletions canton
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ usage() {
deploy <file.dar> Upload a prebuilt DAR to App Provider + App User participants
logs [service] Tail container logs (optionally filter to one service)
reset Wipe all data and stop containers, full clean slate
validator <verb> Manage validators (list, add, start, stop, rm, info)
version Print version info

EXAMPLES
Expand All @@ -40,6 +41,7 @@ usage() {
App User JSON API → http://localhost:2975
App Provider Wallet → http://wallet.localhost:3000
App User Wallet → http://wallet.localhost:2000
Custom Wallets → http://wallet.<name>.localhost:5500
Scan UI → http://scan.localhost:4000
Keycloak → http://keycloak.localhost:8082

Expand All @@ -64,13 +66,16 @@ fi

case "$DEVREL_COMMAND" in
start)
exec "$CANTON_DEVREL_DIR/scripts/start.sh"
shift 2
exec "$CANTON_DEVREL_DIR/scripts/start.sh" "$@"
;;
stop)
exec "$CANTON_DEVREL_DIR/scripts/stop.sh"
shift 2
exec "$CANTON_DEVREL_DIR/scripts/stop.sh" "$@"
;;
status)
exec "$CANTON_DEVREL_DIR/scripts/status.sh"
shift 2
exec "$CANTON_DEVREL_DIR/scripts/status.sh" "$@"
;;
deploy)
shift 2
Expand All @@ -81,7 +86,12 @@ case "$DEVREL_COMMAND" in
exec "$CANTON_DEVREL_DIR/scripts/logs.sh" "${@:-}"
;;
reset)
exec "$CANTON_DEVREL_DIR/scripts/reset.sh"
shift 2
exec "$CANTON_DEVREL_DIR/scripts/reset.sh" "$@"
;;
validator)
shift 2
exec "$CANTON_DEVREL_DIR/scripts/validator.sh" "$@"
;;
version|--version|-v)
echo "canton builder v${VERSION}"
Expand Down
40 changes: 40 additions & 0 deletions overlays/attach-localnet.overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Overlay applied to every per-custom validator-<name> project.
# Attaches the validator to the shared 'localnet' network created by the
# localnet stack, and removes the per-project nginx host binding (UI is
# served via localnet's nginx on :5500 with wallet.<name>.localhost).
networks:
localnet:
external: true

services:
participant:
networks:
- splice_validator
- localnet
# Bind gRPC + JSON ledger APIs to per-validator host ports so user scripts
# and `validator_info`'s advertised URLs work. Container ports match the
# bundle's validator participant config (5001 gRPC, 7575 JSON ledger).
ports:
- "${LEDGER_API_PORT}:5001"
- "${JSON_API_PORT}:7575"
validator:
networks:
- splice_validator
- localnet
# Bind /api/validator on the host so readyz and SDK clients can reach it.
# The bundle's per-validator nginx is unbound below (avoid :80 collisions
# across multiple validator-<name> projects), so without this the validator
# backend would be reachable only from inside the splice_validator network.
ports:
- "${VALIDATOR_API_PORT}:5003"
wallet-web-ui:
networks:
- splice_validator
- localnet
ans-web-ui:
networks:
- splice_validator
- localnet
nginx:
# !reset overrides the base sequence rather than appending — Compose merge semantics.
ports: !reset []
17 changes: 17 additions & 0 deletions overlays/customs.overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Overlay applied to the localnet compose project.
# - Mounts a patched nginx.conf that also globs the customs/ subdir (the bundle's
# nginx.conf only includes /etc/nginx/conf.d/*.conf at the top level, so per-
# custom server blocks rendered into customs/ would otherwise be invisible).
# - Surfaces ~/.canton-builder/nginx-customs/*.conf inside nginx at
# /etc/nginx/conf.d/customs/ so wallet.<name>.localhost:5500 can be served.
# - Exposes nginx on :5500 to host. Port 5500 is chosen over the more obvious
# 5000 because macOS Monterey+ binds 5000 to AirPlay Receiver by default,
# which silently intercepts the connection and returns 403 with an
# `AirTunes/*` Server header — confusing to debug.
services:
nginx:
ports:
- "${HOST_BIND_IP:-127.0.0.1}:5500:5500"
volumes:
- ${OVERLAYS_DIR}/nginx-conf/nginx.conf:/etc/nginx/nginx.conf:ro
- ${CANTON_DEVREL_DIR}/nginx-customs:/etc/nginx/conf.d/customs:ro
31 changes: 31 additions & 0 deletions overlays/nginx-conf/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
events {
worker_connections 64;
}

http {
include mime.types;
default_type application/octet-stream;

# Logging
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.log json_combined;
error_log /var/log/nginx/error.log;

include /etc/nginx/conf.d/*.conf;
# canton-devrel: pick up per-custom-validator server blocks rendered into
# the customs/ subdirectory. The bundle's nginx.conf only globs the parent
# dir, so without this line wallet.<name>.localhost requests get no match
# and nginx returns 444/403 from the default behavior.
include /etc/nginx/conf.d/customs/*.conf;
}
17 changes: 17 additions & 0 deletions overlays/party-hint.overlay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Workaround for a Splice v0.5.18 bundle bug.
#
# The bundle's env/app-user-auth-on.env and env/app-provider-auth-on.env set:
# APP_USER_PARTY_HINT=app_user_${PARTY_HINT}
# APP_PROVIDER_PARTY_HINT=app_provider_${PARTY_HINT}
#
# Splice v0.5.18 validates these against <alphanumeric>-<alphanumeric>-<int>.
# The literal `app_user_` / `app_provider_` prefixes contain underscores,
# so the resulting hints always fail validation regardless of PARTY_HINT.
# Splice exits 0 on init failure; restart: always loops it forever.
#
# `environment:` here overrides the values supplied via env_file:.
services:
splice:
environment:
APP_USER_PARTY_HINT: appuser-localparty-1
APP_PROVIDER_PARTY_HINT: appprovider-localparty-1
Loading