### Run Docker Compose and expected startup log Source: https://vikunja.io/docs/docker-walkthrough Run 'sudo docker compose up' to start Vikunja. The expected log output shows the version and the HTTP server starting on port 3456, indicating successful setup. ```bash sudo docker compose up ``` ```log vikunja_1 | 2024-02-09T14:44:06.990677157+01:00: INFO ▶ cmd/func29 05d Vikunja version 0.23.0 vikunja_1 | ⇨ http server started on [::]:3456 ``` -------------------------------- ### Start Vikunja web server with `vikunja web` Source: https://vikunja.io/docs/cli Starts Vikunja's web server, serving the API and frontend. ```bash vikunja web ``` -------------------------------- ### Install frontend dependencies with pnpm Source: https://vikunja.io/docs/build-from-sources Installs all frontend dependencies using pnpm. Requires pnpm to be installed. ```bash pnpm install ``` -------------------------------- ### Install Vikunja with pacman Source: https://vikunja.io/docs/installing Install Vikunja from the pacman repository. After installation, configure Vikunja using the config file in /etc/vikunja/config.yml. ```bash sudo pacman -Sy vikunja ``` -------------------------------- ### Install Vikunja binary and create symlink Source: https://vikunja.io/docs/installing Unzips the Vikunja archive, makes the binary executable, and creates a symlink so `vikunja` can be run from anywhere. Installs to `/opt/vikunja` by default. ```bash mkdir -p /opt/vikunja unzip -d /opt/vikunja chmod +x /opt/vikunja sudo ln -s /opt/vikunja/vikunja /usr/bin/vikunja ``` -------------------------------- ### Install mage Source: https://vikunja.io/docs/magefile Install the mage CLI, which is required to run all Vikunja Mage tasks. Run this before using any of the commands described in the documentation. ```bash go install github.com/magefile/mage ``` -------------------------------- ### Create installation folder and copy binary Source: https://vikunja.io/docs/installing Creates /mnt/vikunja, copies the compiled binary from the build directory, and makes it executable. ```shell mkdir /mnt/vikunja cp /mnt/GO/code.vikunja.io/api/vikunja /mnt/vikunja cd /mnt/vikunja chmod +x /mnt/vikunja ``` -------------------------------- ### Enable and start Vikunja systemd service Source: https://vikunja.io/docs/installing Enables the Vikunja service to start on boot and starts it immediately. ```bash sudo systemctl enable vikunja sudo systemctl start vikunja ``` -------------------------------- ### Install Vikunja from Alpine repository Source: https://vikunja.io/docs/installing Add the Vikunja Alpine repository, import its signing key, and install Vikunja. Replace 'stable' with 'unstable' to track builds from the main development branch. ```bash # Add the APK signing key wget -O /etc/apk/keys/vikunja-apk.rsa.pub https://dl.vikunja.io/repos/apk/vikunja-apk.rsa.pub # Add the repository echo "https://dl.vikunja.io/repos/apk/stable/main" >> /etc/apk/repositories apk update && apk add vikunja ``` -------------------------------- ### GET /api/v1/projects/1/views/5/tasks - Example API request Source: https://vikunja.io/docs/filters Example API request showing filters, sort, and order parameters. Use the `filter` query parameter with URL-encoded operators (e.g., `%20%3C` for ` <`, `%20%26%26` for ` &&`). ```text GET /api/v1/projects/1/views/5/tasks?filter=due_date%20%3C%20now%20%26%26%20done%20%3D%20false&sort_by=priority&order_by=desc ``` -------------------------------- ### Install required packages on FreeBSD Source: https://vikunja.io/docs/installing Updates the package repository and installs nano, git, gmake, and the mage build tool via go install. ```shell pkg update && pkg upgrade -y pkg install nano git go gmake go install github.com/magefile/mage ``` -------------------------------- ### Build the API with Mage Source: https://vikunja.io/docs/build-from-sources Builds the API binary in the root of the repo. Requires Go 1.21+ and Mage installed. ```bash mage build ``` -------------------------------- ### Build all releases Source: https://vikunja.io/docs/magefile Builds binaries for all platforms and zips them with a copy of the templates folder. All built zip files are stored into dist/zips/, binaries into dist/binaries/, and binaries bundled with templates into dist/releases/. Uses xgo, which is installed automatically. ```bash mage release ``` -------------------------------- ### Register and implement a listener Source: https://vikunja.io/docs/events-and-listeners Example of registering a listener in RegisterListeners() and implementing the listener type. The listener must be registered to be executed; this example increments a task counter when the event fires. ```go // RegisterListeners registers all event listeners func RegisterListeners() { events.RegisterListener((&ListCreatedEvent{}).Name(), &IncreaseListCounter{}) } // IncreaseTaskCounter represents a listener type IncreaseTaskCounter struct {} // Name defines the name for the IncreaseTaskCounter listener func (s *IncreaseTaskCounter) Name() string { return "task.counter.increase" } // Handle is executed when the event IncreaseTaskCounter listens on is fired func (s *IncreaseTaskCounter) Handle(payload message.Payload) (err error) { return keyvalue.IncrBy(metrics.TaskCountKey, 1) } ``` -------------------------------- ### Install Vikunja via direct .deb download Source: https://vikunja.io/docs/installing Installs a downloaded .deb package. Installs to /opt/vikunja; configure via /etc/vikunja/config.yml. Default CORS requires setting service.publicurl or disabling cors.enable. ```bash dpkg -i vikunja.deb ``` -------------------------------- ### Install veans standalone binary Source: https://vikunja.io/docs/veans Installs the standalone veans binary from the downloads page and places it on the PATH. ```bash sudo install ./veans /usr/local/bin/ ``` -------------------------------- ### Install Vikunja via dnf/yum repository (RPM) Source: https://vikunja.io/docs/installing Creates the Vikunja yum repository file and installs the package. Replace 'stable' with 'unstable' in baseurl to track development builds. ```bash sudo tee /etc/yum.repos.d/vikunja.repo <<'EOF' [vikunja] name=Vikunja baseurl=https://dl.vikunja.io/repos/rpm/stable/$basearch enabled=1 gpgcheck=1 gpgkey=https://dl.vikunja.io/repos/gpg.key EOF sudo dnf install vikunja ``` -------------------------------- ### Start Vikunja service Source: https://vikunja.io/docs/installing Starts the Vikunja service using the service command. ```shell service vikunja start ``` -------------------------------- ### Install Vikunja via apt repository (Debian) Source: https://vikunja.io/docs/installing Adds the Vikunja apt repository and installs the package. Requires importing the GPG signing key. Replace 'stable' with 'unstable' to track development builds. ```bash # Import the GPG signing key curl -fsSL https://dl.vikunja.io/repos/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/vikunja.gpg # Add the repository echo "deb [signed-by=/usr/share/keyrings/vikunja.gpg] https://dl.vikunja.io/repos/apt stable main" \ | sudo tee /etc/apt/sources.list.d/vikunja.list sudo apt update && sudo apt install vikunja ``` -------------------------------- ### Install Vikunja via direct .rpm download Source: https://vikunja.io/docs/installing Installs a downloaded .rpm package. Configure via /etc/vikunja/config.yml. Default CORS requires setting service.publicurl or disabling cors.enable. ```bash rpm -i vikunja.rpm ``` -------------------------------- ### Check Vikunja Startup Logs Source: https://vikunja.io/docs/plugins Commands to check Vikunja logs for plugin loading status, covering systemd, direct run, and Docker setups. ```bash # View recent logs journalctl -u vikunja -n 50 ``` ```bash # Or if running directly tail -f vikunja.log ``` ```bash # Or with docker docker logs ``` -------------------------------- ### Create systemd service file for Vikunja Source: https://vikunja.io/docs/installing Systemd service unit file to automatically start Vikunja on boot. Save to `/etc/systemd/system/vikunja.service` and adapt paths and dependencies as needed. ```ini [Unit] Description=Vikunja After=syslog.target After=network.target # Depending on how you configured Vikunja, you may want to uncomment these: #Requires=mysql.service #Requires=mariadb.service #Requires=postgresql.service #Requires=redis.service [Service] RestartSec=2s Type=simple WorkingDirectory=/opt/vikunja ExecStart=/usr/bin/vikunja Restart=always # If you want to bind Vikunja to a port below 1024 uncomment # the two values below ### #CapabilityBoundingSet=CAP_NET_BIND_SERVICE #AmbientCapabilities=CAP_NET_BIND_SERVICE [Install] WantedBy=multi-user.target ``` -------------------------------- ### Sending a notification with Notify Source: https://vikunja.io/docs/notifications Example of sending a notification using the Notify method from the notifications package. It takes a notifiable and a notification as input. This example shows sending an EmailConfirmNotification when a new user registers. ```go n := &EmailConfirmNotification{ User: update.User, IsNew: false, } err = notifications.Notify(update.User, n) return ``` -------------------------------- ### Documenting an API endpoint with swagger annotations (Login example) Source: https://vikunja.io/docs/modifying-swagger-api-docs Comment above a handler function that the swagger CLI uses to generate API documentation. Includes summary, description, tags, accepted/produced content types, parameters, success/failure responses, and route. ```go // @Summary Login // @Description Logs a user in. Returns a JWT-Token to authenticate further requests. // @tags user // @Accept json // @Produce json // @Param credentials body user.Login true "The login credentials" // @Success 200 {object} auth.Token // @Failure 400 {object} models.Message "Invalid user password model." // @Failure 412 {object} models.Message "Invalid totp passcode." // @Failure 403 {object} models.Message "Invalid username or password." // @Router /login [post] func Login(c echo.Context) error { // Handler logic } ``` -------------------------------- ### Vikunja and Authelia OpenID setup Source: https://vikunja.io/docs/openid-example-configurations Set up Vikunja with Authelia as the OpenID provider. The first block is the Vikunja config, and the second is the corresponding Authelia client configuration. The redirect URI must be https://vikunja.mydomain.com/auth/openid/authelia. ```yaml auth: openid: enabled: true providers: authelia: name: Authelia authurl: https://login.mydomain.com clientid: clientsecret: ``` ```yaml - client_id: client_name: Vikunja client_secret: redirect_uris: - https://vikunja.mydomain.com/auth/openid/authelia scopes: - openid - email - profile ``` -------------------------------- ### Access Vikunja via IP Source: https://vikunja.io/docs/installing Shows the default IP and port where Vikunja is available after starting. ```text 192.168.1.XXX:3456 ``` -------------------------------- ### Run Vikunja with Docker and SQLite Source: https://vikunja.io/docs/installing Starts Vikunja with SQLite and exposes it on port 3456. Creates and chowns the required directories for the container user 1000. Requires setting VIKUNJA_SERVICE_PUBLICURL or disabling CORS with VIKUNJA_CORS_ENABLE=false. ```bash mkdir $PWD/files $PWD/db chown 1000 $PWD/files $PWD/db docker run -p 3456:3456 -v $PWD/files:/app/vikunja/files -v $PWD/db:/db vikunja/vikunja ``` -------------------------------- ### Stricter session lifetime configuration example Source: https://vikunja.io/docs/security-hardening Example YAML configuration showing stricter session lifetime values for the service.jwtttlshort, service.jwtttl, and service.jwtttllong keys. The comments indicate the intended meaning of each value. ```yaml service: jwtttlshort: 300 # 5 minutes jwtttl: 28800 # 8 hours of inactivity jwtttllong: 28800 # "Stay logged in" lasts no longer than a normal session ``` -------------------------------- ### Register file migrator HTTP routes Source: https://vikunja.io/docs/migrations Example of registering HTTP routes for a file migrator using the migrationHandler.FileMigratorWeb helper. The RegisterRoutes(m) method registers routes with the scheme /[MigratorName]/(auth|migrate|status). This example uses the vikunja_file.FileMigrator struct. ```go vikunjaFileMigrationHandler := &migrationHandler.FileMigratorWeb{ MigrationStruct: func() migration.FileMigrator { return &vikunja_file.FileMigrator{} }, } vikunjaFileMigrationHandler.RegisterRoutes(m) ``` -------------------------------- ### Pagination helper usage with xorm Limit Source: https://vikunja.io/docs/new-api-endpoints Use getLimitFromPageIndex(pageIndex, itemsPerPage) to calculate limit and offset for SQL queries. This example feeds the result directly into xorm's Limit function. The helper is only available in the models package. ```go projects := []*Project{} err := x.Limit(getLimitFromPageIndex(pageIndex, itemsPerPage)).Find(&projects) ``` -------------------------------- ### GET /api/v1/projects/{id}/webhooks Source: https://vikunja.io/docs/webhooks Lists all webhooks for a project. Requires authentication with an API token or JWT. ```APIDOC ## [GET] /api/v1/projects/{id}/webhooks ### Description List all webhooks for a project. ### Method GET ### Endpoint /api/v1/projects/{id}/webhooks ### Parameters #### Path Parameters - **id** (string) - Required - The ID of the project. ### Response #### Success Response (200) - **webhooks** (array) - List of webhook objects. #### Response Example { "webhooks": [] } ``` -------------------------------- ### Delete a user Source: https://vikunja.io/docs/cli Starts the user deletion process. Without --now, emails the user for confirmation; with --now, deletes immediately. Use with caution. ```bash vikunja user delete [id] [flags] ``` -------------------------------- ### vikunja dump Source: https://vikunja.io/docs/cli Creates a zip file with all Vikunja-related files, including config, version, all files, and the full database. Supports -p/--path to set the save folder and -f/--filename to set the dump filename, which defaults to 'vikunja-dump_YYYY-MM-DD_HH-II-SS.zip' and gets a .zip extension if missing. ```bash vikunja dump ``` -------------------------------- ### Implement AuthenticatedRouterPlugin and register authenticated routes Source: https://vikunja.io/docs/plugin-development Register routes that require user authentication (JWT/API token). Implement AuthenticatedRouterPlugin; routes are prefixed with /api/v1/plugins/. The example shows a GET /user-profile endpoint that fetches the current user and returns JSON. ```go type AuthenticatedRouterPlugin interface { Plugin RegisterAuthenticatedRoutes(g *echo.Group) } func (p *MyPlugin) RegisterAuthenticatedRoutes(g *echo.Group) { // Routes accessible at /api/v1/plugins/user-profile g.GET("/user-profile", handleUserProfile) } func handleUserProfile(c echo.Context) error { // Get database session s := db.NewSession() defer s.Close() // Get authenticated user user, err := user.GetCurrentUserFromDB(s, c) if err != nil { return echo.NewHTTPError(http.StatusUnauthorized, "User not found") } return c.JSON(http.StatusOK, map[string]interface{}{ "user_id": user.ID, "message": "Hello " + user.Username, }) } ``` -------------------------------- ### Register a cron task with cron.Schedule Source: https://vikunja.io/docs/cron-tasks Registers a cron task using the cron.Schedule method with a schedule string and a function to run. The example schedules a task to run every hour. Call this function in the FullInit() method of the init package to register it. ```go func RegisterSomeCronTask() { err := cron.Schedule("0 * * * *", func() { // Do something every hour } } ``` -------------------------------- ### MySQL/MariaDB database service configuration Source: https://vikunja.io/docs/full-docker-example Replace the `db` section in the Docker Compose examples with this to use MariaDB 10 as the database backend. Sets the character set and collation, defines credentials, and adds a healthcheck using `mysqladmin ping`. ```yaml db: image: mariadb:10 command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci environment: MYSQL_ROOT_PASSWORD: supersecret MYSQL_USER: vikunja MYSQL_PASSWORD: changeme MYSQL_DATABASE: vikunja volumes: - ./db:/var/lib/mysql restart: unless-stopped healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD"] interval: 30s start_period: 15s start_interval: 5s ``` -------------------------------- ### Database connection error log from api container Source: https://vikunja.io/docs/docker-walkthrough Error log indicating the api container cannot connect to the database. This often happens on first start with PostgreSQL because the database container takes a few seconds to become ready. The `restart: unless-stopped` option in the docker compose example will automatically restart the api container until the database is ready. ```log 2020/05/23 15:37:59 Config File "config" Not Found in "[/app/vikunja /etc/vikunja /app/vikunja/.config/vikunja]" 2020/05/23 15:37:59 Using default config. 2020-05-23T15:37:59.974435725Z: CRITICAL ▶ migration/Migrate 002 Migration failed: dial tcp 172.19.0.2:3306: connect: connection refused ``` -------------------------------- ### Create dummy index file for frontend Source: https://vikunja.io/docs/build-from-sources Creates a dummy index.html if the frontend was not built. Required before building the API if the frontend build was skipped. ```bash mkdir -p frontend/dist && touch frontend/dist/index.html ``` -------------------------------- ### Run docker compose in detached mode Source: https://vikunja.io/docs/docker-walkthrough After stopping the foreground stack with CTRL-C, run this command to start containers in the background. Use 'sudo docker compose logs' to check logs later. ```bash sudo docker compose up -d ``` -------------------------------- ### Vikunja database configuration with file-based password and SSL Source: https://vikunja.io/docs/security-hardening Example YAML configuration for Vikunja's database settings. It uses a file-based password for the database user, enables SSL verification with a custom CA certificate, and is intended for PostgreSQL. The password is loaded from a file to avoid exposing it in config files or environment variables. ```yaml database: type: "postgres" host: "db.internal:5432" user: "vikunja" password: file: "/run/secrets/db_password" database: "vikunja" sslmode: "verify-full" sslrootcert: "/etc/vikunja/db-ca.pem" ``` -------------------------------- ### Implement UnauthenticatedRouterPlugin and register public routes Source: https://vikunja.io/docs/plugin-development Register routes that do not require authentication. Implement UnauthenticatedRouterPlugin; routes are prefixed with /plugins/. The example shows a POST /webhook endpoint that binds JSON payload and returns a confirmation. ```go type UnauthenticatedRouterPlugin interface { Plugin RegisterUnauthenticatedRoutes(g *echo.Group) } func (p *MyPlugin) RegisterUnauthenticatedRoutes(g *echo.Group) { g.POST("/webhook", handleWebhook) } func handleWebhook(c echo.Context) error { var payload map[string]interface{} if err := c.Bind(&payload); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid payload") } log.Infof("Received webhook: %+v", payload) return c.JSON(http.StatusOK, map[string]interface{}{ "message": "Webhook processed", }) } ``` -------------------------------- ### Create rc.d startup script for Vikunja Source: https://vikunja.io/docs/installing Opens /etc/rc.d/vikunja in nano to create the FreeBSD service script. Paste the provided script content, save, and exit. ```shell nano /etc/rc.d/vikunja ``` ```shell #!/bin/sh . /etc/rc.subr name=vikunja rcvar=vikunja_enable command="/mnt/vikunja/${name}" load_rc_config $name run_rc_command "$1" ``` -------------------------------- ### Docker Compose for Vikunja with Traefik 2 Source: https://vikunja.io/docs/full-docker-example Use this Docker Compose file to run Vikunja behind Traefik 2. It assumes your domain is vikunja.example.com, the entrypoint is https, and the TLS cert resolver is acme. Ensure Vikunja has write permissions on the files directory before starting the stack. ```yaml services: vikunja: image: vikunja/vikunja environment: VIKUNJA_SERVICE_PUBLICURL: https:// VIKUNJA_DATABASE_HOST: db VIKUNJA_DATABASE_PASSWORD: changeme VIKUNJA_DATABASE_TYPE: postgres VIKUNJA_DATABASE_USER: vikunja VIKUNJA_DATABASE_DATABASE: vikunja VIKUNJA_SERVICE_SECRET: volumes: - ./files:/app/vikunja/files networks: - web - default depends_on: db: condition: service_healthy restart: unless-stopped labels: - "traefik.enable=true" - "traefik.docker.network=web" - "traefik.http.routers.vikunja.rule=Host(`vikunja.example.com`)" - "traefik.http.routers.vikunja.entrypoints=https" - "traefik.http.routers.vikunja.tls.certResolver=acme" db: image: postgres:18 environment: POSTGRES_PASSWORD: changeme POSTGRES_USER: vikunja volumes: - ./db:/var/lib/postgresql restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"] interval: 30s start_period: 15s start_interval: 5s networks: web: external: true ``` -------------------------------- ### SQLite Vikunja container configuration Source: https://vikunja.io/docs/full-docker-example Replace the `vikunja` section in the Docker Compose examples with this to use SQLite as the database backend. Sets the database path to `/db/vikunja.db` and mounts volumes for files and the database directory. The official image presets `VIKUNJA_DATABASE_PATH=/db/vikunja.db`; environment variables take precedence over the config file, so changing `database.path` in a mounted `config.yml` will not move the database. ```yaml vikunja: image: vikunja/vikunja environment: VIKUNJA_SERVICE_SECRET: VIKUNJA_SERVICE_PUBLICURL: http:/// # The official image already presets this to /db/vikunja.db; it is shown # explicitly here so the database file lives in the ./db volume below and # state is persisted even if the container is destroyed. VIKUNJA_DATABASE_PATH: /db/vikunja.db ports: - 3456:3456 volumes: - ./files:/app/vikunja/files - ./db:/db restart: unless-stopped ``` -------------------------------- ### Build frontend with VIKUNJA_FRONTEND_BASE Source: https://vikunja.io/docs/running-vikunja-in-a-subdirectory Run the build command with the VIKUNJA_FRONTEND_BASE environment variable set to the desired subdirectory path. Replace SUBPATH with the actual subdirectory. ```bash VIKUNJA_FRONTEND_BASE=/SUBPATH/ pnpm run build ``` -------------------------------- ### GET /api/v1/filters/{id} Source: https://vikunja.io/docs/filters Gets a specific saved filter by ID. ```APIDOC ## GET /api/v1/filters/{id} ### Description Gets a specific saved filter by ID. ### Method GET ### Endpoint /api/v1/filters/{id} ### Parameters #### Path Parameters - **id** (integer) - Required - ID of the saved filter. ### Response #### Success Response (200) - **id** (integer) - ID of the saved filter. - **title** (string) - Title of the saved filter. - **description** (string) - Description of the saved filter. - **filters** (object) - Filter configuration. - **is_favorite** (boolean) - Favorite flag. - **created** (string) - Creation timestamp. - **updated** (string) - Last update timestamp. #### Response Example { "id": 1, "title": "My urgent tasks", "description": "", "filters": { "sort_by": ["due_date"], "order_by": ["asc"], "filter": "priority >= 3 && done = false", "filter_include_nulls": false }, "is_favorite": false, "created": "2024-01-15T10:30:00Z", "updated": "2024-01-15T10:30:00Z" } ``` -------------------------------- ### Read config value from file (config.yml) Source: https://vikunja.io/docs/config-options Configure Vikunja to read the database password from a file on disk instead of being set directly. The path to the file is given as the child `file` of the target config key. If the file is not accessible, Vikunja will fail to start, and values from the file override any previously set values. ```yaml database: password: file: /path/to/password ``` -------------------------------- ### Build OS packages Source: https://vikunja.io/docs/magefile Builds .deb, .rpm, and .apk packages to dist/os-packages. ```bash mage release:packages ``` -------------------------------- ### vikunja help [command] Source: https://vikunja.io/docs/cli Shows more detailed help about any command. ```bash vikunja help [command] ``` -------------------------------- ### Build the frontend with pnpm Source: https://vikunja.io/docs/build-from-sources Builds the frontend, producing a static JS bundle in the dist/ folder. ```bash pnpm run build ``` -------------------------------- ### PATCH Partial Update Example Source: https://vikunja.io/docs/api-v2 Send a JSON Merge Patch to update only specified fields. The example updates the title of label 42. ```http PATCH /api/v2/labels/42 Content-Type: application/json {"title": "new title"} ``` -------------------------------- ### Install veans via package manager Source: https://vikunja.io/docs/veans Installs veans via the package manager on Debian/Ubuntu, Fedora/RHEL, Arch, or Alpine after adding Vikunja's package repositories. ```bash sudo apt install veans # Debian/Ubuntu sudo dnf install veans # Fedora/RHEL sudo pacman -S veans # Arch sudo apk add veans # Alpine ``` -------------------------------- ### Initialize veans with a Vikunja server Source: https://vikunja.io/docs/veans Initializes veans in a repository that can reach the Vikunja instance. Sets up OAuth 2.0 with PKCE, prompts for a project and Kanban view, creates canonical buckets, creates a bot user, mints an API token, and writes .veans.yml. ```bash veans init --server https://vikunja.example.com ``` -------------------------------- ### Make rc.d script executable and edit rc.conf Source: https://vikunja.io/docs/installing Makes the startup script executable and opens /etc/rc.conf to add the vikunja_enable line. ```shell chmod +x /etc/rc.d/vikunja nano /etc/rc.conf ``` -------------------------------- ### Active Directory minimal configuration (environment variables) Source: https://vikunja.io/docs/ldap Environment variable equivalents for the Active Directory minimal configuration. ```bash VIKUNJA_AUTH_LDAP_ENABLED=true VIKUNJA_AUTH_LDAP_HOST=ad.your-domain.tld VIKUNJA_AUTH_LDAP_BASEDN="DC=company,DC=com" VIKUNJA_AUTH_LDAP_USERFILTER="(&(objectClass=user)(sAMAccountName=%[1]s))" VIKUNJA_AUTH_LDAP_BINDDN="CN=ServiceAccount,OU=ServiceAccounts,DC=company,DC=com" VIKUNJA_AUTH_LDAP_BINDPASSWORD="very-secret-password" VIKUNJA_AUTH_LDAP_ATTRIBUTE_USERNAME=sAMAccountName ``` -------------------------------- ### Authentik configuration (environment variables) Source: https://vikunja.io/docs/ldap Environment variable equivalents for the Authentik configuration. ```bash VIKUNJA_AUTH_LDAP_ENABLED=true VIKUNJA_AUTH_LDAP_PORT=636 VIKUNJA_AUTH_LDAP_VERIFYTLS=false VIKUNJA_AUTH_LDAP_HOST=auth.your-domain.tld VIKUNJA_AUTH_LDAP_BASEDN=dc=ldap,dc=your-domain,dc=tld VIKUNJA_AUTH_LDAP_USERFILTER="(&(objectClass=user)(cn=%s))" VIKUNJA_AUTH_LDAP_BINDDN="cn=ldap-service-account,ou=users,dc=ldap,dc=your-domain,dc=tld" VIKUNJA_AUTH_LDAP_BINDPASSWORD="very-secret-password" ``` -------------------------------- ### vikunja migrate list Source: https://vikunja.io/docs/cli Shows a list with all database migrations. ```bash vikunja migrate list ``` -------------------------------- ### Implement the Base Plugin Interface Source: https://vikunja.io/docs/plugin-development Every plugin must implement this interface. Name() must return a unique identifier, Version() should follow semantic versioning, Init() is called once during startup to register event listeners and initialize resources, and Shutdown() is called during graceful shutdown to clean up resources. Additionally, the plugin must export a NewPlugin() function that returns your plugin instance. ```go type Plugin interface { Name() string // Unique plugin identifier Version() string // Plugin version (semver recommended) Init() error // Called during plugin initialization Shutdown() error // Called during graceful shutdown } ``` -------------------------------- ### GET /api/v1/filters Source: https://vikunja.io/docs/filters Lists all saved filters. ```APIDOC ## GET /api/v1/filters ### Description Lists all saved filters. ### Method GET ### Endpoint /api/v1/filters ### Parameters None ### Response #### Success Response (200) - **filters** (array) - List of saved filter objects. #### Response Example [ { "id": 1, "title": "My urgent tasks", "description": "", "filters": { "sort_by": ["due_date"], "order_by": ["asc"], "filter": "priority >= 3 && done = false", "filter_include_nulls": false }, "is_favorite": false, "created": "2024-01-15T10:30:00Z", "updated": "2024-01-15T10:30:00Z" } ] ``` -------------------------------- ### Recommended plugin project structure Source: https://vikunja.io/docs/plugin-development Shows the recommended project structure for a Vikunja plugin. Only `main.go` is required; other files are optional. Due to a yaegi limitation, the loader evaluates `.go` files individually, so multi-file plugins may encounter order-dependency issues. Keeping the plugin in a single `main.go` is safest. ```text my-plugin/ ├── main.go # Plugin implementation (required) ├── handlers.go # Route handlers (optional) ├── listeners.go # Event listeners (optional) └── migrations.go # Database migrations (optional) ``` -------------------------------- ### GET /user/sessions Source: https://vikunja.io/docs/api-login-session-migration Returns a paginated list of active sessions for the authenticated user. ```APIDOC ## GET /user/sessions ### Description Returns a paginated list of active sessions for the authenticated user. ### Method GET ### Endpoint /user/sessions ### Parameters #### Headers - **Authorization** (string) - Required - Valid JWT token. ### Response #### Success Response (200) - **id** (string) - Session UUID. - **device_info** (string) - User agent string. - **ip_address** (string) - IP address of the session. - **last_active** (string) - Timestamp of last activity. - **created** (string) - Timestamp of session creation. #### Response Example [ { "id": "550e8400-e29b-41d4-a716-446655440000", "device_info": "Mozilla/5.0 ...", "ip_address": "192.168.1.1", "last_active": "2026-02-25T10:00:00Z", "created": "2026-02-24T08:00:00Z" } ] ``` -------------------------------- ### Create a Yaegi plugin with main.go Source: https://vikunja.io/docs/plugin-development Create a Go source file for a Yaegi plugin. Must define a struct with Name, Version, Init, and Shutdown methods and export a factory function `NewPlugin` returning `plugins.Plugin`. This is the entry point required for plugin loading. ```go package main import ( "github.com/vikunja/vikunja/pkg/plugins" "github.com/vikunja/vikunja/pkg/log" ) type MyPlugin struct{} func (p *MyPlugin) Name() string { return "my-plugin" } func (p *MyPlugin) Version() string { return "1.0.0" } func (p *MyPlugin) Init() error { log.Infof("MyPlugin initialized") return nil } func (p *MyPlugin) Shutdown() error { log.Infof("MyPlugin shutting down") return nil } // Required: Export factory function for plugin loading func NewPlugin() plugins.Plugin { return &MyPlugin{} } ``` -------------------------------- ### GET /api/v1/user/settings/webhooks Source: https://vikunja.io/docs/webhooks Lists all webhooks for the current user. Requires authentication with an API token or JWT. ```APIDOC ## [GET] /api/v1/user/settings/webhooks ### Description List all webhooks for the current user. ### Method GET ### Endpoint /api/v1/user/settings/webhooks ### Response #### Success Response (200) - **webhooks** (array) - List of webhook objects. #### Response Example { "webhooks": [] } ``` -------------------------------- ### GET /api/v1/projects/1/views/5/tasks Source: https://vikunja.io/docs/filters Lists tasks with filters, sorting, and pagination. Filters are passed as query parameters. ```APIDOC ## GET /api/v1/projects/1/views/5/tasks ### Description Lists tasks in a project view with optional filters, sorting, and pagination. Filters are passed as query parameters. ### Method GET ### Endpoint /api/v1/projects/{projectID}/views/{viewID}/tasks ### Parameters #### Query Parameters - **filter** (string) - Optional - The filter query string (e.g., `done = false && priority >= 3`). - **filter_include_nulls** (boolean) - Optional - When `true`, includes tasks where the filtered field has no value set. Default: `false`. - **filter_timezone** (string) - Optional - Timezone for resolving relative date expressions like `now`. Uses the server timezone if unset. - **s** (string) - Optional - Simple text search across task titles and descriptions. Cannot be combined with `filter`. - **sort_by** (string) - Optional - Field(s) to sort by (comma-separated). Options: `id`, `title`, `done`, `done_at`, `due_date`, `start_date`, `end_date`, `priority`, `percent_done`, `created`, `updated`, `position`. - **order_by** (string) - Optional - Sort direction(s) for each `sort_by` field: `asc` or `desc`. - **per_page** (integer) - Optional - Number of results per page. ### Request Example GET /api/v1/projects/1/views/5/tasks?filter=due_date%20%3C%20now%20%26%26%20done%20%3D%20false&sort_by=priority&order_by=desc ### Response #### Success Response (200) - **tasks** (array) - List of tasks matching the filter. #### Response Example { "tasks": [] } ``` -------------------------------- ### Generate config.yml sample Source: https://vikunja.io/docs/magefile Generates a config.yml.sample file from the content in config-raw.json file. This happens automatically when building packages and only really needs to happen at that time. ```bash mage generate:config-yaml ``` -------------------------------- ### Create directories for SQLite volumes Source: https://vikunja.io/docs/full-docker-example Run these commands to create the `files` and `db` directories and set their ownership to user ID 1000, which is required for the Vikunja container to write to them. ```bash mkdir $PWD/files $PWD/db chown 1000 $PWD/files $PWD/db ``` -------------------------------- ### GET /oauth/authorize Source: https://vikunja.io/docs/oauth-server Redirects the user to the authorization page to obtain an authorization code. The user must be authenticated in the browser or webview. ```APIDOC ## [GET] /oauth/authorize ### Description Redirects the user to the authorization page to obtain an authorization code. The user must be authenticated in the browser or webview. ### Method GET ### Endpoint https:///oauth/authorize ### Parameters #### Query Parameters - **response_type** (string) - Required - Must be `code`. - **client_id** (string) - Required - Any string that identifies your application. Must match at token exchange. - **redirect_uri** (string) - Required - Where Vikunja redirects after authorization. Must use a `vikunja-` scheme. - **code_challenge** (string) - Required - Base64url-encoded SHA-256 hash of the `code_verifier` (see RFC 7636 Section 4.2). - **code_challenge_method** (string) - Required - Must be `S256`. - **state** (string) - Recommended - An opaque value to prevent CSRF. Your app should verify it when receiving the callback. ### Request Example https:///oauth/authorize?response_type=code&client_id=&redirect_uri=vikunja-flutter://callback&code_challenge=&code_challenge_method=S256&state= ### Response #### Success Response (302) Redirects to the `redirect_uri` with the authorization code and state. #### Response Example vikunja-flutter://callback?code=&state= ``` -------------------------------- ### Public Plugin Endpoint Request Source: https://vikunja.io/docs/plugins Example curl command to call a public plugin endpoint without authentication, noting it is rate-limited. ```bash # Example: Webhook endpoint curl -X POST \ -H "Content-Type: application/json" \ -d '{"event": "test"}' \ https://your-vikunja.com/plugins/webhook ``` -------------------------------- ### Build a native plugin with mage plugins:build Source: https://vikunja.io/docs/plugin-development Build a native plugin as a shared library using the mage command. The build produces a `.so` file in the plugins/ directory. After building, copy the `.so` file to your configured plugins directory, enable the plugin system in the Vikunja configuration, and restart Vikunja to load it. If loading fails, an error message is logged. ```sh # Build single plugin mage plugins:build path/to/your/plugin ``` -------------------------------- ### Success Log Output for Plugin Loading Source: https://vikunja.io/docs/plugins Expected log output indicating successful plugin loading and initialization. ```text INFO Loaded plugin example-plugin INFO example-plugin initialized ``` -------------------------------- ### Authenticated Plugin Endpoint Request Source: https://vikunja.io/docs/plugins Example curl command to call an authenticated plugin endpoint, requiring a valid JWT token or API key. ```bash # Example: Get user profile from auth plugin curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \ https://your-vikunja.com/api/v1/plugins/user-profile ``` -------------------------------- ### Configure initial config file location with VIKUNJA_SERVICE_ROOTPATH Source: https://vikunja.io/docs/config-options Shows setting an initial config location via environment variable and the config file it points to. The rootpath defined inside that config file is not used for further config search. ```bash export VIKUNJA_SERVICE_ROOTPATH=/etc/vikunja vikunja ``` ```yaml service: rootpath: /opt/vikunja ``` -------------------------------- ### Database hostname mismatch error Source: https://vikunja.io/docs/docker-walkthrough Error indicating the `VIKUNJA_DATABASE_HOST` value does not match the service name of the database container in `docker-compose.yml`. For example, if the database service is called `db`, the host must be `db`. ```log Migration failed: dial tcp: lookup db on 127.0.0.11:53: server misbehaving ``` -------------------------------- ### Run pre-conversion script and generate alterTables.sql Source: https://vikunja.io/docs/utf-8-settings Run the pre-conversion script to generate the actual migration script for your database structure. The output is saved to `alterTables.sql`. ```bash mysql -uroot < preAlterTables.sql | egrep '^ALTER' > alterTables.sql ``` -------------------------------- ### Run vikunja CLI as service user Source: https://vikunja.io/docs/systemd-hardening Runs Vikunja CLI commands as the service user to avoid writing root-owned files into the data directory, which would cause the service to fail to start afterwards. ```bash sudo -u vikunja vikunja migrate ``` -------------------------------- ### GET /user/sessions - Success Response Source: https://vikunja.io/docs/api-login-session-migration Response body returned when listing active sessions for the authenticated user. Returns a paginated list of session objects with id, device_info, ip_address, last_active, and created fields. ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "device_info": "Mozilla/5.0 ...", "ip_address": "192.168.1.1", "last_active": "2026-02-25T10:00:00Z", "created": "2026-02-24T08:00:00Z" } ] ``` -------------------------------- ### Format code Source: https://vikunja.io/docs/magefile Formats all source code using go fmt. ```bash mage fmt ``` -------------------------------- ### Initialize db fixtures for test package Source: https://vikunja.io/docs/testing Initializes db fixtures for a package's tests. Define which tables your package needs in your test init function; here, the users table is initialized and a fatal error is logged on failure. ```go err = db.InitTestFixtures("users") if err != nil { log.Fatal(err) } ``` -------------------------------- ### Deploy a Yaegi plugin with cp and systemctl Source: https://vikunja.io/docs/plugin-development Copy the plugin source directory into Vikunja's configured plugins directory and restart the Vikunja service. The restart triggers the yaegi interpreter to load the plugin; no build step is required. This feature is available from Vikunja version 2.3.0 onward. ```sh cp -r my-plugin/ /path/to/vikunja/plugins/ systemctl restart vikunja ``` -------------------------------- ### Conditional Requests (ETag) Source: https://vikunja.io/docs/api-v2 Single-resource GET responses include an ETag header. Clients can send it back as If-None-Match to receive a 304 Not Modified when the resource hasn't changed. If-Match and If-Unmodified-Since preconditions are also supported. ```APIDOC ## Conditional Requests (ETag) ### Description Single-resource GET endpoints return an ETag header. By sending this ETag back in the If-None-Match header, clients receive a 304 Not Modified response when the resource hasn't changed, saving bandwidth. If-Match and If-Unmodified-Since preconditions are also supported. ### Method GET ### Endpoint Any single-resource v2 endpoint (e.g., /api/v2/labels/{id}) ### Parameters #### Headers - **If-None-Match** (string) - Optional - ETag value from a previous response. - **If-Match** (string) - Optional - ETag value for precondition. - **If-Unmodified-Since** (string) - Optional - Date for precondition. ### Request Example ``` GET /api/v2/labels/42 If-None-Match: "etag-value" ``` ### Response #### Success Response (200) Returns the resource with an ETag header. #### Not Modified Response (304) Returned when the resource hasn't changed. #### Response Example No explicit response example provided in the source. ``` -------------------------------- ### Fetch and inspect OpenID Connect discovery document Source: https://vikunja.io/docs/openid Fetches the OpenID Connect discovery document to debug endpoint URL issues. Verify that the authorization_endpoint, token_endpoint, and userinfo_endpoint values point to the correct host and port. ```bash curl https://your-provider/.well-known/openid-configuration | jq . ``` -------------------------------- ### Download Vikunja binary Source: https://vikunja.io/docs/installing Downloads a copy of Vikunja from the download page for your architecture. ```bash wget ```