{"id":50298120,"url":"https://github.com/alexyorke/clatterdrive","last_synced_at":"2026-05-28T10:31:11.472Z","repository":{"id":354963688,"uuid":"1214131993","full_name":"alexyorke/clatterdrive","owner":"alexyorke","description":"WebDAV-backed novelty HDD simulator that adds mechanical latency and procedural hard-drive sounds to normal file activity.","archived":false,"fork":false,"pushed_at":"2026-05-01T06:02:38.000Z","size":13886,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-01T08:10:02.186Z","etag":null,"topics":["audio-synthesis","hard-drive","novelty","python","simulator","sound-design","webdav"],"latest_commit_sha":null,"homepage":"https://alexyorke.github.io/clatterdrive/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexyorke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-18T06:49:40.000Z","updated_at":"2026-05-01T06:02:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alexyorke/clatterdrive","commit_stats":null,"previous_names":["alexyorke/clatterdrive"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/alexyorke/clatterdrive","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexyorke%2Fclatterdrive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexyorke%2Fclatterdrive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexyorke%2Fclatterdrive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexyorke%2Fclatterdrive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexyorke","download_url":"https://codeload.github.com/alexyorke/clatterdrive/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexyorke%2Fclatterdrive/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33605377,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-synthesis","hard-drive","novelty","python","simulator","sound-design","webdav"],"created_at":"2026-05-28T10:31:09.202Z","updated_at":"2026-05-28T10:31:11.463Z","avatar_url":"https://github.com/alexyorke.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ClatterDrive\n\n\u003e [!WARNING]\n\u003e `ClatterDrive` is a novelty project for entertainment purposes. Contributions are welcome, but this is not a serious or trustworthy HDD simulator, and it should not be treated as accurate for storage, acoustics, or performance work.\n\n`ClatterDrive` makes a normal directory feel and sound more like a mechanical hard drive.\n\n- It exposes a directory over WebDAV.\n- It injects HDD-like latency into reads, writes, metadata churn, standby/wake, and write-back behavior.\n- It synthesizes drive noise procedurally from the same runtime events.\n- It does **not** use prerecorded HDD sound effects.\n\n## Audio Demo\n\n- [Listen in the browser](https://alexyorke.github.io/clatterdrive/)\n\nChecked-in sample renders:\n\n| Scenario | Why it sounds different | File |\n| --- | --- | --- |\n| Spin-up, idle, park | Desk-coupled startup/body with a final park transient | [spinup-idle-park.wav](samples/spinup-idle-park.wav) |\n| Idle, standby, wake | Quieter state change, then wake-up and renewed activity | [idle-standby-wake.wav](samples/idle-standby-wake.wav) |\n| Metadata storm | Brighter, busier short/medium seeks on a barer mounting profile | [metadata-storm.wav](samples/metadata-storm.wav) |\n\n## Quick Start\n\nThis repo uses `uv` and the committed [uv.lock](uv.lock).\n\n```powershell\nuv sync --group dev\nuv run python main.py\n```\n\nOr:\n\n```powershell\nuv run python -m clatterdrive\nuv run clatterdrive\n```\n\nThe explicit backend commands are:\n\n```powershell\nuv run clatterdrive serve\nuv run clatterdrive profiles --json\nuv run clatterdrive doctor --json\n```\n\nDefault URL:\n\n- `http://127.0.0.1:8080`\n\n## Windows App\n\nThe Windows-first desktop path is a native WPF launcher plus a packaged Python backend. It is not Tkinter, Electron, or a browser shell.\n\nLocal developer build:\n\n```powershell\nscripts\\build-windows.ps1\n```\n\nRelease zip:\n\n```powershell\nscripts\\package-windows.ps1\n```\n\nMSI installer:\n\n```powershell\nscripts\\build-installer.ps1\n```\n\nThe packaged launcher lets a non-developer choose the backing folder, port, drive/acoustic profile, audio mode/device, start/stop the backend, open the WebDAV URL, copy the `net use` command, copy the unmount command, and inspect logs.\n\nInstaller E2E intentionally refuses to run on an unmarked local host because it installs and uninstalls the app. Run it in GitHub Actions, or inside a disposable Windows VM with `CLATTERDRIVE_INSTALLER_E2E_VM=1`:\n\n```powershell\nscripts\\test-installer.ps1 -IncludeUiE2E -IncludeMappedDrive\n```\n\n## macOS App\n\nThe macOS desktop path is a native SwiftUI launcher plus a packaged Python backend. Build and package it on macOS, not from Windows or Linux:\n\n```bash\nbash scripts/build-macos.sh\nbash scripts/package-macos.sh\n```\n\nThe release artifacts are architecture-specific DMGs:\n\n- `ClatterDrive-macos-arm64.dmg`\n- `ClatterDrive-macos-x64.dmg`\n\nRun macOS checks on a Mac or on GitHub-hosted macOS runners:\n\n```bash\nbash scripts/test-macos.sh\nbash scripts/test-macos-e2e.sh --include-mount\nbash scripts/test-macos-e2e.sh --packaged --from-dmg --include-mount\n```\n\nThe macOS launcher copies `mount_webdav` and unmount commands instead of auto-mounting. Signing and notarization are conditional in release builds when Apple Developer ID secrets are configured; otherwise DMGs are unsigned internal builds.\n\nSee [docs/macos-vm.md](docs/macos-vm.md) for the VM decision. Short version: use GitHub-hosted macOS first; a local macOS VM is only appropriate on Apple hardware.\n\nOn an Apple Silicon Mac, the repeatable local VM path is:\n\n```bash\nbash scripts/test-macos-tart.sh\n```\n\n## Using It\n\nThe simulator serves `backing_storage/` by default. Use the WebDAV URL, not the backing directory directly, or you will bypass the latency/audio path.\n\nWindows options:\n\n- open `http://127.0.0.1:8080/` in Explorer as a network location\n- upload/download with `curl.exe`\n- map it as a WebDAV drive, for example `net use X: \\\\127.0.0.1@8080\\DavWWWRoot /persistent:no`\n\nWindows `curl.exe` example:\n\n```powershell\n$demoName = \"demo-$([guid]::NewGuid().ToString('N').Substring(0, 8))\"\n$uploadPath = Join-Path $env:TEMP \"clatterdrive-upload.bin\"\n$downloadPath = Join-Path $env:TEMP \"clatterdrive-download.bin\"\n\n\"hello from clatterdrive\" | Set-Content -Path $uploadPath -Encoding ascii\n\ncurl.exe -X MKCOL \"http://127.0.0.1:8080/$demoName/\"\ncurl.exe -T $uploadPath \"http://127.0.0.1:8080/$demoName/file.bin\"\ncurl.exe \"http://127.0.0.1:8080/$demoName/file.bin\" --output $downloadPath\n```\n\nNotes:\n\n- `MKCOL` only works for a directory that does not already exist. If you reuse a name like `demo/`, `405 Method Not Allowed` is expected.\n- The `C:\\path\\to\\...` strings were placeholders; replace them with real paths, or use the temp-file example above as-is.\n- `curl.exe --output` will fail if the parent directory does not exist.\n\nWSL option:\n\n- use `davfs2` and disable client-side locks for this server\n\nVerified in WSL 2 (Ubuntu 22.04) on this machine:\n\n```bash\nsudo apt-get update\nsudo apt-get install -y davfs2\nmkdir -p ~/.davfs2 ~/mnt/clatterdrive\ncat \u003e ~/.davfs2/clatterdrive.conf \u003c\u003c'EOF'\nuse_locks 0\ndelay_upload 0\nEOF\nsudo mount -t davfs http://127.0.0.1:8080/ ~/mnt/clatterdrive -o uid=$(id -u),gid=$(id -g),file_mode=0644,dir_mode=0755,conf=$HOME/.davfs2/clatterdrive.conf\necho \"hello from wsl davfs\" \u003e /tmp/clatterdrive-src.txt\ncp /tmp/clatterdrive-src.txt ~/mnt/clatterdrive/wsl-copy-test.txt\ncat ~/mnt/clatterdrive/wsl-copy-test.txt\ncmp -s /tmp/clatterdrive-src.txt ~/mnt/clatterdrive/wsl-copy-test.txt \u0026\u0026 echo matches=true\n```\n\nWSL notes:\n\n- `davfs2` will prompt for a username and password; press Enter for both because the local server is anonymous.\n- `use_locks 0` is currently required for `davfs2` writes against this server. Without it, `davfs2` tries to lock a not-yet-created path and uploads fail with `Input/output error`.\n\nmacOS options:\n\n- Finder: `Go -\u003e Connect to Server...` and enter `http://127.0.0.1:8080/`\n- Terminal with the built-in WebDAV client:\n\n```bash\nmkdir -p ~/mnt/clatterdrive\nmount_webdav http://127.0.0.1:8080/ ~/mnt/clatterdrive\ncp /path/to/local-file ~/mnt/clatterdrive/\ncat ~/mnt/clatterdrive/local-file\n```\n\nmacOS note:\n\n- the macOS E2E script verifies the standard built-in `mount_webdav` path on GitHub-hosted macOS runners; this Windows host cannot execute it locally.\n\nIf live audio is enabled, directory creation and listing, uploads, downloads, overwrites, deletes, fragmented reads, and wake-from-standby activity all feed the audio model.\n\n## Useful Environment Variables\n\n- `FAKE_HDD_HOST`: bind to a different interface\n- `FAKE_HDD_PORT`: use a different port\n- `FAKE_HDD_BACKING_DIR`: use a different backing directory\n- `FAKE_HDD_AUDIO=off`: disable live audio\n- `FAKE_HDD_AUDIO_DEVICE`: pick an explicit output device index/name for PortAudio\n- `FAKE_HDD_AUDIO_TEE_PATH`: record rendered output to a WAV, even when live audio is off\n- `FAKE_HDD_EVENT_TRACE_PATH`: export a structured storage-event JSON trace on shutdown\n- `FAKE_HDD_TRACE_EVENTS=on`: print compact event debug lines to stderr\n- `FAKE_HDD_COLD_START=off`: start already ready\n- `FAKE_HDD_ASYNC_POWER_ON=off`: disable background startup sequencing\n- `FAKE_HDD_DRIVE_PROFILE`: choose a drive preset\n- `FAKE_HDD_ACOUSTIC_PROFILE`: choose an installation/acoustic preset\n\nCurrent drive presets:\n\n- `desktop_7200_internal`\n- `archive_5900_internal`\n- `enterprise_7200_bare`\n- `wd_ultrastar_hc550`\n- `seagate_ironwolf_pro_16tb`\n- `external_usb_enclosure`\n\nCurrent acoustic presets:\n\n- `bare_drive_lab`\n- `mounted_in_case`\n- `external_enclosure`\n- `drive_on_desk`\n\nExample:\n\n```powershell\n$env:FAKE_HDD_DRIVE_PROFILE = \"archive_5900_internal\"\n$env:FAKE_HDD_ACOUSTIC_PROFILE = \"drive_on_desk\"\nuv run python main.py\n```\n\n## Docker\n\nDocker is mainly for the headless WebDAV/latency path. Native host execution is better for live audio.\n\n```powershell\ndocker compose up --build\n```\n\nOr:\n\n```powershell\ndocker build -t clatterdrive .\ndocker run --rm -p 8080:8080 -e FAKE_HDD_AUDIO=off -v \"${PWD}/backing_storage:/data\" clatterdrive\n```\n\nAudible container output needs an explicit host-audio bridge; Docker does not expose your speakers automatically. The supported path is a host PulseAudio/PipeWire server that the container can reach via `PULSE_SERVER`.\n\nExample:\n\n```powershell\n$env:FAKE_HDD_AUDIO = \"live\"\n$env:PULSE_SERVER = \"host.docker.internal\"\ndocker compose up --build\n```\n\nIf your host audio server requires a specific PortAudio device, also set:\n\n```powershell\n$env:FAKE_HDD_AUDIO_DEVICE = \"0\"\n```\n\nIf you do not already have a PulseAudio/PipeWire network endpoint on the host, use the native host run instead of Docker for audible playback.\n\nHeadless Docker smoke test for WebDAV plus rendered audio artifacts:\n\n```powershell\nuv run python -m tools.docker_webdav_audio_smoke\n```\n\nThis writes temporary artifacts under `.runtime/docker-e2e/`, uploads and downloads through WebDAV, then verifies both the event trace and tee WAV are nonempty.\nIt also exercises large transfer, many-small-file, fragmented, and large-directory-listing workloads.\n\n## Repo Layout\n\n- [clatterdrive](clatterdrive): packaged application code\n- [tools](tools): repo-local generators, profilers, trace exporters, audits, and fitting utilities\n- [samples](samples): checked-in demo WAVs\n- [docs](docs): GitHub Pages demo assets plus the internal MH tuning lab\n- [tests](tests): test suite\n\nKey files:\n\n- [main.py](main.py): thin startup entrypoint\n- [clatterdrive/app.py](clatterdrive/app.py): server startup and wiring\n- [clatterdrive/webdav/provider.py](clatterdrive/webdav/provider.py): WebDAV interception layer\n- [clatterdrive/hdd/latency.py](clatterdrive/hdd/latency.py): HDD timing and power-state model\n- [clatterdrive/hardware_priors.py](clatterdrive/hardware_priors.py): source-backed hardware priors and bounded calibration helpers\n- [clatterdrive/audio/core.py](clatterdrive/audio/core.py): audio plant and render logic\n- [clatterdrive/audio/physics.py](clatterdrive/audio/physics.py): labeled physical-state, plausible-model, and artistic-calibration audio primitives\n- [clatterdrive/audio/engine.py](clatterdrive/audio/engine.py): runtime audio shell\n\n## Development\n\nOne-command local PR gate:\n\n```powershell\nscripts\\ci.ps1\n```\n\nBackend-only quick gate:\n\n```powershell\nscripts\\ci.ps1 -PythonOnly -SkipE2E\n```\n\nOptional desktop and mapped-drive E2E additions:\n\n```powershell\nscripts\\ci.ps1 -IncludeUiE2E -IncludeMappedDrive\n```\n\nIndividual checks:\n\n```powershell\nscripts\\bootstrap.ps1\nscripts\\lint.ps1\nscripts\\test.ps1\nscripts\\test-e2e.ps1\nscripts\\test-ui.ps1\n```\n\nmacOS checks:\n\n```bash\nbash scripts/bootstrap-macos.sh\nbash scripts/test-macos.sh\nbash scripts/test-macos-e2e.sh --include-mount\n```\n\nRelease-package E2E:\n\n```powershell\nscripts\\test-release.ps1 -IncludeUiE2E -IncludeMappedDrive\n```\n\nDouble-click/Command Prompt wrappers are available beside each PowerShell script, for example:\n\n```cmd\nscripts\\ci.cmd\n```\n\nCI runs the same scripts on GitHub Actions. The normal PR workflows cover Linux/Windows Python lint/type/unit tests, backend E2E, WPF launcher build/tests, PyInstaller packaging, packaged smoke, macOS SwiftUI launcher tests, macOS DMG packaging, `mount_webdav` E2E, and Docker smoke.\n\nThe nightly/release desktop workflow is for checks that need real Windows desktop and WebClient behavior: extracted-package launcher FlaUI, bundled-backend start/stop, `net use`, mapped-drive write/read/delete, and clean shutdown. That runner can be a small Windows 11 VM or a physical Windows machine. Windows Docker containers are fine for backend/headless smoke, but they are not an accurate replacement for WPF UI automation or WebClient mapped-drive behavior because they do not provide the same interactive desktop and shell integration.\n\nRegenerate the public sample clips:\n\n```powershell\nuv run python -m tools.generate_readme_demo_samples\n```\n\nAudio traces and audits:\n\n```powershell\nuv run python -m tools.trace_audio_scenarios\nuv run python -m tools.audit_audio_stack\n```\n\nInternal calibration tooling:\n\n```powershell\nuv run python -m tools.fit_mh_reference\nuv run python -m tools.calibrate_ironwolf_physics\n```\n\nNotes:\n\n- `tools.fit_mh_reference` is internal calibration tooling for the MH thrash lab, not part of the normal runtime path.\n- It requires a local reference bundle under `.runtime/local_refs/` that is intentionally not tracked in git.\n- `tools.calibrate_ironwolf_physics` fits the Seagate IronWolf Pro 16TB source-backed hardware prior and writes its report under `.runtime/`.\n\n## Limits\n\n- This is not a real block device or kernel filesystem.\n- Out-of-band edits to the backing tree are only partially reconciled.\n- WebDAV locks are in-memory and exist to satisfy clients like the Windows WebClient; they are not persisted across restart.\n- The project is physically structured and benchmark-gated, but it is not yet a reference-grade HDD/acoustics model without measured drive constants and transfer functions.\n- The audio model is explicitly labeled by tier: physical state covers runtime variables such as spindle phase/RPM and actuator position/velocity; physical model covers normalized mechanics, source routing, modal radiation, and gain staging; artistic calibration is kept as an audit tier for any future nonphysical exception.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexyorke%2Fclatterdrive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexyorke%2Fclatterdrive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexyorke%2Fclatterdrive/lists"}