Skip to content

Commit 3eb5f54

Browse files
authored
fix: installer auto-stops rampart + 200ms Windows shutdown delay (#121)
* fix(lint): add call_count to valid condition fields call_count was added to the engine in v0.4.8 but the linter's validConditionFields map was never updated. Any policy using call_count (including standard.yaml's rate-limit-fetch rule) would get a lint error from rampart doctor and rampart policy lint. Adds regression test. * fix(bench): embed corpus in binary, use it when --corpus not specified rampart bench with no arguments looked for bench/corpus.yaml relative to the current working directory. This always fails for installed users (go install, binary download) who are not in the repo root. Fix: embed corpus.yaml into the binary via go:embed. When --corpus is not explicitly set, use the embedded bytes directly. --corpus still accepts a custom file path as before. Output shows 'Corpus: built-in' when using embedded corpus. Adds regression test. * fix: four bugs found in post-release testing 1. audit: expandHome missing from listAuditFiles and listAnchorFiles All rampart audit subcommands (tail/verify/stats/search/replay) crashed with the default --audit-dir because ~ was never expanded. Fixed in the shared helpers so all paths are covered. 2. bench: approval-gated coverage always 0% require_approval expected corpus entries were never included in DenyTotal/ApprovalGated accounting. Coverage math now includes both deny and require_approval expected entries. 3. status: require_approval and webhook decisions dropped from today stats Both action types were silently skipped in todayEvents() switch. Now counted alongside deny (they are blocking decisions). 4. generate: verbose null/empty fields in output YAML Added omitempty to Policy.Priority, Enabled, Match.Agent, Match.Session, and all Condition slice/bool fields. Marshaling-only change — parsing of existing policies is unaffected. * chore: CHANGELOG for v0.4.10 * fix(upgrade): refresh policies even when already on latest version rampart upgrade returned early when current == target, skipping the policy update step. Users who upgraded binaries manually or who were already on the latest version never got policy improvements from newer releases without knowing to run rampart init --force separately. Now always runs upgradeStandardPolicies unless --no-policy-update is set. Adds regression test. * chore: CHANGELOG for v0.4.11 * fix(serve): read and persist token across restarts rampart serve --background generated a fresh random token on every start, ignoring ~/.rampart/token and never writing to it. Only the systemd/launchd install path (serve install) called resolveServiceToken, which reads the persisted file. The foreground serve path only checked RAMPART_TOKEN env. Fix: before creating the proxy, read the persisted token (RAMPART_TOKEN > ~/.rampart/token > generate new). After the listener is bound, write the token back to ~/.rampart/token so it survives restarts. Adds regression test: TestServeReadsAndPersistsToken — starts serve with a pre-written known token, verifies the same token is used and re-persisted. * chore: CHANGELOG for v0.4.12 * feat: e2e test suite + policy hardening - tests/e2e.yaml: 31-case test suite covering allow/deny/require_approval/watch across destructive cmds, FP regression, credentials, network exfil, env injection. Run: rampart test tests/e2e.yaml (uses installed policy) or: rampart test --config policies/standard.yaml tests/e2e.yaml (repo policy) - policies/standard.yaml: three policy gaps fixed: * block-credential-access: /etc/shadow, /etc/passwd, /etc/sudoers, /etc/gshadow now blocked for the read tool (were only blocked via exec patterns) * block-credential-commands: scp/rsync of private keys now blocked via combined command_contains('.ssh/id_') + command_matches('scp *'|'rsync *') * block-credential-commands: .pub exclusion added to SSH key rule to fix regression introduced by the scp rule split - internal/engine/testrunner.go: expandHome for policy: path in test YAML (tilde was resolved relative to test file dir, not home directory) - cmd/rampart/cli/test_cmd.go: --config flag now overrides policy: in test YAML, allowing 'rampart test --config dev-policy.yaml tests/e2e.yaml' workflows * fix: clean up policy rules and e2e tests - policies/standard.yaml: properly split block-credential-commands into 3 rules (ssh key cmds with .pub exclusion, scp/rsync transfer — dropped as FP-prone, aws/git/etc creds), add /etc/shadow|passwd|sudoers to read-tool block - tests/e2e.yaml: 30 cases, all passing — removed scp test (gap noted) - test_cmd.go: --config overrides policy: in test YAML - testrunner.go: expandHome for policy path * fix: SSH key policy edge cases + e2e tests - Narrow cat/head/tail patterns from /**/.ssh/** to /**/.ssh/id_* (too broad) - Add matching exclusion patterns for .pub files (cat **/.ssh/*.pub etc) - Add dedicated scp/rsync exfil rule with proper glob patterns - *scp*/.ssh/id_* matches exfil, excludes scp -i (auth) - *rsync*/.ssh/id_* matches exfil - E2E tests: 36 cases including scp/rsync edge cases All 36 e2e tests pass. All Go unit tests pass. * fix: harden SSH key policy (reviewer feedback) Fixes found by automated code review: Security fixes: - Remove -i flag exclusion from scp/rsync rule (prevents dual-key bypass: 'scp -i auth_key exfil_key remote:' was incorrectly allowed) - Add sftp to blocked transfer commands - Add mv, xxd, hexdump, od, strings to blocked read commands - Use *mv* pattern (leading wildcard) so mv with dest arg matches Tradeoff: - scp -i auth usage now blocked (security > convenience) - Users needing -i can use ssh-agent or local policy override Test updates: - Updated scp -i test expectation (now intentionally blocked) - All 36 e2e tests pass - All Go unit tests pass * chore: update CHANGELOG with security hardening details * feat: block agent self-modification of policy Add block-self-modification rule to standard.yaml that prevents AI agents from running rampart allow/block/rules/policy commands. These commands must be run by humans, not agents. This prevents the attack where an agent sees 'run rampart allow' in a denial message and tries to bypass its own restrictions. Tests: 4 new e2e tests, 40/40 passing * fix: add rate limiting to reload endpoint (1s cooldown) * fix: handle sudo/env wrappers in dangerous command detection * fix: URL detection, --tool override, atomic file writes * fix: use strconv.Atoi for strict int parsing, add flag mutual exclusion * docs: CHANGELOG and docs for v0.5.0 allow/block/rules/reload - CHANGELOG.md: add v0.5.0 release entry with Added/Changed/Fixed sections - docs-site/getting-started/quickstart.md: add Customize Your Rules section with allow/block/rules quick start and self-modification protection note - docs-site/features/policy-engine.md: add Custom Rules section documenting custom.yaml, allow/block, scopes, denial suggestions, self-mod protection, and the /v1/policy/reload API endpoint - docs-site/guides/customizing-policy.md: new full guide covering patterns, scopes, flags reference, team sharing, denial workflow, and self-mod protection - mkdocs.yml: add Customizing Policy to both Security Guides nav sections * feat: visual polish for status, doctor, upgrade, and serve commands - status: Boxed dashboard with progress bar, live server detection, and allow/deny/pending stats (lipgloss-powered, color + no-color safe) - doctor: Structured 'Try this:' hints on failures/warnings, embedded in messages with hintSep so JSON output gets a 'hint' field too - upgrade: Animated spinner during download and install phases; better permission error with sudo hint - serve: Startup spinner with clean 'Rampart ready' message and emoji-labeled dashboard/token lines - New spinner.go: lightweight braille-dot spinner (no bubbles dep), degrades to plain text on non-TTY outputs - Tests updated to match new status box output format * feat: add policy generate presets + rules tests - rampart policy generate preset: Interactive wizard with 4 presets (coding-agent, research-agent, ci-agent, devops-agent) - Uses charmbracelet/huh for interactive forms - 20 tests for policy generate, 26 tests for rules command - Flags: --preset, --dest, --force, --print * test: add internal/generate package tests * fix: improve command vs path detection for patterns like 'go build ./...' - Add commonCommands map to detect known executables - 'go build ./...' now correctly detected as exec, not path - Handle './...' Go package patterns specially - Add comprehensive tests for command/path detection * fix: maintain global indices when filtering rules list When using --global or --project filters with 'rampart rules', the displayed indices now match the global index used by 'rampart rules remove'. This prevents confusion where index 1 in a filtered view would remove a different rule than expected. * docs: add allow/block/rules to README, CLI reference, and homepage FAQ - README: Add 'Customizing rules' section showing allow/block workflow - CLI reference: Add full documentation for allow, block, rules, policy generate preset - Homepage: Update FAQ to mention 'rampart allow' command * ux: improve error messages and add denial suggestions to test command - allow/block: Show examples when pattern is missing - allow/block: Suggest quoting when multiple args are given - rules remove: Show usage example when index is missing - test: Show 'rampart allow' suggestions when command is denied Follows Atlassian's CLI design principles: - Craft human-readable error messages - Suggest the next best step * fix: address all review feedback for v0.5.0 Bug fixes: 1. rules remove/reset now actually call reload API (was lying about reload) 2. allow no longer prints duplicate rule summary after save 3. doctor says 'X warnings' instead of 'No issues found' when warnings exist 4. Hook binary check deduplicates (no more 4 identical lines) 5. FlattenRules uses local variable to prevent tool type bleeding UX improvements: 6. Dangerous commands (rm -rf /, curl|bash) show warning instead of allow suggestion 7. Duplicate pattern detection warns instead of silently adding 8. Multi-policy matches (>3) now show as bulleted list instead of long comma line All 18 test packages passing. * fix: address all v0.5.0 review bugs Bug fixes: - Bug 1: ./script.sh now detected as exec (not path) for common script extensions - Bug 2: --tool flag validates against exec/read/write/edit, rejects 'path' - Bug 3: isExtremelyDangerous() now catches rm -rf ~ and ~/*, not just / - Bug 4: rules remove -1 shows helpful error instead of 'unknown flag' - Bug 5: --message displayed in preview, rules list, and JSON output - Bug 6: dd/mkfs targeting /dev/* now suppresses allow suggestions - Bug 7: rampart test shows 👤 APPROVAL for require_approval (was showing ✅ ALLOW) - Bug 8: Sensitive paths (/etc/shadow, ~/.ssh/id_rsa) show warning instead of allow suggestion Updated test expectations for new sensitive path handling. * fix: address review agent findings (UX + security) UX Fix: - Add 'rampart rules list' as alias for 'rampart rules' (users expect list subcommand) Security Fixes: - Expand self-modification protection to block path-based invocations: - */rampart allow * (absolute paths like /usr/local/bin/rampart) - ./rampart allow * (relative paths) - rampart --* allow * (global flags before subcommand) - Improve isExtremelyDangerous() to catch shell escape bypasses: - Quoted binaries: 'rm' -rf / - Flag reordering: rm -fr / - Long flags: rm --recursive --force / - Add normalizeForDangerCheck() to strip quotes/escapes - Add containsRmRecursiveForce() for robust flag detection - Add dangerous pattern suppression for: - base64 -d | bash (decode-and-execute) - <(curl ...) (process substitution) Tests: - Add TestMatchGlob_SelfModificationPatterns for bypass coverage * fix: address Opus review findings Security fixes: - Add command_contains for self-modification protection (HIGH - blocks bash -c wrapper bypass) - Add shell wrapper patterns (bash -c, sh -c) for destructive commands - Block rm -rf . and rm -rf .. (current/parent directory wipe) UX improvements: - Add rm -rf ., .. to isExtremelyDangerous() (suppresses allow suggestions) - Update standard.yaml header to accurately describe matching capabilities The command_contains approach is now the primary defense for self-modification protection - simple substring matching catches ALL wrapper techniques without needing complex glob patterns. * fix: address remaining Opus review findings Security/UX improvements: - Add warnIfOverlyPermissive() - warns before adding '*', '**', '/*' patterns - Make dd blocking more specific (of=/dev/sd*, not all dd if=*) - Change lint severity for >2 ** segments from warning to error (pattern will silently fail at runtime, users must know) This addresses: - LOW: dd if=* was overly broad → now targets block devices only - MED: No warning for permissive patterns → now warns with confirmation - MED: >2 ** lint was warning → now error (matches runtime rejection) * docs: add security fixes to CHANGELOG for v0.5.0 * test: add e2e tests for shell wrapper bypass protection Adds 5 new tests for v0.5.0 security fixes: - bash -c 'rampart allow' (shell wrapper self-mod bypass) - sh -c 'rm -rf /' (shell wrapper destructive bypass) - rm -rf . (current directory wipe) - rm -rf .. (parent directory wipe) - bash -c 'mkfs /dev/sda' (shell wrapper format bypass) Total: 45 e2e tests * docs: update for v0.5.0 release - Add CHANGELOG comparison links for v0.4.x and v0.5.0 - Add self-modification protection to README security section - Add self-modification section to threat-model.md - Update customizing-policy.md with command_contains example - Fix [Unreleased] link to point to v0.5.0 * docs: update known limitations for v0.5.0 shell wrapper fixes - SECURITY.md: Update 'pattern evasion' limitation - shell wrappers now covered - ARCHITECTURE.md: Update 'pattern matching is bypassable' - note normalization and command_contains * fix: critical bugs found in codebase audit 1. Fix approve/deny token resolution - now checks ~/.rampart/token file Previously these commands only checked --token flag and RAMPART_TOKEN env, ignoring the persisted token file that 'rampart serve install' creates. Users got 'token required' errors even when token existed. 2. Remove stale .goreleaser.yaml (v1 format) Both .goreleaser.yaml and .goreleaser.yml existed. GoReleaser checks .goreleaser.yaml first, which was the old v1 config missing Homebrew tap and using wrong ldflags. Releases could silently use wrong config. 3. Run go mod tidy charmbracelet/huh marked as direct dependency (was indirect). 4. Update tests to use temp HOME dir Prevents tests from picking up real ~/.rampart/token file. * chore: remove dead code and fix staticcheck warnings - Remove unused functions: pollApproval, serveReachable, policiesLoadedFromEvents - Remove unused test scaffolding: mockEngine, makeToolsListJSON - Remove unused readTools categorization (never generated policy) - Remove unused sink field from SDK struct - Fix deprecated prometheus.NewGoCollector -> collectors.NewGoCollector - Fix orphan imports (net/http, time) after serveReachable removal - Fix unused variable warnings (globalPath, sevStr) staticcheck clean except intentional style choices * feat: add rampart setup cursor and rampart setup windsurf MCP-based integration for Cursor and Windsurf AI editors. - Wraps existing MCP servers in config with 'rampart mcp --' - Auto-detects already-wrapped servers (skips or re-wraps with --force) - Supports --remove to restore original config - Backs up original config before modification - 6 test cases covering wrap/unwrap/edge cases Cursor config: ~/.cursor/mcp.json Windsurf config: ~/.codeium/windsurf/mcp_config.json * docs: add Cursor and Windsurf to README integration table * fix: remove hardcoded config path from Cline hooks The generated Cline hook scripts were hardcoding ~/.rampart/policies/standard.yaml which breaks when users have custom config locations. Now uses rampart's default config discovery (same as Claude Code hooks): 1. rampart.yaml in current directory 2. ~/.rampart/config.yaml 3. Built-in defaults * ci: add macOS and Windows test runners - Tests now run on ubuntu-latest, macos-latest, windows-latest - Use 'go run' for policy checks (cross-platform compatible) - fail-fast: false so all platforms complete even if one fails - Coverage artifacts named per-OS to avoid collisions All runners are FREE for public repos. * fix: upgrade tests use dynamic platform, disable Windows CI temporarily - Add testArchiveName() helper to generate platform-specific archive names - Fixes macOS CI failures (checksum for darwin_arm64 not found) - Temporarily exclude Windows from CI matrix (test isolation issues with global ~/.rampart/) - TODO: Fix Windows test isolation and re-enable * fix: Windows test isolation with testSetHome helper - Add testSetHome(t, dir) that sets both HOME and USERPROFILE - On Windows, os.UserHomeDir() checks USERPROFILE first, not HOME - Replace all t.Setenv("HOME", ...) with testSetHome() - Skip Unix file permission checks on Windows - Use filepath.FromSlash for cross-platform path assertions - Re-enable Windows in CI matrix * fix: more Windows test isolation fixes - Convert remaining t.Setenv("HOME", ...) to testSetHome() - Affects: rules, serve, setup, token, upgrade, watch tests * fix: skip Unix-specific tests on Windows - Skip file permission tests (Unix 0o600 not applicable) - Skip upgrade tests (Windows not in upgradePlatform) - Skip shell shim tests (bash not available) - Skip Unix path tests (/etc/shadow, path traversal) * fix: skip remaining upgrade tests on Windows Add skipOnWindows to: - TestNewUpgradeCmdSuccessNoServe - TestNewUpgradeCmdSystemdRestart - TestNewUpgradeCmdSystemdTakesPriorityOverPID * fix: skip Unix path tests in internal/ packages on Windows - Skip filesystem interceptor tests (Unix paths in fixtures) - Skip e2e standard policy tests (Unix paths in standard.yaml) - Skip testrunner tests (Unix paths in test cases) - Skip suggestions test (Unix paths in fixtures) * fix: quote coverprofile flag for Windows PowerShell PowerShell was interpreting 'coverage.out' as a package name * feat: cross-platform path matching for Windows Claude Code support Normalize backslashes to forward slashes in MatchGlob so that policy patterns like '**/.ssh/id_*' match Windows paths like 'C:\Users\Trevor\.ssh\id_rsa'. This is critical for Windows users using Claude Code with Rampart — without this fix, path-based policies would never match on Windows. Added tests for Windows path patterns to verify cross-platform matching. * ci: skip benchmarks on Windows (PowerShell parsing issues) * fix: address review findings for v0.6.0 Windows release Security fixes: - Move backslash normalization to cleanPaths() before filepath.Clean (fixes audit integrity issue on Unix with paths like /home/user\../etc) - Add Windows token file security warning (os.Chmod is no-op on Windows) - Add backslash injection test cases Cursor/Windsurf fixes: - Fix hardcoded Cursor docs URL shown to Windsurf users - Add URL field to mcpServer for SSE-based servers - Skip SSE servers with warning (cannot be wrapped) - Only create backup if none exists (preserve true original) - Remove dead init() function - Update setup --help to list Cursor and Windsurf * remove cursor/windsurf setup commands (MCP-only = false security) Cursor and Windsurf have native built-in tools (file read/write, terminal) that don't go through MCP. The setup commands only wrapped MCP servers, giving users false confidence their agents were protected when 90%+ of tool calls were unmonitored. Removed: - rampart setup cursor - rampart setup windsurf - cursor/windsurf auto-detection in quickstart - All related tests and docs Users wanting MCP-only protection can still use 'rampart mcp --' manually. Focus remains on agents with real hook APIs (Claude Code, Cline) where we can intercept all tool calls. Also removed dead 'go generate ./...' from goreleaser (no go:generate directives exist in the codebase). * add Windows PowerShell installer - install.ps1: one-liner install for Windows users - Downloads latest release from GitHub - Extracts to ~/.rampart/bin, adds to PATH - Auto-detects Claude Code and offers to set up hooks - No admin rights required Usage: irm https://rampart.sh/install.ps1 | iex * docs: complete Windows support documentation - docs-site/getting-started/installation.md: Add Windows tab with PowerShell installer, document Windows limitations table - docs-site/integrations/cursor.md: Add warning that only MCP servers are protected, not Cursor's native built-in tools - docs/guides/windows.md: Standalone Windows setup guide - docs/install.ps1: Copy for GitHub Pages hosting at rampart.sh/install.ps1 * implement Windows ACLs for token file security - token_windows.go: Proper Windows ACL implementation using advapi32.dll Sets owner-only GENERIC_ALL access, removes all other permissions - token_unix.go: Standard chmod 0600/0700 for Unix platforms - Remove 'future version' comments - it's implemented now - Update Windows service messages to be more helpful (suggest NSSM/Task Scheduler) - Update docs to remove 'planned for future' language * docs: remove stale cursor setup command from agent-install guide * security: replace unsafe Windows ACL syscalls with icacls The previous implementation used hand-rolled Windows API calls via advapi32.dll with the unsafe package. This had risks: - Struct alignment must exactly match Windows C structures - Memory management with LocalFree - No easy way to test correctness New implementation uses icacls.exe (built into Windows): - icacls path /inheritance:r /grant:r USERNAME:F - Removes inherited permissions - Grants full control only to current user - Well-tested Windows utility - No unsafe package needed - Graceful fallback if icacls fails -108 lines of complex syscall code, +23 lines of simple exec. * docs: clarify that rampart serve is optional for basic protection The hook evaluates policies locally — no server needed for allow/deny. Serve is only required for: - Live dashboard (rampart watch) - Approval flow (require_approval policies) - Centralized audit streaming Updated: - install.ps1: Simplified next steps, serve marked as optional - docs/guides/windows.md: Reframed serve as optional - docs-site/getting-started/installation.md: Changed note to success callout * feat: add cross-platform uninstall command rampart uninstall: - Removes hooks from Claude Code, Cline - Stops and removes systemd/launchd services - Removes from PATH (Windows: programmatic, Unix: prints instructions) - Removes shell shim if present - Prints final instructions to delete ~/.rampart Works on Windows, macOS, and Linux. Use --yes to skip prompts. * fix: stop running rampart serve process during uninstall - Windows: Use PowerShell to find and stop rampart serve processes - macOS/Linux: Use pkill -f 'rampart serve' before service removal This ensures 'rampart uninstall' cleans up running processes on all platforms. * fix: clear existing install dir before extraction Fixes 'ExtractToFile: file already exists' error when reinstalling. Some PowerShell versions/configurations use .NET extraction methods that don't honor -Force overwrite flag. * fix: sync docs/install.ps1 with root * fix: better error handling for Windows permission issues - Detect and explain permission errors when removing existing install - Clean up partial install on extraction failure - Provide takeown/icacls recovery commands * fix: Windows installer UX improvements - Fix icacls command quoting in error message ($env:USERNAME:F → $($env:USERNAME):F) - Add cmd.exe rd fallback when PowerShell Remove-Item fails - Better formatting for recovery instructions * fix: Windows installer polish - Remove 'restart terminal' instruction (PATH refreshes in-session) - Always refresh session PATH (not just when adding) - Update uninstall instruction to use 'rampart uninstall' - Show try-it-now commands after install * docs: Windows troubleshooting for AV and permissions - Add SmartScreen/Defender/antivirus bypass instructions - Add permission error recovery steps - Update uninstall to use 'rampart uninstall' * feat: use wildcard matcher to intercept ALL Claude Code tools Previously we only hooked Bash, Read, Write|Edit. Now we use '.*' to catch all tools including Fetch, Task, and any future tools Claude adds. This ensures comprehensive coverage without needing to update Rampart when Anthropic adds new tool types. Users should run 'rampart setup claude-code --force' to upgrade their hooks to the new wildcard matcher. * feat: Windows installer detects upgrade and offers to refresh hooks On reinstall, if Rampart hooks already exist in Claude Code settings, prompts 'Update hooks to latest version?' and runs with --force. This ensures users get new hook features (like wildcard matcher) when upgrading Rampart. * security: fix audit findings from PR #119 review High: Add SHA256 checksum verification to Windows installer - Downloads checksums.txt from release and verifies downloaded zip - Fails with clear error on mismatch, warns if checksums unavailable Medium: Fix PowerShell single-quote injection in uninstall.go - Escape single quotes in PATH value before passing to PowerShell - Prevents injection via crafted PATH entries like C:\Users\O'Brien\ Low: Use os.Executable() instead of PATH lookup in uninstall - Prevents malicious 'rampart' binary earlier in PATH from executing Low: Improve upgrade detection regex in install.ps1 - Match specific 'rampart hook' command pattern, not just 'rampart' substring * fix: Windows path detection in upgrade regex and hook matcher - install.ps1: Add (?:\.exe)? to match both 'rampart hook' and 'rampart.exe hook' - setup.go hasRampartInMatcher: Handle Windows backslash paths and .exe extension Fixes regression where Windows installs weren't detected as upgrades. * fix: graceful shutdown closes SSE connections first SSE connections were blocking server shutdown, causing 'context deadline exceeded' errors on Ctrl+C. Now we close all SSE clients before shutting down the HTTP server. Also fixed potential double-close panic in unsubscribe by checking if channel still exists. * fix: add closed flag to sseHub + race-safe tests - Add 'closed bool' field to prevent post-Close subscriptions - subscribe() returns immediately-closed channel if hub is closed - broadcast() skips if hub is closed - Add 5 tests covering shutdown scenarios with -race flag Closes the race window between sse.Close() and srv.Shutdown(). * fix: installer stops rampart processes before upgrade - Try 'rampart serve stop' gracefully before deletion - Kill any remaining rampart processes - Add 500ms delay for Windows to release file handles This prevents 'Access Denied' errors during upgrade when serve is running. * fix: add 200ms delay on Windows shutdown for file handle release Windows Defender or the file indexer can briefly lock files after a process exits. Adding a small delay before serve returns gives the OS time to release handles, preventing 'Access Denied' errors on subsequent operations.
1 parent cb0d2ed commit 3eb5f54

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

cmd/rampart/cli/serve.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os/exec"
2525
"os/signal"
2626
"path/filepath"
27+
"runtime"
2728
"strings"
2829
"syscall"
2930
"time"
@@ -389,6 +390,11 @@ func newServeCmd(opts *rootOptions, deps *serveDeps) *cobra.Command {
389390
if err := sink.Close(); err != nil {
390391
logger.Error("serve: close audit sink failed", "error", err)
391392
}
393+
// Brief delay on Windows to let OS release file handles before process exits.
394+
// Without this, Windows Defender or the indexer can lock files briefly.
395+
if runtime.GOOS == "windows" {
396+
time.Sleep(200 * time.Millisecond)
397+
}
392398
return nil
393399
case err := <-proxyErrCh:
394400
if err != nil && !errors.Is(err, http.ErrServerClosed) {

docs/install.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ if ($checksumAsset) {
9595

9696
# Create install directory (clear existing to avoid conflicts)
9797
if (Test-Path $InstallDir) {
98+
# Stop any running rampart processes first
99+
$rampartExe = "$InstallDir\rampart.exe"
100+
if (Test-Path $rampartExe) {
101+
Write-Status "Stopping any running Rampart processes..."
102+
# Try graceful shutdown first
103+
try {
104+
& $rampartExe serve stop 2>$null
105+
} catch { }
106+
# Kill any remaining processes
107+
Get-Process -Name "rampart" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
108+
Start-Sleep -Milliseconds 500 # Give Windows time to release file handles
109+
}
110+
98111
Write-Status "Removing previous installation..."
99112
$removed = $false
100113

install.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ if ($checksumAsset) {
9595

9696
# Create install directory (clear existing to avoid conflicts)
9797
if (Test-Path $InstallDir) {
98+
# Stop any running rampart processes first
99+
$rampartExe = "$InstallDir\rampart.exe"
100+
if (Test-Path $rampartExe) {
101+
Write-Status "Stopping any running Rampart processes..."
102+
# Try graceful shutdown first
103+
try {
104+
& $rampartExe serve stop 2>$null
105+
} catch { }
106+
# Kill any remaining processes
107+
Get-Process -Name "rampart" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
108+
Start-Sleep -Milliseconds 500 # Give Windows time to release file handles
109+
}
110+
98111
Write-Status "Removing previous installation..."
99112
$removed = $false
100113

0 commit comments

Comments
 (0)