This page describes what happens between mvn package and the first 200 OK. It follows the code through vidocq-runtime-maven-plugin, vidocq-runtime-core and the SPI, and details the extension grammar.

Overview — from source to runtime

Diagram

Boot sequence diagram

Diagram

Phases 4 and 5 run in distinct virtual threads via StructuredTaskScope, allowing Cassini to register its routes while Mansart validates the database connection.

Extension mechanism

Public SPI

The vidocq-runtime-spi module exposes the contribution interfaces. The main classes shipped today:

Type Role

io.vidocq.runtime.spi.VidocqExtension

Marker interface declared via ServiceLoader or META-INF/services. An extension implements configure(ExtensionContext) to register itself.

io.vidocq.runtime.spi.ExtensionContext

Contribution API — access to the BuildItem registry, configuration, current phase.

io.vidocq.runtime.spi.VidocqConfiguration

Interface read at build time to resolve the extension’s options.

io.vidocq.runtime.spi.config.VidocqConfig

Re-exposed MicroProfile Config facade with Converter<T> and ConfigSource.

Source: vidocq-runtime-spi/src/main/java/io/vidocq/runtime/spi/.

APT processor

The vidocq-runtime-processor (derived from vauban-processor) scans classes annotated @BuildStep, @Recorder, @Path, @ApplicationScoped, etc. It produces:

  • an index META-INF/vidocq/extensions.list (one line per extension class);

  • an index META-INF/vauban/beans.list (reused by Vauban);

  • the configuration option file consumed by recorders.

No runtime reflection — the index is read at startup with a plain getResourceAsStream.

Class-File API generation

When a build step calls a method on a @Recorder, the engine does not execute the call: it records it. At the end of the graph, vidocq-runtime-core synthesises a RuntimeBootstrap class via Class-File API (JEP 484). That class contains a sequence of invokevirtual instructions which, replayed at startup, reproduce the desired effect with no reflection.

Concretely:

@Recorder
public class HttpRecorder {
    public RuntimeValue<Server> startServer(int port) { ... }
}

@BuildStep
@Record(RUNTIME_INIT)
public ServerStartedBuildItem boot(HttpRecorder rec, HttpConfig cfg) {
    rec.startServer(cfg.port());                  // <-- recorded
    return new ServerStartedBuildItem();
}

At compile time, vidocq-runtime-core emits:

// generated class — pseudo-code
class RuntimeBootstrap_HttpRecorder {
    static void __runtimeInit() {
        new HttpRecorder().startServer(8080);     // <-- replayed
    }
}

Threading model

  • Bootstrap — a single platform thread, no pool. Boot is intentionally sequential to ease diagnosis.

  • HTTP I/OExecutors.newVirtualThreadPerTaskExecutor() on the Chappe side. One connection = one virtual thread. No reactor, no callbacks.

  • Persistence — JDBC pool from Mansart with non-fair Semaphore. ScopedValue<Connection> propagates the current connection inside a transaction without ThreadLocal.

  • Periodic health checks — virtual threads scheduled via StructuredTaskScope.

No platform thread pool is created by default. The rule is documented in the workspace root CLAUDE.md.

  • Source: vidocq-runtime-core/src/main/java/io/vidocq/runtime/core/ (boot orchestrator)

  • SPI: vidocq-runtime-spi/src/main/java/io/vidocq/runtime/spi/

  • Maven plugin: vidocq-runtime-maven-plugin/

  • Examples: vidocq-runtime-examples/vidocq-runtime-cassini-rest-example/, vidocq-runtime-mansart-h2-example/

Next steps