The root postinstall runs scripts/link-libsodium.mjs, but the Dockerfile
never copied scripts/ into either stage - npm ci ran with only the
package.json files present, so the script was missing and npm exited 1.
This was latent until the postinstall stopped ending in `|| true`. The old
shell one-liner swallowed its own failure, so a missing script looked like a
successful install; making it fail-loud surfaced the gap on the first build
after that change.
CI did not catch it because actions/checkout gives the workflow a full
working tree, so scripts/ is always present there. Only the image build
exercises the COPY set, and only the deploy workflow builds the image.
Verified: with scripts/ present the postinstall exits 0 ("already matches the
source, nothing to do"); without it, node throws MODULE_NOT_FOUND, which is
the exact failure in the deploy logs. libsodium-sumo is a production
dependency (dev=false), so the --omit=dev runtime stage installs it and the
script finds its source there too.
Protocol parameter fields (minFeeA, minFeeB, maxTxSize, maxValSize)
lacked fallback defaults, causing "Cannot convert undefined to a BigInt"
when Lucid-Evolution processed them. Added safe defaults sourced from
Vector public testnet and updated deposit values to match. Made
lovelaceToAda() defensive against undefined/null. Added diagnostic
logging to getWalletInfo and getProtocolParameters error paths.
Converted Dockerfile to multi-stage build and added docker-compose.yml
with inline Vector testnet environment config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>