You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As a maintainer, I want the upstream CN Token Standard CLI to read our holdings and
transactions unmodified, so that the claim of standard compliance rests on a
registry-agnostic third-party client rather than on tests we wrote ourselves.
Every conformance claim today is self-referential:
The 38 Daml scripts and 89 registry tests were written by us, against our own
reading of the standard. A shared misreading of an interface is invisible to
both.
The registry's responses are validated against the vendored OpenAPI specs, which
proves the shape of a body but not that a client built for another registry can
drive the flow end to end.
One encoding has never been exercised at all: AnyValue inside a submitted
choice context. The service hands a context out and that much is live-verified,
but nothing has ever sent one back to a participant. A third-party client doing
a real transfer is the cheapest way to close that gap.
Expected outcome
Pointed at a seeded sandbox, the CLI decodes our holdings (plain and locked) and
renders our transactions, without special-casing for canton-token-forge. A tracked CONFORMANCE.md records which standard interfaces are implemented, which commands
were run against what, their real output, and every deviation from the standard,
so a reader can tell what is proven from what is claimed.
Acceptance criteria
Getting the CLI running
The CLI runs against our sandbox, either from the vendored tree or from a
published package, and the document records which and at what version
If the vendored tree needed changes to install, they are made in scripts/fetch-dep.sh (not by hand-editing deps/, which is gitignored)
or the workaround is written down
The exact invocation for every command is recorded, including the ledger URL
and how the auth token was supplied
Read path, all must pass
list-holdings <party> for a seeded party decodes owner, instrument id,
amount, and lock state
Both a plain Token and a LockedToken are decoded, and the lock is
visible on the locked one
list-holding-txs <party> renders the mint, a faucet tap, and a completed
transfer
list-holding-txs --strict is run and its result recorded; a strict failure
is a conformance defect on our side and is filed as a bug rather than worked
around in the document
list-transfer-instructions <party> lists a pending offer for its receiver
Write path
transfer and accept-transfer-instruction are either exercised against the
service's transfer factory, or the document states plainly that they require
external-party signing keys we do not set up, and what was done instead
If they are exercised, the issue records that this is the first submitted AnyValue choice context, closing the last unverified wire encoding
The document
CONFORMANCE.md is tracked (see Technical notes: docs/ is gitignored)
It lists each of the seven splice-api-token-* interfaces as implemented or
not, naming the implementing template for each
It states that AllocationInstruction is deliberately not implemented: allocateImpl returns a completed allocation with no pending instruction
step, while the registry still advertises splice-api-token-allocation-instruction-v1 in supportedApis because it
serves the allocation-factory HTTP endpoint
It records each deviation explicitly: no economics (no decay, fees, mining
rounds, rewards, or DSO governance); one admin party is both the instrument
admin and the registry identity, and instruments are created directly by
admin tooling; the registry service is read-only; direct transfers require a
receiver-signed TokenTransferPreapproval inside its validity window and
otherwise become a two-step offer; holdings are co-signed by their owner and
locks by their holders; (admin, instrumentId) uniqueness is not enforced
on-ledger and surfaces as a 409 on get-by-id and the factory routes while GET /instruments dedupes and answers 200; decimals is display guidance
bounded 0..10, not an enforced amount scale
It states that the registry's request and response bodies validate against
the vendored OpenAPI specs, and how that is checked
Every command in the document is shown with its real output, not a summary
Alternatives considered
Write our own standard-compliance suite instead. Rejected: it would re-test
our own reading of the specs, which is exactly what this issue exists to
cross-check.
Document conformance from the specs without running anything. Rejected: a
conformance document that cites no output is a claim, not evidence.
Wait for allocation support in the CLI. Rejected: the CLI's transaction
interface list is holding + transfer-factory + transfer-instruction only
(upstream TODO #563), and the transfer path alone is worth proving now. Record
the allocation gap as a limitation of the tool.
Technical notes
The vendored CLI does not install as-is. deps/token-standard/cli/package.json carries two file: dependencies that the
sparse clone does not bring down: @canton-network/canton-json-api-v2-openapi -> ../dependencies/canton-json-api-v2/openapi-ts-client (the whole dependencies/
tree is absent) and @canton-network/transfer-instruction-openapi -> ../splice-api-token-transfer-instruction-v1/openapi-ts-client (that directory
holds only daml/, daml.yaml, docs/, openapi/). Either extend scripts/fetch-dep.sh's sparse-clone paths or generate the clients from the
vendored OpenAPI specs. Expect this to be the first half hour of the work.
Commands available (deps/token-standard/cli/src/token-standard-cli.ts): list-holdings, list-holding-txs, transfer, list-transfer-instructions, accept-transfer-instruction. Every one requires --ledger-url and --auth-token; our sandbox is unauthenticated, so pass a placeholder token. The
JSON API port comes from .canton-ports.json (RUNBOOK section 1).
transfer and accept-transfer-instruction require --public-key and --private-key, so they drive external-party signing. scripts/seed.mjs
allocates ordinary local parties, so those two are not runnable against a plain
seed without standing up external parties first. The three read commands are.
The CLI resolves contracts by package-name interface id
(#splice-api-token-holding-v1:Splice.Api.Token.HoldingV1:Holding) and matches
on the Module:Entity suffix, so it finds our holdings through the interface
rather than the template. That is the same package-name rule the RUNBOOK records.
docs/ is gitignored (.gitignore:11, confirmed with git check-ignore), so
the originally specified docs/CONFORMANCE.md would be untracked and invisible
to everyone else. Put CONFORMANCE.md at the repo root beside RUNBOOK.md and architecture.md.
The deviation list must describe the current model. The operator role, TokenRegistry, InstrumentConfigProposal, the propose-accept gate, and the /admin/* endpoints were all removed by Collapse the operator role into the instrument admin #57 and must not appear.
Additional context
Part of #23. The sandbox runbook and seed it depends on landed with #41. The write
path overlaps #42, which drives a transfer through the service directly; if #42
lands first, this issue inherits a working transfer to inspect.
User story / Problem statement
As a maintainer, I want the upstream CN Token Standard CLI to read our holdings and
transactions unmodified, so that the claim of standard compliance rests on a
registry-agnostic third-party client rather than on tests we wrote ourselves.
Every conformance claim today is self-referential:
reading of the standard. A shared misreading of an interface is invisible to
both.
proves the shape of a body but not that a client built for another registry can
drive the flow end to end.
AnyValueinside a submittedchoice context. The service hands a context out and that much is live-verified,
but nothing has ever sent one back to a participant. A third-party client doing
a real transfer is the cheapest way to close that gap.
Expected outcome
Pointed at a seeded sandbox, the CLI decodes our holdings (plain and locked) and
renders our transactions, without special-casing for canton-token-forge. A tracked
CONFORMANCE.mdrecords which standard interfaces are implemented, which commandswere run against what, their real output, and every deviation from the standard,
so a reader can tell what is proven from what is claimed.
Acceptance criteria
Getting the CLI running
published package, and the document records which and at what version
scripts/fetch-dep.sh(not by hand-editingdeps/, which is gitignored)or the workaround is written down
and how the auth token was supplied
Read path, all must pass
list-holdings <party>for a seeded party decodes owner, instrument id,amount, and lock state
Tokenand aLockedTokenare decoded, and the lock isvisible on the locked one
list-holding-txs <party>renders the mint, a faucet tap, and a completedtransfer
list-holding-txs --strictis run and its result recorded; a strict failureis a conformance defect on our side and is filed as a bug rather than worked
around in the document
list-transfer-instructions <party>lists a pending offer for its receiverWrite path
transferandaccept-transfer-instructionare either exercised against theservice's transfer factory, or the document states plainly that they require
external-party signing keys we do not set up, and what was done instead
AnyValuechoice context, closing the last unverified wire encodingThe document
CONFORMANCE.mdis tracked (see Technical notes:docs/is gitignored)splice-api-token-*interfaces as implemented ornot, naming the implementing template for each
AllocationInstructionis deliberately not implemented:allocateImplreturns a completed allocation with no pending instructionstep, while the registry still advertises
splice-api-token-allocation-instruction-v1insupportedApisbecause itserves the allocation-factory HTTP endpoint
rounds, rewards, or DSO governance); one admin party is both the instrument
admin and the registry identity, and instruments are created directly by
admin tooling; the registry service is read-only; direct transfers require a
receiver-signed
TokenTransferPreapprovalinside its validity window andotherwise become a two-step offer; holdings are co-signed by their owner and
locks by their holders;
(admin, instrumentId)uniqueness is not enforcedon-ledger and surfaces as a 409 on get-by-id and the factory routes while
GET /instrumentsdedupes and answers 200;decimalsis display guidancebounded 0..10, not an enforced amount scale
the vendored OpenAPI specs, and how that is checked
Alternatives considered
our own reading of the specs, which is exactly what this issue exists to
cross-check.
conformance document that cites no output is a claim, not evidence.
interface list is holding + transfer-factory + transfer-instruction only
(upstream TODO #563), and the transfer path alone is worth proving now. Record
the allocation gap as a limitation of the tool.
Technical notes
deps/token-standard/cli/package.jsoncarries twofile:dependencies that thesparse clone does not bring down:
@canton-network/canton-json-api-v2-openapi->../dependencies/canton-json-api-v2/openapi-ts-client(the wholedependencies/tree is absent) and
@canton-network/transfer-instruction-openapi->../splice-api-token-transfer-instruction-v1/openapi-ts-client(that directoryholds only
daml/,daml.yaml,docs/,openapi/). Either extendscripts/fetch-dep.sh's sparse-clone paths or generate the clients from thevendored OpenAPI specs. Expect this to be the first half hour of the work.
deps/token-standard/cli/src/token-standard-cli.ts):list-holdings,list-holding-txs,transfer,list-transfer-instructions,accept-transfer-instruction. Every one requires--ledger-urland--auth-token; our sandbox is unauthenticated, so pass a placeholder token. TheJSON API port comes from
.canton-ports.json(RUNBOOK section 1).transferandaccept-transfer-instructionrequire--public-keyand--private-key, so they drive external-party signing.scripts/seed.mjsallocates ordinary local parties, so those two are not runnable against a plain
seed without standing up external parties first. The three read commands are.
(
#splice-api-token-holding-v1:Splice.Api.Token.HoldingV1:Holding) and matcheson the
Module:Entitysuffix, so it finds our holdings through the interfacerather than the template. That is the same package-name rule the RUNBOOK records.
docs/is gitignored (.gitignore:11, confirmed withgit check-ignore), sothe originally specified
docs/CONFORMANCE.mdwould be untracked and invisibleto everyone else. Put
CONFORMANCE.mdat the repo root besideRUNBOOK.mdandarchitecture.md.TokenRegistry,InstrumentConfigProposal, the propose-accept gate, and the/admin/*endpoints were all removed by Collapse the operator role into the instrument admin #57 and must not appear.Additional context
Part of #23. The sandbox runbook and seed it depends on landed with #41. The write
path overlaps #42, which drives a transfer through the service directly; if #42
lands first, this issue inherits a working transfer to inspect.