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
27 changes: 27 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# NOTE: Baseline coverage is ~29% (27.85% on develop as of 2026-Q2).
# The project/patch range [60%, 80%] below is aspirational for Phase 3
# coverage push (post-fast-check + property tests + Vitest utility tests).
# CI's ci.yml intentionally runs with "no thresholds, no gates", so this
# file does NOT currently block PRs — it only affects Codecov status
# badges. Update the range after Phase 2 coverage lands.
codecov:
notify:
wait_for_ci: true
require_ci_to_pass: true
comment:
behavior: default
layout: reach,diff,flags,tree
show_carryforward_flags: false
coverage:
precision: 2
range:
- 60.0
- 80.0
round: down
status:
changes: false
default_rules:
flag_coverage_not_uploaded_behavior: include
patch: true
project: true
slack_app: true
32 changes: 32 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.rs]
indent_size = 4

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

# Makefiles require tabs (syntax)
[Makefile]
indent_style = tab

# Nix files: prettier handles formatting, allow 2-space indent
[*.nix]
indent_style = space
indent_size = 2

# JSON files: Prettier handles formatting with 2-space indent
[*.json]
indent_style = space
indent_size = 2
17 changes: 17 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Security-sensitive routes — require review
# Auth: webauthn, passkey, MFA, OIDC, TOTP
/server/server/api/v1/auth/ @BillyOutlast
/server/server/internal/auth/ @BillyOutlast

# Metadata providers — external HTTP integration, complex fallthrough
/server/server/internal/metadata/ @BillyOutlast

# Nitro server core (server/ dir)
/server/server/ @BillyOutlast

# Build, deps, CI
/server/.env.example @BillyOutlast
/.github/workflows/ @BillyOutlast
/AGENTS.md @BillyOutlast
/CLAUDE.md @BillyOutlast
/CONTRIBUTING.md @BillyOutlast
154 changes: 154 additions & 0 deletions .github/actions/rust-ci/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Rust CI
description: >
Reusable Rust CI steps for Drop monorepo workspaces.
Handles checkout, toolchain, cache, system deps, fmt, clippy/check,
tests, coverage (llvm-cov + Codecov), and advisory cargo-audit.

inputs:
working-directory:
required: true
description: >
Working directory for cargo commands.
Example: libraries/droplet, cli, desktop/src-tauri

cache-workspaces:
required: true
description: >
Workspace mapping for swatinem/rust-cache.
Example: "./libraries/droplet -> target"

system-dependencies:
required: false
description: >
Shell commands to install system dependencies (apt-get etc.).
Omit or leave empty when no system deps are needed.
default: ""

lint-command:
required: true
description: >
Cargo lint/build command.
Example: cargo clippy --all-targets --all-features -- -D warnings

coverage-path:
required: true
description: >
Path to coverage.lcov relative to repo root (for Codecov upload).
Example: libraries/droplet/coverage.lcov

test-command:
required: false
description: Cargo test command.
default: cargo test --all-features --all --verbose
Comment on lines +39 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Default cargo test command uses deprecated --all flag and may not always match workspace intent

The default test-command runs cargo test --all-features --all --verbose. --all is deprecated (now equivalent to --workspace) and triggers warnings in newer Cargo versions, and some workspaces may not want --all-features by default. Consider changing the default to cargo test --workspace --all-features --verbose (or just cargo test --workspace), and let callers override it when they need different feature or target scopes.

Suggested change
test-command:
required: false
description: Cargo test command.
default: cargo test --all-features --all --verbose
test-command:
required: false
description: Cargo test command.
default: cargo test --workspace --all-features --verbose


test-continue-on-error:
required: false
description: Whether to continue on test failure.
default: "false"

lint-continue-on-error:
required: false
description: Whether to continue on lint failure.
default: "false"

components:
required: false
description: Rust toolchain components (comma-separated).
default: rustfmt, clippy

runs:
using: composite
steps:
# ── Setup ──────────────────────────────────────────────────────
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4fd1da8b0805d2d2e936788875a7d65dbd677dc2
with:
toolchain: nightly
components: ${{ inputs.components }}
Comment on lines +63 to +67

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .github/actions/rust-ci/action.yml (excerpt) =="
nl -ba .github/actions/rust-ci/action.yml | sed -n '50,85p'

echo
echo "== rust-toolchain files =="
git ls-files | rg '(^|/)(rust-toolchain(\.toml)?|Cargo\.toml)$' || true

echo
echo "== nightly references =="
rg -n "toolchain:\s*nightly|nightly" .github/actions/rust-ci action . -g '!*target*' -g '!*.lock' || true

Repository: BillyOutlast/drop

Length of output: 246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .github/actions/rust-ci/action.yml (excerpt) =="
sed -n '50,85p' .github/actions/rust-ci/action.yml | cat -n

echo
echo "== rust-toolchain files =="
git ls-files | rg '(^|/)(rust-toolchain(\.toml)?|Cargo\.toml)$' || true

echo
echo "== nightly references =="
rg -n "toolchain:\s*nightly|nightly" .github/actions/rust-ci . -g '!*target*' -g '!*.lock' || true

Repository: BillyOutlast/drop

Length of output: 3075


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in cli/rust-toolchain.toml desktop/src-tauri/rust-toolchain.toml; do
  echo "== $f =="
  sed -n '1,40p' "$f" | cat -n
  echo
done

echo "== action.yml context =="
sed -n '1,120p' .github/actions/rust-ci/action.yml | cat -n

Repository: BillyOutlast/drop

Length of output: 5041


Pin the nightly toolchain everywhere. toolchain: nightly still floats to the latest compiler, and the existing cli/rust-toolchain.toml and desktop/src-tauri/rust-toolchain.toml do the same. Use a dated nightly, or pin those files to a specific nightly, so CI stays reproducible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/rust-ci/action.yml around lines 63 - 67, Pin the Rust
nightly version consistently in the “Install Rust toolchain” step and the
existing cli and desktop rust-toolchain.toml configurations. Replace floating
nightly references with the same dated nightly value across all three locations
while preserving the configured components.


- name: Rust cache
# pinned to v2
uses: swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae
with:
workspaces: ${{ inputs.cache-workspaces }}

# ── System dependencies ────────────────────────────────────────
- name: Install system dependencies
if: ${{ inputs.system-dependencies != '' }}
shell: bash
run: ${{ inputs.system-dependencies }}

# ── Format ─────────────────────────────────────────────────────
- name: Check formatting
shell: bash
working-directory: ${{ inputs.working-directory }}
run: cargo fmt --all -- --check

# ── Lint / Build ───────────────────────────────────────────────
- name: Lint / Build
shell: bash
working-directory: ${{ inputs.working-directory }}
continue-on-error: ${{ inputs.lint-continue-on-error == 'true' }}
run: ${{ inputs.lint-command }}

# ── Test ───────────────────────────────────────────────────────
- name: Run tests
shell: bash
working-directory: ${{ inputs.working-directory }}
continue-on-error: ${{ inputs.test-continue-on-error == 'true' }}
run: ${{ inputs.test-command }}

# ── Coverage ───────────────────────────────────────────────────
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
Comment on lines +102 to +103

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '90,135p' .github/actions/rust-ci/action.yml

Repository: BillyOutlast/drop

Length of output: 2052


🏁 Script executed:

set -euo pipefail

for ref in cargo-llvm-cov cargo-audit; do
  echo "== $ref =="
  curl -fsSL "https://api.github.com/repos/taiki-e/install-action/git/refs/tags/$ref" \
    | python3 - <<'PY'
import sys, json
data = json.load(sys.stdin)
print("ref:", data.get("ref"))
print("type:", data.get("object", {}).get("type"))
print("sha:", data.get("object", {}).get("sha"))
PY
  echo
done

Repository: BillyOutlast/drop

Length of output: 830


🌐 Web query:

taiki-e/install-action cargo-llvm-cov cargo-audit GitHub Action tag release immutable commit

💡 Result:

The GitHub Action taiki-e/install-action uses both @v (semantic version) tags and convenience tags (e.g., @cargo-audit) that are updated with each release [1][2][3]. While all GitHub Releases for this action are immutable [1][2][3], the tags pointing to these releases behave differently regarding stability: 1. Immutable vs. Mutable Tags: GitHub Actions generally allow tags to be moved (mutable) or locked (immutable via GitHub's "Immutable Releases" feature) [4][5][6]. The taiki-e/install-action repository documentation explicitly states that all its releases are immutable [1][2]. 2. Stability and Pinning: To enhance workflow stability and security, the action author recommends pinning the version using the @v... tag or its corresponding full commit SHA [1][2][7]. Pinning by hash is considered the most secure way to ensure the workflow uses a specific, unchanging version of the action [8]. 3. Convenience Tags: The use of convenience tags like @cargo-audit is discouraged for pinning purposes [1][2]. These tags are updated with each release; using them by hash can cause issues because they may refer to a commit that is not present on the repository if a new version is released [1][2][3]. 4. Dependency Cooldown: When using convenience tags (@<tool_name> or @latest) to install tools, a "dependency cooldown" (a delay of one to a few days) is applied to mitigate supply chain risks [1][2][3]. Pinning the action's @v... tag or commit SHA also pins the version of the tool being installed, which may subject the tool to a longer cooldown period [1][2][7]. In summary, for maximum stability and security, you should pin taiki-e/install-action to a specific version tag (e.g., @v2.81.11) or its full-length commit SHA rather than using mutable convenience tags [9][2][8].

Citations:


SHA-pin both taiki-e/install-action uses. @cargo-llvm-cov and @cargo-audit are moving convenience tags; pin both install steps to immutable full commit SHAs instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/rust-ci/action.yml around lines 102 - 103, Update both
taiki-e/install-action steps in the workflow, including the cargo-llvm-cov and
cargo-audit uses, to reference immutable full commit SHAs instead of the moving
convenience tags. Preserve each step’s existing action and purpose while
applying SHA pinning consistently.


- name: Generate code coverage
# Was continue-on-error: true — switched to false so coverage
# failures surface in CI. Codecov upload below still uses
# fail_ci_if_error: false, so generation failure is visible
# but won't block the pipeline.
continue-on-error: false
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
cargo llvm-cov --all-features --workspace \
--codecov --output-path coverage.lcov

- name: Upload coverage to Codecov
# pinned to v5
uses: codecov/codecov-action@04b047e8bb82a0c002c8312c1c880fbc6a999d45
with:
files: ${{ inputs.coverage-path }}
fail_ci_if_error: false

# ── Security audit ─────────────────────────────────────────────
- name: Install cargo-audit
uses: taiki-e/install-action@cargo-audit

- name: Audit dependencies
# cargo audit exits 1 on ANY advisory. Keep non-blocking here; the
# follow-up step fails only when a NEW (un-ignored) advisory is found
# that is not already documented in security/risk-register.yaml.
continue-on-error: true
shell: bash
working-directory: ${{ inputs.working-directory }}
run: cargo audit --json > /tmp/cargo-audit.json 2>/dev/null || true

- name: Check for new Rust advisories
# Run on success or failure of the audit step, but not on cancel.
# Use --min-severity high for cargo to catch DoS-class advisories
# (RUSTSEC-2026-0194/0195 in quick-xml are severity "high"); the
# script handles missing/empty/malformed JSON and missing risk
# register gracefully (exits 0 with a warning in both cases).
# Resolve the script via $GITHUB_WORKSPACE because this composite
# action is invoked with working-directory set to a sub-crate
# (cli/, desktop/src-tauri/, libraries/droplet/), where a relative
# `scripts/check-new-vulns.cjs` would not exist.
if: success() || failure()
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
node "$GITHUB_WORKSPACE/scripts/check-new-vulns.cjs" \
--format cargo \
--json /tmp/cargo-audit.json \
--min-severity high
158 changes: 158 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
version: 2

registries:
# Allow Dependabot to resolve private/skipped registry hosts from lockfiles
# (e.g. buf schema registry, GitHub Packages). Public registries need no entry.
npm-pkg-github:
type: "npm-registry"
url: "https://npm.pkg.github.com"
token: "${{secrets.GITHUB_TOKEN}}"
Comment on lines +3 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Dependabot dependabot.yml registries key must be referenced by update entry, GITHUB_TOKEN not available for private registries

💡 Result:

The error message "registries key must be referenced by update entry" occurs in your dependabot.yml file because you have defined a private registry at the top level of your configuration, but have not explicitly instructed Dependabot to use it within your specific update ecosystem block [1][2][3]. To resolve this, ensure your dependabot.yml is structured as follows: 1. Top-Level Definition: Define your private registry details at the root level under the registries key [1][2]. 2. Reference in Updates: Within the updates block for your specific package-ecosystem, you must add the registries key and either list the specific registry names you defined or use "*" to include all defined private registries [1][2][3]. Example configuration: version: 2 registries: my-private-registry: type: maven-repository url: https://example.com/repo username: ${{secrets.MY_USERNAME}} password: ${{secrets.MY_PASSWORD}} updates: - package-ecosystem: "maven" directory: "/" registries: - my-private-registry # Required reference schedule: interval: "monthly" Regarding the GITHUB_TOKEN and private registries: As of June 2026, Dependabot can automatically authenticate to private GitHub Packages registries (e.g., ghcr.io or *.pkg.github.com) without requiring a Personal Access Token (PAT) or explicit configuration in the registries section of your dependabot.yml [4][5]. To enable this, ensure you have granted your repository "Read" access to the specific package via the "Manage Actions access" settings on the package's page in your organization or personal account [4][5]. If this is configured, Dependabot will automatically use its internal GITHUB_TOKEN to pull the dependencies, and you can remove any existing PAT-based registry entries from your dependabot.yml [4][5]. For non-GitHub private registries (e.g., Artifactory, Nexus, Azure Artifacts), you must continue to use the top-level registries key with appropriate credentials stored as repository or organization secrets [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the whole Dependabot config with line numbers, focusing on update entries.
sed -n '1,220p' .github/dependabot.yml | cat -n

printf '\n---\n'

# Search for registry references in update entries.
rg -n 'registries:|npm-pkg-github|GITHUB_TOKEN|DEPENDABOT' .github/dependabot.yml

Repository: BillyOutlast/drop

Length of output: 5799


🏁 Script executed:

#!/bin/bash
set -euo pipefail

if [ -f .github/dependabot.yml ]; then
  cat -n .github/dependabot.yml
else
  echo "missing .github/dependabot.yml"
fi

Repository: BillyOutlast/drop

Length of output: 5719


Reference npm-pkg-github from the npm update entry, or drop the unused registry block.
It’s defined at the top level but never listed under any updates[].registries, so Dependabot won’t use it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/dependabot.yml around lines 3 - 9, Update the Dependabot
configuration so the npm update entry references the existing npm-pkg-github
registry under its updates[].registries list, or remove npm-pkg-github if no
update entry needs it. Keep the registry configuration consistent with the
selected approach.


updates:
# ----- Node / pnpm workspace (root, server, sites/*, desktop) -----
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 10
groups:
# Patch + minor bumps bundled together to avoid PR spam
node-minor:
update-types: ["minor", "patch"]
# Pin major bumps separately for explicit review
node-major:
update-types: ["major"]
commit-message:
prefix: "deps"
prefix-development: "chore(deps-dev)"
labels: ["dependencies", "javascript"]
reviewers: ["BillyOutlast"]
# Keep lockfile in sync; pnpm-workspace.yaml declares onlyBuiltDependencies
# — keep Dependabot from re-enabling builds that the workspace intentionally skips.
rebase-strategy: "auto"

# ----- Nuxt 4 desktop app (separate pnpm workspace) -----
# desktop/main/ has its own pnpm-workspace.yaml and pnpm-lock.yaml,
# so root npm entry at "/" does not cover it. Scanned independently.
- package-ecosystem: "npm"
directory: "/desktop/main"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
desktop-minor:
update-types: ["minor", "patch"]
desktop-major:
update-types: ["major"]
commit-message:
prefix: "deps(desktop)"
labels: ["dependencies", "javascript"]
rebase-strategy: "auto"

# ----- Rust workspace: CLI -----
- package-ecosystem: "cargo"
directory: "/cli"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(cli)"
labels: ["dependencies", "rust"]

# ----- Rust workspace: droplet library -----
- package-ecosystem: "cargo"
directory: "/libraries/droplet"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(droplet)"
labels: ["dependencies", "rust"]

# ----- Rust workspace: native_model library -----
- package-ecosystem: "cargo"
directory: "/libraries/native_model"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(native_model)"
labels: ["dependencies", "rust"]

# ----- Rust workspace: desktop (Tauri) -----
- package-ecosystem: "cargo"
directory: "/desktop/src-tauri"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
groups:
rust-minor:
update-types: ["minor", "patch"]
rust-major:
update-types: ["major"]
commit-message:
prefix: "deps(desktop)"
labels: ["dependencies", "rust"]

# ----- Dockerfile (root image) -----
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 5
commit-message:
prefix: "deps(docker)"
labels: ["dependencies", "docker"]

# ----- GitHub Actions -----
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "06:00"
timezone: "America/New_York"
open-pull-requests-limit: 10
groups:
actions-minor:
update-types: ["minor", "patch"]
actions-major:
update-types: ["major"]
commit-message:
prefix: "deps(ci)"
labels: ["dependencies", "github-actions"]
Loading
Loading