Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 13 additions & 2 deletions .cursor/skills/localnet-testing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# LocalNet testing

Read the public [LocalNet guide](https://github.com/Fairmint/canton-node-sdk/wiki/LocalNet-testing)
first. This repository's current `package.json`, `bin/canton-localnet`, and integration tests are
the source of truth for `localnet:start`, `localnet:stop`, `test:integration`, and related commands.
first.

**ENG-1635:** `@fairmint/canton-dev-tools@0.1.1+` owns LocalNet lifecycle, pins, and shared test

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- package manifests and scripts ---'
for f in package.json package-lock.json npm-shrinkwrap.json yarn.lock pnpm-lock.yaml; do
  if [ -f "$f" ]; then
    printf '\n[%s]\n' "$f"
    rg -n -C 2 '`@fairmint/canton-dev-tools`|localnet:' "$f" || true
  fi
done

printf '\n--- documentation references ---\n'
rg -n -C 2 '`@fairmint/canton-dev-tools`|LocalNet|localnet:' \
  AGENTS.md .cursor/skills/localnet-testing/SKILL.md README.md CLAUDE.md 2>/dev/null || true

printf '\n--- exact dependency declarations ---\n'
python3 - <<'PY'
import json
from pathlib import Path

for name in ("package.json",):
    path = Path(name)
    if not path.is_file():
        continue
    data = json.loads(path.read_text())
    for section in ("dependencies", "devDependencies", "optionalDependencies", "peerDependencies"):
        value = data.get(section, {})
        if "`@fairmint/canton-dev-tools`" in value:
            print(f"{name}: {section}: {value['`@fairmint/canton-dev-tools`']!r}")
PY

Repository: Fairmint/canton-node-sdk

Length of output: 6866


Use the exact Dev Tools version in LocalNet guidance.

Replace @fairmint/canton-dev-tools@0.1.1+ with @fairmint/canton-dev-tools@0.1.1 in .cursor/skills/localnet-testing/SKILL.md and AGENTS.md.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 10: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

📍 Affects 2 files
  • .cursor/skills/localnet-testing/SKILL.md#L6-L6 (this comment)
  • AGENTS.md#L3-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/skills/localnet-testing/SKILL.md at line 6, Replace the version
range with the exact `@fairmint/canton-dev-tools`@0.1.1 reference in
.cursor/skills/localnet-testing/SKILL.md lines 6-6 and AGENTS.md lines 3-6,
preserving the surrounding LocalNet guidance.

Source: Coding guidelines

helpers. This repository does not ship LocalNet scripts or a `canton-localnet` binary.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Describe the retained compatibility paths.

The PR retains bin/canton-localnet and the deprecated scripts/localnet-cloud.sh fallback. This statement incorrectly says that the repository does not ship LocalNet scripts or a canton-localnet binary. State that Dev Tools owns LocalNet and that the repository paths are deprecated compatibility shims.

🧰 Tools
🪛 SkillSpector (2.5.1)

[warning] 10: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.cursor/skills/localnet-testing/SKILL.md at line 7, Update the repository
description in SKILL.md to state that Dev Tools owns LocalNet, while
bin/canton-localnet and scripts/localnet-cloud.sh remain deprecated
compatibility shims. Replace the inaccurate claim that no LocalNet scripts or
canton-localnet binary are shipped, preserving the existing compatibility-path
details.


- Commands: `npm run localnet:*` (wired to `canton-dev-tools`) or
`npx @fairmint/canton-dev-tools <command>`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- Helpers: `@fairmint/canton-dev-tools/testing`
- Pins: Dev Tools
[COMPATIBILITY.md](https://github.com/Fairmint/canton-dev-tools/blob/main/COMPATIBILITY.md)

Domain integration tests under `test/integration/localnet/**` remain in this repo; only their
imports come from Dev Tools.
3 changes: 2 additions & 1 deletion .github/workflows/package-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ jobs:

- name: Upgrade npm
# Keep PR package checks aligned with the trusted-publishing npm CLI.
run: npm install --global npm@^11.10.0
# npm >=11.17 required for .npmrc min-release-age-exclude (@fairmint/*).
run: npm install --global npm@^11.17.0

- name: Install dependencies
run: npm i
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ jobs:
registry-url: 'https://registry.npmjs.org'

- name: Upgrade npm
# Trusted publishing requires npm CLI >= 11.5.1
run: npm install --global npm@^11.10.0
# Trusted publishing requires npm CLI >= 11.5.1;
# npm >=11.17 required for .npmrc min-release-age-exclude (@fairmint/*).
run: npm install --global npm@^11.17.0
Comment on lines +34 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

if rg -n --glob '*.yml' --glob '*.yaml' \
  'npm install --global npm@\^' .github/workflows; then
  echo "Floating npm version found" >&2
  exit 1
fi

Repository: Fairmint/canton-node-sdk

Length of output: 450


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- workflow files ---'
for f in .github/workflows/publish.yml .github/workflows/test-cn-quickstart.yml .github/workflows/package-artifacts.yml; do
  echo "### $f"
  sed -n '1,90p' "$f"
done

printf '%s\n' '--- npm configuration and version references ---'
rg -n --hidden --glob '!node_modules' \
  'min-release-age-exclude|npm@|setup-node|node-version|packageManager|engines|trusted publishing' \
  .npmrc package.json package-lock.json .github . 2>/dev/null | head -250

Repository: Fairmint/canton-node-sdk

Length of output: 17193


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import json
import re
import urllib.request
from pathlib import Path

files = [
    Path(".github/workflows/publish.yml"),
    Path(".github/workflows/test-cn-quickstart.yml"),
    Path(".github/workflows/package-artifacts.yml"),
]
pattern = re.compile(r"npm install --global npm@(\S+)")
found = {}
for path in files:
    text = path.read_text()
    matches = pattern.findall(text)
    found[str(path)] = matches
    print(f"{path}: {matches}")

if any(matches != ["^11.17.0"] for matches in found.values()):
    raise SystemExit("The three workflows do not all use ^11.17.0")

with urllib.request.urlopen("https://registry.npmjs.org/npm", timeout=10) as response:
    metadata = json.load(response)

def version_tuple(version):
    match = re.fullmatch(r"(\d+)\.(\d+)\.(\d+)", version)
    return tuple(map(int, match.groups())) if match else None

matching = []
for version in metadata.get("versions", {}):
    parsed = version_tuple(version)
    if parsed and parsed >= (11, 17, 0) and parsed < (12, 0, 0):
        matching.append(version)

print("Published versions accepted by ^11.17.0:")
print(", ".join(sorted(matching, key=version_tuple)[-10:]))

if not any(version_tuple(v) > (11, 17, 0) for v in matching):
    raise SystemExit("Registry data does not demonstrate floating resolution")
PY

Repository: Fairmint/canton-node-sdk

Length of output: 2602


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

files = [
    Path(".github/workflows/publish.yml"),
    Path(".github/workflows/test-cn-quickstart.yml"),
    Path(".github/workflows/package-artifacts.yml"),
]

pattern = re.compile(r"npm install --global npm@(\S+)")
for path in files:
    matches = pattern.findall(path.read_text())
    print(f"{path}: {matches}")
    if matches != ["^11.17.0"]:
        raise SystemExit(f"Unexpected npm range in {path}")

def parse(version):
    return tuple(map(int, version.split(".")))

# For a major version greater than zero, ^MAJOR.MINOR.PATCH means
# >=MAJOR.MINOR.PATCH and <(MAJOR+1).0.0.
lower = parse("11.17.0")
upper = (lower[0] + 1, 0, 0)
examples = ["11.17.0", "11.17.1", "11.18.0", "11.99.99", "12.0.0"]

for version in examples:
    value = parse(version)
    accepted = lower <= value < upper
    print(f"{version}: accepted_by_^11.17.0={accepted}")

if not (parse("11.17.1") >= lower and parse("11.17.1") < upper):
    raise SystemExit("The range must accept a later npm 11 release")
if parse("12.0.0") < upper:
    raise SystemExit("The range must exclude npm 12")
PY

Repository: Fairmint/canton-node-sdk

Length of output: 491


Pin the npm CLI version in all three workflows.

^11.17.0 accepts any npm 11 release from 11.17.0 through, but excluding, 12.0.0. Use the exact 11.17.0 version or centralize an exact version in .github/workflows/publish.yml, .github/workflows/test-cn-quickstart.yml, and .github/workflows/package-artifacts.yml.

🧰 Tools
🪛 zizmor (1.29.0)

[warning] 36-36: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile

(adhoc-packages)

📍 Affects 3 files
  • .github/workflows/publish.yml#L34-L36 (this comment)
  • .github/workflows/test-cn-quickstart.yml#L32-L33
  • .github/workflows/package-artifacts.yml#L28-L29
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish.yml around lines 34 - 36, Pin the npm CLI
installation to the exact 11.17.0 version in all three workflows:
.github/workflows/publish.yml lines 34-36,
.github/workflows/test-cn-quickstart.yml lines 32-33, and
.github/workflows/package-artifacts.yml lines 28-29. Replace the caret range at
each installation site while preserving the existing workflow steps.

Source: Linters/SAST tools


- name: Install dependencies
run: npm i
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/test-cn-quickstart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ jobs:
node-version: '22.14'

- name: Upgrade npm
run: npm install --global npm@^11.10.0
# npm >=11.17 required for .npmrc min-release-age-exclude (@fairmint/*).
run: npm install --global npm@^11.17.0

- name: Initialize Submodules
run: |
Expand Down Expand Up @@ -63,7 +64,7 @@ jobs:
uses: actions/cache/restore@v6
with:
path: ${{ github.workspace }}/libs/cn-quickstart/quickstart/.env.local
key: quickstart-env-${{ runner.os }}-${{ hashFiles('.github/workflows/test-cn-quickstart.yml', 'package.json', 'package-lock.json', 'scripts/localnet-cloud.sh') }}-${{ steps.quickstart-env-cache-key.outputs.submodules }}
key: quickstart-env-${{ runner.os }}-${{ hashFiles('.github/workflows/test-cn-quickstart.yml', 'package.json', 'package-lock.json') }}-${{ steps.quickstart-env-cache-key.outputs.submodules }}

- name: Restore Node Dependencies Cache
id: node-cache-restore
Expand Down
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
min-release-age=1
# Fairmint-owned packages are published by us; allow immediate consumption after cutovers.
min-release-age-exclude[]=@fairmint/*
Comment on lines +2 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

printf '%s\n' '--- .npmrc ---'
cat -n .npmrc 2>/dev/null || true
printf '%s\n' '--- Fairmint package references ---'
rg -n --hidden -g '!node_modules' -g '!dist' -g '!build' '`@fairmint/`' . 2>/dev/null | head -200
printf '%s\n' '--- package manifests ---'
find . -name package.json -not -path '*/node_modules/*' -print

Repository: Fairmint/canton-node-sdk

Length of output: 5509


🏁 Script executed:

printf '%s\n' '--- package.json dependency/config context ---'
sed -n '1,115p' package.json
printf '%s\n' '--- workflow npm setup ---'
sed -n '1,70p' .github/workflows/publish.yml
sed -n '1,55p' .github/workflows/package-artifacts.yml
sed -n '1,55p' .github/workflows/test-cn-quickstart.yml
printf '%s\n' '--- local npm availability and config metadata ---'
npm --version 2>/dev/null || true
npm config get min-release-age 2>/dev/null || true
npm config get min-release-age-exclude 2>/dev/null || true

Repository: Fairmint/canton-node-sdk

Length of output: 8886


🌐 Web query:

npm CLI min-release-age-exclude glob patterns documentation

💡 Result:

The npm CLI configuration option min-release-age-exclude allows you to exempt specific packages or groups of packages from the min-release-age and before publish-time filters [1][2]. Key details regarding this configuration: Glob Patterns: The option supports minimatch glob patterns [1][2]. This allows you to exclude entire scopes or namespaces (e.g., @myorg/) from release-age restrictions [1][2]. Usage: You can specify multiple values by setting the configuration multiple times [1][2]. For example, in an.npmrc file or via command-line arguments: min-release-age=7 min-release-age-exclude[]=@myorg/ min-release-age-exclude[]=my-internal-pkg Behavior: 1. When a package matches a name or glob pattern in the exclusion list, it is exempt from the release-age window, allowing the latest version to be resolved even if it is too new based on your global settings [1][2]. 2. Exemption applies only to the matched package; the dependencies of that package are still subject to the release-age policy unless they also match a specified pattern [1][2]. 3. This configuration is supported in operations such as install, update, outdated, and query [3][4]. Official documentation can be found in the npm CLI config documentation [1][2].

Citations:


🏁 Script executed:

npm_root="$(npm root -g 2>/dev/null || true)"
printf '%s\n' "--- npm global root: $npm_root ---"
if [ -n "$npm_root" ]; then
  rg -n -C 4 'min-release-age-exclude|min-release-age' "$npm_root/npm" 2>/dev/null | head -160
fi
printf '%s\n' '--- all Fairmint package names in manifests ---'
python3 - <<'PY'
import json
from pathlib import Path

for path in Path(".").rglob("package.json"):
    if any(part in {"node_modules", "dist", "build"} for part in path.parts):
        continue
    data = json.loads(path.read_text())
    names = []
    for section in ("dependencies", "devDependencies", "optionalDependencies", "peerDependencies"):
        names.extend(k for k in data.get(section, {}) if k.startswith("`@fairmint/`"))
    print(f"{path}: name={data.get('name')!r}, Fairmint dependencies={names}")
PY

Repository: Fairmint/canton-node-sdk

Length of output: 31847


Restrict min-release-age-exclude to @fairmint/canton-dev-tools.

@fairmint/* exempts every current and future package in the scope. This repository only declares @fairmint/canton-dev-tools as a Fairmint dependency. Use min-release-age-exclude[]=@fairmint/canton-dev-tools``.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.npmrc around lines 2 - 3, Update the min-release-age-exclude configuration
in .npmrc to target only `@fairmint/canton-dev-tools` instead of the entire
`@fairmint/`* scope.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n \
  --glob '*.yml' \
  --glob '*.yaml' \
  --glob 'package.json' \
  --glob 'README.md' \
  --glob 'AGENTS.md' \
  'npm (install|i|ci)|npm@|min-release-age-exclude' \
  .github package.json README.md AGENTS.md 2>/dev/null || true

Repository: Fairmint/canton-node-sdk

Length of output: 1509


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- npm references ---'
rg -n -i \
  --glob '!node_modules' \
  --glob '!dist' \
  --glob '!build' \
  '(^|[^[:alnum:]_])npm([[:space:]@:/]|$)|min-release-age-exclude|corepack|setup-node|node-version|engines' \
  . 2>/dev/null || true

printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*' | sort

printf '%s\n' '--- package metadata ---'
cat -n package.json | sed -n '1,220p'

printf '%s\n' '--- workflow contents ---'
for f in $(git ls-files '.github/workflows/*' | sort); do
  printf '\n### %s\n' "$f"
  cat -n "$f"
done

printf '%s\n' '--- setup and documentation files ---'
for f in AGENTS.md README.md .npmrc; do
  if [ -f "$f" ]; then
    printf '\n### %s\n' "$f"
    cat -n "$f"
  fi
done

Repository: Fairmint/canton-node-sdk

Length of output: 34526


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- repository tool/version files ---'
git ls-files | rg '(^|/)(\.nvmrc|\.node-version|\.tool-versions|package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|Dockerfile[^/]*|devcontainer\.json|.*\.md)$' | sort

printf '%s\n' '--- all npm and npx command entry points ---'
rg -n -i \
  --glob '!node_modules' \
  --glob '!dist' \
  --glob '!build' \
  '(^|[^[:alnum:]_])(npm|npx)([[:space:]]|$)' \
  . 2>/dev/null || true

printf '%s\n' '--- package manager metadata ---'
if [ -f package-lock.json ]; then
  jq '{lockfileVersion, name, version, packageManager}' package-lock.json
fi
if jq -e '.packageManager or .engines.npm' package.json >/dev/null 2>&1; then
  jq '{packageManager, engines}' package.json
else
  jq '{packageManager, engines}' package.json
fi

printf '%s\n' '--- workflow npm setup adjacency ---'
python3 - <<'PY'
from pathlib import Path
import re

for path in sorted(Path(".github/workflows").glob("*")):
    if not path.is_file():
        continue
    lines = path.read_text().splitlines()
    hits = [
        (i + 1, line.strip())
        for i, line in enumerate(lines)
        if re.search(r'(?<![A-Za-z0-9_])(npm|npx)(?:\s|$)', line, re.I)
    ]
    if hits:
        print(f"\n{path}")
        for line_no, text in hits:
            print(f"{line_no}: {text}")
        print("has_setup_node:", any("setup-node@" in line for line in lines))
        print("has_npm_11_17_install:", any(re.search(r'npm@\\^?11\\.17(?:\\.0)?', line) for line in lines))
PY

Repository: Fairmint/canton-node-sdk

Length of output: 7672


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- Node version declaration ---'
cat -n .nvmrc

printf '%s\n' '--- documented and automation setup references ---'
for f in CLAUDE.md .cursor/skills/localnet-testing/SKILL.md .github/copilot-instructions.md; do
  if [ -f "$f" ]; then
    printf '\n### %s\n' "$f"
    rg -n -i -C 3 'node|npm|npx|install|setup' "$f" || true
  fi
done

printf '%s\n' '--- workflow command/setup summary ---'
python3 - <<'PY'
from pathlib import Path
import re

for path in sorted(Path(".github/workflows").glob("*")):
    if not path.is_file():
        continue
    lines = path.read_text().splitlines()
    hits = []
    for i, line in enumerate(lines):
        if re.search(r"(?<![A-Za-z0-9_])(npm|npx)(?:\s|$)", line, re.I):
            hits.append(i)
    if hits:
        print(f"\n{path}")
        print("commands:")
        for i in hits:
            print(f"  {i+1}: {lines[i].strip()}")
        print("setup-node:", [f"{i+1}: {lines[i].strip()}" for i in range(len(lines)) if "setup-node@" in lines[i]])
        print("npm-upgrade:", [f"{i+1}: {lines[i].strip()}" for i in range(len(lines)) if re.search(r"npm@(?:\\^)?11\\.17(?:\\.0)?", lines[i])])

printf("\nAll setup-node declarations:\n")
for path in sorted(Path(".github/workflows").glob("*")):
    if path.is_file():
        for i, line in enumerate(path.read_text().splitlines()):
            if "setup-node@" in line or "node-version:" in line:
                print(f"{path}:{i+1}: {line.strip()}")
PY

Repository: Fairmint/canton-node-sdk

Length of output: 4321


🌐 Web query:

Node.js 22.14.0 bundled npm version

💡 Result:

Node.js version 22.14.0 is bundled with npm version 10.9.2 [1][2].

Citations:


🌐 Web query:

npm min-release-age-exclude introduced npm 11.17.0

💡 Result:

Yes, the min-release-age-exclude configuration option was introduced in npm v11.17.0, which was released on June 11, 2026 [1][2]. This configuration option allows users to specify a list of package names or minimatch glob patterns that are exempt from the min-release-age and before filters [3]. By using min-release-age-exclude, packages can be permitted to resolve to their newest versions even when a release-age window is otherwise enforced, which is particularly useful for prioritizing immediate updates for internally maintained packages or urgent patches while maintaining strict age policies for third-party dependencies [3][4][5].

Citations:


Pin npm 11.17 or newer for every npm entry point.

The workflows that run npm upgrade to ^11.17.0. check-pinned-deps and documented npx paths do not. .nvmrc provides npm 10.9.2 through Node.js 22.14. Add an npm 11.17-or-newer requirement to all automated and documented entry points.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.npmrc around lines 2 - 3, Update the npm version requirements across all
automated and documented entry points to require npm 11.17.0 or newer, including
check-pinned-deps, documented npx usage, and the Node.js version represented by
.nvmrc; keep the existing workflow upgrades consistent with this minimum.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — Fairmint-owned packages are published by us and should be consumable immediately after cutovers. Keeping the @fairmint/* exclude rather than narrowing to a single package name.

30 changes: 23 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
# canton-node-sdk

See [CLAUDE.md](CLAUDE.md), [README.md](README.md), and
`.cursor/skills/localnet-testing/SKILL.md`. `package.json` (`localnet:*` scripts) and
`bin/canton-localnet` are the source of truth for LocalNet commands.
`.cursor/skills/localnet-testing/SKILL.md`. `package.json` (`localnet:*` scripts) is the source of
truth for LocalNet commands in this repo; lifecycle is owned by
`@fairmint/canton-dev-tools@0.1.1+`.

## LocalNet ownership (ENG-1635)

**`@fairmint/canton-dev-tools` owns LocalNet** (CLI, pins, and shared test helpers). This SDK does
not ship a LocalNet engine or `canton-localnet` binary.

- Install pin: `devDependency` `@fairmint/canton-dev-tools@0.1.1` (exact).
- Repo scripts: `npm run localnet:*` → `canton-dev-tools <command>`.
- Integration helpers: import from `@fairmint/canton-dev-tools/testing`.
- Pins / auth defaults: see Dev Tools
[COMPATIBILITY.md](https://github.com/Fairmint/canton-dev-tools/blob/main/COMPATIBILITY.md).
- Workspace TypeScript/Jest map `@fairmint/canton-node-sdk` to local `src/` so Dev Tools
peer types match this checkout (`tsconfig.lint.json` paths, `jest.config.js`
`moduleNameMapper`).

## Cursor Cloud specific instructions

Expand All @@ -11,11 +26,12 @@ Repo checks (`npm install`, `npm run fix`, `npm test`, `npm run build`) need no

### Canton LocalNet on a cloud VM

LocalNet runs Canton Network Quickstart in Docker. `npm run localnet:start` (=
`bin/canton-localnet start`, infra-only + OAuth2 by default) is self-provisioning on the cloud image:
it `apt`-installs Docker, starts a `dockerd` (vfs storage driver, iptables-legacy) via passwordless
`sudo`, adds `scan.localhost`/`sv.localhost`/`wallet.localhost` to `/etc/hosts`, runs cn-quickstart
`make setup`, brings up the compose stack, and waits for the Validator, Scan, and Ledger JSON APIs.
LocalNet runs Canton Network Quickstart in Docker via `@fairmint/canton-dev-tools`.
`npm run localnet:start` (= `canton-dev-tools start`, infra-only + OAuth2 by default) is
self-provisioning on the cloud image: it `apt`-installs Docker, starts a `dockerd` (vfs storage
driver, iptables-legacy) via passwordless `sudo`, adds
`scan.localhost`/`sv.localhost`/`wallet.localhost` to `/etc/hosts`, runs cn-quickstart `make setup`,
brings up the compose stack, and waits for the Validator, Scan, and Ledger JSON APIs.

Prerequisites (on demand — heavy, not in the dashboard update script):

Expand Down
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ Low-level TypeScript SDK for Canton Network nodes (Ledger JSON API, Validator AP

## Developer documentation

The public [GitHub wiki](https://github.com/Fairmint/canton-node-sdk/wiki) is the canonical guide for
configuration, API boundaries, external signing, LocalNet, examples, and contribution. The public
[`src/index.ts`](https://github.com/Fairmint/canton-node-sdk/blob/main/src/index.ts) defines the
supported package surface; use the installed declarations and public
The public [GitHub wiki](https://github.com/Fairmint/canton-node-sdk/wiki) is the canonical guide
for configuration, API boundaries, external signing, LocalNet, examples, and contribution. The
public [`src/index.ts`](https://github.com/Fairmint/canton-node-sdk/blob/main/src/index.ts) defines
the supported package surface; use the installed declarations and public
[`examples/`](https://github.com/Fairmint/canton-node-sdk/tree/main/examples) and
[`test/`](https://github.com/Fairmint/canton-node-sdk/tree/main/test) for exact methods, request
shapes, and error behavior.
Expand All @@ -19,10 +19,10 @@ npm install @fairmint/canton-node-sdk
```

```ts
import { Canton } from "@fairmint/canton-node-sdk";
import { Canton } from '@fairmint/canton-node-sdk';

async function main(): Promise<void> {
const canton = new Canton({ network: "localnet" });
const canton = new Canton({ network: 'localnet' });
const version = await canton.ledger.getVersion();
console.log(version);
}
Expand All @@ -44,4 +44,31 @@ npm test
npm run build
```

Run `npm run localnet:verify` for the full LocalNet smoke and integration path.
### LocalNet (owned by `@fairmint/canton-dev-tools`)

LocalNet lifecycle and shared test helpers live in
[`@fairmint/canton-dev-tools@0.1.1`](https://www.npmjs.com/package/@fairmint/canton-dev-tools)
(see its [COMPATIBILITY.md](https://github.com/Fairmint/canton-dev-tools/blob/main/COMPATIBILITY.md)).
This SDK does not publish a LocalNet CLI or `scripts/localnet-cloud.sh`.

```bash
npm install # installs @fairmint/canton-dev-tools as an exact-pinned devDependency
npm run localnet:start
npm run localnet:smoke
npm run localnet:stop

# Or call the Dev Tools CLI directly:
npx @fairmint/canton-dev-tools start
```

Integration helpers:

```ts
import {
buildIntegrationTestClientConfig,
getLocalnetParticipantAdminLedgerClient,
} from '@fairmint/canton-dev-tools/testing';
Comment on lines +67 to +70

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use buildLocalnetClientConfig across the migration.

buildIntegrationTestClientConfig is a deprecated compatibility alias. Use the canonical helper in the new README example and migrated test imports. (raw.githubusercontent.com)

  • README.md#L67-L70: replace the documented helper import with buildLocalnetClientConfig.
  • test/integration/localnet/ledger-api/interactive-submission.test.ts#L17-L17: replace the imported helper name.
  • test/integration/localnet/ledger-api/paid-traffic-cost.test.ts#L13-L13: replace the imported helper name.
  • test/integration/localnet/ledger-api/setup.ts#L6-L6: replace the shared setup helper import.
📍 Affects 4 files
  • README.md#L67-L70 (this comment)
  • test/integration/localnet/ledger-api/interactive-submission.test.ts#L17-L17
  • test/integration/localnet/ledger-api/paid-traffic-cost.test.ts#L13-L13
  • test/integration/localnet/ledger-api/setup.ts#L6-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 67 - 70, Replace the deprecated
buildIntegrationTestClientConfig import with the canonical
buildLocalnetClientConfig helper in README.md lines 67-70,
test/integration/localnet/ledger-api/interactive-submission.test.ts line 17,
test/integration/localnet/ledger-api/paid-traffic-cost.test.ts line 13, and
test/integration/localnet/ledger-api/setup.ts line 6; update corresponding
usages where needed while preserving the existing configuration behavior.

Source: MCP tools

```

See [docs/package-boundary.md](docs/package-boundary.md) for what the npm package publishes versus
CI-only surfaces (`npm run check:package-artifacts`).
213 changes: 0 additions & 213 deletions bin/canton-localnet

This file was deleted.

Loading
Loading