Skip to content

Native production-readiness roadmap #103

Description

@justrach

@pranavp311 tracking this from PR #100 so the remaining production work has one place to live.

PR #100 already works locally for the macOS shell. We verified:

  • zig build test
  • zig build cli
  • zig build native-build -Doptimize=ReleaseSmall
  • zig build package -Doptimize=ReleaseSmall
  • launching zig-out/MerJS.app
  • app window appears as MerJS
  • embedded localhost server responds with 200 OK

This issue is for making mer native production-ready, cross-platform, and testable.

Scope

1. macOS code signing

Current integration points:

  • build.zig, native package step around the .app bundle creation
  • cli.zig, cmdPackage
  • mer.app.zon / src/native/manifest.zig

Requested work:

  • add signing config to mer.app.zon
  • support a signing identity / team id / entitlements file
  • add a build or CLI path for signing the generated .app
  • likely command shape:
mer package --sign
zig build package-sign

Acceptance tests:

  • unsigned package still works by default
  • signed package is produced when signing config is present
  • codesign --verify --deep --strict zig-out/<App>.app passes
  • generated Info.plist remains valid after signing

2. macOS notarization

Current integration points:

  • build.zig package step
  • cli.zig package command

Requested work:

  • add notarization config to manifest or env-based CLI config
  • support xcrun notarytool submit ... --wait
  • support xcrun stapler staple zig-out/<App>.app
  • document expected Apple credentials / keychain profile setup

Acceptance tests:

  • notarization can be skipped in local dev
  • notarization command fails clearly if credentials are missing
  • notarized app has a stapled ticket
  • spctl --assess --type execute --verbose zig-out/<App>.app passes when configured

3. auto-updater

Current integration points:

  • src/native/manifest.zig
  • src/native/bridge.zig
  • cli.zig package/build metadata

Requested work:

  • add update metadata to mer.app.zon
  • support signed update manifests/artifacts
  • support checking current version against a feed
  • optionally expose update commands through window.mer.invoke

Possible manifest shape:

.update = .{
    .provider = "github-releases",
    .feed_url = "https://example.com/mer-native/update.json",
    .public_key = "...",
}

Acceptance tests:

  • update check returns no update when current version is latest
  • update check returns metadata when a newer version exists
  • invalid signature is rejected
  • downgrade / rollback is rejected
  • malformed feed is rejected without crashing

4. full Linux support

Current state:

  • src/native/shell.zig returns UnsupportedPlatform for non-macOS
  • current backend is src/native/macos.zig
  • current command backend is src/native/macos_commands.zig

Requested work:

  • add src/native/linux.zig using WebKitGTK
  • add src/native/linux_commands.zig
  • add Linux build/link logic in build.zig
  • update generated native_build_snippet in cli.zig
  • document Linux package dependencies

Acceptance tests:

  • zig build native-build succeeds on Linux with documented deps
  • native window opens on Linux
  • window.mer.invoke("mer.ping", {}) works on Linux
  • clipboard/dialog/open/window commands either work or fail with clear unsupported errors

5. full Windows support

Current state:

  • no Windows WebView backend yet

Requested work:

  • add src/native/windows.zig using WebView2
  • add src/native/windows_commands.zig
  • add Windows build/link logic in build.zig
  • update generated native_build_snippet in cli.zig
  • document WebView2 runtime requirements

Acceptance tests:

  • native binary builds on Windows
  • native window opens on Windows
  • window.mer.invoke("mer.ping", {}) works on Windows
  • installer/package path is documented, even if full installer support is deferred

6. hardened permission model

Current state:

  • src/native/bridge.zig has size limit, command registry, permission checks, malformed JSON handling
  • src/native/macos.zig checks frame origin before dispatch
  • src/native/manifest.zig has global permissions and global allowed origins

Requested work:

  • add per-command permission allowlists
  • add per-command origin allowlists
  • deny or intercept WebView navigation to non-allowed origins
  • add argument schemas per command
  • restrict open.path and open.external
  • consider user prompts for clipboard/filesystem/open permissions

Acceptance tests:

  • command without permission is denied
  • command with broad permission but wrong origin is denied
  • hostile iframe cannot call privileged native commands
  • oversized payload is denied
  • embedded NUL payload is denied
  • malformed JSON is denied
  • unknown command is denied
  • open.external rejects disallowed schemes like javascript: and unexpected local file URLs
  • open.path rejects paths outside allowed roots when roots are configured

7. mature plugin system

Current state:

  • command registry is static in src/native/bridge.zig
  • app-level custom command registries are deferred

Requested work:

  • define plugin API exported from src/native/mer.zig
  • support app-provided command registries at comptime
  • let plugins declare permissions and capabilities
  • let manifest enable/disable plugin commands
  • add docs and an example plugin

Acceptance tests:

  • app can register a custom native command
  • plugin command is denied unless enabled in manifest
  • plugin command permission is enforced
  • plugin command appears in generated docs/debug output if applicable

8. production security audit

Requested deliverable:

  • add docs/native-security.md or similar
  • threat model the native shell, loopback server, WebView, bridge, updater, and packaging
  • document what is production-ready and what is still experimental

Audit checklist:

  • WKWebView navigation policy
  • bridge message origin validation
  • JSON parsing and schema validation
  • file/path/url handling
  • update signing and rollback prevention
  • macOS entitlements and sandbox assumptions
  • CSP interaction with injected window.mer shim
  • local loopback server exposure
  • logging of sensitive data

Required test matrix

At minimum, please add or document these checks:

zig build test
zig build cli
zig build native-build -Doptimize=ReleaseSmall
zig build package -Doptimize=ReleaseSmall

Manual macOS smoke test:

open zig-out/MerJS.app

Expected:

  • app window opens
  • embedded server listens on 127.0.0.1:<ephemeral port>
  • root route loads with HTTP 200
  • closing the final window exits the app

Bridge smoke test:

  • create a small page or test fixture that calls:
const result = await window.mer.invoke("mer.ping", {});

Expected:

{ "pong": true }

Security smoke tests:

  • unknown command returns an error
  • missing permission returns PermissionDenied
  • bad origin returns OriginNotAllowed
  • oversized payload returns PayloadTooLarge
  • malformed JSON returns ParseError

Cleanup requested from PR #100 before merge

  • remove harness.trajectory.jsonl
  • avoid zeroing/changing codedb.snapshot unless intentional
  • rebase or retarget onto main so the diff contains only native work

The core feature is strong. This issue is the checklist for making it production-grade.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions