Vikunja
vikunja.io Web, Android LinuxVikunja is an open-source to-do application. It is suitable for a wide variety of projects, supporting List, Gantt, Table and Kanban views to visualize all tasks in different contexts. For collaboration, it has sharing support via private teams or public links. It can be self-hosted or used as a managed service for a small fee.
- Homepage: vikunja.io
- GitHub: github.com/go-vikunja/vikunja
- Web info: web-check.xyz/check/vikunja.io
Vikunja Source Code
Author
Description
The task manager you actually own.
Homepage
https://vikunja.ioLicense
AGPL-3.0
Created
28 Nov 18
Last Updated
29 Jul 26
Latest version
Primary Language
Go
Size
86,803 KB
Stars
4,925
Forks
573
Watchers
4,925
Language Usage
Star History
Top Contributors
-
@kolaente (7188)
-
@renovate[bot] (1199)
-
@dpschen (778)
-
@vikunja-bot (208)
-
@tink-bot (107)
-
@Elscrux (35)
-
@Copilot (28)
-
@dependabot[bot] (27)
-
@maggch97 (17)
-
@WofWca (16)
-
@claude (14)
-
@JohnStarich (13)
-
@mdrkrg (13)
-
@xela-zone (6)
-
@adrinux (6)
-
@profi248 (5)
-
@jyte (4)
-
@Tokra110 (4)
-
@davidangel (4)
-
@shilch (4)
-
@jtojnar (4)
-
@freaktechnik (4)
-
@andreymal (4)
-
@TowyTowy (3)
-
@surfingbytes (3)
-
@Viehlieb (3)
-
@rhclayto (3)
-
@NeoHuncho (3)
-
@mithileshgupta12 (3)
-
@LucaBernstein (3)
-
@eljef (3)
-
@13rac1 (3)
-
@nithinvarma411 (2)
-
@zapp88 (2)
-
@kallegrens (2)
-
@gabeklavans (2)
-
@bradmartin333 (2)
-
@azymondrian (2)
-
@vlasov-y (2)
-
@CrazyWolf13 (2)
-
@MGChecker (2)
-
@Quiwy (2)
-
@mnr73 (2)
-
@furai (2)
-
@KaibutsuX (2)
-
@javabrett (2)
-
@subnut (1)
-
@TheEdgeOfRage (1)
-
@deadLocks21 (1)
-
@Uenluom (1)
-
@v-yarotsky (1)
-
@vovochka404 (1)
-
@XiangCany (1)
-
@TheZoker (1)
-
@blacksmith-sh[bot] (1)
-
@confor (1)
-
@cr1xu5 (1)
-
@treysu (1)
-
@Saxos-Simone (1)
-
@IAMSamuelRodda (1)
-
@remilapeyre (1)
-
@RyanHecht (1)
-
@RoboMagus (1)
-
@szuro (1)
-
@rhysmcneill (1)
-
@Rein-R3 (1)
-
@Raymi306 (1)
-
@probecat (1)
-
@IDDQD69 (1)
-
@the-darkvoid (1)
-
@taiwithers (1)
-
@stephen-hill (1)
-
@SteffeyDev (1)
-
@simonsmd (1)
-
@rudd6617 (1)
-
@rriski (1)
-
@primeapple (1)
-
@naleo (1)
-
@maximilize (1)
-
@leggettc18 (1)
-
@kompetenzbolzen (1)
-
@carl-unique (1)
-
@jontyms (1)
-
@jonastheis (1)
-
@jayden-chan (1)
-
@j-hugo (1)
-
@graves501 (1)
-
@erri120 (1)
-
@edelgrace (1)
-
@earnestma (1)
-
@devadattas (1)
-
@hcuk94 (1)
-
@HarryEMartland (1)
-
@hangya (1)
-
@Jackymancs4 (1)
-
@Hudint (1)
-
@fleaz (1)
-
@rbtr (1)
-
@ZeWaren (1)
-
@SDonCode (1)
Recent Commits
-
Frederick [Bot] (29 Jul 26)
[skip ci] Updated swagger docs
-
kolaente (29 Jul 26)
fix(notifications): delete stored notifications of unknown types 349e6a590 renamed ListCreatedNotification's name from list.created to project.created without rewriting the notifications.name column, so instances upgraded from 0.19 or earlier still hold list.created rows carrying a full project payload. Nothing can hydrate them, so nothing can scope them either, and the read paths pass unrecognised names through unfiltered — leaking the project's title, description, identifier, colour and owner to users since removed from it. Retaining rows no type can render only preserves that leak, so they go.
-
kolaente (29 Jul 26)
fix(web): stop logging the full auth object on permission denials log.Warningf formatted a web.Auth with %v on the create, read, update and delete denial paths. That holds a *user.User, which has a Password field carrying the bcrypt hash and no String method, so every denied request wrote a password hash into the log at warning level. Log the user id instead.
-
kolaente (29 Jul 26)
fix(notifications): require a persisted notification to declare its project The capability interface carrying a notification's project was optional, and ProjectIDOf defaulted a missing implementation to 0 — which means account-scoped, which means always visible. So a new project-scoped type whose author forgot the method would have leaked task titles, project names and comment bodies to users with no access to the project, with no compile error and no test failure. Register now takes a factory returning PersistedNotification, which requires the method. Registering is what makes a notification persist, so a stored row that cannot be permission-checked no longer compiles. The three account-scoped types say so by returning 0 explicitly instead of by omission.
-
kolaente (29 Jul 26)
fix(notifications): check project access when reading notifications Notification rows outlived access. A subscription survives a project being unshared, so every notification already written for a revoked user stayed readable — comment bodies, task titles, project names, deletion notices. The read paths filtered on notifiable_id alone, with no permission check anywhere. #3325 stopped the sender writing new ones; this is the other half. The project a notification is about is persisted on the row when it is written and the read paths filter on it in SQL, so LIMIT, OFFSET and total are all computed on the filtered set. Notification types declare their project through a capability interface in pkg/notifications, the same way they already declare SubjectID, ThreadID and ToTitle — which is what lets the package below pkg/models stay ignorant of what a project is. project_id 0 means account-scoped and always visible, a positive value is checked against the projects the caller can read, and -1 marks a project-scoped row whose project could not be determined, so it is visible to nobody. Filtering reuses the existing accessibleProjectIDsSubquery, so the page query and the count cannot drift apart. A migration backfills existing rows from their stored payloads, resolving through soft-deleted tasks so task.deleted rows still land on their project. Covers every read path: the v1 and v2 list endpoints, mark-as-read (which echoes the payload back), the Atom feed, and the websocket push — the last of which is load-bearing, since a row is still written for a revoked subscriber. Deliberately no instance-admin bypass: notifications are always the caller's own, and being an admin says nothing about whether they should still read a comment out of a project they were removed from.
-
kolaente (28 Jul 26)
test(models): restore notification faking after user delete tests The user delete tests left the notification backend un-faked, leaking into whichever test ran next.
-
kolaente (28 Jul 26)
fix(notifications): don't notify subscribers who lost access to the entity Subscriptions outlive access: nothing purges them when a project is unshared, and access can change with no revocation event at all, so a user who can no longer open a task kept receiving its comment bodies, assignment details and deletion notices by mail and in the feed. Filter subscribers by current read permission when the subscription is fetched, so every listener is covered by one check. Rows are kept rather than deleted - a subscription is user intent and resumes if access does. GetSubscriptionsForDeletedTask keeps its own lookup because a soft-deleted task cannot be resolved back to its project, but it now reuses the same filter with the project id it already holds.
-
kolaente (28 Jul 26)
refactor(projects): resolve read permissions for many projects at once Checking one project per user at a time meant a query per pair. Resolve them in a batch so callers with a list of projects pay one round trip.
-
kolaente (28 Jul 26)
fix(projects): don't report database errors as a missing project A failed permission lookup was surfaced as ErrProjectDoesNotExist, so a database error read as "no such project" to every caller.
-
renovate[bot] (28 Jul 26)
chore(deps): update docker/login-action action to v4.5.2
-
kolaente (27 Jul 26)
fix(migration): prevent stack overflow on ticktick parentId cycles sortParentsBeforeChildren marked a task as placed only after recursing into its parent, so a parentId cycle in an uploaded TickTick export made place() recurse forever. That is a Go stack overflow, a runtime fatal error the recover middleware cannot catch, so a two-line CSV from any authenticated user took down the whole process. Track a tri-state per task and mark it before recursing, which breaks the cycle. Acyclic input is unaffected.
-
Tink (29 Jul 26)
fix(migration): detect sqlite indexes created with lowercase SQL (#3354)
-
renovate[bot] (29 Jul 26)
chore(deps): update dependency vite-plugin-vue-devtools to v8.2.1
-
renovate[bot] (28 Jul 26)
chore(deps): update dependency ip-address to v10.3.1
-
renovate[bot] (28 Jul 26)
fix(deps): update aws-sdk-go-v2 monorepo
-
renovate[bot] (28 Jul 26)
chore(deps): update dependency basic-ftp to v6.0.2
-
Frederick [Bot] (29 Jul 26)
chore(i18n): update translations via Crowdin
-
kolaente (28 Jul 26)
fix(filter): don't rewrite in/not in/like inside quoted values The filter preprocessing replaced " in ", " not in " and " like " with their fexpr sigils using blind whole-string replacements, corrupting any value that happened to contain those words: `title like 'stuff in progress'` became `title ~ 'stuff ?= progress'`, so the filter matched the wrong tasks or failed to parse with no hint as to why. Walk the filter instead and skip over quoted runs, matching fexpr's own scanner: both ' and " open a string and a backslash escapes the next character. An unclosed quote is treated as an ordinary character so bare values with an apostrophe keep working. " not in " is still matched before " in " so the longer operator wins.
-
kolaente (28 Jul 26)
fix(frontend): force rel=noopener on links in project descriptions ProjectInfo re-permits the target attribute via ADD_ATTR, so a link in a project description could open a page that keeps a live window.opener handle to the Vikunja tab and navigate it elsewhere. Force rel via an afterSanitizeAttributes hook rather than allowing it through ADD_ATTR, so a description supplying its own rel cannot drop noopener.
-
Tink (28 Jul 26)
feat(desktop): add a Content Security Policy to the app window (#3351)
-
kolaente (27 Jul 26)
fix(api/v2): stop leaking the healthcheck cause to unauthenticated callers GET /api/v2/health passed the raw error from health.Check() into huma.Error500InternalServerError, which serialized it into the response body. The endpoint is public, so a routine DB or Redis outage exposed internal hostnames, private IPs, ports, the DB technology and DB usernames to anyone who asked: {"title":"Internal Server Error","status":500, "detail":"Internal server error", "errors":[{"message":"dial tcp 127.0.0.1:6390: connect: connection refused"}]} Log the cause with context instead and return a bare generic 500.
-
kolaente (27 Jul 26)
fix(api/v2): strip internal error detail in NewError, not NewErrorWithContext The 5xx sanitizer lived in the NewErrorWithContext override, but huma's Error5xx* helpers (Error500InternalServerError and friends) call NewError directly, so anything built through them kept the raw cause in the problem+json `errors[]`. Huma's dispatch loop then writes an already-built StatusError as-is, so the framework could not re-sanitize it either. Move the strip into NewError so every 5xx passes through it by construction and no future handler can reintroduce the leak. NewErrorWithContext is now left at huma's default, which delegates to NewError — keeping both would log the same cause twice. 4xx errors keep their details, including ErrorDetailer locations and the Vikunja `code`/`i18n_params` fields. Huma's registration-time schema probe calls NewError(0, ""), which is below the threshold and unaffected.
-
kolaente (28 Jul 26)
feat(audit): audit full personal data export requests Around 38 events are registered for audit logging, including every admin action, but a full personal data export left no trace at all. It is dispatched from both v1 and v2, so one registration covers both.
-
kolaente (28 Jul 26)
fix(license): refuse redirects and use the SSRF-safe http client for checks The license servers are hardcoded, but the check client followed redirects without any policy and dialed without the SSRF guard, so a hijacked or poisoned license host could forward the license key to an internal address. Redirects are refused outright rather than capped: the check is a POST to a fixed JSON API that never redirects.
-
kolaente (28 Jul 26)
fix(gravatar): route avatar requests through the SSRF-safe http client avatar.gravatarbaseurl is operator-configurable, so the request destination was never a fixed constant - pkg/utils/avatar.go already uses the SSRF-safe client for the same job. The previous 5s timeout is kept as a context deadline so the configured (30s by default) client timeout does not apply here.
-
kolaente (28 Jul 26)
fix(unsplash): route api requests through the SSRF-safe http client doGet built its own http.Client, unlike its siblings in the same file which already use utils.NewSSRFSafeHTTPClient(). The previous 10s timeout is kept as a context deadline so the configured (30s by default) client timeout does not apply here.
-
kolaente (28 Jul 26)
fix(webhooks): bound the error response body read The webhook target URL is user-configured, so a hostile target can answer a delivery with a 4xx/5xx carrying an arbitrarily large body. That body was read whole into memory and written whole to the log. Cap the read at 4KiB, which is plenty for a diagnostic log line, matching the LimitReader already used for the license server response.
-
kolaente (28 Jul 26)
fix(auth): gate the v2 login route on local or ldap auth being enabled v1 only registers /login when local or LDAP auth is enabled, but v2 registered it unconditionally. With auth.local.enabled=false a pre-existing local password still authenticated on /api/v2/login. /logout stays unconditional - it terminates any session, OIDC included.
-
Tink (28 Jul 26)
fix(caldav): close username enumeration oracle in basic auth (#3349)
-
Tink (28 Jul 26)
fix(oauth2): burn authorization code even when validation fails (#3350)
Vikunja Security
Security Advisories (46)
- high Patched CVSS 8.1
GHSA-vvcv-vpph-h844 Link-share principal ID collision allows cross-account API token issuance and management
- high Patched
GHSA-rj9j-8772-4h6c Link-share token reads any tenant's kanban buckets and enumerates usernames/IDs instance-wide (BOLA)
- high Patched
CVE-2026-62367 OIDC email-fallback account linking ignores email_verified, enabling local-account takeover
- high Patched CVSS 8.1
CVE-2026-62376 Plaintext storage of password-reset/email-confirm tokens in database enables account takeover on DB read access
- medium Patched
CVE-2026-54766 Project duplication bypasses write-permission check on the target parent project
- medium Patched CVSS 5
CVE-2026-55067 Authenticated cross-tenant kanban-bucket relocation via `project_view_id` mass-assignment
- high Patched CVSS 8.1
CVE-2026-57458 Scoped API token can mint unrestricted OAuth session credentials
- high Patched CVSS 7.1
CVE-2026-55066 Cross-tenant IDOR in kanban move-task endpoint via unauthorized body task_id
- high Patched CVSS 8.1
CVE-2026-55065 Improper Authorization and Authorization Bypass Through User-Controlled Key in code.vikunja.io/api
- medium Patched CVSS 4.3
CVE-2026-55064 Incomplete fix for CVE-2026-35595: Write-only user can detach shared project from parent hierarchy via parent_project_id=0
- medium Patched CVSS 4.3
CVE-2026-40103 Scoped API tokens with projects.background permission can delete project backgrounds
- high Patched CVSS 7.4
CVE-2026-34727 TOTP Two-Factor Authentication Bypass via OIDC Login Path
- medium Patched CVSS 4.1
CVE-2026-35601 iCalendar Property Injection via CRLF in CalDAV Task Output
- medium Patched CVSS 5.4
CVE-2026-35600 HTML Injection via Task Titles in Overdue Email Notifications
- medium Patched CVSS 6.5
CVE-2026-35599 Algorithmic Complexity DoS in Repeating Task Handler
- medium Patched CVSS 5.4
CVE-2026-35602 File Size Limit Bypass via Vikunja Import
- medium Patched CVSS 4.3
CVE-2026-35598 Missing Authorization on CalDAV Task Read
- medium Patched CVSS 5.9
CVE-2026-35597 TOTP Brute-Force Due to Non-Functional Account Lockout
- medium Patched CVSS 4.3
CVE-2026-35596 Broken Access Control on Label Read via SQL Operator Precedence Bug
- high Patched CVSS 8.3
CVE-2026-35595 Privilege Escalation via Project Reparenting
- medium Patched CVSS 6.5
CVE-2026-35594 Link Share JWT tokens remain valid for 72 hours after share deletion or permission downgrade
- medium Patched
CVE-2026-33700 Link Share Delete IDOR — Missing Project Ownership Check Allows Cross-Project Link Share Deletion
- high Patched
CVE-2026-33668 Disabled/Locked User Accounts Can Still Authenticate via API Tokens, CalDAV, and OpenID Connect
- medium Patched CVSS 6.4
CVE-2026-33679 SSRF via OpenID Connect Avatar Download Bypasses Webhook SSRF Protections
- medium Patched CVSS 6.4
CVE-2026-33675 SSRF via Todoist/Trello Migration File Attachment URLs Allows Reading Internal Network Resources
- medium Patched CVSS 6.5
CVE-2026-33676 Cross-Project Information Disclosure via Task Relations — Missing Authorization Check on Related Task Read
- medium Patched CVSS 6.5
CVE-2026-33677 Webhook BasicAuth Credentials Exposed to Read-Only Project Collaborators via API
- high Patched CVSS 8.1
CVE-2026-33678 IDOR in Task Attachment ReadOne Allows Cross-Project File Access and Deletion
- high Patched CVSS 7.5
CVE-2026-33680 Link Share Hash Disclosure via ReadAll Endpoint Enables Permission Escalation
- critical Patched CVSS 9.1
GHSA-2pv8-4c52-mf8j Unauthenticated Instance-Wide Data Breach via Link Share Hash Disclosure Chained with Cross-Project Attachment IDOR
- high Patched
CVE-2026-33334 Any frontend XSS escalates to Remote Code Execution due to nodeIntegration in Vikunja Desktop
- high Patched
CVE-2026-33335 Arbitrary local application invocation via unvalidated shell.openExternal in Vikunja Desktop
- critical Patched
CVE-2026-33336 Remote Code Execution via same-window navigation in Vikunja Desktop
- medium Patched
CVE-2026-33313 IDOR in Task Comments Allows Reading Arbitrary Comments
- medium Patched
CVE-2026-33312 Read-only users can delete project background images via broken object-level authorization
- medium Patched
CVE-2026-33315 2FA Bypass via Caldav Basic Auth
- high Patched CVSS 8.1
CVE-2026-33316 Improper Access Control Enables Bypass of Administrator-Imposed Account Disablement
- medium Patched CVSS 5.7
CVE-2026-33473 TOTP Reuse During Validity Window
- high Patched
CVE-2026-33474 DoS via Image Preview Generation
- medium Patched CVSS 5.3
CVE-2026-29794 Rate-Limit Bypass for Unauthenticated Users via Spoofed Headers
- critical Patched CVSS 9.8
CVE-2026-28268 Account Takeover via Password Reset Token Reuse
- high Patched CVSS 7.2
CVE-2026-27819 Path Traversal in CLI Restore
- critical Patched CVSS 9.1
CVE-2026-27575 Weak Password Policy Combined with Persistent Sessions After Password Change
- high Patched CVSS 7.3
CVE-2026-27616 Stored Cross-Site Scripting (XSS) via Unsanitized SVG Attachment Upload Leading to Token Exposure
- medium Patched CVSS 6.1
CVE-2026-27116 Reflected HTML Injection via filter Parameter in Projects Module
- high Patched
CVE-2026-25935 XSS Via Task Preview
Vikunja Website
Website
Vikunja: The task manager you actually own
Vikunja is open-source task management you can self-host. Lists, Kanban, Gantt, and more — on your server or ours. Made and hosted in the EU.
Redirects
Does not redirect
Security Checks
All 65 security checks passed
Server Details
- IP Address 104.21.47.206
- Location San Francisco, California, United States of America, NA
- ISP CloudFlare Inc.
- ASN AS13335
Associated Countries
-
US -
DE
Safety Score
Website marked as safe
100%
Blacklist Check
vikunja.io was found on 0 blacklists
- AntiSocial Blacklist
- Artists Against 419
- Badbitcoin
- Bambenek Consulting
- CERT Polska
- CoinBlockerLists
- CRDF
- CryptoScamDB
- EtherAddressLookup
- EtherScamDB
- Fake Website Buster
- MetaMask EthPhishing
- NABP Not Recommended Sites
- OpenPhish
- PetScams
- PhishFeed
- PhishFort
- Phishing.Database
- PhishStats
- PhishTank
- Phishunt
- RPiList Not Serious
- Scam.Directory
- SecureReload Phishing List
- Spam404
- StopGunScams
- Suspicious Hosting IP
- ThreatFox
- ThreatLog
- TweetFeed
- URLhaus
- ViriBack C2 Tracker
Website Preview
Vikunja Docker
Container Info
Vikunja
The to-do app to organize your life.| Before use create custom template and Edit: VIKUNJA_SERVICE_FRONTENDURL , VIKUNJA_API_URL & VIKUNJA_SERVICE_JWTSECRET
Run Command
docker run -d \
-e PUID=${PUID} \
-e PGID=${PGID} \
-e PORT=${PORT} \
Compose File
version: 3.8
services:
vikunja:
environment:
PUID: 1000
PGID: 1000
PORT: Environment Variables
- Var Name Default
- PUID 1000
- PGID 1000
- PORT null
Vikunja Reviews
More Cloud Productivity Suites
-
A zero knowledge cloud productivity suite. Provides Rich Text, Presentations, Spreadsheets, Kanban, Paint a code editor and file drive. All notes and user content, are encrypted by default, and can only be accessed with specific URL. The main disadvantage, is a lack of Android, iOS and desktop apps - CryptPad is entirely web-based. You can use their web service, or you can host your own instance. Price for hosted: free for 50mb or $5/ month for premium.
-
A platform providing online services based on principles of freedom, privacy, federation and decentralization. It is an implementation of NextCloud, with strong encryption configured - it is widely used by journalists, activists and whistle-blowers. It is free to use, but there have been reported reliability issues of the cloud services.
-
A complete self-hosted productivity platform, with a strong community and growing app store. NextCloud is similar to (but arguably more complete than) Google Drive, Office 365 and Dropbox. Clear UI and stable native apps across all platforms, and also supports file sync. Supports encrypted files, but you need to configure this yourself. Fully open source.
-
An open source platform for self-hosting web apps. Once you've set it up, you can install items from the Sandstorm App Market with -click, similar to NextCloud in terms of flexibility.
About the Data: Vikunja
Change History
- Added #36
API
You can access Vikunja's data programmatically via our API. Simply make a GET request to:
https://api.awesome-privacy.xyz/v1/services/vikunja The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.
Share Vikunja
Help your friends compare Cloud Productivity Suites, and pick
privacy-respecting software and services.
Share Vikunja and Awesome Privacy with your network!