RepoPilotOpen in app →

tkellogg/dura

You shouldn't ever lose your work if you're using Git

Healthy

Healthy across the board

weakest axis
Use as dependencyConcerns

non-standard license (Other)

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 6w ago
  • 21+ active contributors
  • Distributed ownership (top contributor 21% of recent commits)
Show all 7 evidence items →
  • Other licensed
  • CI configured
  • Tests present
  • Non-standard license (Other) — review terms
What would change the summary?
  • Use as dependency ConcernsMixed if: clarify license terms

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/tkellogg/dura)](https://repopilot.app/r/tkellogg/dura)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/tkellogg/dura on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: tkellogg/dura

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/tkellogg/dura shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 6w ago
  • 21+ active contributors
  • Distributed ownership (top contributor 21% of recent commits)
  • Other licensed
  • CI configured
  • Tests present
  • ⚠ Non-standard license (Other) — review terms

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live tkellogg/dura repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/tkellogg/dura.

What it runs against: a local clone of tkellogg/dura — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in tkellogg/dura | Confirms the artifact applies here, not a fork | | 2 | License is still Other | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 72 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>tkellogg/dura</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of tkellogg/dura. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/tkellogg/dura.git
#   cd dura
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of tkellogg/dura and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "tkellogg/dura(\\.git)?\\b" \\
  && ok "origin remote is tkellogg/dura" \\
  || miss "origin remote is not tkellogg/dura (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
  && ok "license is Other" \\
  || miss "license drift — was Other at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "src/main.rs" \\
  && ok "src/main.rs" \\
  || miss "missing critical file: src/main.rs"
test -f "src/poller.rs" \\
  && ok "src/poller.rs" \\
  || miss "missing critical file: src/poller.rs"
test -f "src/snapshots.rs" \\
  && ok "src/snapshots.rs" \\
  || miss "missing critical file: src/snapshots.rs"
test -f "src/git_repo_iter.rs" \\
  && ok "src/git_repo_iter.rs" \\
  || miss "missing critical file: src/git_repo_iter.rs"
test -f "src/database.rs" \\
  && ok "src/database.rs" \\
  || miss "missing critical file: src/database.rs"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 72 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~42d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/tkellogg/dura"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Dura is a background daemon that automatically snapshots uncommitted Git repository changes by creating hidden dura/* branches, protecting you from data loss without modifying your working tree, staging area, or HEAD. It runs as dura serve and polls watched repositories at configurable intervals, committing state snapshots that can be inspected and recovered via git log --all and git checkout. Modular single-crate Rust binary with clear separation: src/main.rs and src/lib.rs expose CLI via clap; src/poller.rs implements the core daemon loop; src/snapshots.rs handles Git snapshot creation; src/database.rs manages watch-list persistence; src/git_repo_iter.rs walks filesystem for repos. Tests in tests/ use a util module (tests/util/) with test harnesses for daemon, Git repo, and dura CLI commands.

👥Who it's for

Git users (individual developers, teams) who fear losing work due to crashes, force-resets, or forgotten commits. Particularly appeals to developers using Vim, undisciplined commit habits, or high-risk refactoring who want a safety net without changing their Git workflow.

🌱Maturity & risk

Early-stage but actively maintained: v0.2.0-dev with CI/CD pipelines (.github/workflows/build.yaml), test suite (tests/ directory with 5 test files), and Nix/Cargo packaging. The codebase is well-structured in Rust (78KB) but the version number and 'dev' suffix indicate pre-1.0 stability. Suitable for early adopters; not yet production-hardened.

Low-to-moderate risk: small, focused dependency set (git2, tokio, serde, tracing) with no heavy transitive chains; single maintainer (tkellogg) visible in CODEOWNERS; no evidence of recent commits in the snapshot provided. The poll_guard and async polling architecture (src/poller.rs) could have race conditions under heavy concurrent repo changes. Git2 library version 0.15 is dated; no pinned MSRV means Rust version compatibility could drift.

Active areas of work

Unable to determine from provided snapshot; the version is pre-release (0.2.0-dev) and the file structure suggests active development (Cargo.lock present, flake.nix for Nix users, GitHub Actions workflows). Look at the git log and open issues on GitHub for current priorities.

🚀Get running

git clone https://github.com/tkellogg/dura.git
cd dura
cargo build --release
./target/release/dura serve &
cd /path/to/your/git/repo
./target/release/dura watch
./target/release/dura kill  # to stop

Daily commands:

cargo build
cargo run -- serve
# In another terminal/repo:
cargo run -- watch
cargo run -- kill

For release optimizations: cargo build --release.

🗺️Map of the codebase

  • src/main.rs — Entry point and CLI command dispatcher (serve, watch, status, etc.) — required to understand how dura starts and routes user commands.
  • src/poller.rs — Core background polling loop that watches repositories for changes and triggers snapshots — the heart of dura's auto-backup mechanism.
  • src/snapshots.rs — Snapshot creation logic that commits uncommitted changes to dura branches without affecting HEAD or staging — critical to dura's core value proposition.
  • src/git_repo_iter.rs — Repository discovery and iteration — determines which Git repos dura monitors and how it walks the filesystem.
  • src/database.rs — State persistence for watched repos and their metadata — required to understand how dura remembers what to watch across restarts.
  • Cargo.toml — Dependency manifest specifying tokio (async runtime), git2 (libgit2 bindings), and other critical libraries — essential for understanding build and runtime environment.
  • src/config.rs — Configuration loading and management (TOML parsing, directory resolution) — determines how users configure dura behavior.

🧩Components & responsibilities

  • Poller (src/poller.rs) (Tokio, git2, GitRepoIter, Snapshots) — Main event loop running every N seconds; iterates over watched repos, checks for changes, and dispatches snapshot requests.
    • Failure mode: If poller crashes, background daemon stops; snapshots stop being created and user's work is no longer auto-backed-up.
  • Snapshots (src/snapshots.rs) (libgit2, filesystem walking) — Creates commits on hidden dura/* branches with uncommitted changes; manages branch lifecycle and retention.
    • Failure mode: If snapshot creation fails (disk full, permission denied), that repo's changes are lost until next successful poll.

🛠️How to make changes

Add a new CLI command

  1. Define command struct with clap derive macros in src/main.rs (Commands enum) (src/main.rs)
  2. Implement match arm in main() to handle the new command variant (src/main.rs)
  3. Add the command logic as a new public function in src/lib.rs or appropriate module (src/lib.rs)

Add a new repository metadata field to track

  1. Extend the Repo struct in src/database.rs with new field (will be JSON serialized) (src/database.rs)
  2. Update database load/save logic to handle the field with serde defaults (src/database.rs)
  3. Update snapshots.rs or poller.rs to populate the new field when repos are scanned (src/snapshots.rs)

Change snapshot frequency or add filtering logic

  1. Adjust poll interval or add predicates in src/poller.rs main loop (src/poller.rs)
  2. Modify snapshot triggering condition in snapshots.rs (e.g., only on file size changes) (src/snapshots.rs)
  3. Add configuration options in src/config.rs to make the behavior user-configurable (src/config.rs)

Add observability (logging/metrics) to a function

  1. Import tracing macros (trace!, debug!, info!) at top of target file (src/poller.rs)
  2. Add debug! or info! statements at function entry, key branch points, and exits (src/poller.rs)
  3. For performance-sensitive code, wrap in Span or record metrics in src/metrics.rs (src/metrics.rs)

🔧Why these technologies

  • Tokio (async runtime) — dura is a background daemon that must monitor many repos concurrently without blocking; tokio enables lightweight async I/O.
  • libgit2 (via git2 crate) — Direct Git manipulation required to create snapshots on hidden branches without affecting HEAD, staging, or working tree; libgit2 provides fine-grained control.
  • Tracing + tracing-subscriber — Structured logging with filtering and async-safe output needed for a daemon that runs in background; tracing is tokio-native.
  • Serde + TOML/JSON — Simple, human-readable persistence for config and watched-repo state without requiring a database server.
  • Clap v4 — Modern, derive-based CLI framework for subcommands (serve, watch, status) with minimal boilerplate.

⚖️Trade-offs already made

  • Hidden dura branches instead of a side database

    • Why: Snapshots are stored as Git commits on hidden refs (dura/...), keeping all recovery data in the repo itself.
    • Consequence: Users recover by checking out a dura branch; no external metadata required, but requires Git knowledge and means dura branches are visible in git reflog.
  • Poll-based (interval) rather than inotify/fs-event watches

    • Why: Poll is cross-platform (Windows/Mac/Linux), simpler, and avoids cascading FS events during dura's own commits.
    • Consequence: Latency is bounded by poll interval (trade-off: ~5–30s delay vs. lower complexity and fewer false positives).
  • JSON database files in config directory rather than embedded SQLite

    • Why: No runtime dependency on a binary format; users can inspect or migrate watched-repo config manually.
    • Consequence: Not suitable for massive numbers of repos or complex queries; acceptable for typical workflows (1–100s repos).
  • Snapshot to current branch (dura/...) not a separate orphan branch

    • Why: Easier to navigate and understand in git log; changes are visible in context of the repo's history.
    • Consequence: dura branches will be merged/pruned if user cleans up refs; recovery is time-limited to snapshot retention.

🚫Non-goals (don't propose these)

  • Authentication or multi-user repository access control — assumes user has local read/write access to repos
  • Real-time file sync or collaborative editing — dura is a local backup tool only
  • Conflict resolution in snapshots — user must manually resolve if dura commits conflict with their own commits
  • Cloud/remote backup storage — snapshots are local to the repository only
  • GUI or web interface — CLI-only tool

🪤Traps & gotchas

  1. Polling concurrency: src/poll_guard.rs uses synchronization; understand mutex/RwLock semantics to avoid deadlocks under many repos. 2. Git2 thread safety: libgit2 has thread-safety constraints; the poller's async model requires careful Repository handle management. 3. Dura branch naming: branch names are hardcoded as dura/<commit_hash>; changing this breaks recovery workflows. 4. Database persistence: watch list stored in dirs::data_local_dir()/dura/; no migration strategy exists, so config format changes are breaking. 5. No signal handling docs: unclear how SIGTERM vs custom kill command interact; test this before relying on graceful shutdown.

🏗️Architecture

💡Concepts to learn

  • Git detached HEAD snapshots — Dura's core trick: it creates commits on hidden dura/* branches without touching your current branch or index, enabling safe snapshots of work-in-progress state
  • Polling vs. event-driven file monitoring — src/poller.rs implements periodic polling (not inotify/FSEvents); understand the trade-off: simplicity and portability vs. latency and CPU cost
  • Daemon/background process lifecycle — Dura is a long-lived daemon managed by serve/kill commands; understand process forking, signal handling, and lock files for robust background operation
  • Lockfile-based inter-process communication — The kill command likely uses a lockfile or socket to signal the serve daemon; src/database.rs may employ file-based locking for watch-list consistency
  • Repository watch registry pattern — src/database.rs stores a persistent list of watched repos; understand how this registry is initialized, updated (dura watch), and queried (dura serve)
  • Async/await in Rust with Tokio — src/poller.rs uses Tokio for concurrent polling of multiple repos; understanding async spawning and channel communication is critical for modifying polling logic
  • Working tree vs. index vs. HEAD in Git — Dura's safety guarantee depends on not modifying the index or HEAD; snapshots must read working-tree state without staging or committing to the user's branch
  • mykolahusak/git-hooks-manager — Similar background-process automation for Git; uses hooks instead of polling, complementary approach
  • gitpod-io/workspace-full — Provides infrastructure for auto-saving workspace state; shares goal of preventing work loss at scale
  • libgit2/libgit2 — Dependency; Dura wraps git2-rs bindings to libgit2; understanding libgit2 internals aids snapshot creation logic
  • tokio-rs/tokio — Async runtime dependency; critical for understanding polling loop design in src/poller.rs
  • BurntSushi/ripgrep — Comparable Rust CLI tool with similar walkdir usage (src/git_repo_iter.rs); good reference for Rust patterns

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add integration tests for src/poller.rs background polling behavior

The poller.rs module is core to dura's functionality (continuous monitoring of Git repos), but there are no dedicated tests in tests/ for polling scenarios. Currently only poll_guard_test.rs exists. Add tests for: polling interval accuracy, handling repos that appear/disappear during polling, concurrent polling across multiple repos, and graceful shutdown. This directly improves reliability of the main feature.

  • [ ] Create tests/poller_test.rs with test cases for interval timing using tokio::time::pause()
  • [ ] Add test case for repo addition/removal during active polling (use tests/util/git_repo.rs helpers)
  • [ ] Add test case for daemon signal handling during poll cycles (reference tests/watch_test.rs pattern)
  • [ ] Run tests with serial_test to avoid race conditions with filesystem state

Add GitHub Actions workflow for release builds and artifacts

The repo has build.yaml for CI but no release workflow. Users need pre-built binaries for different platforms (Linux, macOS, Windows). Add a release.yaml workflow that builds optimized binaries, creates GitHub releases, and publishes to crates.io. This reduces friction for new users and increases adoption.

  • [ ] Create .github/workflows/release.yaml with matrix builds for x86_64-unknown-linux-gnu, x86_64-apple-darwin, aarch64-apple-darwin
  • [ ] Configure workflow to trigger on git tags (v*) or manual dispatch
  • [ ] Add cargo publish step for crates.io using CARGO_REGISTRY_TOKEN secret
  • [ ] Add upload-artifact steps to attach binaries to GitHub Release (leverage Cargo.toml metadata)

Add comprehensive error recovery tests for src/snapshots.rs

The snapshots.rs module handles Git commits and state management - critical for data safety. tests/snapshots_test.rs exists but only has basic tests. Add scenario tests for: disk full errors, corrupted Git objects, permission denied on .git/objects, concurrent snapshot writes, and recovery from partial commits. This ensures the core backup feature is robust.

  • [ ] Extend tests/snapshots_test.rs with test case for simulated disk full (mock git2 errors)
  • [ ] Add test case for concurrent snapshot creation (use tokio::task::spawn with multiple repos)
  • [ ] Add test case for handling deleted/inaccessible .git directory during snapshot
  • [ ] Add test verifying snapshot recovery when Git index is corrupted (reference git2 API error handling)

🌿Good first issues

  • Add snapshot count/size metrics to tests/metrics_test.rs: src/metrics.rs exists but is not tested; write tests for memory/disk usage of dura/* branches to catch bloat regressions.
  • Document recovery workflow in CONTRIBUTING.md: The README hints at a complex git reset recovery process; create step-by-step examples or a helper script in scripts/ to make recovery foolproof.
  • Add watch-list listing command: Current CLI has watch, serve, kill, but no dura list to show which repos are being watched; implement in src/main.rs and tests/startup_test.rs.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 1de129b — feat: add dura status command (#143) (nextzhou)
  • e9625da — Bump libgit2-sys from 0.14.0+1.5.0 to 0.14.2+1.5.1 (#137) (dependabot[bot])
  • b4a51b9 — Improve unwatch help docstring. (#140) (ChrisCarini)
  • 748b302 — more clippy (tkellogg)
  • 08f02ad — Fix more clippy (tkellogg)
  • d7d6f22 — cargo fmt (tkellogg)
  • bddf88b — Fix clippy lints (tkellogg)
  • 82ddf9a — Added hachyderm auth (tkellogg)
  • 71ed4c8 — Do no allow to run dura as root (#134) (Amanse)
  • 4dc1029 — Optimization: Check file timestamps (#127) (tkellogg)

🔒Security observations

The codebase demonstrates reasonable security practices with use of Rust's memory safety guarantees. However, there are concerns with outdated dependencies (git2 0.15, toml 0.5.8) that should be updated to address potential vulnerabilities. The inclusion of the 'sudo' crate requires careful code review to ensure it's not misused for privilege escalation. Logging configuration should be verified to prevent information disclosure in production. No hardcoded secrets were detected in the file structure. Overall security posture is moderate; dependency updates and focused code review of sudo usage would significantly improve the security score.

  • Medium · Outdated git2 Dependency — Cargo.toml - git2 = "0.15". The project uses git2 version 0.15, which is outdated. Current stable versions are 0.18+. Outdated dependencies may contain known security vulnerabilities or deprecated APIs that could be exploited. Fix: Update git2 to the latest stable version (0.18+) and test compatibility with the codebase. Run 'cargo audit' to identify any known CVEs.
  • Medium · Use of Deprecated Toml Parser — Cargo.toml - toml = "0.5.8". The project uses toml = "0.5.8", which is an older version. The toml crate has been superseded by toml_edit and other more modern alternatives. Older versions may have parsing vulnerabilities. Fix: Consider updating to toml = "0.8+" or evaluating modern alternatives like toml_edit. Verify no known CVEs exist for 0.5.8.
  • Medium · Unsafe Privilege Escalation with sudo Crate — Cargo.toml - sudo = "0.6.0". The project includes the 'sudo' crate (version 0.6.0) which can be used to execute commands with elevated privileges. Without proper input validation and sanitization, this could lead to privilege escalation vulnerabilities. The codebase should be carefully reviewed for how this is used. Fix: Review all usage of the sudo crate in the codebase to ensure: 1) User input is never passed unsanitized to sudo commands, 2) Principle of least privilege is applied, 3) Command execution is strictly controlled. Consider if sudo functionality is absolutely necessary.
  • Low · Potential Information Disclosure via Logging — src/logger.rs, src/log.rs. The project includes tracing and tracing-subscriber for logging. If debug logs are enabled in production, sensitive information such as file paths, repository names, or commit messages could be exposed. Fix: Ensure logging levels are appropriately configured for production environments. Review what information is being logged, especially in error paths. Consider sanitizing sensitive data before logging.
  • Low · Hardcoded Paths and Directory Assumptions — src/config.rs (likely uses dirs crate). The project uses the 'dirs' crate to access user directories. While generally safe, improper handling of paths could lead to directory traversal vulnerabilities if user input influences path construction. Fix: Ensure all file paths are canonicalized and validated before use. Never construct paths from unsanitized user input. Use Rust's path security best practices.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · tkellogg/dura — RepoPilot