The beta channel is a zip attached to a GitHub release (https://github.com/kivuliapp/extension-stellar/releases), loaded unpacked. That choice has sharp edges: unpacked installs never auto-update, and removing an install deletes the wallet vault, so both halves of this document matter: the maintainer procedure, and the update procedure users must follow.
Everything runs on Stellar testnet, so no real funds are ever at stake. The vault discipline below still matters: a wiped vault costs a tester their receive ladder, their per-site accounts, and a full re-onboarding, even though the XLM involved is free.
This build's manifest carries no key, so Chrome derives each install's
extension ID from the install path. Two testers get two different IDs, and one
tester moving the folder gets a "new" extension. That is tolerable while
distribution is hand-to-hand; it is not tolerable for a public release, because
anything keyed to the ID (bug reports, dApp allowlists, a future Web Store
migration) fractures across installs.
Before the first public release, a maintainer must generate a release
keypair and pin its public half in manifest.config.ts:
mkdir -p ~/.kivuli
openssl genrsa -out ~/.kivuli/release-key.pem 2048
openssl rsa -in ~/.kivuli/release-key.pem -pubout -outform DER \
| openssl base64 -A > ~/.kivuli/release-key.pub.b64Paste the contents of release-key.pub.b64 as the key field in
manifest.config.ts. From then on Chrome derives the extension ID from that
public key instead of the path, so every install of every official zip shares
one ID, on every machine, in any folder.
The key value is the public half of the keypair. The private half must
never enter the repo (.gitignore blocks *.pem defensively) and plays no
part in the zip flow: the manifest key alone pins the ID for unpacked
installs. Keep the private key anyway, backed up like a secret, because it is
what lets a future .crx packaging or a Chrome Web Store listing keep the
same ID. If it is ever lost, unpacked releases continue unaffected as long as
manifest.config.ts keeps the same key string.
Note that a pinned ID is consistency, not authenticity: the key is public, so a matching ID proves installs agree with each other, never that a build is official. Official builds come from GitHub releases only.
- Move the
unreleasednotes inCHANGELOG.mdunder the version being cut, and bumpversioninpackage.json(the manifest and the About screen both read it, nothing else to bump). npm test: must be green.npm run release: builds (typecheck + vite +finalize-dist), then packagesrelease/kivuli-extension-stellar-v<version>.zip. The packager refuses a build whose version disagrees withpackage.json, or whereweb_accessible_resourcessurvived. Either would ship a broken channel, not just a broken build. It also reports the extension ID when a release key is pinned, and says plainly when none is.- Smoke the artifact itself, not
dist/: unzip into a fresh folder,chrome://extensions→ Developer mode → Load unpacked. Confirm Settings → About shows the new version and the service-worker console is clean. Once a release key is pinned, also confirm the card shows the pinned ID. Remove this throwaway install when done. - Commit, tag
v<version>, and push the tag. The release workflow (.github/workflows/release.yml) takes it from there: it re-runs the tests, rebuilds and repackages, refuses a tag that disagrees withpackage.json, generates SHA-256 checksums, attests build provenance, and publishes the GitHub release with the zip attached. The published notes are the matchingCHANGELOG.mdentry plus the install and update procedure below, pasted in every time: users read release notes, not docs folders. - Review the published release. If the workflow is unavailable, the manual
fallback is the old procedure: create the release by hand with the zip, a
SHA256SUMS.txt, the changelog entry, and the update instructions below. The release private key plays no part in either path and stays off CI.
- Download
kivuli-extension-stellar-v<version>.zipfrom the GitHub release. - Unzip it into a folder you will keep (e.g.
~/kivuli-extension), not Downloads. Chrome loads the extension from this folder: if it is later deleted, moved, or cleaned up, the extension stops loading. chrome://extensions→ enable Developer mode → Load unpacked → select the folder (the one containingmanifest.json).- Sanity check: the card shows Kivuli for Stellar, and Settings → About inside the wallet shows the version you downloaded.
- Optional but recommended: verify the download before unzipping. Compare
sha256sum kivuli-extension-stellar-v<version>.zipagainst theSHA256SUMS.txtattached to the release, and check GitHub's build provenance withgh attestation verify kivuli-extension-stellar-v<version>.zip --repo kivuliapp/extension-stellar.
Unpacked installs never auto-update. No release, however critical, reaches you until you re-download. Watch the repo (GitHub → Watch → Custom → Releases) so security fixes are not missed.
To update:
- Download the new zip.
- Delete the contents of your existing extension folder, not the folder itself, and unzip the new version into that same folder.
chrome://extensions→ Kivuli for Stellar card → Reload (circular arrow).- Settings → About should now show the new version. If it shows the old one, the reload picked up stale files. Check you unzipped into the right folder.
Never update by loading a new folder, and never click Remove. Your wallet vault lives in the extension's browser storage, which survives overwrite-in-place + Reload but is deleted the moment the extension is removed. Loading the new version from a different folder either collides with the existing install or tempts you to remove the old one, and removing it wipes the vault. (Until a release key is pinned, a different folder also mints a different extension ID, which splinters anything keyed to it.)
Nothing is lost. Everything (the meta-address, every per-site account, every receive address on the ladder) derives from your 12 words. If an update goes wrong (vault wiped by a remove/re-add, popup won't open, wallet shows as fresh):
- Get a clean install working in the same folder (re-download the zip, overwrite, Reload, or if the extension was removed, Load unpacked again).
- On the welcome screen choose the restore path and enter your 12 words, then set a new passcode.
- Let the first scan finish: received payments are re-discovered on Stellar testnet via the seed-derived ladder and the announcement scanner.
This is also why the backup ceremony refuses to activate a wallet until the words are verified: on this channel, the phrase is not a nice-to-have. It is the recovery path for an update gone wrong.