vidocq-runtime-cli is the zero-dependency, JPMS-native command-line interface for the Vidocq runtime. It scaffolds new applications, runs them in a live-reloading dev mode, wraps the packaging goals of the Maven plugin, manages extensions and configuration, and diagnoses your environment — all from a single vidocq command. This page is a complete user guide; jump straight to Getting started — Hello World for a Hello-World walkthrough.

Coordinates

Artefact

io.vidocq.runtime:vidocq-runtime-cli:0.4.0-SNAPSHOT

JPMS module

io.vidocq.runtime.cli

Main class

io.vidocq.runtime.cli.VidocqCli

Dependencies

None beyond the JDK and io.vidocq.runtime.core (hand-rolled arg parser, pure Java 25)

Prerequisites

  • Java 25 (Temurin recommended) — the CLI refuses to run a project on an older JVM and vidocq doctor flags it.

  • Maven 3.9.16 — pinned per project via .sdkmanrc (sdk env). The build/clean commands reuse the project’s mvnw wrapper, falling back to mvn on the PATH.

Installing the vidocq command

The installer downloads the CLI distribution into ~/.vidocq/cli/<version> and puts a vidocq launcher on your PATH:

curl -fsSL https://vidocq.dev/install.sh | sh                            # latest release
curl -fsSL https://vidocq.dev/install.sh | VIDOCQ_VERSION=0.3.0 sh       # a given release
curl -fsSL https://vidocq.dev/install.sh | VIDOCQ_VERSION=snapshot sh    # latest build of main

On Windows (PowerShell), set the same variable before running the script:

irm https://vidocq.dev/install.ps1 | iex
$env:VIDOCQ_VERSION = 'snapshot'; irm https://vidocq.dev/install.ps1 | iex

VIDOCQ_VERSION=snapshot NEW takes the newest -SNAPSHOT of the CLI from the Central snapshot repository, where every build of main is published. A SNAPSHOT is downloaded again on every run, since it changes with every build; a release already installed is kept. A CLI installed this way scaffolds projects against its own SNAPSHOT runtime (see Trying a SNAPSHOT NEW). Run the installer again to go back to the latest release.

Without the installer, download vidocq-runtime-cli-<version>-cli.zip from Maven Central, unzip it anywhere and add its bin/ directory to your PATH. The launcher runs the CLI as a JPMS module:

java -m io.vidocq.runtime.cli/io.vidocq.runtime.cli.VidocqCli <command> [options]

Enable tab-completion with vidocq completion install — see vidocq completion.

Getting started — Hello World

This walkthrough scaffolds, inspects, and boots a brand-new Vidocq application in under a minute, using only the CLI.

1. Check your environment

vidocq doctor

doctor verifies the Java version, JAVA_HOME, the Maven wrapper, the project layout, the extensions in the project’s runtime dependencies, and your vidocq.properties keys. Fix any ✘ before continuing; ⚠ warnings are safe to ignore for now.

2. Scaffold the project NEW

vidocq create --name hello-world --group-id com.example

This generates a ready-to-build Maven project:

hello-world/
├── pom.xml                                  # inherits vidocq-runtime-parent, depends on core
└── src/main/
    ├── java/
    │   ├── module-info.java                 # module com.example.hello.world { requires io.vidocq.runtime.core; }
    │   └── com/example/hello/world/
    │       └── HelloWorldApp.java            # main() that boots the runtime
    └── resources/
        └── vidocq.properties                 # vidocq.http.port=8080

The generated entry point is a complete, runnable Vidocq application. Its main is a Vidocq.run trampoline: the runtime re-resolves the application into the Vauban layer, which reads the build-time generated code through META-INF/services without a provides in your module-info.java, the same way in the IDE, with vidocq start and in a jlink image:

package com.example.hello.world;

import io.vidocq.runtime.core.Vidocq;
import io.vidocq.runtime.spi.VidocqMain;

/**
 * Entry point. {@code main} is a trampoline: {@code Vidocq.run} resolves the application
 * into the Vauban layer and boots it there, from the IDE, {@code vidocq start} or a jlink
 * image alike. Put no code before it: it would run outside that layer.
 */
@VidocqMain
public final class HelloWorldApp {

    public static void main(String[] args) {
        Vidocq.run(args);
    }
}

The project name is converted to a PascalCase class name with an App suffix (hello-world → HelloWorldApp) and the default package is <groupId>.<name> with dashes turned into dots (com.example.hello.world). That package is also the module name, so every dot-separated part must be a Java identifier that is not a keyword: create refuses a name such as ft-030 (io.example.ft.030) and asks for --package. Override the package with --package and add extensions at creation time with -x (repeatable).

3. Move in and build

cd hello-world
vidocq build --skip-tests

vidocq build (no type) runs the Maven package lifecycle through the project wrapper and produces a standalone distribution. Add --dry-run to preview the exact Maven command without executing it.

4. Run it

vidocq start

The runtime boots in the foreground and awaits shutdown (Ctrl-C). To iterate with live reload instead, use dev mode:

vidocq dev

dev boots the runtime and watches src/main/java, src/main/resources and target/classes; any .java / .properties / .xml / .yml change is debounced and re-applies your configuration and resources in-process without leaving the CLI.

5. Add an HTTP endpoint

A bare core application boots the runtime but serves no routes. To expose a REST resource, add the Cassini REST extension and follow the full Jakarta REST + CDI walkthrough:

vidocq extension add cassini-rest

See Getting started for a complete REST + persistence example, including the resource class, the JPMS module declarations, and curl calls.

Command reference

Run vidocq help for the command list, or vidocq help <command> for per-command options.

Command Description

vidocq version

Print the CLI and runtime version string. A SNAPSHOT CLI adds its build time, 0.4.0-SNAPSHOT (built 2026-10-06T09:13:13Z), as the help header and vidocq info do NEW.

vidocq info

Display the CLI/Java version and the extensions of the project in the current directory.

vidocq doctor [--verbose]

Run environment & project health checks (see vidocq doctor).

vidocq create --name <name> …

Scaffold a new Vidocq Maven application (see vidocq create).

vidocq dev [--port] [--profile] [--debug]

Run the project in dev mode with live reload, mvn vidocq:dev (see vidocq dev).

vidocq start [--port] [--config] [--debug]

Build and run the project’s application, mvn vidocq:run (see vidocq start).

vidocq build [type] …

Build/package the project, wrapping the Maven plugin goals (see vidocq build & vidocq clean).

vidocq clean [--offline] [--dry-run]

Remove build output (mvn clean).

vidocq extension <list|add|remove> …

Manage project extensions (see vidocq extension).

vidocq config <get|set|list> …

Read or write keys in vidocq.properties (see vidocq config).

vidocq completion <bash|zsh>

Print a shell-completion script (see vidocq completion).

vidocq completion install|uninstall [bash|zsh]

Set up (or remove) completion in your shell’s rc file (see vidocq completion).

vidocq update [--check]

Update the CLI itself to the latest release, or the latest SNAPSHOT build (see vidocq update NEW).

vidocq help [command]

Show the command list or detailed help for one command.

vidocq create

Scaffolds a minimal Maven project (pom.xml, module-info.java, an …App entry point and vidocq.properties).

Option Description

--name, -n <name>

Project / artifact name (required).

--group-id, -g <groupId>

Maven groupId (default io.example).

--package <pkg>

Root Java package, also the module name (default <groupId>.<name>, dashes turned into dots); must be a valid Java package name.

--extension, -x <id>

Extension to enable; repeatable.

--parent-version <version>

Version of vidocq-runtime-parent the project inherits, and of every Vidocq dependency it declares (default: the runtime version of the CLI).

vidocq create --name my-api -g com.acme -x cassini-rest -x knock-health

Trying a SNAPSHOT NEW

Every build of main publishes the Vidocq SNAPSHOTs to the Central snapshot repository, https://central.sonatype.com/repository/maven-snapshots/. When the parent version is a SNAPSHOT, the generated pom.xml declares that repository, for dependencies and for plugins, with releases disabled: the SNAPSHOT artifacts come from there, everything else from Maven Central.

vidocq create --name my-api -x cassini-rest --parent-version 0.4.0-SNAPSHOT

A CLI installed from a SNAPSHOT (VIDOCQ_VERSION=snapshot, see Getting started) scaffolds with its own SNAPSHOT runtime, so --parent-version is not needed there. A SNAPSHOT changes with every push to main: run mvn -U package to pick up the newest one.

vidocq dev

NEW Runs mvn process-classes vidocq:dev in the project, through ./mvnw when there is one: the application starts with its dependencies and extensions on the module path, the dev console and a debug agent on port 5005, and restarts on every source change — exactly as mvn vidocq:dev does, see the Maven plugin for its settings (continuous testing, dev services, …).

Option Description

--port, -p <n>

HTTP listening port, passed as vidocq.chappe.listener.default.port so it outranks the project’s own vidocq.http.port.

--profile, -P <name>

Config profile to activate (-Dvidocq.profile, default dev).

--debug

Force the debug agent on (-Dvidocq.dev.debug=true); dev mode opens it by default, on this machine only.

Ctrl+C stops Maven and the application; the CLI returns once both are down. vidocq dev and vidocq start used to boot the runtime inside the CLI’s own JVM, which holds neither the project’s classes nor its extensions: the runtime came up empty, with nothing listening.

vidocq start

NEW Runs mvn vidocq:run in the project, through ./mvnw when there is one: the goal compiles the project and starts the application with its dependencies and extensions on the module path.

Option Description

--port, -p <n>

HTTP listening port, over the project’s own setting.

--config, -c <file>

Path to an external vidocq.properties file.

--debug

Open a debug agent on port 5005 (-Dvidocq.run.debug=true).

vidocq build & vidocq clean

Thin, coloured wrappers around the real vidocq-runtime-maven-plugin goals. The CLI resolves the project mvnw wrapper (walking the cwd and its ancestors) and falls back to mvn; Maven’s output streams straight to the terminal.

Type Maven invocation Result

package (default)

mvn package

Standalone distribution ZIP.

jlink

mvn package vidocq:jlink

Self-contained jlink runtime image (no java on target).

jpackage

mvn package vidocq:jpackage

Native installer (.dmg/.deb/.msi) or app-image.

docker

mvn package vidocq:docker

Dockerfile around the jlink image.

Shared options:

Option Description

--offline, -o

Run Maven offline (-o).

--skip-tests

Skip tests (build only, -DskipTests).

--dry-run

Print the resolved Maven command without running it.

-- <args…>

Pass everything after -- straight through to Maven.

vidocq build jlink --skip-tests
vidocq clean
vidocq build -- -Dvidocq.docker.build=true   # extra args after --

vidocq extension

Manages project-level extensions by editing ./pom.xml in place (text-based, preserving formatting; idempotent thanks to a StAX dependency scan).

Sub-command Description

list [--installed|--available|--all] [--refresh]

List extensions. --installed (default) lists the extensions of the project in the current directory; --available queries the registry; --all shows both.

add <id…>

Inject the matching <dependency> into pom.xml, the extension’s APT codegen bundle into the compiler’s annotationProcessorPaths when it ships one, and its directives into src/main/java/module-info.java NEW. The dependency and the codegen path carry the parent’s version, written out NEW.

remove <id…>

Remove the matching project-level <dependency>, its codegen path and its module-info.java directives — undoing add NEW.

An id may be a known short name (e.g. cassini-rest; the catalog now has grimm-openapi, grimm-openapi-ui, migration, flyway-migration and liquibase-migration too NEW), an explicit groupId:artifactId, or any name (resolved by convention as io.vidocq.runtime:vidocq-runtime-<id>-extension).

In module-info.java, add declares requires on the extension’s module, which re-exports most extension APIs (MicroProfile Health through knock-health, Fault Tolerance through heisenberg-fault-tolerance, …). cassini-rest also needs Jakarta REST, JSON-B, CDI and the Cassini API, requires static java.compiler for its generated adapters, and opens the application package its resources live in; mansart-data needs the same requires static and opens. Each added line ends with // vidocq:<id>, which is what remove takes back — never a line written by hand, except a requires of the removed extension’s own module, which no longer resolves. vidocq create -x writes the same directives.

NEW list --installed, vidocq info and vidocq doctor resolve the project’s runtime dependencies with Maven (maven-dependency-plugin:list, compile + runtime scopes, transitively) and keep every JAR whose module provides io.vidocq.runtime.spi.VidocqExtension. An extension pulled in by another one — chappe-webserver under cassini-rest — is listed and marked (transitive). Resolving takes as long as a Maven run, and may download artifacts on first use, so the answer is cached in ~/.vidocq/cache/project-extensions/, one file per project:

  • the entry is keyed by the content of pom.xml — any edit resolves again;

  • when nothing resolved is a SNAPSHOT, the entry is final: a release never changes;

  • otherwise it records the size and modification time of every SNAPSHOT JAR and POM in the local repository (and of a SNAPSHOT parent POM), and resolves again as soon as one of them was re-downloaded. A newer SNAPSHOT still sitting in the remote repository is not seen until something fetches it: vidocq extension list --refresh forces a resolution.

vidocq extension list --available
vidocq extension add cassini-rest knock-health
vidocq extension remove knock-health

The public extension registry (registry.vidocq.dev) is not yet live, so list --available currently serves a built-in offline catalog. The CLI reports the data source (live, cached, or offline).

vidocq config

Reads and writes the project’s vidocq.properties (project root, falling back to src/main/resources). set preserves comments, blank lines, key order and separator spacing, and creates the file when none exists.

Sub-command Description

get <key>

Print the value bound to <key>.

set <key> <value>

Set (or append) <key> to <value>.

list

Print every key=value pair.

vidocq config get vidocq.http.port
vidocq config set vidocq.http.port 9090
vidocq config list

vidocq doctor

Inspects the local environment and current project and prints a ✔/⚠/✘ report. It exits non-zero when a blocking issue is found, so it doubles as a CI pre-flight gate (vidocq doctor && vidocq build).

Check Meaning

Java version

✔ running JVM ≥ minimum (25); ✘ below minimum.

JAVA_HOME

✔ set and a directory; ⚠ unset / not a directory.

Maven wrapper

✔ mvnw in the cwd or an ancestor; ⚠ none found.

Vidocq project

✔ pom.xml references io.vidocq.runtime; ⚠ no pom / not a Vidocq pom.

Extensions

✔ ≥ 1 extension in the project’s runtime dependencies; ⚠ none found, or Maven could not resolve them.

Config

✔ no vidocq.properties or all keys recognised; ⚠ unknown vidocq.* key(s).

--verbose, -v appends a per-status summary footer and the minimum Java version. Warnings never fail the command; only ✘ checks set a non-zero exit code.

vidocq completion

Prints a self-contained completion script to stdout. It completes NEW commands, sub-commands (extension list|add|remove, config get|set|list, …), each command’s options, build types, and values: the catalog’s extension ids after extension add and create -x, less those the pom.xml already declares, the extensions the current pom.xml declares after extension remove, the keys of the project’s vidocq.properties after config get|set. These values are read by the script itself, so pressing TAB never starts a JVM.

# bash — load for the current shell
source <(vidocq completion bash)

# zsh — install into the completion path
vidocq completion zsh > "${fpath[1]}/_vidocq"

vidocq completion install NEW does it for you: it writes the script to ~/.vidocq/completion/vidocq.<shell> and adds a marked block sourcing it to ~/.zshrc or ~/.bashrc. The shell comes from $SHELL unless named (vidocq completion install bash). Running it again replaces the block instead of adding another. The script records which CLI build wrote it, and every vidocq run rewrites an installed script another build wrote, so the completion follows the CLI however it was replaced. vidocq completion uninstall removes the block and the script.

On macOS, Terminal starts bash as a login shell, which reads ~/.bash_profile, not ~/.bashrc. Source ~/.bashrc from ~/.bash_profile if it does not already.

vidocq update NEW

Updates the CLI itself, from the same repositories as install.sh:

  • a release CLI moves to the latest release on Maven Central (0.3.0 → 0.4.0);

  • a SNAPSHOT CLI moves to the latest build of the highest SNAPSHOT line in the Central snapshot repository. It records the build it installed, so the next run knows whether a newer one was published. A SNAPSHOT CLI without that record — installed by install.sh, or built locally — takes the published build only when it was deployed after the running CLI was built: a local build of newer sources is never downgraded.

The new version is unpacked next to the others in ~/.vidocq/cli and swapped in by renaming the directory, the vidocq launcher in ~/.local/bin (or $VIDOCQ_BIN) is pointed at it, and an installed completion script is regenerated. Earlier versions stay where they are.

vidocq update --check   # only report
vidocq update

VIDOCQ_CENTRAL and VIDOCQ_SNAPSHOTS point it at repository mirrors, as for install.sh.

Configuration & profiles

The CLI operates on the standard Vidocq configuration model. vidocq.properties lives in the project root or src/main/resources; profile overlays (vidocq-<profile>.properties) are layered by vidocq dev --profile. Any key is overridable via -Dkey=value, environment variables, or an external file — see Reference for the full ordinal hierarchy.

Exit codes

Code Meaning

0

Success (including doctor runs with only ✔/⚠ checks).

1

A usage error, a failed command, or a doctor run with at least one ✘ check.

Extending the CLI (plugin SPI)

Third-party modules can contribute extra top-level commands without forking the CLI by implementing io.vidocq.runtime.cli.spi.VidocqCliPlugin and declaring it as a ServiceLoader provider. Any command token that is not a built-in is matched against the registered plugins; built-ins always win, and vidocq help lists discovered plugins.

Next steps