Clone-and-run reference implementation · version 4.1.10 · Apache-2.0
Halvorsen Foods, a fictitious food manufacturer, shipped a mixed pallet to a large retailer and took a three percent deduction for a non-compliant ship notice. Eleven days of reconstruction found no mistake: the retailer's rule had changed on a date the notice did not carry, and a pallet identifier had stopped being valid on a date the notice also did not carry. Two correct systems, one working integration, and a verdict nobody could examine. HalvorsenDemo runs both sides on your machine, from the order to the payment, and settles every deduction once, with a signed Receipt either party can verify offline.
Nothing to sign up for and nothing hosted. The Receipts are committed with the repository, so
make demo never reaches the issuer: verification runs
on what you cloned.
Two ways in
If you sign for supply chain or finance
Open the supplier's console and the retailer's side by side. Every order, response, dispatch advice, receipt advice, invoice, credit note and payment run is a record on both. Eight receipts carry an exception: four for a pallet identifier that lapsed the day after dispatch, two for a pack specification the retailer replaced in July, and two for shipments that left on 30 June packed to the version in force that day and arrived on 1 July, when it no longer was. Nobody generated those two on purpose. They are the story's notice exactly.
The question to ask at the end is the one an eleven-day reconstruction exists to answer: which rule applied, on which date, and can both of us check it? Here, the Settlements page answers it from a signed Receipt.
What to look at, and what to askIf you build integrations
Two stacks on one machine exchange OASIS UBL 2.3 documents. Each is written from a Governed
Data Record on the way out of one stack, validated against the OASIS schema of its type, and
read back into a record on the way in to the other. The writers and readers are plain modules
under ubl/; where UBL has no element for something a
record names, the module says where it went.
The eight models and the eight applications are committed exactly as SDCStudio produced them,
under sdcstudio_downloads/. They compose the published
component libraries by identifier slot: the party on the same Default organization and
address components a NIEM person and a FHIR organization compose, provenance and audit from
ProvGov. The eleven saved queries are files in sparql/.
See it
One saved query per beat, each with a table and a graph, every node opening the record behind it. The smallest step at a time, so each beat teaches one thing.
Beat 1
One order as table, document and graph, beside the UBL Order it was written to, byte for byte the same when read back.
Beat 2
Buyer, seller and delivery party on the same Default components a NIEM person and a FHIR organization compose.
Beat 3
Each response joined to its order on the Order ID: accepted, cut, rejected or substituted, line by line.
Beat 4
Each dispatch with the pack specification version its pallets were packed to, beside the day it was sent. The retailer's version changed on 1 July.
Beat 5
Each pallet identifier with the day it stops being valid: sent inside the period, arrived outside it.
Beat 6
Each receipt with the two answers every pallet carries and the receiver's decision. A failing pallet puts the record in OrderProblem.
Beat 7
Each invoice joined to the receipt it settles: the amount due beside the exception it will be argued over, in one row.
Beat 8
Each deduction settled once on the Verifiable Settlement Layer: both parties' triggers, the signed Receipt, and one refusal kept beside them.
Beat 9
A second retailer publishes its order profile as a model on the same components. Each retailer's first order, under both models, shows what each refuses.
Beat 10
The supplier credits each deduction a Receipt permitted against the invoice, the Receipt named as what the credit used.
Beat 11
Monthly payment runs, every invoice due less its credit note. The chain is complete: order to payment.
Run it
You need Docker or Podman with the compose plugin, about 14 GB of free RAM for the two
stacks, and Python 3.12 on the host for data generation. make demo
starts both, generates a year of purchase orders, responses, dispatch advices, receipt
advices and invoices (about thirty seconds), with the second retailer's monthly orders, the
credit notes and the payment runs, and loads each side.
The data is deterministic and pinned to a data version, not to the release number, so the bytes the committed Receipts name are the bytes you generate. That is how a Receipt issued before you cloned still verifies on your machine.
HALVORSEN_ORDERS=260 make demo generates five years.
make down stops both stacks.
Then open
http://localhost:18300/console/ the retailer, Kestrel Mercantile.
http://localhost:18200/console/ the supplier, Halvorsen Foods.
A correct run of 4.1.10 shows:
| Records on the retailer | 288 |
| Records on the supplier | 293 |
| Invalid records | 0 |
| Receipts in OrderProblem | 8 |
| Settlement Receipts verified (PERMIT / DENY) | 9 / 1 |
What is inside
Seven documents, both directions
Order, Order Response, Despatch Advice, Receipt Advice, Invoice, Credit Note and Remittance Advice, written as conformant UBL 2.3 from a record, validated against the OASIS schema of their type, and read back on the other side. The round trips are tests in the repository.
Built from open standards
The business documents components were authored from the OASIS UBL 2.3 common library and GS1's identifiers, on the published Default and ProvGov libraries by identifier slot. Every identifier in the demo is GS1-shaped under a prefix GS1 does not issue.
Dates the notice never carried
Each dispatch names the pack specification version it was packed to and the day each pallet identifier stops being valid. The two facts that cost Halvorsen eleven days are fields, checked on the day of receipt.
Settled once
Nine PERMIT with both parties' triggers, one DENY kept beside them. Each names the record by its bytes and the model by its schema's bytes, and sdcreceipt verifies it offline against the issuer's saved key document and the parties' own.
A second retailer
Torvale Markets publishes its order profile as a model on the same components: one delivery rule, a required pack specification version and a delivery window, as assertions in its schema. An order that breaks one does not validate.
The chain, complete
The invoice for what the receipt says arrived, the credit note for the deduction a Receipt permitted, and the monthly payment run less its credits, each naming the documents it settles.
Why it is not another integration
1
The notice carries the dates that decide it
Which rule version, and until when the identifier is valid, bound into the record before it is sent.
2
The verdict can be examined by both parties
One signed Receipt, verified offline, instead of two reconstructions that disagree.
3
The second partner starts from the first one's work
Its profile is a model on the same components, not a new map built from a blank page.
None of it changes who sends what to whom. Each document still goes out in the partner's format, written by your own translator from the record, over the network you already use.
The demo proves the machinery on a supplier and two retailers we invented. The 30-Day Audit runs on your own ship notices and last quarter's deductions. And if the demo earned your time, a star on the repository tells the next engineer it is worth theirs.
Found something wrong, or something missing? Open an issue. The repo is Apache-2.0 and the models are in it, unmodified.