How Canton works · how I’d build above it · trade-offs that matter.
§1
Conceptual Overview
slides 1–4
§2
Building on Canton
slides 5–8
§3
Technical Deep Dive
slides 9–12
Naim Katiman
April 2026
hydrax-layer.up.railway.app
§1
01
§1 · conceptual overview
Canton in one frame
The shortest accurate definition I can give before the rest of this deck builds on it.
Canton is a privacy-preserving synchronisation protocol for multi-party workflows in regulated markets—not a blockchain in the public-L1 sense. Independent participants run their own nodes, hold only the contracts they are entitled to see, and coordinate state changes through synchronisers that order transactions without seeing their contents.
what it is
•Per-participant private contract store
•Sub-transaction privacy by stakeholder set
•Atomic multi-party state transitions via Daml choices
•Synchroniser orders, never inspects payloads
what it isn’t
•Not a public broadcast L1 with global state
•Not a permissioned chain with full replication per node
•Not a workflow engine—no SLAs, no retries, no humans
•Not a database—no global queries, no shared mempool
Built for regulated multi-party finance, not public-chain transparency.
docs/canton-homework.md
02
§1
· three primitives
Participants, synchronisers, Daml contracts
Three primitives compose every Canton deployment. Everything else is operational scaffolding around them.
Participant Nodes
PER-INSTITUTION
Holds private contracts for parties it hosts
Issuer, distributor, custodian each have their own
Authorises commands on behalf of parties
Holds party signing keys, never the application
Exposes Ledger API
gRPC + JSON for app integration
Each org runs its own
Synchronisers
ORDERING SERVICE
Order without inspecting
Sees commitments, not payloads
Atomic commit across stakeholders
All-or-nothing per transaction
Per-domain or Global
Single sync vs. Global Synchronizer
Confidential coordination
Daml Contracts
THE LANGUAGE
Templates = state shape
signatory / observer / agreement
Choices = state transitions
Controllers authorise; preconditions enforce
Compiles to DAR
Uploaded to participant; party-scoped
Authorised state model
Three primitives. Everything operational sits around these.
docs/canton-homework.md
03
§1
· how canton is wired
Four layers, one direction of authority
Apps drive participant nodes; participants commit through synchronisers; the ledger is the persistent multi-party truth that emerges.
Applications
gRPC / Daml JSON API
Daml Templates
Choices
Parties
Participant Nodes
Per-institution ledger view
Private Contract Store + Auth
Issuer
Asset creation
Distributor
Asset distribution
Custodian
Asset custody
Regulator
Oversight
Synchronizer / Domain
Per-domain ordering & atomic commit service
Ordering
Atomic Commit
Global Synchronizer
Cross-domain interop
Canton Ledger
Multi-party truth
View-based Privacy
Transaction Views
Apps run off-ledger. The chain stores only what must be multi-party-authorised.
docs/canton-homework.md
04
§1
· comparative shape
Canton vs public L1 vs traditional permissioned DLT
Same labels (“ledger,” “smart contracts”) hide very different operational shapes. The differences below are the ones that matter when you’re actually picking infrastructure.
dimension
Public L1
e.g. Ethereum
Permissioned DLT
Fabric / Corda
Canton
Daml + synchroniser
Visibility model
All state public on-chain; pseudonymous addresses
Channel/private-data scoped; full-node sees its channel
Per-stakeholder sub-transaction views; you see only contracts you sign or observe
Consensus
Open-set PoS; global ordering of all txs
BFT / Raft on a closed validator set
Synchroniser orders; commits ratified by stakeholders only
Composability
Atomic synchronous calls across all contracts
Atomic within channel; cross-channel needs custom plumbing
Atomic within domain; cross-domain via Global Synchronizer
Interoperability
Bridges + wrapped assets; weakest-link security
Mostly within consortium; off-chain handoff between networks
First-class multi-domain composition; same Daml model spans domains
Privacy
None at protocol level; ZK rollups bolt it on
Channel- or collection-level isolation
Sub-transaction privacy native; no shared mempool
Throughput shape
Global ceiling; gas fees throttle
Channel-bounded; high within channel
Scales with parallel domains; bound by stakeholder-set size
Pick the row that hurts most in your domain. For regulated finance: visibility and privacy.
docs/canton-homework.md
§2
05
§2
· building on canton
Developer toolchain that actually feels like dev
Canton ships a Daml SDK that runs locally with no synchroniser, then graduates to a real participant + sync. The shape is closer to “modern web framework” than “blockchain client.”
Daml SDK + Assistant
daml build · daml test · daml start · daml damlc
Three local runtimes
IDE-ledger (in-process, fastest) · Sandbox (single-node ledger) · Canton local (real participant + sync)
App-layer integration
Navigator UI · Daml Script for scenarios · codegen for Java / TS / Python · JSON API for HTTP-only clients
first-iteration loop
~/hydrax-governance
# compile + run scripts in IDE-ledger
$ daml build
$ daml test --files daml/Scripts.daml
All scripts pass: 5 / 5# spin up a local participant + JSON API
$ daml start
# exercise from the app layer
$ curl -X POST http://localhost:7575 \
-H "Authorization: Bearer $TOKEN" \
-d @issue-product.json
created: ProductIssued@...# codegen for the app
$ daml codegen ts && daml codegen java
Inner loop is sub-second.IDE-ledger runs in-process, no Docker, no synchroniser. You write a template, save, watch a Daml Script re-run; the rest of the slide is what happens once you graduate.
Verified locally with the hydrax-governance spike: 5 Daml Scripts green on IDE-ledger.
docs/canton-homework.md
06
§2
· deploy path
Local → testnet →mainnet
Three promotion stages. Each one introduces real things you don’t have to think about at the previous one.
stage 1~minutes
Local
IDE-ledger / Sandbox
•No synchroniser, in-process
•Run Daml Scripts to fake multi-party flows
•Hot-reload templates & choices
•App talks to JSON API on localhost
You can ship 80% of contract design without ever touching a synchroniser.
stage 2~weeks
Testnet
Global Synchronizer testnet
•Real participant nodes, real sync
•Multi-org flow with peer counterparties
•DAR upload + party allocation
•First time auth + KMS show up
Most surprises live here—identity, key custody, observer scoping.
Same Daml model survives all three stages; the surrounding infra is what changes.
docs/canton-homework.md
07
§2
· the boundary
Where Canton stops, where Web2 takes over
The whole point of building above Canton is not to push everything onto the ledger. The line below is the design discipline that keeps the system honest.
canton handleson-ledger truth
Atomic state transitions
All-or-nothing across stakeholders
Multi-party privacy
Sub-transaction views per stakeholder set
Signed choices
Authority bound to controller parties
Cross-domain composition
Same model spans synchronisers
app must ownoff-ledger workflow
Auth / SSO
Workflow state
SLA timers
Retries
Escalations
Notifications
Document storage
CRM / KYC
Each item on the right is a service in our scaffold. Each one exists because Canton intentionally won’t do it.
docs/canton-homework.md
08
§2
· opinions before code
Practical assumptions if I were starting today
Six positions. Each one closes a debate I’d rather not have mid-build.
01
Single-domain Canton first
Multi-domain is real engineering. Defer until a cross-jurisdictional flow forces it.
02
Mock the rails behind a stable interface
A hydrax-adapter-style boundary lets workflow code ship without blocking on rails availability.
03
Workflow plane lives off-ledger
Only the outcome of an approval ceremony goes to Daml. The ceremony itself stays in Postgres-backed services.
04
Daml models what’s authorised, not how
Contracts encode authority and post-conditions. The journey to authorisation is application-layer concern.
05
Participant-node ops separated from app ops
Different runbooks, different on-call. Treating the participant like a dependency stops shipping bad assumptions.
06
Every Daml command carries a workflow-id
Logged before the participant call so off-ledger audit can join with on-ledger events deterministically.
These are positions, not scriptures. They get re-litigated only with new evidence.
docs/canton-homework.md
§3
09
§3
· technical deep dive
Privacy & security model
The single property that separates Canton from every other DLT in this conversation. Three role types, sub-transaction privacy, and a shape that maps directly onto institutional flows.
SignatoriesCO-OWNERS
Authority co-holders. Their consent is required to create or archive the contract. Each signatory’s participant stores the full payload.
ObserversDISCLOSED
Read-only stakeholders. They see the contract because regulation, audit, or business design says so—but they cannot drive transitions.
ControllersCHOICE-AUTH
Per-choice authority. The set of parties whose signature is required to exercise a specific choice. Different choices, different controllers.
Sub-transaction privacy: a single transaction can touch many contracts; each participant sees only the views in which a party it hosts is signatory or observer. Other views are projected away cryptographically.
Subscription.daml
excerpt
-- Who must sign for the contract to exist?
template Subscription
with
investor : Party
issuer : Party
auditor : Party
units : Decimal
where
signatory investor, issuer
observer auditor-- Only the issuer can settle.
choice Settle : ContractId Holding
controller issuer
do
assertMsg "units > 0" (units > 0.0)
create Holding with owner = investor; units
-- Either party can cancel before settle.
choice Cancel : ()
controller investor, issuer
do return ()
Why it matters: no shared mempool exposing order intent, no global state read leaking balances, payload only ever touches investor + issuer + auditor nodes. Everything else stays dark.
This is the property that makes Canton viable for institutions that legally cannot share order books.
docs/canton-homework.md
10
§3
· multi-domain
Single sync vs. Global Synchronizer
Two operational models. Most projects should start with one synchroniser. The decision to graduate to multi-domain is one of the few in Canton that has no clean rollback.
Single synchroniser
one domain, one operator
•One ordering domain, one set of operator SLAs
•Atomicity is “free”—every commit is in scope
•Latency uniform across stakeholders
•Easier governance, easier audit, easier upgrades
Global Synchronizer
cross-domain interop
•Multiple domains, atomically composable
•Per-domain operator + governance separation
•Latency & failure domains scoped per domain
•Cross-domain commits stitch via Daml choices
when multi-domain becomes necessary
Cross-jurisdictional flows
Different operators required per region for legal residency.
Latency isolation
High-frequency settlement domain shouldn’t share a queue with onboarding.
Regulatory partitioning
Distinct supervisory regimes that cannot share an operator.
Trade-off: operational complexity multiplies. Atomicity guarantees still hold across domains, but observability, key custody, and DR run per-domain. Default to single-domain first.
Multi-domain is a graduation, not a starting position.
docs/canton-homework.md
11
§3
· tokenization & lifecycle
Daml choices as state transitions
Tokenization on Canton looks like a state machine over a multi-party contract—not an ERC-20 with a balance map. The differences explain why “just port the token” usually doesn’t work.
•Schema-compatible new package version uploads alongside the old
•Choice signatures stay stable; new fields default sensibly
•Breaking changes require an explicit migration choice on each contract
why this isn’t ERC-20
•No shared global registry — balance lives per stakeholder set
•No approve / transferFrom — transfers are signed multi-party choices
•No public balance read — queries scoped to parties hosted
•Issuer/custodian/regulator are first-class parties, not contract roles
Tokenization here is a workflow over rights, not a single mutable balance.
docs/canton-homework.md
12
§3
· proof of thought
What I built above Canton
Concrete artifacts produced under the same discipline as the slides above. Not a finished platform—a coherent slice that proves the boundary works.
9 backend servicesGo · Node/TS
workflow-svc
approval-svc
audit-svc
hydrax-adapter
canton-adapter
notify-svc
integration-svc
bff
daml spike5 / 5 scripts
hydrax-governance package green on IDE-ledger. Templates for Product, SubscriptionRequest, Holding; choices for Approve, Settle, Cancel, Redeem.
off-ledger workflow planePostgres-backed
Approval ceremonies, SLA timers, escalations, retries live in workflow-svc and approval-svc. Daml only sees the outcome.
5 white-label portalsReact + RTK
issuer
distributor
investor
ops
admin
tenant theming
mock rails behind interfacedeferred-not-resolved
HydraX rails behind a stable Go interface. Real integration gated on engagement; workflow code ships unblocked.
deployedRailway
Operator console prototype + portals reachable at hydrax-layer.up.railway.app.
Evidence the boundary holds, not a claim that the platform is finished.
docs/canton-homework.md
13
deliberate
deferrals
Trade-offs now, roadmap next
Disciplined scope choices today versus execution path for tomorrow.
Single synchronizer first
Multi-domain Canton when cross-network atomic flows justify it. Not day-1.
Mock rails behind stable interface
HydraX rails behind a stable interface. Real integration gated on engagement.
Web2 IdP off-ledger
Identity stays in integration-svc. On-chain identity is not a v1 problem.
Postgres FSM
SLA timers can’t run on a Daml contract. State machines live where time lives.
Execution Roadmap
5 milestones
Next 6 months
01
Real synchronizer + party allocation
Replace --ide-ledger with running participant
Q1
02
Real HydraX rails
Swap MockRails for live integration
Q1
03
Notifications fan-out
Email + webhook + in-app toast routing
Q2
04
KYC + SSO
integration-svc with passkeys → OIDC
Q2
05
Multi-domain Canton
When cross-network atomic flows justify it
Q3
Naim Katiman · April 2026
homework submission · April 2026 · hydrax-layer.up.railway.app
Disciplined scope now, progressive rollout next.
docs/canton-homework.md
14
deep dive · tokenization
Tokens are Daml templates, not new primitives
The interesting design choice isn't the asset model. It's who is on the contract, what choices they can make, and what off-ledger systems own which fields.
Token = Daml template instance
stakeholders, fields, choices
•Stakeholder set declared at template definition — privacy boundary, not a network policy
•Lifecycle as enumerated choice transitions, not free-form mutations
•Issuer holds the original; distributors and investors are explicitly added at issuance
•Off-ledger fields (KYC docs, marketing, fee schedules) live in Postgres — ledger keeps only the truth that needs multi-party signing
Issuer portal → Product detail → Token Model section. Renders the template name, the stakeholder set, the lifecycle states allowed by the contract, and the off-ledger fields the workflow service owns. Demo data today; same surface when rails go live.
Composability moves up the stack: orchestration plane, not the asset itself.
docs/canton-homework.md · PRD §14
15
deep dive · composability
Composability moves up the stack
Public-chain DeFi composes because state is globally readable. Canton's contract-level privacy disqualifies that pattern. Composition lives in the orchestration plane — "bring the parties to the contract".
What we don't get
vs public-chain DeFi
•Anyone-can-call composition — a contract is opaque to non-stakeholders by construction
•Global state reads — no shared mempool, no public RPC of all contracts
•Atomic same-block flash composition across arbitrary protocols built by strangers
•Permissionless interop with adversarial counterparties
What we get instead
orchestration-plane composition
•Stakeholders declared at modelling time — cross-contract logic is a deliberate design step
•"Bring parties to the contract" — workflow-svc adds the right principals at issuance so downstream choices are even possible
•Compliance-by-construction — you cannot accidentally compose a custodian into a flow they shouldn't see
•Atomicity preserved — the same Daml choice can mutate multiple contracts in one signed step (within a domain)
where it lands in the portal
Admin portal → Composability map. Renders the active contract templates as cards with their stakeholder rosters, and shows which workflows are responsible for adding each principal. Mock data today; production view drives off the workflow-svc projection.
Get the stakeholder set wrong at modelling time and no UI work fixes it.
docs/canton-homework.md §3A
16
deep dive · infrastructure
9 services, 5 portals, one Railway project
Production topology is boring on purpose. Each binary is its own service, each portal is its own static site, observability is a single composite health roll-up.
Tenant isolation via CSS-vars theme config injected at runtime; no shared mutable state
Role-aware shells — not the same SPA with feature flags
Postgres + Mongo as Railway addons; DATABASE_URL + MONGODB_URI per service env
One health roll-up: bff /healthz/composite aggregates all 8 upstream /healthz probes
Deploy: railway up --detach per linked service root
where it lands in the portal
Ops console → Health. Polls bff /healthz/composite every 5s, renders one tile per upstream service with status, latency, HTTP code, and any error text. Same component already shipping in investor-portal.
Boring infrastructure is the prerequisite for interesting product work.
services/ · web/apps/
17
deep dive · data & sync
Ledger truth, read-model speed
The ledger holds shared truth. Portals read from off-ledger projections in Postgres + Mongo. The synchroniser orders global state; projections give us tractable query latency.
Admin portal → Projections. One row per read model: name, store (postgres/mongo), last-event timestamp, lag in seconds, events/sec, last error. Click-through to the recent events stream for that projection.
The ledger is the source of truth. The projection is the source of speed.