diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ff0be71..a10f152 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,12 +7,12 @@ permissions: {}
jobs:
release:
- name: Build, Sign & Release
+ name: Build & Draft Release
runs-on: ubuntu-latest
permissions:
- contents: write # create tag, release, and upload assets
- id-token: write # OIDC token for cosign keyless signing and build provenance
- attestations: write # SLSA build provenance
+ contents: write
+ id-token: write
+ attestations: write
steps:
- name: Harden the runner (Audit all outbound calls)
@@ -36,12 +36,11 @@ jobs:
tag="v${version}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
- echo "Detected version: ${version} (tag: ${tag})"
- name: Check tag does not already exist
run: |
if git rev-parse "refs/tags/${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
- echo "::error::Tag ${{ steps.version.outputs.tag }} already exists. Bump Version in internal/buildinfo/version.go before releasing."
+ echo "::error::Tag ${{ steps.version.outputs.tag }} already exists."
exit 1
fi
@@ -69,70 +68,36 @@ jobs:
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- - name: Prepare release artifacts for signing
+ - name: Locate binary
+ id: binary
run: |
- # Copy binaries to match the exact names users download from the release.
- # GoReleaser uploads as name_template (e.g. stepsecurity-dev-machine-guard_darwin_amd64)
- # but keeps them in build subdirs locally. We copy to dist/ with release names
- # so cosign signs the same bytes users verify against.
- AMD64_SRC=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*darwin_amd64*' | head -1)
- ARM64_SRC=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*darwin_arm64*' | head -1)
-
- for label in "amd64:${AMD64_SRC}" "arm64:${ARM64_SRC}"; do
- name="${label%%:*}"
- path="${label#*:}"
- if [ -z "$path" ] || [ ! -f "$path" ]; then
- echo "::error::Binary not found for ${name}"
- find dist -type f
- exit 1
- fi
- done
-
- cp "$AMD64_SRC" dist/stepsecurity-dev-machine-guard_darwin_amd64
- cp "$ARM64_SRC" dist/stepsecurity-dev-machine-guard_darwin_arm64
- echo "Prepared release artifacts for signing"
+ BINARY=$(find dist -type f -name '*darwin_unnotarized' | head -1)
+ if [ -z "$BINARY" ] || [ ! -f "$BINARY" ]; then
+ echo "::error::Binary not found"
+ find dist -type f
+ exit 1
+ fi
+ echo "path=$BINARY" >> "$GITHUB_OUTPUT"
- - name: Sign artifacts with Sigstore (keyless)
+ - name: Sign artifacts with Sigstore
run: |
- cosign sign-blob dist/stepsecurity-dev-machine-guard_darwin_amd64 \
- --bundle dist/stepsecurity-dev-machine-guard_darwin_amd64.bundle --yes
- cosign sign-blob dist/stepsecurity-dev-machine-guard_darwin_arm64 \
- --bundle dist/stepsecurity-dev-machine-guard_darwin_arm64.bundle --yes
+ cosign sign-blob "${{ steps.binary.outputs.path }}" \
+ --bundle "${{ steps.binary.outputs.path }}.bundle" --yes
cosign sign-blob stepsecurity-dev-machine-guard.sh \
--bundle dist/stepsecurity-dev-machine-guard.sh.bundle --yes
- - name: Generate checksums
- run: |
- # Separate checksum file for cosign-signed artifacts (script + bundles).
- # GoReleaser already generates checksums for the Go binaries in its own SHA256SUMS file.
- sha256sum dist/stepsecurity-dev-machine-guard_darwin_amd64 > dist/cosign-checksums.txt
- sha256sum dist/stepsecurity-dev-machine-guard_darwin_arm64 >> dist/cosign-checksums.txt
- sha256sum stepsecurity-dev-machine-guard.sh >> dist/cosign-checksums.txt
-
- - name: Upload signature bundles and checksums to release
+ - name: Upload cosign bundles
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ steps.version.outputs.tag }}" \
- dist/stepsecurity-dev-machine-guard_darwin_amd64.bundle \
- dist/stepsecurity-dev-machine-guard_darwin_arm64.bundle \
+ "${{ steps.binary.outputs.path }}.bundle" \
dist/stepsecurity-dev-machine-guard.sh.bundle \
- dist/cosign-checksums.txt \
--clobber
- - name: Mark release as immutable (not a draft, not a prerelease)
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release edit "${{ steps.version.outputs.tag }}" \
- --draft=false \
- --prerelease=false \
- --latest
-
- name: Attest build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
- dist/stepsecurity-dev-machine-guard_darwin_amd64
- dist/stepsecurity-dev-machine-guard_darwin_arm64
+ ${{ steps.binary.outputs.path }}
stepsecurity-dev-machine-guard.sh
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 74747e1..c5aeda9 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -21,14 +21,21 @@ builds:
env:
- CGO_ENABLED=0
-archives:
- - format: binary
- name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}"
+universal_binaries:
+ - id: universal
+ ids:
+ - stepsecurity-dev-machine-guard
+ replace: true
+ name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized"
-checksum:
- name_template: "{{ .ProjectName }}_{{ .Version }}_SHA256SUMS"
- algorithm: sha256
+archives:
+ - ids:
+ - universal
+ formats:
+ - binary
+ name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized"
release:
+ draft: true
extra_files:
- glob: stepsecurity-dev-machine-guard.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d4a2343..1c6316e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,17 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
See [VERSIONING.md](VERSIONING.md) for why the version starts at 1.8.1.
+## [1.9.1] - 2026-04-07
+
+### Fixed
+
+- Config `quiet: false` now correctly shows progress (was ignored previously).
+- Enterprise auto-detect mode respects the configured quiet setting instead of overriding it.
+- Release now produces a single universal macOS binary (amd64 + arm64).
+
## [1.9.0] - 2026-04-03
Migrated from shell script to a compiled Go binary. All existing scanning features, detection logic, CLI flags, output formats, and enterprise telemetry are preserved — this release changes the implementation, not the functionality.
### Added
+
- **Go binary**: Single compiled binary (`stepsecurity-dev-machine-guard`) replaces the shell script. Zero external dependencies, no runtime required.
- **`configure` / `configure show` commands**: Interactive setup and display of enterprise credentials, search directories, and preferences. Saved to `~/.stepsecurity/config.json`.
## [1.8.2] - 2026-03-17
### Added
+
- `--search-dirs DIR [DIR...]` flag to scan specific directories instead of `$HOME` (replaces default; repeatable)
- Accepts multiple directories in a single flag: `--search-dirs /tmp /opt /var`
- Supports repeated use: `--search-dirs /tmp --search-dirs /opt`
@@ -28,6 +38,7 @@ Migrated from shell script to a compiled Go binary. All existing scanning featur
First open-source release. The scanning engine was previously an internal enterprise tool (v1.0.0-v1.8.1) running in production. This release adds community mode for local-only scanning while keeping the enterprise codebase intact.
### Added
+
- **Community mode** with three output formats: pretty terminal, JSON, and HTML report
- **AI agent and CLI tool detection**: Claude Code, Codex, Gemini CLI, Kiro, Aider, OpenCode, and more
- **General-purpose AI agent detection**: OpenClaw, ClawdBot, GPT-Engineer, Claude Cowork
@@ -41,17 +52,20 @@ First open-source release. The scanning engine was previously an internal enterp
- ShellCheck CI workflow with Harden-Runner
### Changed
+
- Enterprise config variables are now clearly labeled and placed below the community-facing header
- Progress messages suppressed by default in community mode (enable with `--verbose`)
- Node.js scanning off by default in community mode (enable with `--enable-npm-scan`)
### Enterprise (unchanged from v1.8.1)
+
- `install`, `uninstall`, and `send-telemetry` commands
- Launchd scheduling (LaunchDaemon for root, LaunchAgent for user)
- S3 presigned URL upload with backend notification
- Execution log capture and base64 encoding
- Instance locking to prevent concurrent runs
+[1.9.1]: https://github.com/step-security/dev-machine-guard/compare/v1.9.0...v1.9.1
[1.9.0]: https://github.com/step-security/dev-machine-guard/compare/v1.8.2...v1.9.0
[1.8.2]: https://github.com/step-security/dev-machine-guard/compare/v1.8.1...v1.8.2
[1.8.1]: https://github.com/step-security/dev-machine-guard/releases/tag/v1.8.1
diff --git a/README.md b/README.md
index 483910b..dca9199 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
diff --git a/cmd/stepsecurity-dev-machine-guard/main.go b/cmd/stepsecurity-dev-machine-guard/main.go
index 916d038..d6cc6b5 100644
--- a/cmd/stepsecurity-dev-machine-guard/main.go
+++ b/cmd/stepsecurity-dev-machine-guard/main.go
@@ -37,26 +37,27 @@ func main() {
}
if !cfg.OutputFormatSet && config.OutputFormat != "" {
cfg.OutputFormat = config.OutputFormat
- cfg.OutputFormatSet = true // treat saved format as explicitly set
+ // Note: do NOT set OutputFormatSet here — saved config is a default preference,
+ // not an explicit CLI flag. Enterprise auto-detection should still work
+ // when no CLI flags are passed.
if config.OutputFormat == "html" && cfg.HTMLOutputFile == "" && config.HTMLOutputFile != "" {
cfg.HTMLOutputFile = config.HTMLOutputFile
}
}
exec := executor.NewReal()
- quiet := !cfg.Verbose
- // Apply saved quiet preference
- if config.Quiet != nil && *config.Quiet {
- quiet = true
+
+ // Quiet resolution: config is the base, CLI overrides.
+ quiet := true
+ if config.Quiet != nil {
+ quiet = *config.Quiet
}
- // --verbose always overrides quiet config
if cfg.Verbose {
quiet = false
}
if cfg.OutputFormat == "json" {
quiet = true
}
- // Enterprise commands (send-telemetry, install) always show progress
if cfg.Command == "send-telemetry" || cfg.Command == "install" {
quiet = false
}
diff --git a/docs/release-process.md b/docs/release-process.md
index 73c35c8..3e69ca5 100644
--- a/docs/release-process.md
+++ b/docs/release-process.md
@@ -1,6 +1,6 @@
# StepSecurity Dev Machine Guard — Release Process
-This document describes how releases are created, signed, and verified.
+This document describes how releases are created, signed, notarized, and verified.
> Back to [README](../README.md) | See also: [CHANGELOG](../CHANGELOG.md) | [Versioning](../VERSIONING.md)
@@ -8,16 +8,12 @@ This document describes how releases are created, signed, and verified.
## Overview
-Releases are created via a manually triggered GitHub Actions workflow (`workflow_dispatch`) that requires approval from the `release` environment. The workflow uses [GoReleaser](https://goreleaser.com/) to:
+Releases are a two-phase process:
-1. Read the version from `internal/buildinfo/version.go` (`const Version = "1.9.0"`)
-2. Verify the tag does not already exist (immutability)
-3. Build platform-specific binaries with GoReleaser
-4. Sign the binaries with [Sigstore](https://www.sigstore.dev/) cosign (keyless)
-5. Generate SHA256 checksums
-6. Create a Git tag and GitHub Release
-7. Attach binaries, Sigstore bundles, and checksums as release assets
-8. Generate SLSA build provenance attestation
+1. **CI (automated)** — GitHub Actions builds the universal macOS binary, signs it with Sigstore, and creates a **draft** release with the binary named `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized`.
+2. **Apple notarization (manual)** — Download the binary, sign and notarize it with an Apple Developer account, upload the notarized binary to the draft release, and publish.
+
+---
## How to Create a Release
@@ -26,109 +22,110 @@ Releases are created via a manually triggered GitHub Actions workflow (`workflow
Update `Version` in `internal/buildinfo/version.go`:
```go
-const Version = "1.9.0"
+const Version = "1.9.1"
```
-Update the [CHANGELOG.md](../CHANGELOG.md) with a new section for the version.
-
-Commit and push to `main`.
+Update [CHANGELOG.md](../CHANGELOG.md). Commit and push to `main`.
### 2. Trigger the release workflow
1. Go to [Actions > Release](https://github.com/step-security/dev-machine-guard/actions/workflows/release.yml)
-2. Click **Run workflow**
-3. Select the `main` branch
-4. Click **Run workflow**
+2. Click **Run workflow** on the `main` branch
-### 3. Approve the release
+The workflow will:
+- Create a git tag (`v1.9.1`)
+- Build a universal macOS binary (amd64 + arm64) via GoReleaser
+- Sign with Sigstore cosign (keyless)
+- Upload as `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized` to a **draft** release
+- Record the SHA256 of the unnotarized binary in the release notes
+- Generate SLSA build provenance attestation
-The workflow uses a GitHub Environment called `release` that requires approval. A designated reviewer must approve the run before it proceeds.
+### 3. Apple notarization (manual)
-### 4. Verify the release
+On a Mac with the Apple Developer certificate installed:
-Once approved, the workflow will create the tag, build the binaries, sign them, create the GitHub Release, and upload the artifacts. Check the [Releases page](https://github.com/step-security/dev-machine-guard/releases) to confirm.
+```bash
+VERSION="1.9.1"
----
+# Download the unnotarized binary
+gh release download "v${VERSION}" --repo step-security/dev-machine-guard \
+ --pattern "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized"
-## Release Artifacts
+# Rename for signing
+cp "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized" \
+ "stepsecurity-dev-machine-guard-${VERSION}-darwin"
-Each release includes the following artifacts:
+# Sign with Apple Developer ID
+codesign --sign "Developer ID Application: