🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

🛠 ResQ Dev Setup

One command to bootstrap the entire ResQ development environment.

LicenseShellNixMake


⚡ Onboarding in one curl

# Default clone target is ~/resq; override with RESQ_DIR=...
curl -fsSL https://get.resq.software | sh

This endpoint is not a redirect to main. It serves one pinned commit and SHA-256 verifies every byte before sending it; on mismatch it returns 502 with a shell snippet that exits non-zero, never installer bytes.

Merging to main does not by itself change what you get here. What you receive is decided by the pinned digests in worker/src/index.ts, so it changes only when a reviewed pin bump merges and deploys. Cutting a release does not move this endpoint either: the release publishes the artifacts, and a separate reviewed pull request repoints the pins at them.

You do not have to take that on faith. Verify against a single immutable release, so the installer and the digest list cannot describe different versions:

REL=https://get.resq.software/v0.4.3
curl -fsSL "$REL/SHA256SUMS"
curl -fsSL "$REL/install.sh"| sha256sum # must match the install.sh line# Or just read it before running it
curl -fsSL "$REL/install.sh" -o install.sh
less install.sh && sh install.sh

The unversioned paths work too, but a deploy landing between the two requests would leave you comparing an installer from one release against digests from another.

Pin to an exact release, which never changes:

curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

What happens, in order:

  1. Installs gh (GitHub CLI) if missing
  2. Authenticates with GitHub
  3. Installs Nix via the Determinate Systems installer for reproducible toolchains
  4. Lets you choose which repo to clone
  5. Runs nix develop to build the dev environment
  6. Installs the canonical git hooks (delegating to resq pre-commit)
  7. Offers to scaffold a repo-type-specific local-pre-push (auto-detects Rust / Python / Node / .NET / C++ / Nix)

Run unattended (CI / provisioning):

REPO=npm YES=1 RESQ_DIR=/srv/work \
curl -fsSL https://get.resq.software | sh

For provisioning, prefer a version-pinned URL so a later release cannot change what your machines install without you deciding to:

REPO=npm YES=1 curl -fsSL https://get.resq.software/v0.4.3/install.sh | sh

📦 Repositories

RepoWhatLanguages
programsSolana on-chain programsRust (Anchor)
dotnetClean/Hexagonal building blocks — the frame, not the domainC#
dotnet-sdk.NET client librariesC#
pypiPython packages (MCP + DSA)Python
cratesRust workspace (CLI + DSA + resq binary)Rust
npmTypeScript packages (UI + DSA)TypeScript
vcpkgC++ librariesC++
viz3D visualization — Three.js/Cesium web + UnityTypeScript / C#
docsDocumentation siteMDX
devThis repo — install scripts and onboardingShell / PowerShell

Public repos sync to the monorepo automatically.

This table is the public, non-archived, non-fork set (excluding .github), and it is the same list the installers offer. landing used to appear here and in the installer menu; it is now private, so choosing it failed at clone time. The rule is mechanical on purpose — repo-drift.yml re-derives it from the GitHub API and fails when this list and reality disagree, so the next such change is caught by CI rather than by whoever runs the installer next.


🛠 Standalone scripts

Each script can be run on its own without going through the full onboarding flow.

ScriptUse caseBootstrap
install.sh / install.ps1Full onboarding — installs prereqs, clones a repo, sets up dev env + hookscurl -fsSL https://get.resq.software | sh
install-hooks.sh / install-hooks.ps1Drop the canonical git hooks into any repo. Asks to scaffold local-pre-push if resq is on PATHcd <repo> && curl -fsSL https://get.resq.software/hooks.sh | sh
install-resq.shInstall the resq CLI binary from the latest GitHub Release (SHA256-verified). Falls back to cargo install --git --rev <pinned commit> when no release asset matches — today that fallback is the only path, since no resq-cli-v* release exists yetcurl -fsSL https://get.resq.software/resq.sh | sh

Every one of these is served pinned and hash-verified, and each has a version-locked form — https://get.resq.software/v0.4.3/hooks.sh and so on. https://get.resq.software/SHA256SUMS lists the digest of all of them, keyed by the route name you fetch, so it works directly with sha256sum -c:

curl -fsSLO https://get.resq.software/SHA256SUMS
curl -fsSLO https://get.resq.software/install.sh
curl -fsSLO https://get.resq.software/hooks.sh
sha256sum --ignore-missing -c SHA256SUMS

Be clear about what that check is worth, because it is easy to overstate.

SHA256SUMS and the artifacts both come from get.resq.software. Comparing them detects a truncated download or corruption in transit; it does not detect a compromised endpoint, because anything able to alter the artifact can serve a manifest that agrees with it. Two files from one origin are one source, not two, and this check has no independent trust anchor.

The pinning described above defends a different hop. The Worker verifies what GitHub returns against digests baked into its deploy, so a tampered raw.githubusercontent.com response — or anything altered between the Worker and GitHub — is refused before a byte reaches you. That is real, and it is not the same as protecting you from us.

Closing the remaining gap needs a signature verifiable offline against a public key rather than against our word. Tracked in #58; until it lands, treat these digests as an integrity check, not proof of origin.

Common env vars across all of them:

  • YES=1 — skip prompts (CI / provisioning)
  • GIT_HOOKS_SKIP=1 — disable installed hooks for a session
  • RESQ_SKIP_LOCAL_SCAFFOLD=1 — opt out of the local-pre-push scaffold prompt

To pin a revision, use a version-locked URL rather than an environment variable. (This section previously documented RESQ_DEV_REF=<sha|tag>; no script has ever implemented it, so it silently did nothing.)

install.sh additionally honours REPO, RESQ_DIR, RESQ_BIN_DIR, SKIP_RESQ_CLI and NO_COLOR — run sh install.sh --help for the current list.


🚀 Quick Start per Repo

RepoLanguageSetup
programsRust / Anchoranchor build
dotnetC# / .NET 9dotnet restore
dotnet-sdkC# / .NET 9dotnet restore
pypiPythonuv sync
cratesRustcargo build
npmTypeScriptbun install
vcpkgC++cmake --preset default
vizTypeScript / C#bun install · dotnet restore
docsMDX / Mintlifymintlify dev
devShell / PowerShell / TypeScriptshellcheck install.sh · cd worker && npm ci && npm test

🏷 Releasing

One authored value, one action:

echo 0.4.4 > VERSION # whatever comes next; 0.4.3 is current
sh bin/stamp.sh # propagates VERSION + hook digests into both installers# open a PR, merge it — that is the release

Do not tag by hand. Merging a VERSION change to main is what releases: CI validates it, creates the tag itself, publishes the Release and SHA256SUMS, and opens a pin-bump PR. Merging that is what changes the bytes https://get.resq.software serves.

Two gates, both ordinary code review:

mergingchanges
a VERSION bumpwhat is published as a release
the pin-bump PRwhat users actually receive

Everything else is generated. bin/stamp.sh --check runs on every pull request and verifies by regeneration, so a stamped value cannot be forgotten — forgetting it is a diff. Values marked GENERATED should never be hand-edited.

No Cloudflare credential is stored in GitHub. Deployment is Cloudflare Workers Builds pulling from this repository, and worker-live afterwards checks that the endpoint serves exactly what main declares — hashing the bytes on the wire, not trusting the Worker's own claim about them.

AGENTS.md has the full reasoning, including why tagging is an output of the release rather than its trigger.

🔎 What CI actually verifies

Most of this repo is two shell scripts and a Worker. Most of the engineering is in refusing to take anything on trust — including its own claims. The first two groups run on pull requests; the last runs after a deploy.

The distribution chain

checkwhat would otherwise rot silently
hook digests re-fetched from the pinned crates commita stale pin makes every fresh onboard fail a checksum
pinned crates commits are ancestors of mastera rebased-away or mistyped SHA breaks cargo install --rev
install-resq.sh pins its cargo fallbackan unpinned build of a moving branch, reached from curl | sh
Nix and Bun installer digests re-checked against the live URLsupstream changing what we execute
stamp.sh --checka generated version or hook digest left unstamped
gen-pins missing-PINS guard is reachablethe guard itself regressing, which has happened

The code

shellcheck at warning severity, plus an explicit POSIX-dialect check on install.sh — every "In POSIX sh, X is undefined" rule is warning-severity, so gating at error cannot catch a bashism entering the curl-piped installer.

The Worker is TypeScript, typechecked under strict with noUncheckedIndexedAccess, linted by Biome, and covered by a suite that fetches from live GitHub — so a rotted digest surfaces as a failing test rather than a 502 in production. It ships zero runtime dependencies.

windows-smoke loads the PowerShell installers on a Windows runner under both Windows PowerShell 5.1 and pwsh 7. Parsing them on Linux, which is all that happened before, cannot catch a variable that only exists in PowerShell 6+.

After deploy

worker-live fetches from the real endpoint and hashes the bytes on the wire against what main declares — it does not trust the Worker's own headers about what it served.

repo-drift re-derives the repository list from the GitHub API and fails when this README, install.sh and install.ps1 disagree with reality.

Contributor guide

Every ResQ repo ships an AGENTS.md at the root — the canonical plain-text dev guide. That's where the build/test/lint commands, architecture notes, and standards for that specific repo live. Read it first.

Org-wide guidance (onboarding, hooks contract, commit format, PR process) lives in the .github org repo: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md. Every public repo falls back to those automatically.

🔧 Toolchain

Everything is pinned via Nix flakes. No "works on my machine" issues.

LanguageTools
Rustrustc, cargo, clippy, rustfmt, cargo-deny
TypeScriptbun, node, turbo
Pythonpython 3.12, uv, ruff, mypy
C#dotnet 9
C++gcc, cmake, clang-format
Protobufbuf, protoc
Solanasolana-cli, anchor

✅ Quality gates — canonical git hooks

Six hook shims live in resq-software/crates — embedded in the resq binary and served at a stable raw URL. install-hooks.sh picks the best path automatically:

  1. resq on PATH → calls resq hooks install, which scaffolds the 6 canonical hooks from the templates embedded in the binary. Offline, versioned with the installed resq.
  2. No resq → fetches them over HTTPS from a pinned commit in resq-software/crates, and verifies each of the six against a digest baked into the installer before any of them is written.

That second path used to fetch from master — a mutable branch — with no verification at all, which meant six files that git executes on every commit and push came from wherever that branch happened to point. It is now a commit SHA, which cannot be repointed, plus a SHA-256 check that fails closed: on any mismatch nothing is installed.

Precisely what that buys, since it is easy to overclaim:

  • A failed verification publishes nothing. All six are downloaded and checked in a temp directory first, so a mismatch on the last file cannot leave the first five installed and active.
  • Each hook lands by atomic rename, so an interrupt during publication can never leave a truncated file that git would still execute.
  • The set is not atomic. An interrupt between files can leave a mix of old and new hooks, each individually valid. Making the set atomic would mean swapping the whole directory, which would discard the local-<hook> customisations this design keeps there — a worse trade. Re-running the installer is the fix.

RESQ_CRATES_REF still exists for testing an unreleased hook change, but a pinned digest cannot describe an arbitrary ref, so using it requires RESQ_ALLOW_UNVERIFIED=1 and says so.

required.yml re-fetches all six from the pinned commit on every pull request and fails if the digests in either installer disagree — so a stale pin is a red check rather than a broken onboard.

The hooks delegate logic back to the resq binary (resq pre-commit, etc.), so updates roll out via install-resq.sh without editing every repo. That installer prefers a digest-verified release asset and otherwise builds from a pinned commit (cargo install --git --rev); an unpinned build of the default branch requires RESQ_ALLOW_UNVERIFIED=1.

HookWhat it gates
pre-commitresq pre-commit — copyright, secrets, audit, polyglot format
commit-msgConventional Commits + ! marker; blocks WIP: / fixup! / squash! on main
prepare-commit-msgPrepends [TICKET-123] from branch name
pre-pushForce-push guard, branch-naming convention (feat/, fix/, …, changeset-release/* allowed)
post-checkout / post-mergeNotifies on lock-file changes (Cargo, bun, uv, flake)

Each hook then dispatches to .git-hooks/local-<hook-name> (if executable) — the only place a repo commits hook customization. Generate one with the right language template:

resq hooks scaffold-local --kind auto # detects rust/python/node/dotnet/cpp/nix

resq hooks doctor reports drift, resq hooks update re-syncs from the embedded canonical, resq hooks status prints a one-line shell-friendly summary.

The canonical content lives in exactly one place: crates/resq-cli/templates/git-hooks/. The crates repo's own .git-hooks/ (for dog-fooding) is kept identical via hooks-sync.yml. The dev/ repo used to ship a third copy and was retired in Phase 4 — install-hooks.sh now fetches from the crates source (or lets resq hooks install do it offline). Bats + Rust integration tests cover the hook behavior end-to-end.

📄 License

Apache License 2.0

About

One-command developer environment bootstrap and onboarding for ResQ Software. Features secure SHA256-verified installers, Nix-powered reproducible toolchains, git hooks, and setup scripts.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages