Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 0 additions & 43 deletions .github/dependabot.yml

This file was deleted.

53 changes: 53 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"enabledManagers": ["github-actions", "npm", "cargo"],
"timezone": "America/Los_Angeles",
"schedule": ["* * * * 1"],
"packageRules": [
{
"matchManagers": ["npm", "cargo"],
"rangeStrategy": "update-lockfile"
},
{
"matchManagers": ["github-actions"],
"groupName": "github-actions",
"groupSlug": "github-actions",
"separateMajorMinor": false
},
{
"matchManagers": ["npm"],
"matchDepTypes": ["dependencies"],
"groupName": "npm-production",
"groupSlug": "npm-production",
"separateMajorMinor": false
},
{
"matchManagers": ["npm"],
"matchDepTypes": ["devDependencies"],
"groupName": "npm-development",
"groupSlug": "npm-development",
"separateMajorMinor": false
},
{
"matchManagers": ["cargo"],
"groupName": "cargo",
"groupSlug": "cargo",
"separateMajorMinor": false
},
{
"matchManagers": ["npm", "cargo"],
"matchUpdateTypes": ["patch"],
"minimumReleaseAge": "1 day"
},
{
"matchManagers": ["npm", "cargo"],
"matchUpdateTypes": ["minor"],
"minimumReleaseAge": "3 days"
},
{
"matchManagers": ["npm", "cargo"],
"matchUpdateTypes": ["major"],
"minimumReleaseAge": "14 days"
}
]
}
28 changes: 26 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,33 @@ jobs:
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

- name: Install system dependencies
# apt mirrors are flaky: a healthy run finishes in <1min, but a degraded
# Azure mirror can dribble bytes for 15-80min, and retrying the SAME mirror
# doesn't help. Wrap each apt call in `timeout` (turns "slow" into a failed
# attempt) and rotate the mirror host between attempts so a sustained
# single-mirror problem is escaped. Azure first (fast, same datacenter),
# archive.ubuntu.com as fallback. --no-install-recommends trims the download.
timeout-minutes: 10
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev
mirrors=(azure.archive.ubuntu.com archive.ubuntu.com)
src_files=(/etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list)
for i in 0 1 2; do
host=${mirrors[$(( i % ${#mirrors[@]} ))]}
for f in "${src_files[@]}"; do
[ -f "$f" ] && sudo sed -i -E "s|https?://[a-z.]*archive\.ubuntu\.com/ubuntu|http://$host/ubuntu|g" "$f"
done
echo "::group::apt attempt $((i + 1)) via $host"
if sudo timeout 120 apt-get update -q && \
sudo timeout 180 apt-get install -y -q --no-install-recommends \
libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev; then
echo "::endgroup::"; exit 0
fi
echo "::endgroup::"
echo "::warning::apt via $host failed or timed out; rotating mirror, retrying in 15s"
sleep 15
done
echo "::error::apt failed after 3 attempts across mirrors"
exit 1

- name: Install npm dependencies
run: pnpm install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/security-audit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
with concrete evidence — file path and line number, API
response excerpt, or command output. Then do a qualitative
pass over `.github/workflows/`, `.config/tend.yaml`,
`.github/dependabot.yml`, `scripts/`, and any code that
`.github/renovate.json`, `scripts/`, and any code that
touches secrets.

The default `$GH_TOKEN` in this step is a workflow
Expand Down
10 changes: 5 additions & 5 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Dormouse is a terminal, so users trust it with shells, source trees, credentials

## Dependency Supply Chain

Dormouse keeps its runtime dependency surface intentionally small. We add dependencies only when they are necessary, and we expect dependency changes to justify their value against their supply-chain risk. We use maturity gating inside our pnpm configuration and also inside our [dependabot configuration](.github/dependabot.yml).
Dormouse keeps its runtime dependency surface intentionally small. We add dependencies only when they are necessary, and we expect dependency changes to justify their value against their supply-chain risk. We use maturity gating inside our pnpm configuration and also inside our [Renovate configuration](.github/renovate.json).

Every dependency shipped in the end-user application is listed at <https://dormouse.sh/supply-chain>. This includes:

Expand All @@ -16,19 +16,19 @@ Every dependency shipped in the end-user application is listed at <https://dormo

Those dependency snapshots are generated from the lockfiles and reviewed as part of release work. If a production dependency is added, removed, or upgraded, the dependency lists must be regenerated and committed.

The standalone app ships a Node.js runtime binary (`standalone/src-tauri/build.rs` copies it into the bundle as a Tauri sidecar). Its version is pinned exactly in the root `package.json` under `devEngines.runtime.version`, and the build is the authority: `build.rs` runs `--version` on the binary it is about to bundle and fails the build unless it matches the pin. On Windows the build then flips one byte of the bundled `node.exe` — the PE Optional Header's `Subsystem` field from `IMAGE_SUBSYSTEM_WINDOWS_CUI` (3) to `IMAGE_SUBSYSTEM_WINDOWS_GUI` (2) — to suppress Windows Terminal's default-terminal handoff, which would otherwise spawn a stray terminal window behind the app. The version check runs before the byte flip and the patch leaves Node.js semantics unchanged (Node reads its stdio handles from `STARTUPINFO`, which is subsystem-agnostic); the bundled `node.exe` is therefore not byte-identical to the upstream archive — it differs at exactly the documented 2-byte field. The supply-chain page reads the same pin, so the version disclosed there provably equals the runtime users receive — it cannot drift to whatever Node happened to be on the build machine's PATH. Locally, pnpm honors `devEngines` (`onFail: "download"`) so scripts run under the pinned Node; CI extracts the same field to drive `actions/setup-node`. The version is a deliberate, manual pin (no Dependabot ecosystem tracks it); the workflows that do not bundle the runtime are free to track the latest `22`.
The standalone app ships a Node.js runtime binary (`standalone/src-tauri/build.rs` copies it into the bundle as a Tauri sidecar). Its version is pinned exactly in the root `package.json` under `devEngines.runtime.version`, and the build is the authority: `build.rs` runs `--version` on the binary it is about to bundle and fails the build unless it matches the pin. On Windows the build then flips one byte of the bundled `node.exe` — the PE Optional Header's `Subsystem` field from `IMAGE_SUBSYSTEM_WINDOWS_CUI` (3) to `IMAGE_SUBSYSTEM_WINDOWS_GUI` (2) — to suppress Windows Terminal's default-terminal handoff, which would otherwise spawn a stray terminal window behind the app. The version check runs before the byte flip and the patch leaves Node.js semantics unchanged (Node reads its stdio handles from `STARTUPINFO`, which is subsystem-agnostic); the bundled `node.exe` is therefore not byte-identical to the upstream archive — it differs at exactly the documented 2-byte field. The supply-chain page reads the same pin, so the version disclosed there provably equals the runtime users receive — it cannot drift to whatever Node happened to be on the build machine's PATH. Locally, pnpm honors `devEngines` (`onFail: "download"`) so scripts run under the pinned Node; CI extracts the same field to drive `actions/setup-node`. The version is a deliberate, manual pin (no automated ecosystem tracks it); the workflows that do not bundle the runtime are free to track the latest `22`.

- FAIL IF `node website/scripts/generate-deps.js` changes `website/src/data/dependencies-npm.json`, `website/src/data/dependencies-cargo.json`, or `website/src/data/dependencies-runtime.json` when run from a clean checkout.
- FAIL IF the root `package.json` is missing `devEngines.runtime.version`, or its value is not an exact Node.js version (a bare major such as `22` is not acceptable; it must be `MAJOR.MINOR.PATCH`).
- FAIL IF `standalone/src-tauri/build.rs` no longer verifies that the bundled Node.js binary matches `package.json`'s `devEngines.runtime.version` (this verification is what makes the disclosed runtime version provable).
- FAIL IF the `build-standalone` job in `.github/workflows/release.yml` does not install the pinned runtime by reading `devEngines.runtime.version` from `package.json` and passing it to `actions/setup-node` (other jobs may pin `node-version` inline since their interpreter is never bundled).
- FAIL IF `pnpm-workspace.yaml` is missing `minimumReleaseAge: 1440`.
- FAIL IF `.github/dependabot.yml` is missing npm coverage for `/` or Cargo coverage for `/standalone/src-tauri`.
- FAIL IF `.github/dependabot.yml` is missing dependency cooldown windows.
- FAIL IF `.github/renovate.json` is missing `npm` or `cargo` from `enabledManagers` (npm covers `/`; cargo covers `/standalone/src-tauri`).
- FAIL IF `.github/renovate.json` is missing `minimumReleaseAge` package rules for `npm`/`cargo` updates (the Renovate equivalent of dependency cooldown windows).

## GitHub Actions Policies

GitHub Actions are always pinned by commit hash, not version tag. Dependabot will update the hashes as necessary.
GitHub Actions are always pinned by commit hash, not version tag. Renovate will update the hashes as necessary.

**Agent-managed workflows** are `tend-*.yaml`, `workflow-audit.yaml`, and `security-audit.yaml`. They implement the repo's automation and self-audit infrastructure, and are exempt from the two rules below because they need to modify issues, PRs, or code, or fetch an OIDC token. Their bounded scope is defined in the "Automated Maintainer" section.

Expand Down
Loading