Privacy & data handling
What we store, how long, who we share it with, and what we explicitly don't store.
DevUpdate.io is a developer tool, so the trust bar is high. This page lists what we keep, what we don't, and the third parties involved.
What we store #
-
Account data: email, hashed password (or OAuth identity), display name, timezone, digest preferences. If you sign up with GitHub and your primary email is set to private, GitHub gives us a
users.noreply.github.comaddress we can't deliver mail to, so we'll ask you for a reachable email before you can use the app, and verify it via a one-click confirmation link. -
Source list: the GitHub URLs and vendor URLs you've added as tracked sources.
-
Tracker requests: if you ask us to build a deterministic parser for a source (the "Request a deterministic tracker" flow), we store the URL you submitted, the optional product name and note, and which account raised it, so we can prioritize by demand and follow up. These rows survive account deletion (the user link is nulled) as an anonymous demand signal.
-
Lockfile snapshots: package names and versions extracted from uploaded lockfiles. The original file is not retained.
-
Release analysis output: summaries, risk scores, and detected changes for each release we've processed. Cached so the AI never has to re-analyze the same diff twice.
-
Security-issue matches: for each monitored dependency, which public OSV/GHSA advisories affect the version you pin (advisory ids, severity, fix status, and the advisory's own workaround text). Derived entirely from your parsed package + version pairs and public advisory data; refreshed daily and deleted when the issue resolves or the lockfile is removed.
-
Public discussion metadata. For each tracked GitHub repo we periodically read its public open issues so the Pulse "News & discussions" section can surface ongoing problems. We store the issue title, URL, author handle, reaction/comment counts, labels, and a short excerpt of the body (public data only). Private-repo issues are only read when you've explicitly installed the DevUpdate.io GitHub App on the repo (read-only permissions, revocable from GitHub).
-
Billing & usage metering: subscription tier, billing cadence, invoice history, AI-usage events (token counts per operation, kept for internal cost monitoring and fair use, never billed per use), and the EU CRD §16(m) withdrawal-waiver consent record (timestamp, IP, terms version).
-
Authentication tokens: RS256-signed JWT access tokens (1 h) and refresh tokens (30 d), plus hashed API tokens for programmatic access. Refresh-token reuse is detected via a per-session JTI; a detected replay invalidates every active session for the affected account.
-
Connected AI clients (hosted MCP server): when you authorize an MCP client (Claude, Cursor, etc.) via OAuth, we store the client's self-registered metadata (name, redirect URLs), the scopes you approved, and SHA-256 hashes of the issued access and refresh tokens (never the raw token values), plus created/last-used timestamps so the Settings list can show you what's connected. Revoking a client from Settings → Connected AI Clients deletes its access immediately; consent grants and revocations are recorded in the audit log.
-
GitHub App user access token (when you connect GitHub): a short-lived (8 h) user-to-server token, plus its refresh token, stored against your account row, Fernet-encrypted at rest with a key that lives in the runtime environment, not the database, so a DB snapshot alone doesn't surface the tokens. The backend can call GitHub as you and let GitHub's own permission model decide which repositories we can read. The token is issued by the DevUpdate.io GitHub App with read-only access to repository contents and metadata (no write, ever) and scoped to repositories you explicitly grant when installing the App. The exact GitHub scopes we request are:
- Repository permissions:
contents: read,metadata: read - Account permissions:
email addresses: read
No other repository, organisation, account, or write permission is ever requested. Both tokens are deleted when you disconnect GitHub or delete your account. Revocable at any time from github.com → Settings → Applications. Revocation from GitHub takes effect on our next call without any action on our side.
The Repositories with access panel in the Lockfiles tab lists the repos you've granted the App by calling GitHub's user-installations API as you at view time. This needs no additional scope beyond the read-only access above, and the list itself is not stored: it's fetched on demand and only briefly cached server-side to stay within GitHub's rate limits.
- Repository permissions:
-
Session metadata: IP and User-Agent per session for security audit, with a 30-minute idle timeout and a 3-concurrent-session cap.
-
Audit log: an append-only
audit_eventstable records security-relevant actions on your account (login success/fail, logout, refresh-token reuse detection, password reset, account deletion, AI-client MCP authorizations and revocations) with timestamp, IP, and user-agent. Used internally for incident review; not shared. -
Email send log: an append-only
email_send_logtable records each transactional or lifecycle email we hand to AWS SES for delivery (verification, password reset, the day-4 unverified warning, and the inactive-account nudges described under Retention below), with your address, the email type, and a timestamp. The row reflects that SES accepted the send request, not that the message reached your mailbox (we don't track bounces or opens here). Used internally for abuse defense and to confirm our lifecycle email loops dispatched; not shared.
How tenant isolation is enforced #
Every table that holds user-owned data (tracked sources, lockfiles,
share links, OAuth connections, sessions, notifications) is
guarded by Postgres Row Level Security. Each request binds the
authenticated user's id to a session-local Postgres setting, and the
RLS policy on every tenant-scoped table refuses to surface rows that
don't match. A bug that forgets a user_id filter is downgraded from
"another tenant's data leaks" to "the query returns nothing".
Private repositories and the public explorer #
The source catalog behind Explore is shared across the whole community (a single row per source, watched by many users), so it is not a per-tenant table. A private GitHub repository you track (via the GitHub App) is flagged private on that shared row and excluded from every public read path: it never appears in the explorer list or search, and the explorer's detail page and release-summaries feed return "not found" for a private source regardless of who asks (the public explorer never serves it, not even to you; authorization there isn't the point, the source simply isn't part of the public catalog). The anonymous realtime stream likewise refuses to subscribe to it. You still see the repository and its releases on your own dashboard. In short: tracking a private repo on DevUpdate.io never publishes its name, description, or release notes.
What we don't store #
- Lockfile raw bytes. Parsed and discarded; only package + version pairs persist.
- Source code. We never clone repositories. Diff analysis goes through GitHub's compare API and the result is summarized; the raw diff is not stored.
- OAuth tokens beyond what we need. We hold the minimum permissions required to power the features you use. Google sign-in stays at basic profile + email. GitHub sign-in is handled by the DevUpdate.io GitHub App, whose user-authorization flow grants read-only access to repository contents and metadata, scoped to repositories you select when installing the App on your account or org. There is no "full control" or write permission anywhere in the request, and authorization alone (without an install) is enough for public-repo features. We only call GitHub when you take an action that requires it (e.g. add a source, sync a lockfile), and the call is restricted to the repo you named.
- Anything beyond parsed dependencies from connected repos. When you use a live lockfile connection, we read only the connected lockfile and store only the package + version pairs parsed from it, never the raw file or other repository contents.
Third parties #
| Service | What they see |
|---|---|
| OpenAI (LLM, currently the only provider) | Diff text + release notes for analysis, plus public marketplace metadata (extension manifest, install count, publisher info) and source URLs/descriptions when we synthesize a trust score for a VS Code / Open VSX extension or an ad-hoc web source. Not used for training per OpenAI's API terms. We may engage other LLM providers in the future. |
| OpenSSF Scorecard (api.securityscorecards.dev) | Public GitHub repo identifier (owner/name) when computing a trust score. Anonymous public API; we send only the project identifier, never any of your data. |
| Google deps.dev (api.deps.dev) | Public package name + ecosystem (npm, PyPI, Go, Cargo) when mapping a lockfile dependency to its upstream GitHub repository for trust scoring. Anonymous public API; we send only the package identifier. |
| OSV.dev (api.osv.dev) | Public package name + ecosystem when counting open advisories for a trust score, and package name + the pinned version from your lockfile when verifying the security flag on an available upgrade. Anonymous public API; we never send your account identity, repo names, or the rest of the lockfile. |
| Public package registries (registry.npmjs.org, pypi.org, crates.io, packagist.org, Go module proxy) | Public package names when discovering a dependency's source repository (see Package matching), and, for npm/PyPI, when validating that an offered upgrade version was actually published. Anonymous public APIs; only the package identifier is sent. |
| Google (sign-in OAuth) | Email, name, profile picture when you sign in with Google. |
| GitHub (sign-in via DevUpdate.io GitHub App) | Email, username, avatar URL. Sign-in issues a short-lived user-to-server access token (plus refresh token) carrying read-only access to repository contents and metadata for any repos where you've installed the App. Tokens are stored against your account row and deleted on disconnect. |
| GitHub App (per-repo install, required for private repos) | The same DevUpdate.io GitHub App must be installed on repos (or whole orgs) you want private-repo features for: starred-sync of private stars, live lockfile sync from private repos, source-tracking of private repos. You pick which repos at install time; access stays read-only (contents + metadata only) and is revocable from GitHub settings. We store only the parsed dependency list for live lockfile connections. |
| AWS SES | Outbound email (digests, transactional). |
| AWS S3 | Encrypted storage of AI-generated release summaries and analysis output. |
| PayPro Global | Billing details (name, billing address, payment method). |
| PostHog (EU-hosted) | Product analytics: page views, feature usage. Pseudonymized by account ID, never shared externally. |
| Source icons (your browser, not our backend) | To show a logo next to each source, your browser loads a small image directly: a repo owner's public avatar from github.com/<owner>.png for GitHub sources, the extension icon from the VS Code Marketplace's own gallery CDN (*.gallery.vsassets.io) for Marketplace sources, or /favicon.ico from any other web source's own domain. Only the owner name, extension id, or hostname appears in the image URL, the referrer is suppressed, and no account data is sent. These requests go straight from your browser to the source's own platform: no new third-party service is involved, and they fall back to a plain initial if the image can't be loaded. |
Retention #
- Active accounts: all data retained.
- Unverified email/password signups: the row is removed automatically 7 days after signup if the email is still unverified. A reminder email goes out around day 4. OAuth signups are created already verified and are never affected by this sweep.
- Inactive verified free-tier accounts: if you signed up but never added a source and never connected a lockfile, the account is removed automatically 30 days after signup. We send up to five reminder emails over the preceding 27 days, the last of which is an explicit deletion warning. Paid-plan accounts and team members are excluded; adding a source, upgrading, or joining a team takes you out of this cohort.
- Closed accounts: see Account → Deletion. Account-deletion is self-serve and removes personal data within 30 days, with cryptographic confirmation.
- Billing record retention obligation: invoices, subscription history and withdrawal-waiver consent may be retained up to 10 years after account deletion to satisfy German tax/commercial retention obligations (§147 AO, §257 HGB), reduced to the minimum required by law.
Data export #
You can export a JSON dump of your account data (sources, lockfiles,
analysis history, billing) on request. Email info@devupdate.io from
your account address. It's a manual job today, not yet self-serve.