Summary
A story about being stuck at 95% completion, gRPC handshakes that refused to shake, and the moment I stopped debugging and started questioning.
---
The Silence of Almost
There's a specific kind of silence when you're at 95%.
Not the peaceful kind. The kind that hums with almost. The kind where your terminal is full of green checkmarks except for one stubborn red line that refuses to turn.
I had been migrating a Hyperledger Fabric blockchain network to Kubernetes for weeks. Peers were running. Orderers were ordering. Certificate Authorities were issuing. The database was replicated. The monitoring dashboards glowed green.
Everything worked.
Except the one thing that mattered: the chaincode wouldn't connect.
---
When Handshakes Fail
The error was deceptively simple:
gRPC handshake failedFour words. No stack trace. No helpful suggestion. Just a handshake that never completed.
I had deployed the chaincode as an external service, a Kubernetes pod running independently from the peer, communicating over gRPC. This was the "external builder" pattern, the supposedly elegant way to run smart contracts in a cloud-native environment.
The peer was ready to talk. The chaincode was ready to listen. But somewhere between them, the conversation died.
---
The Grind
I did what engineers do when stuck: I dug.
Day one: Network traces. Packet captures. The TCP connection established, then hung. Something about TLS.
Day two: Certificate inspection. SANs matched. Chain validated. Root CA trusted. Nothing wrong, and yet nothing right.
Day three: Configuration permutations. Different ports. Different protocols. Different timeouts. Each change felt like progress until it wasn't.
Day four: The Fabric documentation, read for the fifth time. GitHub issues, filtered by "gRPC" and "handshake" and "external." Other people had this problem. Their solutions didn't work for me.
Day five: The kind of exhaustion where you start questioning whether Kubernetes itself is broken.
I learned something that week: the hardest bugs aren't the ones that crash loudly.
They're the ones that fail quietly, politely, without telling you why.
---
The Question That Changed Everything
On day six, I stopped debugging.
I started questioning.
The external builder pattern was elegant. It was documented. It was what Fabric recommended for Kubernetes deployments. But I had been assuming that "recommended" meant "will work."
What if the pattern itself was wrong for my setup?
What if there was another way?
---
The Pivot
I found it buried in the Fabric 2.5 release notes: Chaincode-as-a-Service (CCAAS).
Not a new external builder you configure. A built-in builder that Fabric already knows how to use. The chaincode still runs as an external service, but the connection handshake follows a different path, one that Fabric controls end-to-end.
The documentation was sparse. The examples were minimal. But the architecture made sense.
I deleted my custom builder configuration.
I rewrote the chaincode's main.go to use the CCAAS shim.
I redeployed.
---
The Sound of 100%
The first log line was different.
Chaincode registered successfullyThen:
Chaincode startedThen:
Invoke receivedThe handshake completed. The chaincode responded. The peer committed the transaction to the ledger.
After weeks at 95%, I watched the number tick to 100%.
I didn't celebrate. I just sat there, staring at the logs, feeling the specific kind of relief that comes from a problem that's finally solved, not by trying harder, but by trying different.
---
What I Learned
1. Persistence has a shadow side.
I spent days debugging a pattern that was never going to work in my environment. My persistence kept me from stepping back and asking whether the approach itself was flawed. Sometimes the most productive thing you can do is stop and question your assumptions.
2. "Recommended" doesn't mean "right for you."
The external builder pattern works. It's documented. It's used in production. But it wasn't the right fit for my specific Kubernetes setup, my TLS configuration, my network topology. Best practices are starting points, not guarantees.
3. The answer is often adjacent, not ahead.
I was looking for a fix within the external builder approach. The real solution was beside it, a different pattern that achieved the same goal through a different path. When you're stuck, sometimes you need to zoom out, not zoom in.
4. The logs don't show the moment you pivoted.
If you scroll through my Git history, you won't see the doubt. You'll see commits that say "switch to CCAAS" and "remove external builder config." But the real change happened before those commits, in the moment I stopped assuming I was on the right path.
---
The Wall Isn't the Problem
Every project has a 95% wall. A point where everything is almost working, where the finish line is visible but unreachable.
The wall isn't the problem. The problem is how you respond to it.
You can keep hitting the wall, hoping it breaks before you do. Or you can step back and ask: is this even the right wall?
The gRPC handshake taught me that sometimes the breakthrough isn't a clever fix. It's the willingness to abandon an approach you've invested in, to admit that the path you're on might not lead where you need to go.
---
Closing
The chaincode is running now. It's been stable for months. Transactions flow, blocks commit, the ledger grows.
But I still remember those five days of silence. The terminal full of almost-green. The handshake that wouldn't shake.
I learned more from that wall than from the weeks of progress that preceded it.
And I learned the most important lesson of all:
Sometimes the answer isn't in the logs.
It's in the question you haven't asked yet.
---
Technical Notes
For anyone facing similar issues with Hyperledger Fabric external chaincode on Kubernetes:
main.go should use shim.ChaincodeServer with the CCAAS-specific configuration, not a generic gRPC server.connection.json in your chaincode package tells the peer where to find your chaincode service.If you're stuck on gRPC handshakes with the external builder pattern, try CCAAS. It might be the pivot you need.
