Production Checklist
Run through before every release. Each item links to the relevant guide.
Build & package
Pick your runtime consciously
-
lite(~6.5 MB) is default-correct for most apps. -
full(~44 MB) only if you need native npm bindings, a CPU-bound JS hot path, fullIntl.*, HTTP/2, or specific Bun/Node APIs.
See Runtimes.
tynd.config.ts is complete
-
runtime,backend,frontendDirset. -
window.title,width,heightset. -
bundle.identifieris reverse-DNS (com.yourco.appname). -
package.jsonhasname,version,description,author,homepage— used for installer metadata.
See tynd.config.ts.
Icon is SVG and square
- One file in
public/(SVG preferred). - Square canvas. Non-square SVGs are auto-wrapped but design-intent matters.
- Flattened text (convert
<text>to paths).
See Icons & Branding.
Installers produce on every target host
- macOS runner →
.app+.dmg - Linux runner →
.deb+.rpm(ifrpmbuildavailable) +.AppImage - Windows runner → NSIS
.exesetup +.msi
See Bundling.
Signing & distribution
Windows is signed
-
bundle.sign.windows.certificateset (.pfxor cert-store ref). -
bundle.sign.windows.passwordviaenv:NAME. -
bundle.sign.windows.timestampUrlset (defaulthttp://timestamp.digicert.com). -
signtool verify /pa /v release/*.exepasses.
macOS is signed + notarized
-
bundle.sign.macos.identity= yourDeveloper ID Application. -
bundle.sign.macos.entitlementsincludescom.apple.security.cs.allow-jit. -
bundle.sign.macos.notarizeconfigured withenv:APPLE_ID,env:APPLE_APP_PASSWORD,env:APPLE_TEAM_ID. -
spctl --assess -vv release/YourApp.appreportssource=Notarized Developer ID.
Linux signing decided
- Detached
.sigfor.AppImage/.deb/.rpm(gpg), or accept unsigned Linux. - Public key documented in your release notes.
See Code Signing.
Auto-update
Keypair generated, private key offline
-
tynd keygen --out release/updaterrun once. -
release/updater.keynever committed. Stored in password manager / HSM / CI secrets as base64. -
release/updater.pubbaked into the app source:export const UPDATER_PUB_KEY = "cFpG...RVDv/RQ=";
Updater wired up
-
updater.check({ endpoint, currentVersion })at app startup + periodic interval. -
updater.downloadAndVerify({ url, signature, pubKey: UPDATER_PUB_KEY }). -
updater.install({ path })swaps + relaunches on user confirm. - Manifest hosted at a stable HTTPS URL.
- CI signs every release artifact with
tynd signand writes the.siginto the manifest.
See Auto-Updates.
Safety & reliability
Single instance
-
singleInstance.acquire("com.yourco.myapp")at app startup. - Second launch
process.exit(0). -
onSecondLaunch/onOpenUrlhandlers registered in the primary.
Unsaved-changes handling
-
tyndWindow.onCloseRequestedguards against losing user work. - 500 ms watchdog understood;
cancelClose()used for async confirms.
Crash reporter
-
window.addEventListener("error", …)posts uncaught exceptions to your crash endpoint. -
window.addEventListener("unhandledrejection", …)same for promises. - Backend uncaught errors logged to a file sink.
Secrets in keyring, not store
- OAuth tokens, API keys, session cookies, passwords all in
keyring. - Nothing sensitive in
store(plain JSON on disk).
CSP tight
- Default CSP kept (no
'unsafe-inline'/'unsafe-eval'). - Custom CSP via
<meta http-equiv>only on pages that genuinely need it.
See Security.
UX polish
Window state persisted
- Width, height, position saved and restored. See the Remember Window Size recipe.
Dark mode respected
-
os.isDarkMode()read at startup. -
tyndWindow.onThemeChangedapplied at runtime. - CSS
@media (prefers-color-scheme)tested in both.
Accessibility
- Keyboard-only navigation works.
- Screen reader tests pass (VoiceOver / Narrator / Orca).
- Color contrast AA minimum (4.5:1 body, 3:1 large text).
-
prefers-reduced-motionrespected. - Focus visible on every interactive element.
See Accessibility.
i18n
-
os.locale()read at startup. - UI strings externalized to catalogs.
-
Intl.*alternatives shipped if targetinglite(date-fns,numbro). - RTL tested if you ship Arabic / Hebrew.
See Internationalization.
Testing
Unit tests
- Pure backend logic covered with
bun test. - OS APIs mocked at a module boundary (interface-based).
Integration smoke test
- Binary launches and stays alive ≥ 2 s in CI.
- Exit code 0 or SIGTERM expected.
See Testing.
Manual QA matrix
- Windows 11 (latest) + WebView2 present.
- macOS 14+ (x64 + arm64 if you ship both).
- Ubuntu 22.04 LTS.
- Fedora 40 (or equivalent RPM target).
- Offline launch (no internet).
- First-run without the updater being reachable.
Release
Version bump
-
package.json::versionincremented. - Conventional Commit format used (for release-please).
Tag + CI
-
git tag v<version>; push the tag. - Build-host workflow produces binaries for every target.
- Signing runs for every platform (check the artifacts’ signatures).
-
.sigfiles uploaded alongside each release artifact. - Update manifest JSON regenerated from the new
.sigvalues. - GitHub Release is published (not draft) so the postinstall downloader works.
Post-release
- Docs site deployed (auto via
deploy-docs.ymlafter binaries publish). - npm packages published (
@tynd/cli,@tynd/core,@tynd/host). - CHANGELOG published.
- Update-check confirmed from a shipped copy of the previous version.
Telemetry (optional)
Respect privacy
- Opt-in, not opt-out.
- Exposed in Settings / Preferences with a clear description.
- Default off.
Minimal payload
- Crash reports: stack + OS version + app version.
- Usage: event name + app version. No PII.
- Collected via HTTPS POST — no third-party SDKs unless you need them.
Deprecation plan
Document supported OS versions
- Windows 10 build 19041+ (or just “Windows 10/11 with WebView2”).
- macOS 11+ (or the minimum you’re testing).
- Ubuntu 20.04+ / equivalent with WebKitGTK 4.1.
Document runtime requirements
- End users need nothing (runtime bundled).
- Linux users need
libwebkit2gtk-4.1-0. - Windows users need WebView2 runtime (install or bundle the Evergreen Bootstrapper).
Related
Last updated on