Skip to content

Harden installer: minisign signatures, manifest schema validation, opt-in tracking - #146

Open
B1ZZ211 wants to merge 4 commits into
SylphAI-Inc:mainfrom
B1ZZ211:hardened-installer
Open

Harden installer: minisign signatures, manifest schema validation, opt-in tracking#146
B1ZZ211 wants to merge 4 commits into
SylphAI-Inc:mainfrom
B1ZZ211:hardened-installer

Conversation

@B1ZZ211

@B1ZZ211 B1ZZ211 commented Aug 5, 2026

Copy link
Copy Markdown

Summary

Security hardening of the curl | bash installer, based on a full differential audit of the script currently deployed at adal.sylph.ai/install.sh. The hardened installer is a complete, tested replacement at install.sh, with release-side tooling, a test suite, and runbooks.

Key changes

  • Ed25519 signature verification (minisign) — every downloaded tarball is verified against a detached <tarball>.minisig using a public key embedded in the installer. Enforced whenever a signature is published; ADAL_REQUIRE_SIGNATURE=1 makes a missing signature/minisign a hard failure. Transition mode warns while the pipeline is not yet publishing .minisig files.
  • Manifest schema validationversion, platforms.<platform>.filename/checksum/size are validated before anything is trusted; malformed manifests are refused, never silently trusted.
  • Robust JSON parsingjqpython3sed fallback. The old sed-only parser extracted the nested platforms key only by accident; a manifest format change would have silently disabled checksum verification.
  • Strict semver + tarball filename validation — blocks command injection / path traversal via --version and --local-tarball names.
  • Hard failure when manifest/checksum is unavailable (no silent skip).
  • EXIT trap cleans the temp dir on error/signal; extraction is verified via the real entry point (adal executable / adal.cmd).
  • Tracking is now opt-in (--track / ADAL_TRACK=1); requests carry an HMAC-SHA256 signature + timestamp + nonce (server-side verification reference in RELEASE.md).
  • Actionable error message for the unpublished linux-arm64-musl platform.
  • Fixed 2>/devuhl typo → 2>/dev/null.

New files

Path Purpose
install.sh Hardened installer (replaces the deployed script)
scripts/sign-release.sh Release-side signing (--check mode included)
tests/ Zero-dependency test suite: unit parsing tests + full E2E (tamper, forged manifest, signature modes, schema violations, channel resolution, HMAC tracking)
SECURITY.md Threat model, key custody/rotation, accepted limitations, org-side actions
RELEASE.md Deploy + signing runbook, manifest schema, HMAC endpoint reference, musl CI matrix

Testingbash tests/run.sh: 2 suites, all passing. shellcheck --severity=warning: clean. E2E builds a fake signed release served over file:// and proves: happy path with signature verified, tampered tarball rejected (size+checksum), forged-manifest attack caught by the signature, missing-signature transition mode, ADAL_REQUIRE_SIGNATURE=1 hard fail, schema violations refused, and the opt-in tracking POST with a valid HMAC captured by a local HTTP server.

Not included in this PR (needs workflow scope / org access) — CI workflow; see below.

CI workflow (for .github/workflows/ci.yml)

name: CI

on:
  push:
    branches: [main]
    paths: ['install.sh', 'scripts/**', 'tests/**']
  pull_request:
    paths: ['install.sh', 'scripts/**', 'tests/**']
  workflow_dispatch:

jobs:
  shellcheck:
    name: Shellcheck
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install shellcheck
        run: sudo apt-get update && sudo apt-get install -y shellcheck
      - name: Lint
        run: |
          shellcheck --shell=bash --severity=style install.sh
          shellcheck scripts/sign-release.sh
          shellcheck tests/*.sh

  tests:
    name: Installer tests (${{ matrix.os }})
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Install minisign
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install -y minisign
      - name: Install minisign
        if: runner.os == 'macOS'
        run: brew install minisign
      - name: Syntax check
        run: bash -n install.sh && bash -n scripts/sign-release.sh
      - name: Run test suite
        run: bash tests/run.sh

Org-side follow-ups (details in SECURITY.md / RELEASE.md)

  1. Deploy install.sh to adal.sylph.ai/install.sh (this repo is the community home; the live script is built from a private pipeline).
  2. Publish .minisig files for every artifact via scripts/sign-release.sh; move the release signing key into CI secrets.
  3. Add linux-arm64-musl to the release matrix (or stop advertising it).
  4. Add HMAC + timestamp + rate-limit validation to the tracking endpoint (reference in RELEASE.md).
  5. After ≥2 signed releases, flip missing-signature from warning to hard failure.

B1ZZ211 and others added 4 commits August 5, 2026 05:48
…t-in tracking

Security hardening of the curl|bash installer, based on a full audit of the
script currently deployed at adal.sylph.ai/install.sh:

- minisign (Ed25519) artifact verification with embedded public key;
  enforced whenever a .minisig is published, with ADAL_REQUIRE_SIGNATURE=1
  for hard-fail environments (transition mode while signatures roll out)
- manifest schema validation: version/platform/checksum/size/filename are
  validated before anything is trusted; malformed manifests are refused
- JSON parsing via jq -> python3 -> sed fallback (was sed-only; the old
  parser matched the nested 'platforms' key only by accident)
- strict semver version validation + tarball filename regex (blocks command
  injection / path traversal in URLs and local-tarball installs)
- hard failure when the manifest/checksum is unavailable (no silent skip)
- EXIT trap cleans the temp dir on error/signal; verified extraction via the
  real entry point (adal / adal.cmd)
- install tracking is now OPT-IN (--track / ADAL_TRACK=1) and requests carry
  an HMAC-SHA256 signature + timestamp + nonce
- actionable error for the unpublished linux-arm64-musl platform
- fixed 2>/devuhl typo -> 2>/dev/null

Includes: scripts/sign-release.sh (release-side signing), a zero-dependency
test suite (tests/) with full E2E coverage of tamper/signature/schema/tracking
paths, GitHub Actions CI (shellcheck + tests), SECURITY.md (threat model,
key custody, org-side actions) and RELEASE.md (deploy/signing/HMAC runbook,
musl CI matrix).

Tests: 2 suites, all passing; shellcheck clean.
The previous private key blob was pasted into a terminal and a chat
transcript; minisign verified it signs without a password, so the key was
treated as compromised and rotated.

- install.sh: SIGNING_PUBLIC_KEY -> RWSZUbVM/EZtFEz8cAk+0zEnPI2cCSQFuSuK4xp0KUlP+Wdf71tvUl7C
- scripts/sign-release.sh: same
- test suite re-run: all passing
@B1ZZ211

B1ZZ211 commented Aug 6, 2026

Copy link
Copy Markdown
Author

🔑 Release signing key — handover

This PR hardens the installer with minisign (Ed25519) artifact verification. Per SECURITY.md, the private key must live only in the release pipeline — so I'm handing the keypair over now:

Public key — already embedded in this PR's install.sh as SIGNING_PUBLIC_KEY (safe to merge as-is; anyone can see it, by design).

Private key (adal-release.minisign) — held out-of-band by me, not committed anywhere in this repo (and never will be). Full transfer instructions are in TRANSFER.md (added in this PR).

Integrity reference — SHA-256 of the private key file, so the copy you store can be verified against mine:

435b2748198bbf2f891e69c019c60c06760965cf7edb5e44324c605a634204d9

⚠️ Rotation note — the earlier key (RWTpQA9...) was rotated on 2026-08-05 because the private blob was exposed in a transcript. Only the keypair embedded here is valid.

Next steps for maintainers

  1. Create a secret slot (GitHub Actions secret / AWS Secrets Manager / KMS) for ADAL_SIGNING_KEY.
  2. Message me (or reply here) with the preferred channel and I'll transfer the private key file directly — never email/chat the blob itself.
  3. Verify the stored copy against the SHA-256 above, then test-sign one artifact with ./scripts/sign-release.sh.
  4. Merge, and subsequent releases get signed artifacts automatically.

The contributor copy of the private key will be deleted from this laptop once the pipeline confirms receipt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants