Where you are. Module 3 opened by proving the problem: lesson 1 stood up two closed ledger worlds, dollars and yen, and asserted that no account bridges them. Lesson 2 split the instruction from the value: a message travels, money does not. Lesson 3 built the bridge out of relationships - the correspondent relay, a payment hopping through banks that hold accounts at each other, every hop a fee, a delay and a screening stop. This lesson stops the relay at one of those accounts and stares at it properly: one account, recorded on two banks’ books at once, under two different names. Module 1 taught you the discipline that keeps two records of one balance honest; this lesson stretches that mirror across a border and shows you the one lie only it can catch.
The morning file from Tokyo
Alder’s operations floor, early. Tokyo’s business day ended while New York slept, and overnight Kiri’s statement arrived: every movement Kiri recorded yesterday on the account Alder keeps there, and the closing balance Kiri believes. On the next screen, Alder’s own ledger holds its own version of the same account, with the balance Alder believes. Two numbers that are supposed to be one number. Someone now compares them, entry by entry - and someone does this every business morning, at every bank, for every account it keeps at another bank. Whole teams do nothing else. There is software whose only job is matching yesterday’s two files, and a name for every line that refuses to match: a “break”, which gets a case number and an investigator. An entire profession lives in the gap between two records of one balance. This lesson is about that gap: why it exists, what honestly lives in it, and the check that closes it.
The idea in one paragraph
The account Alder keeps at Kiri is one relationship written down twice. Alder’s books carry it as an asset, due from Kiri: money Kiri owes us - the nostro, ours-with-you, the kind of account lesson 3’s relay ran on. Kiri’s books carry the very same account as a liability: a deposit named alder, money we owe them - and Kiri’s word for it is vostro, yours-with-us. Same account, same balance, two names that differ only in where you stand. You have met this pairing twice already: your deposit is your asset and your bank’s liability, and a bank’s reserves are its asset and the central bank’s liability - a mirror module 1’s World re-checked after every change. Across the border the pattern holds but the enforcer is gone: the two views live on two institutions’ ledgers in two countries, and nothing stands above both to compare them. Agreement is kept only by a deliberate act - fetch the other side’s view, compare line by line, chase every difference - and the exercise builds exactly that check, then proves it catches the one forgery no single balance sheet can see.
One account, two names
Start from what the relay needs. For Alder to pay anyone in yen, some bank in Tokyo must move money on its books on Alder’s behalf, and that only works if Alder has money there to move: an account, opened by one bank at another, exactly the way you opened yours. Now look at that account from Kiri’s side, because from Kiri’s side nothing new was invented. The exercise builds it with the same call that built hana’s account: a deposit, opened in the name alder. It sits among Kiri’s liabilities like any other deposit; the customer just happens to be a foreign bank.
From Alder’s side the same account cannot possibly be a deposit. Alder does not owe this money; Alder owns it. So on Alder’s books it appears as an asset line, due from Kiri - accountants read “due from X” as X owes us, an asset, and “due to X” as we owe X, a liability. One relationship, two entries of opposite kind, each correct from where it is written.
The names are Latin and they are pure perspective: nostro, “ours”, is my account held with you; vostro, “yours”, is your account held with me. Which word a banker uses declares which side of the account they stand on, the way caller and callee name one function call, or client and server one connection. Banking keeps both words because every bank sits on both sides at once: Kiri keeps accounts abroad too, and Kiri’s nostro in some other city is that city’s bank’s vostro. Every such account is both - it depends on who is talking.
A mirror with no enforcer
Module 1 lesson 5 built this exact pairing one tier up and armed it. A bank’s reserves line and the central bank’s matching line are one balance on two books, and assert_world() walked that mirror after every change, raising on the first disagreement. That check was only possible because one object could see both sets of books: the World held the central bank and every bank inside one process.
Across the border no such object exists. The exercise makes the point structurally: two Worlds, usd and jpy, each internally complete, each running its own invariants - and no code path in either one can so much as name an account in the other. Whatever keeps due from Kiri equal to alder will not run by itself. Somebody has to build it, point it at both books, and run it. In the exercise that is assert_mirror, a few honest lines. In a real bank it is the profession you watched on the operations floor: reconciliation, the built, scheduled, human-staffed version of the check the domestic world got for free.
Wider than the screen; scroll it sideways.
Traffic seen from both sides
Watch one payment cross. ann, in New York, pays hana, in Tokyo. On this rail that is two postings, one per zone:
usd.banks["Alder"].post([("ann", -amount), (NOSTRO, -amount)]) # customer down, due-from down
jpy.banks["Kiri"].post([(VOSTRO, -amount), ("hana", amount)]) # Alder's account down, hana up
Alder’s posting balances the module 1 way: a deposit IOU extinguished, an asset spent, both sides of the sheet down together. Kiri’s posting never touches its assets at all: one liability shrinks, another grows, and hana is paid in ordinary Kiri deposits. The shared account fell by the same amount on both books - provided both banks posted, and posted the same number. That “provided” is the entire subject of this lesson. Remember lesson 2: what crossed the border was an instruction. Each bank edits its own books on receipt. One event, two independent appliers. An inbound payment - hana paying ann - runs the same two postings with every sign flipped, and the shared account refills.
Notice what never moved: reserves. Domestically, every interbank payment ended one tier up, on the central bank’s ledger. Here there is no tier above both banks - the dollar zone and the yen zone share no central bank - so the thing that settles this payment is Alder’s deposit at Kiri itself. A partner bank’s deposit is standing exactly where reserves stood at home, and that quiet substitution runs under the whole module: it is why the account must be funded before it is useful, which the next lesson prices, and why it matters enormously who moves first, which lesson 10 names.
The forgery only the other book can catch
Module 1 lesson 5 staged its forgeries in order: the crude one broke the forger’s own sheet and the per-ledger check caught it; only the carefully balanced lie needed the mirror. Rerun that logic here, one tier sideways. The exercise’s sheet_neutral_forgery has Kiri move 500 out of alder and into hana’s deposit with no instruction from Alder. One liability down, another up, net zero: Kiri’s sheet balances, and assert_balanced sails through. Go further: jpy.assert_world() passes too, because the yen zone’s own tier mirror - Kiri’s reserves against its central bank’s line - never moved. Every check the zone can run is green, and the zone is wrong. The missing fact, that Alder authorised nothing, has exactly one witness: Alder’s books, still saying 132,295 while Kiri’s now say 131,795. No invariant inside either country can see the lie. Only the comparison across the border can, which is why the harness commits the forgery only after making you build assert_mirror.
Review
One account, two names
The account one bank keeps at another abroad is one relationship written down twice. On the holder’s books it is an asset, due from them, money they owe us: the nostro, ours- with-you. On the correspondent’s books the very same account is a liability, a deposit in the holder’s name, money we owe them: the vostro, yours-with-us. Same account, same balance, two names that differ only in where you stand. You have met this pairing twice already, because your deposit is your asset and your bank’s liability, and a bank’s reserves are its asset and the central bank’s liability.
Across the border, nothing enforces the mirror
At home the pattern holds and something checks it: one ledger re-verifies the mirror after every change. Across the border the pattern holds and the enforcer is gone. The two views live on two institutions’ ledgers in two countries, and nothing stands above both to compare them. Agreement is kept only by a deliberate act: fetch the other side’s view, compare it line by line, and chase every difference. That reconciliation is not bookkeeping hygiene, it is the only detector there is, and it catches the one forgery no single balance sheet can see, because a single side can be internally consistent and still disagree with the only other record of the same account.
Check yourself
1. The balance is 150,000 on both books, yet Alder’s line is an asset and Kiri’s is a liability. How are both right at once?
They record one relationship from its two ends. The money is Kiri’s promise to Alder: a claim Alder holds, so an asset on Alder’s books, and an obligation Kiri owes, so a liability on Kiri’s. You have accepted this twice before - your deposit is your asset and your bank’s liability; a bank’s reserves are its asset and the central bank’s liability. The nostro-vostro pair is the same pattern a third time, sideways instead of up: a deposit whose customer is a foreign bank. One fact, two books, opposite kinds is double entry working across institutions, not a disagreement.
2. Module 1’s assert_world() caught a tier mismatch automatically after every change. Why can nothing equivalent run across the border, and what replaces it?
assert_world() worked because one object held every book it checked: the World contained the central bank and all its banks. Alder’s and Kiri’s books belong to different institutions in different countries, and the exercise encodes that as two Worlds that never meet - neither can read the other’s balances by right. What replaces the automatic check is a built one: fetch the other side’s view, compare, chase the differences. The check itself is a few lines; the reason it is a profession rather than a constraint is that someone must build it, schedule it and act on what it finds.
3. The forgery keeps Kiri’s sheet balanced and leaves jpy.assert_world() green. What did it move, and why can no check inside the yen zone object?
It moved 500 between two of Kiri’s own liabilities: alder down, hana up. Assets untouched, sheet still balanced, and the zone’s tier mirror - Kiri’s reserves against the central bank’s line - never involved. Every invariant the zone can state is a statement about its own books, and its own books are impeccable. The lie is that no instruction from Alder exists, and the only trace of that absence lives on Alder’s books, which still read 132,295. A forgery that balances can only be caught from outside - that is the whole case for the mirror check.
4. Monday morning, the two views differ by exactly one payment’s amount. What do you rule out before phoning anyone?
An in-flight entry. A payment posted on one book late in its day may not yet be posted on the other - cutoffs, time zones and lesson 2’s gap between message and posting all make this routine, and the signature is exactly what you are looking at: a difference matching one known payment to the unit. Check whether that payment sits in yesterday’s traffic on one side and this morning’s on the other. Most breaks close themselves when the entry lands; the investigation exists for the one that does not.
5. The exercise runs assert_mirror after every posting; a real bank reconciles once a day against a statement. What does the tight cadence buy, and what does the loose one cost?
Per-posting checking catches the first bad posting at its own moment, with one candidate: the exercise’s forgery is caught on the very next line. A daily comparison sees only the net drift, with the whole day’s traffic - here, 50 transfers - as suspects, and a forgery followed by a covering reversal inside the same day would never surface at all. The loose cadence is a window in which the two books can quietly disagree, and windows like it are where the rest of this module’s dangers live.
Do this
Twenty minutes, from module-03-across-borders. Open code/nostro_vostro.py. The two zones are built for you: Alder with customer ann in the dollar World, Kiri with customer hana in the yen one, 200,000 each, and the one shared account opened on both sets of books at 150,000 - as due from Kiri, an ASSET, on Alder’s, and as a deposit named alder on Kiri’s. Amounts are dollar-equivalent units, and the opening position arrives against equity: in reality Alder buys that opening balance with dollars, and lessons 7 and 8 price the purchase; today the mirror is the whole subject.
Your work is the mirror and the traffic that crosses it. assert_mirror reads the one balance from both sets of books - alder.balance(NOSTRO) against kiri.balance(VOSTRO) - and raises LedgerError the moment they disagree. outbound and inbound are one instruction told twice: two postings each, one per zone, with ann and the nostro moving together on Alder’s books while alder and hana move together on Kiri’s. The harness then runs 50 seeded transfers, calling both zones’ assert_world() and your assert_mirror after every posting, checks the closing balance arithmetically, and finally commits the forgery: 500 moved out of Alder’s account with no instruction, past assert_balanced, past jpy.assert_world(), and into your check.
python3 code/nostro_vostro.py
Green is this transcript, ending with the final line verbatim:
one account, two names: Alder's asset 'due from Kiri' = 150000, Kiri's liability 'alder' = 150000
50 seeded transfers (49362 out, 31657 in): the mirror held after every posting
forgery caught: mirror mismatch: Alder's books say due from Kiri = 132295, Kiri's books say Alder's account = 131795
a nostro-vostro pair is one balance kept on two books; each sheet can balance while lying, and only reconciling the two views - module 1's tier mirror stretched across a border - tells the truth
If the mirror alarm fires during the seeded transfers instead, your two postings disagree with each other: check the signs - outbound moves both views of the account down together, inbound moves both up - and check that both postings carry the same amount. If the run dies on the mirror check must catch what no single sheet can, your assert_mirror is comparing something other than the two balances, or comparing them and not raising. The completed version is solutions/nostro_vostro.py; compare after you are green.
What you can now do. You can hold one account in both heads at once: Alder’s asset, Kiri’s liability, one balance with two names, and the name declaring where the speaker stands. You can state the invariant that binds the two views, explain why nothing enforces it automatically - no ledger stands above both banks - and build the reconciliation that enforces it deliberately, the check a whole profession runs every morning. You have watched it catch the one forgery that every within-zone invariant waves through, and you know its first suspect when it fires: timing before theft. What the mirror cannot tell you is what the balance costs. 150,000 has stood at Kiri all lesson, doing nothing, waiting for payments that may never come - and the next lesson prices exactly that: the money a bank must park in advance, in every currency it serves, for the relay to work at all.