The contract
The wallet’s and the ledger’s contracts: the two .proto files, buf’s lint and breaking rules, and buf generate, which writes walletpb and ledgerpb with protoc-gen-go and protoc-gen-go-grpc as go tools.
The wallet is two services, each a gRPC service served by grpcserver:
wallet.v1.Wallet, keeps each caller’s own account, for a
bearer token’s subject;ledger.v1.Ledger, keeps the accounts and the transfers
between them, on PostgreSQL.Sending money crosses both:
Send, with a bearer token whose scopes hold
wallet:write;Transfer, from the token’s subject’s
account, over mTLS;app ──gRPC, TLS + bearer──▶ wallet (grpcserver) ──gRPC, mTLS──▶ ledger (grpcserver)
│ │
▼ ▼
the key set, over HTTPS PostgreSQL: ledger
A watch of an account crosses them the same way: the wallet relays the ledger’s, and opens it again when the ledger’s pod goes away.
Its code is a module of its own, at
example/grpc/wallet:
example/grpc/wallet/
├── main.go the wallet: grpcserver.Main(app.Init, ...)
├── app/ the wallet's Init, settings, key set, tokens, grpcauth rules, trust, probes, BuildTelemetry and Faults
├── server/ the wallet.v1.Wallet service, on the ledger's client
├── ledger/
│ ├── main.go the ledger: grpcserver.Main(app.Init, ...)
│ ├── app/ its Init, settings, grpcauth rules, trust, probes, BuildTelemetry and Faults
│ ├── server/ the ledger.v1.Ledger service, on a Store
│ └── store/ the Store on PostgreSQL, its injection point, and schema.sql
├── proto/ wallet/v1/wallet.proto, ledger/v1/ledger.proto, buf.yaml and buf.gen.yaml
├── walletpb/ wallet.v1's generated code, checked in
├── ledgerpb/ ledger.v1's generated code, checked in
├── cmd/walletctl/ a CLI that makes the key set, signs tokens and calls the wallet, to try it by hand
├── internal/certfile/ certificates read from files, and read again when they're renewed
├── cases/ the test cases the in-process tests, the container tests and kind share
├── functional/ the cases against kind
├── deploy/ the base, the kind overlay and up.sh, the alerts and the dashboard
└── Dockerfile one image, with the wallet and the ledger
These pages build the contract, the ledger’s accounts and its server, then
the wallet’s calls to the ledger and its server, then each service’s Init
and main. Then they test both services, deploy them, and observe them,
and end with the checklist they meet.
The wallet’s and the ledger’s contracts: the two .proto files, buf’s lint and breaking rules, and buf generate, which writes walletpb and ledgerpb with protoc-gen-go and protoc-gen-go-grpc as go tools.
The ledger’s accounts and transfers in PostgreSQL: schema.sql, the CHECK that keeps every account but external at zero or more, and the Store, whose transfer is one transaction that posts a transfer once, locks both accounts in order and checks the balance.
The ledger.v1.Ledger service on its Store: its methods and the statuses they answer, with details that say why, the watch that polls its account, mTLS and grpcauth, which let only the internal CA’s certificates call it, its injection point, and its probes.
The wallet’s calls to the ledger: a grpcclient Client over mTLS with its default retries and breaker, why every transfer is safe to retry, ForwardScenario, readiness with Connected, and turning the ledger’s answers into the wallet’s.
The wallet.v1.Wallet service on the ledger’s client: its methods, bearer tokens verified against a key set fetched over HTTPS, the scopes grpcauth requires, the token’s subject as the only account a caller can use, and relaying the ledger’s watch.
Each service’s Init: its settings, sourced and checked, and every variable it reads; its secrets and certificates as files, read again when renewed; PostgreSQL through the Host; MaxConnectionAge; readiness; BuildTelemetry, Faults and TrustScenario; and main.
How the wallet and the ledger are tested: each package as a unit, on fakes and the humustest Host; the Store against PostgreSQL; the key set served by the tests’ own handler; two ledger replicas behind a dialer of the tests’ own; the shared cases, their Target, Parallel and Alone, run inside synctest, against PostgreSQL and on kind; and the failure injection cases.
How the wallet and the ledger are deployed: their image, the kustomization, the Deployments with their gRPC probes, the disruption budgets and the drain, and the kind overlay. Then running them on kind with up.sh, and trying the wallet by hand with walletctl.
What the running wallet and ledger tell you in Grafana on kind: a send’s trace across both services in Tempo, their log records in Loki, their own metrics, humus’s and the Deployments’ in Mimir, the alert rules in deploy/alerts.yaml, and the dashboard in deploy/dashboard.json.
The checklist the wallet and the ledger meet, and how they meet each item, with the page that builds it. Check your own gRPC services against it.