Locara

16 — Build Pipeline

How source code becomes a signed, distributable Locara app artifact.

Two build modes

ModeWhereUsed for
Local build (locara build)Developer’s MacTesting, archiving, sideload distribution
CI build (Locara CI / GitHub Actions)Hosted runnerRegistry publishing

Local builds produce binaries usable on the developer’s machine. Registry distribution requires CI builds. Developers cannot upload binaries — only source refs.

Local build (locara build)

$ locara build
→ locara verify --strict             ✓
→ bun install                       ✓
→ tsc -p .                           ✓
→ vite build (frontend bundle)       ✓ (4.2 MB)
→ Generate Tauri config from locara.json (capabilities → entitlements)
→ cargo build --release (Rust shell + crate dependencies)
→ Embed frontend bundle into Rust binary
→ Tauri bundling: produces .app bundle
→ Code sign with Developer ID Application cert
→ Notarize via Apple (submit, poll for ~5 min)
→ Staple notarization ticket to bundle
→ Output:
  dist/transcribe-0.1.0.app        (notarized)
  dist/transcribe-0.1.0.dmg        (signed disk image)
  dist/transcribe-0.1.0.locapp     (sideload bundle: .app + manifest + provenance)

Total: 2–10 min depending on dependencies + notarization queue.

Local build is reproducible given locara.lock.json — same source + same lockfile = byte-identical artifact (or close, accounting for timestamps in macOS bundles, which we strip where possible).

CI build (publish)

Triggered by locara publish:

1. Developer's CLI pushes submission record to Locara registry:
   { repo_url, commit_sha, manifest_signature, publisher_token }

2. Locara registry triggers the build CI job
   (initially: GitHub Actions; future: dedicated build farm)

3. CI starts in clean ephemeral environment:
   - Fresh runner (GitHub-hosted or self-hosted)
   - No environment carryover
   - Specific macOS version pinned

4. CI clones repo at exact commit_sha

5. CI runs locara build with Locara's signing identity:
   - locara verify --strict
   - locara test
   - Build the standalone Tauri .app bundle
   - Sign with Locara's single Apple Developer ID Application cert
   - Notarize via Apple (Locara's Apple Developer Program account)
   - Staple notarization ticket to bundle
   - Generate provenance attestation:
     {
       commit_sha,
       repo_url,
       build_runner_id,
       runner_image_sha,
       artifact_sha,
       publisher_id,
       timestamp
     }
   - Sign attestation with Locara's Sigstore identity
   - Submit attestation to Sigstore public log

6. CI uploads:
   - Artifact (.app bundle) to R2
   - Manifest to Postgres
   - Attestation to Sigstore + cache

7. CI updates registry status: submission moves to "review queue"

8. Risk-based routing:
   - Low-risk: auto-approve
   - High-risk: human review required (project lead initially)

9. Once approved: app is live in registry

10. CI reports outcome to developer via locara.app dashboard + email

The whole pipeline is transparent and reproducible. Anyone can re-run the build steps from the same commit and verify the same artifact (within deterministic-build tolerances).

Code signing

Single Apple Developer ID, owned by Locara

The Locara project owns one Apple Developer Program enrollment ($99/yr). Every app published through the registry is signed with this single identity. Publishers do not need their own Apple Developer Program membership.

This works because the legal signer of a Mac app and the credited author of a Mac app are different concepts:

  • Signed by: “Locara, Inc.” (or whatever the legal entity is named) — visible in Gatekeeper.
  • Authored by: the publisher, named in the Info.plist and metadata — visible in macOS “Get Info” panel.
  • Built from: the publisher’s source repository at a specific commit, attested via Sigstore — verifiable on the registry.

This is the same pattern used by SaaS-built-app platforms, Tauri Cloud, and others. Apple’s Developer Program License Agreement permits it; Locara takes legal responsibility as the signer.

Two signatures, both from Locara

Each published .app carries two signatures:

  1. Apple Developer ID — what macOS Gatekeeper checks. Same identity for every Locara app.
  2. Sigstore-style provenance attestation — what each Locara app’s runtime verifies on launch. Per-app, attests “this exact binary was built from this exact source commit by Locara CI.”

Combined: macOS trusts the developer; Locara users trust that the developer (Locara) only signs reproducibly-built artifacts.

Publisher signing key (separate from Apple Dev)

Publishers also sign their submission with a Locara-issued key (Ed25519 or similar, not Apple-related):

  • Generated when the publisher creates their account.
  • Used to authenticate the source-commit submission to Locara CI.
  • Lets the registry verify “this commit reference was authorized by the registered publisher.”
  • 2FA + hardware security key recommended for publishers.

This key is independent of Apple’s signing infrastructure. It just authenticates who submitted to Locara CI, not the resulting binary signature.

Locara CI signing key custody

The Apple Developer ID certificate is a trust-root credential:

  • Stored in CI Secret with hardware-security-key-backed access.
  • Never present on a developer machine.
  • Rotated annually + immediately on any incident.
  • Backed up via documented escrow process (see 40-operational-security.md when written; for now, see 27-crisis-runbook.md).
  • Compromise procedure documented in the crisis runbook.

Notarization

Apple notarization is required for Mac apps to install without Gatekeeper warnings on modern macOS.

Process:

  1. Build produces signed .app.
  2. CI submits to Apple via xcrun notarytool submit.
  3. Apple scans binary for malware patterns; signs with notarization ticket.
  4. CI polls for completion (typically 1–5 min, sometimes longer).
  5. CI staples the ticket to the bundle (xcrun stapler staple).
  6. Stapled bundle is what lands in the registry.

Notarization is a requirement for Locara-distributed apps. Failed notarization blocks publish.

Provenance attestations (Sigstore)

Locara uses Sigstore to generate signed, transparency-logged attestations:

{
  "predicateType": "https://slsa.dev/provenance/v1.0",
  "predicate": {
    "buildType": "locara-ci/v1",
    "builder": { "id": "locara-ci/runners/macOS-13" },
    "buildConfig": {
      "commit": "abc...",
      "repo": "github.com/kingtongchoo/transcribe-locara",
      "lockfile_sha": "def..."
    },
    "metadata": {
      "buildStartedOn": "2026-04-30T10:00:00Z",
      "buildFinishedOn": "2026-04-30T10:08:23Z",
      "reproducible": true
    },
    "materials": [...]  // input source tree hash
  },
  "subject": [
    { "name": "transcribe-0.1.0.app", "digest": { "sha256": "..." } }
  ]
}

Signed by Locara’s Sigstore identity, logged to Sigstore’s transparency log (Rekor).

User clients verify:

  1. Signature on the attestation is from Locara CI.
  2. Attestation entry exists in Sigstore log (tamper-evident).
  3. Subject digest matches the artifact being installed.

Any tampering with the attestation is detectable. Any substitution of the artifact requires a valid attestation, which requires Locara’s CI signing key, which requires Locara CI infrastructure to be compromised.

Reproducible builds

Goal: same source + same lockfile = byte-identical artifact (modulo metadata).

Practices:

  • Pin compiler / toolchain versions in lockfile.
  • Strip build-time timestamps where possible.
  • Pin Tauri / Rust / Node / pnpm versions.
  • Use deterministic file ordering in zip / tar bundles.
  • Hash all inputs in provenance.

Reproducibility lets anyone re-run the build, verify the artifact matches what’s in the registry. This is a check on Locara CI itself: if Locara is ever pressured to ship a malicious artifact, third parties can detect the mismatch.

Full reproducibility is aspirational for v1, required for v2.

Build cache

CI uses aggressive caching:

  • Rust crate compilation cached per crate version.
  • Node modules cached per package-lock.
  • Tauri build artifacts cached.
  • Models in lockfile cached (CI doesn’t re-download).

Cache hit reduces a “build everything” 10-min job to a 1-min job. Cache misses (new deps, new versions) take longer.

Self-hosted CI option (future)

For organizations that don’t want to depend on Locara’s CI:

locara publish --ci=https://my-org-ci.example.com

Self-hosted CI must:

  • Run the same pipeline (open-source, reproducible).
  • Generate Sigstore attestations using its own key (registered with Locara).
  • Pass artifacts back to Locara registry (or self-hosted registry).

v1 ships Locara-hosted CI only; self-hosted is phase 4+.

Open questions

  • (open) GitHub Actions vs. self-hosted Mac runners. Apple-Silicon GitHub-hosted runners are now available; cost is reasonable. Probably GH Actions for v1.
  • (open) macOS Apple Silicon-only builds vs Universal 2 (arm64 + x86_64). Probably arm64-only for v1; Universal in v2 if Intel Mac demand justifies the build cost.
  • (open) Should we ship Linux / Windows builds simultaneously? Currently no — Mac-only — but Tauri makes adding them low-friction. Defer.
  • (open) How long are build logs kept? 90 days, public-readable. Reasonable balance between transparency and storage cost.

Cross-references