Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a7ce617
Establish canton-dev-tools package with LocalNet CLI and test helpers
cursoragent Aug 10, 2026
c2efb4b
Apply central LocalNet pins on CLI path and scaffold DAR fixture
cursoragent Aug 10, 2026
bf32f24
Add CIP-56/CIP-112 Splice TestTokenV2 fixture path (no-Docker)
cursoragent Aug 10, 2026
26d09fa
Document ENG-1635 SDK package-boundary audit
cursoragent Aug 10, 2026
a5d5a4d
Link ENG-1635 pack-guard and soft-migration PRs in audit
cursoragent Aug 10, 2026
b6d8a42
Implement LocalNet CIP-56/CIP-112 TestTokenV2 transfer smoke
cursoragent Aug 10, 2026
37a626c
Fix localnet npm script to invoke bash CLI
cursoragent Aug 10, 2026
7b37129
Fix LocalNet CIP-56 smoke party auth and Accept visibility
cursoragent Aug 10, 2026
1f3ae43
Remove private SDK boundary audit from public docs
cursoragent Aug 10, 2026
fd65e8e
Fix Bugbot/Copilot LocalNet client and CLI issues
cursoragent Aug 10, 2026
bb4aa66
Keep OAuth auth for URL-only LocalNet config overrides
cursoragent Aug 10, 2026
474275f
Fix Daml Numeric formatting and align LocalNet pins
cursoragent Aug 10, 2026
17e0e07
Fix tip review findings for decimals and public docs
cursoragent Aug 10, 2026
cac1de0
Fail closed when dpm is missing for DAR fixture build
cursoragent Aug 10, 2026
d96ad22
Clean up quickstart temp dir on checkout failure
cursoragent Aug 10, 2026
e83cd9b
Add OIDC auto-publish workflow for npm on main
cursoragent Aug 10, 2026
9bdb80e
Fix healthcheck set flags so errexit actually enables
cursoragent Aug 10, 2026
7f6d2c1
Retry createParty through LocalNet Validator traffic 429s
cursoragent Aug 10, 2026
9d83dfd
Fix OIDC publish auth and first-release changelog
cursoragent Aug 10, 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
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Unit tests
run: npm run test:ci

- name: Build
run: npm run build

- name: Pack and guard artifacts
run: npm run pack:check
80 changes: 80 additions & 0 deletions .github/workflows/localnet-smoke.yml
Comment thread
HardlyDifficult marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: LocalNet Smoke

# LocalNet lifecycle + CIP-56 transfer smoke. Intended to become a required PR check
# once Docker image caching is finalized (TODO(ENG-1635)).
#
# Tradeoff vs nightly-only: first-run image pulls dominate wall time (~10–15m) and need
# Docker/Compose + disk on the runner. We still run on pull_request so LocalNet regressions
# block merges; keep workflow_dispatch for manual re-runs. No scheduled job — PR coverage is
# the regression gate.
#
# Uses Splice TestTokenV2 (not a Fairmint token).
#
# Docker readiness notes:
# - The ubuntu-latest runner usually has Docker available via the Actions service, but LocalNet
# also needs Compose v2, passwordless sudo for host aliases / dockerd bootstrap in constrained
# environments, and enough disk for Splice image pulls.
# - First-run image pulls dominate wall time; prefer a warmed runner cache once the shared LocalNet
# image strategy is finalized (TODO(ENG-1635)).
# - Port 3000 must be free (cn-quickstart nginx binds 127.0.0.1:3000).
# - If nginx was created without a network after a failed start, remove it before retrying:
# `docker rm -f nginx`.

on:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
localnet-smoke:
runs-on: ubuntu-latest
# Image pulls + DSO bootstrap + CIP-56 transfer routinely exceed 15m on a cold runner.
timeout-minutes: 45

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Fetch Splice TestTokenV2 fixture DAR
run: npm run fixture:splice-test-token-v2:fetch

# TODO(ENG-1635): harden GH Actions Docker provisioning / image caching for LocalNet.
# This job is best-effort until the shared runner image and cache strategy are finalized.
- name: Start LocalNet
id: localnet-start
run: |
set -euo pipefail
npm run localnet:start

- name: Readiness
id: localnet-readiness
run: npm run localnet:readiness

# Prefer a real step with clear failure when LocalNet / Docker is not ready.
# Do NOT invent a Fairmint token — uses fixtures/splice-test-token-v2 (CIP-112 capable).
- name: CIP-56 / CIP-112 transfer smoke (Splice TestTokenV2)
id: cip56-transfer
run: |
set -euo pipefail
npm run localnet:cip56-transfer

- name: Diagnostics on failure
if: failure()
run: npm run localnet:diagnostics

- name: Teardown
if: always()
run: npm run localnet:teardown
97 changes: 97 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Publish to NPM

on:
push:
branches:
- main
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

# OIDC trusted publishing on npmjs.com:
# https://www.npmjs.com/package/@fairmint/canton-dev-tools/access (Trusted Publisher section)
# — or create the package first via "Add Trusted Publisher" on the @fairmint org.
# Provider: GitHub Actions
# Repository: Fairmint/canton-dev-tools
# Workflow filename: publish.yml
# The npm CLI auto-detects the OIDC environment and exchanges the GitHub-issued id-token
# for a short-lived registry token, so no long-lived NPM_TOKEN secret is required for publish.
#
# Required to publish: id-token: write
# Required to push the git tag back: contents: write
#
# Install may still use NODE_AUTH_TOKEN (NPM_TOKEN) to fetch private @fairmint/* peers.
# Do NOT set NODE_AUTH_TOKEN / NPM_TOKEN on the Publish step — that breaks OIDC trusted publishing.

jobs:
publish:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: write
packages: write
id-token: write

steps:
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Node.js
uses: actions/setup-node@v6
with:
# Trusted publishing requires Node >= 22.14.0 (npm docs)
node-version: '22.14'
registry-url: 'https://registry.npmjs.org'
package-manager-cache: false

- name: Upgrade npm
# Trusted publishing requires npm CLI >= 11.5.1. Pin below npm 12 until
# npm@latest no longer fails OIDC publishes with a missing sigstore module
# on hosted runners (see npm/cli#8730).
run: npm install --global npm@11.18.0

- name: Install dependencies
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Build package
run: npm run build

- name: Prepack and check package artifacts
run: npm run check:package-artifacts

- name: Prepare release
run: npm run prepare-release

# setup-node's registry-url writes `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}`
# into the runner npmrc. With NODE_AUTH_TOKEN unset on this step that expands to empty and
# npm treats classic auth as configured, skipping the OIDC Trusted Publisher exchange.
- name: Clear classic npm auth for OIDC publish
run: |
npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}"
if [[ -f "${npmrc}" ]]; then
sed -i '/_authToken/d' "${npmrc}"
fi

# Publishes via npm Trusted Publisher (OIDC); do not set NODE_AUTH_TOKEN here.
# Provenance is omitted: this repository may be private, and --provenance requires
# a public source repo. OIDC trusted publishing still works without provenance.
- name: Publish to NPM
run: npm publish
Comment thread
cursor[bot] marked this conversation as resolved.

- name: Create and push git tag
run: |
NEW_VERSION=$(node -p "require('./package.json').version")
CHANGELOG=$(head -100 CHANGELOG.md)
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git tag -a "v$NEW_VERSION" -m "$CHANGELOG"
git push origin "v$NEW_VERSION"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules/
dist/
coverage/
*.tgz
.DS_Store
.env
.env.local
*.log
.npmrc
libs/
artifacts/
fixtures/**/.daml/
fixtures/**/*.dar
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"semi": true
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

Release notes are prepended here by `npm run prepare-release` on each publish to npm.
55 changes: 55 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# LocalNet Compatibility Pins

`@fairmint/canton-dev-tools` is the **central owner** of Fairmint's shared Canton LocalNet pin set.

Consumers should prefer this package's CLI defaults in `bin/canton-dev-tools` (kept in sync with
`package.json#config` by `pack:check`) instead of maintaining divergent pins in individual SDKs.
`npm run localnet*` delegates to the binary, which applies the four pins and oauth2 auth as
defaults while preserving caller environment overrides.

| Pin | Config key | Current value |
| ----------------------- | --------------------------- | ------------------------------------------ |
| cn-quickstart git ref | `localnet_quickstart_ref` | `2f4edfc17621a7dfb6d44357050c22f4b3914c89` |
| Splice image tag | `localnet_splice_version` | `0.6.14` |
| Scribe image tag | `localnet_scribe_version` | `0.6.14` |
| Canton protocol version | `localnet_protocol_version` | `35` |

## Why both quickstart ref and Splice version?

They are **not redundant** — each owns a different layer:

| Pin | Owns | Why it is separate |
| ----------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `localnet_quickstart_ref` | CN Quickstart **compose / scripts / Daml runtime wiring** (git tree) | Locks how LocalNet is brought up (ports, modules, Keycloak, `.env` defaults). |
| `localnet_splice_version` / `localnet_scribe_version` | **Container image tags** written into Quickstart `.env` via `CANTON_LOCALNET_*` | Lets us run MainNet-aligned Splice/Scribe images on an older, known-good Quickstart tree without waiting for a Quickstart bump that already embeds that tag. |

Quickstart's checked-in `.env` may still say `SPLICE_VERSION=0.6.11` at the pinned ref; our CLI **overrides** that with `localnet_splice_version`. Same pattern as `@fairmint/canton-assets` LocalNet docs (same Quickstart ref + MainNet Splice `0.6.14`).

Do not derive one from the other in this package: consumers may need to advance images before compose, or pin compose while validating a new image.

## Auth mode

| Mode | When to use |
| --------------- | ---------------------------------------------------------------------------- |
| `oauth2` | **Package default** for `npm run localnet*` and bare CLI / `npx` invocations |
| `shared-secret` | **Consumer CI** that signs HS256 JWTs (e.g. `@fairmint/canton-fairmint-sdk`) |

Override with `CANTON_LOCALNET_AUTH_MODE=shared-secret` in consumer packages. Do not change the
package default to shared-secret without coordinating Keycloak / OAuth2 LocalNet smoke coverage.

## Override env vars

These remain supported for one-off local experiments:

- `CANTON_LOCALNET_QUICKSTART_REF`
- `CANTON_LOCALNET_SPLICE_VERSION`
- `CANTON_LOCALNET_SCRIBE_VERSION`
- `CANTON_LOCALNET_PROTOCOL_VERSION`
- `CANTON_LOCALNET_QUICKSTART_DIR`
- `CANTON_LOCALNET_CACHE_DIR`
- `CANTON_LOCALNET_AUTH_MODE`
- `CANTON_LOCALNET_INFRA_ONLY`

Changing the default pins is a breaking compatibility change for shared LocalNet CI and should be reviewed as part of ENG-1635 follow-ups.

SDK package-boundary ownership (what ships in publish tarballs vs CI-only fixtures) is tracked via consumer PRs under [ENG-1635](https://linear.app/fairmint/issue/ENG-1635/establish-canton-dev-tools-and-migrate-shared-canton-test).
Loading
Loading