Skip to content

Repository files navigation

deptend.dev

License: MIT Live dashboard GitHub stars

deptend.dev converts a GitHub repository's dependency data into a prioritized, explainable list of maintenance missions. Instead of a flat vulnerability feed, it tells you what to fix next — combining security impact, ecosystem value, and estimated effort into a single ranked list, with every score's inputs one click away.

Live dashboard: deptend.vercel.app

demo.mp4

deptend.dev mission board — a ranked, explainable list of maintenance missions


Why this exists

Open-source maintainers are often overwhelmed by a flood of alerts, dependency updates, and issues, with no clear path on what to prioritize. deptend.dev cuts through the noise with a maintenance-first, not vulnerability-first, view — the same underlying data, but ranked by what's actually worth doing next.

Three constraints are non-negotiable and shape every decision in this project:

  • Zero budget. Every tool, service, and dependency is free at the tier this project actually uses.
  • Solo developer. Architecture and workflow stay manageable by one person.
  • Transparency-first. No black-box scoring. Every mission shows its formula, its inputs, and its confidence level — never a bare number.

Two ways to use it

The hosted dashboard — visit deptend.vercel.app, no account needed to browse missions. GitHub sign-in is only required to submit a new repo or claim a mission.

The CLI — runs the same scoring engine against a local repo path, entirely in-memory, no account or hosted infrastructure required:

npx deptend <repo-path> --github-url <url> [--output <file>] [--json]
# Example
export GITHUB_TOKEN=<a token with public repo read access>  # recommended, raises GitHub's rate limit
npx deptend . --github-url https://github.com/your-username/your-repo --output missions.json
Flag Required Purpose
<repo-path> Yes (positional) Local path to the repo root — the directory containing package.json (npm), pyproject.toml/requirements.txt (PyPI), or go.mod (Go). Ecosystem is auto-detected, npm first, then PyPI, then Go.
--github-url <url> Yes The repo's GitHub URL — used to fetch stars/open-issues for ecosystem-value scoring. A local checkout alone can't derive this.
--output <file> No Write the full JSON result to this file
--json No Print the full JSON result to stdout instead of the human-readable summary (ignored if --output is set)

GITHUB_TOKEN (env var, optional but recommended) raises the GitHub API rate limit from 60 to 5,000 requests/hour. A fine-grained PAT with public-repo read access is sufficient.

The CLI reuses the exact same scoring and ranking code the dashboard runs — same formula, same tie-break rules, same explainability. It doesn't touch a database and doesn't require the dashboard to be running; the two are independent, cross-verified implementations of the same engine, not a client/server pair.

Note on npx: @deptend/cli's bin entry is set up for npx-style invocation once published, but hasn't been published to the npm registry yet. Until then, run it from a local clone — see Local development below, or use pnpm --filter @deptend/cli exec deptend <args> / node cli/dist/index.js <args> from the repo root after building.

What a mission looks like

Every mission — on the dashboard or from the CLI — includes:

  • What's affected — the package, its declared version range, and whether it's a production or development dependency
  • The advisory — source (OSV/GHSA), severity, CVSS score if available, a link to the original record, and the version that fixes it
  • The recommended action — a plain-language upgrade instruction
  • The score and every input that produced it — never a bare number
  • Confidence — visibly flagged when data is incomplete (no lock file parsed yet, no CVSS score available, no downstream-dependents data, etc.), never hidden

The rescue board

Missions aren't private to a repo's own maintainer. The dashboard's board lists every open (and claimed) mission across all indexed repos, filterable by severity and effort. Any signed-in GitHub user can claim a mission — and release it later if they change their mind — turning a maintainer's backlog into something someone else can actually pick up and ship. No separate account, no gatekeeping beyond GitHub sign-in.

How scoring works

composite_score = impact_score × 0.60 + ecosystem_value_score × 0.40
  • Impact — CVSS score if available, otherwise a severity-based estimate, discounted for development dependencies (they don't ship to end users) and for transitive dependencies.
  • Ecosystem value — log-scaled repo stars, open issues, and (once available) downstream dependents.
  • Effort — semver bump size required to reach the fixed version (patch/minor/major → trivial/low/.../high), refined by migration-guide data once that's ingested.

Missions are ranked by composite_score, bucketed into fixed-width tiers so near-equal scores don't produce an inconsistent order (ADR 0017). Within a tier, effort_label breaks the tie — an intentional "prefer the quick win" rule, not an accident. Below that, the tied advisory's own published_at (newest first) and finally its osv_id guarantee a fully deterministic order regardless of input order or ingestion timing (ADR 0018).

Full detail: docs/adr/0006-scoring-algorithm.md.

On confidence right now: every mission currently shows low confidence. That's not a bug — two scoring inputs (downstream-dependents data, migration-guide/breaking-change signals) don't have a data source wired up yet, so the score stays deliberately conservative instead of implying more precision than the data supports. Flagged openly, not smoothed over — consistent with the zero-black-box principle above.

Ecosystem support

Three ecosystems, auto-detected per repo in this order — npm first, then PyPI, then Go. A repo with manifests for more than one ecosystem detects as whichever is tried first; true multi-ecosystem-per-repo isn't supported.

Ecosystem Manifest read Registry Notes
npm package.json only npm registry First ecosystem — ADR 0003
PyPI pyproject.toml (PEP 621) primary, requirements.txt fallback pypi.org JSON API Poetry's [tool.poetry.dependencies] table is out of scope — ADR 0022
Go go.mod require directives, direct only proxy.golang.org replace/exclude/retract directives not handled — ADR 0024

Lock files (package-lock.json/pnpm-lock.yaml/yarn.lock for npm, go.sum for Go) are detected but never parsed — resolved versions are estimated from declared ranges rather than confirmed. This is visibly flagged as lower confidence wherever it applies, never silently assumed.

A repo whose manifest lives outside the repo root is currently indistinguishable from a repo with no manifest at all — both land on ingestionStatus: 'skipped'. This is a scoping choice (root-only parsing), not a bug.

Tech stack

Layer Choice
Frontend Next.js 15 + Tailwind CSS
Backend Next.js API routes
Database PostgreSQL (Neon free tier)
ORM Drizzle ORM + Drizzle Kit
Auth GitHub OAuth (next-auth v4), JWT sessions
Hosting Vercel Hobby
CI/CD GitHub Actions — lint/typecheck/test on every PR, nightly ingestion cron, on-demand ingestion on repo submission
Data sources OSV.dev / GitHub Advisory Database, npm registry API, pypi.org JSON API, proxy.golang.org, GitHub REST API
CLI Node.js, npx-runnable
Package manager pnpm workspaces
Language TypeScript (JS permitted only in scripts/)
Testing Vitest
Lint/format ESLint 9 (flat config) + typescript-eslint, Prettier

Every choice above is free at the tier this project uses. See docs/adr/ for the reasoning behind each one.

Monorepo structure

deptend.dev/
├── app/              # Next.js frontend + API routes (the hosted dashboard)
├── cli/              # npx-runnable CLI companion
├── packages/core/     # @deptend/core — shared ingestion + scoring engine, used by both app/ and cli/
├── scripts/          # GitHub Actions cron entry point (real ingestion pipeline)
├── docs/
│   ├── adr/           # One Architecture Decision Record per major technical choice, numbered sequentially
│   └── data-model/    # Entity reference, kept in sync with packages/core/src/db/schema.ts
└── .github/workflows/ # ci.yml (lint/typecheck/test), ingest.yml (cron + on-demand ingestion)

packages/core/src/db/schema.ts is the single source of truth for every database type — see ADR 0011.

Local development

Requires Node.js ≥20 and pnpm ≥9 (this project pins 9.15.0).

  1. Clone and install

    git clone https://github.com/SpIob/DepTend
    cd deptend.dev
    pnpm install

    (pnpm install also builds packages/core automatically via a root postinstall hook — nothing else to build by hand for a first-time setup.)

  2. Set up environment variables — copy .env.example to .env.local and fill it in:

    • DATABASE_URL / DATABASE_URL_UNPOOLED — from a free Neon project (pooled and direct connection strings; the direct one is required for schema/migration work). Use a Neon branch, not the same branch Vercel's Production environment points at — Neon branches are free (copy-on-write, no card required) and keep local test submissions off the live public mission board. See ADR 0023.
    • GH_CLIENT_ID / GH_CLIENT_SECRET — from a GitHub OAuth App with callback URL http://localhost:3000/api/auth/callback/github
    • NEXTAUTH_SECRET — generate with openssl rand -base64 32
    • NEXTAUTH_URLhttp://localhost:3000 for local dev
    • GITHUB_TOKEN — a personal access token (read-only, public repos) for ingestion and CLI use
    • GH_DISPATCH_TOKEN / GH_REPOleave blank in local .env.local. These fire a real GitHub Actions run, and that workflow's DATABASE_URL is a repo secret pointing at the production branch — it can never see a repo row that only exists in your local dev branch (ADR 0023). Only set these in Vercel's Production environment, where the submitter's DB and the workflow's DB are the same branch. Leaving them blank locally makes repo submission fall back gracefully to "will be processed on the next scheduled run" instead of dispatching a run that's guaranteed to fail — see step 5 for how to actually ingest a locally-submitted repo.
  3. Apply the database schema (replays the existing migration history — this project uses migration files, not drizzle-kit push)

    pnpm drizzle-kit migrate

    Known issue: drizzle-kit migrate reliably hangs on ALTER TYPE ... ADD VALUE statements specifically — 2-for-2 in this project's own history, and not lock contention (ruled out via pg_stat_activity, see ADR 0021). If it hangs, kill it and apply that one migration's .sql file directly in Neon's SQL Editor instead — that's the expected path for enum additions now, not a last-resort fallback (see ADR 0026). Everything else (table/column changes) has run fine through the CLI so far.

  4. Run the dashboard

    pnpm --filter app dev

    Visit http://localhost:3000. OAuth sign-in only round-trips correctly from a stable URL matching your OAuth App's registered callback — not from arbitrary preview URLs.

  5. Run an ingestion manually — with GH_DISPATCH_TOKEN/GH_REPO unset locally (step 2), this is now the only way to populate mission data for a repo you submitted through the local dashboard, since neither the nightly cron nor an on-demand dispatch can reach your dev branch:

    # For a repo already submitted through the local dashboard's UI:
    node --env-file=.env.local scripts/ingest.js --repo-id <the-repo's-uuid> --triggered-by manual
    
    # Or skip the UI and ingest any repo directly by URL:
    node --env-file=.env.local scripts/ingest.js --repo-url https://github.com/owner/name --triggered-by manual
  6. Build and try the CLI

    pnpm --filter @deptend/cli build
    node cli/dist/index.js /path/to/a/local/repo --github-url https://github.com/owner/name
  7. Verify your changes — the standard loop this project uses before every commit, run from a fully clean state:

    rm -rf packages/core/dist app/.next cli/dist
    pnpm typecheck && pnpm test && pnpm build && pnpm lint && pnpm format:check

Status

DepTend is live and under active development — new features and fixes ship regularly, and the UI is currently undergoing a refactor for a more minimal, modern look. See docs/adr/ for the decision history.

Contributing

This is currently a solo-maintained project without a formal contribution process yet. Looking for a way to help without maintaining anything yourself? The rescue board is the fastest way in — claim an open mission on any indexed repo. Issues and discussion are welcome via the GitHub repo's issue tracker (also used as the project's task board — no external project management tool, per the project's zero-budget/solo-dev principles).

License

MIT — see LICENSE.

About

A maintenance-first, zero-budget coordination tool for solo developers and small open-source teams. Converts dependency and issue data into prioritized maintenance missions — scored by impact, effort, and ecosystem value.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages