Payvol Observe
See your own money arrive. Nobody else has to.
Observe turns settlements on your own participant into records you can match. No relay, no public index, and no third party watching your payments in order to tell you about them.
Read the adapter contract See all modules
@payvol/coreyour nodeno relay In development
The code exists and runs in this repository. It is not published yet, so there is nothing to install.
Where it actually is
On Canton, the answer is not sitting where you would look for it.
Other networks let you scan a public index for a reference and find your payment. Canton has no such index, by design. And once a transfer is accepted, the identifier is no longer on an active contract at all: it survives in the participant's update history, which is a different thing to read and a different thing to keep.
Read the stream, not the contracts
Before the accept, the identifier travels with the transfer. After it, the contract is gone and only the update history still carries it. An adapter that queries active contracts finds nothing and reports nothing, which looks exactly like a payment that never arrived.
Evidence has a lifetime
Write before the window closes
Update history is pruned. An adapter must write its own record while the window is open, and a record produced after it closed is marked missing evidence rather than unmatched, because those two mean different things to whoever reads them.
Three parties can read it, and no more
We fetched the same settlement as four different parties. The payer, the payee and the instrument registry can read the identifier. A party that took no part reads nothing. The registry is a signatory of the template, so this is structural rather than a choice of ours.
Nobody stands between you and your ledger
No middle
A relay would have to see every payment it forwards. An index would have to be told about yours. On Canton you already have an authorised view of everything you are party to, so the honest design is to read it and add nothing in between.
Read from packages/payvol-conformance/fixtures/visibility-devnet.json at rows.length (four parties fetched, three of them could read the identifier)
How it works
Follow, normalise, keep.
01
Follow
- Subscribe to the participant's update stream
- Resume from the last offset you recorded
- Handle several legs inside one update as separate sightings
02
Normalise
- Read the identifier out of the settlement metadata
- Take the effective settlement time, not the time you saw it
- Qualify the asset by its registry, never by a bare string
- Record where the response came from, and hash it
03
Keep
- Persist the record before the history window closes
- Emit it locally for whatever matches it
- Flag missing evidence when you started too late
Three ways to find out you were paid
Only one of them keeps the payment yours.
01
Scan an index
the public chain habit
Watch a shared index for a reference that belongs to you. It works where every transaction is public, which is exactly the property Canton was built not to have.
A model this network does not offer
02
Trust a relay
somebody watches for you
A service subscribes on your behalf and notifies you. Convenient, and it means a third party now holds a record of every payment you receive, plus an outage that becomes yours.
Privacy and uptime, both borrowed
03
Read your own view
Payvol Observe
Your participant already receives everything you are party to. The adapter reads that stream, writes its own record, and asks nobody for permission or help.
The payment stays between the parties
Boundaries
What Observe deliberately does not do.
Most of these are things a hosted service would do for you, which is precisely why this module does not.
For operations teams
A stream in, records out, nothing in between.
Point the adapter at your participant, give it somewhere to write, and it produces a record for every settlement you are party to, whether or not it carries an identifier.
import { recordFromUpdate, windowCovers } from '@payvol/core/draft02'
// one record per sighting, identifier or not
const seen = recordFromUpdate(rawUpdate, {
observationId, observedAt, metadataKeys,
})
// a sighting that is missing a required field says which,
// rather than being dropped
seen.ok // false
seen.missing // ['digest']
// started too late, and the history window had already closed
windowCovers(window, deadline) // false Where it is used
5 of the six situations reach Observe.
B2B invoicing
Send an invoice that matches itself when it is paid
Treasury requests
Ask an internal counterparty for a transfer, with a record
Agent payments
A request a program can produce, check and pay
Validator and node services
Bill for node operation, and read the settlement yourself
Marketplace payouts
Pay many parties, and know which payout was which
Watch a settlement land.
The app replays a captured settlement from DevNet, end to end, with the identifier read back out of it.