gpu4j
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
Target version 0.4.0 (set in gpu4j-core/pom.xml). Stamp this heading as
## [0.4.0] - YYYY-MM-DD when you tag.
On the version number. This range was planned as 0.3.0 and never tagged;
git tag in this repository lists nothing at all. Three further feature areas
then landed on top of it — graph capture and replay, the built-in GEMM, and
fp16 — followed by the gpu4j rename, so it ships as 0.4.0 instead. Entries and
@since tags below that say 0.3.0 describe work that first reaches a user in
0.4.0; they are left as written rather than restamped, because the alternative
is churning ~80 tags to hide a slipped release that the tag list already tells
you about.
Renamed
-
The repository, the modules and the artifacts are now gpu4j.
craton-gpu-java→gpu4j; thecraton-gpumodule →gpu4j-core(io.github.craton-co:gpu4j-core);craton-sidecar→gpu4j-sidecar. ThegroupIdis unchanged, because it is the namespace already being verified for Maven Central.The Java packages are deliberately unchanged. CratonVM's native-builtin registry binds by exact string —
craton/gpu/internal/Native, the three*Implhandle wrappers, and descriptors namingLcraton/gpu/GpuExecutor;and the rest — so renamingcraton.gpuis a coordinated two-repository change and belongs in the 1.0 cut, not in a repository rename.Automatic-Module-Namestayscraton.gpu/craton.sidecarto match.CratonVM's side was updated in the same change: its crate is now
craton-gpu4j, and itsbuild.rsresolves<repo>/gpu4j-core/src/main/javain addition to both older layouts, under both the new checkout name and the old one. That ordering matters —build.rsdoes not fail when it finds nothing, it emits an empty annotations jar and acargo:warning, leaving a VM that silently recognises no@GpuKernelat all.
Added
-
Graph capture and replay.
GpuExecutor.beginCapture()/endCapture()record a sequence of dispatches instead of running them;replay(long)runs the whole recorded sequence as one submission. A launch costs the host something whether or not the device is busy, and a decode step of a 1B-parameter transformer issues 453 of them: measured on an RTX 2060, 3.483 ms of host time to issue against 0.550 ms to replay.graphNodeCount(long)reports what a graph holds — worth checking against the number of dispatches made while capturing, because a graph with fewer nodes replays successfully and does less — andreleaseGraph(long)frees it. -
GpuExecutor.beginReplay(long)/endReplay(), for a caller whose argument values change between replays but whose kernels cannot be changed to read them from device memory. Re-issue the captured dispatch sequence between the two calls; each dispatch rewrites the arguments of its node rather than launching. The sequence is checked against the captured one, and a mismatch refuses rather than submitting: a partially-updated graph would run some nodes with this iteration's arguments and the rest with the previous iteration's, and produce a plausible wrong answer. -
GpuBlas— a built-in matrix multiply.C[MxN] = A[MxK] * B[KxN], row-major and unpadded, overGpuArrayhandles so a weight matrix uploaded once stays resident. Either operand can be readTranspose.TRANSPOSEDin place, with no duplicate tensor and no extra pass.gemmReferenceis the plain triple loop, accumulating indoubleon purpose, so a GPU result has something independent to be checked against.This cannot be a
@GpuKernelmethod: a GEMM is three nested loops wanting a shared-memory tile and a barrier, and the bytecode lowering handles one counted loop or a two-level flattened nest. It rejects the shape rather than mis-lowering it, so a hand-written three-loop Java matmul silently runs on the CPU. The kernel ships with the VM and this class is the typed door to it. -
fp16.
Halfconverts betweenfloatand IEEE-754 binary16, in pure Java because the JDK'sFloat.floatToFloat16arrived in Java 20 and this library targets 17.HalfTestchecks it against the JDK's own implementation exhaustively — all 2^32 floats and all 2^16 half bit patterns — and they agree bit for bit on every input except NaN, where they agree on NaN-ness.GpuArray.wrapHalf(short[])andallocateHalf(int)carry the bits, andGpuBlas.gemmHalfmultiplies them with an fp32 accumulator. -
GpuArray.copyFromHost(...)— overwrites a resident array in place, keeping its device pointer. The counterpart oftoHost(dest), and the way to give a captured graph new input: a replay runs against the pointers it was captured with, so allocating a freshGpuArrayis precisely what does not work. -
LocalModelServer— startsllama-serverorsd-serveron a resolved model, waits until it is genuinely ready, evicts on model switch, and stops it on close.SidecarConfig.llmGpuLayers()carries the-nglknob that used to live on the deleted engine. -
GpuStream.dispatch(...)andGpuStream.synchronize()— ordered, stream-scoped submission.GpuStreamwas a handle and aclose()with nothing that accepted it, while the fire-and-forget dispatch path's entire safety argument rested on stream ordering. The stream owns the submission handles it issues and releases them onclose(), so the leak the bare-handle API makes possible is not expressible here. -
GpuExecutor.prepare(...)returningPreparedKernel— resolve a kernel target once instead of re-proving the same three strings on every dispatch. Its typed overloads skip the argument walk entirely. -
craton.gpu.testing.GpuTestBridge— the supported way to runcraton.gpuagainst a stand-in backend. Previously the swap point was package-private, so the only route was reflection into the privateNative.BRIDGEfield, which this repository's own example documented as the pattern. -
Automatic-Module-Name(craton.gpu,craton.sidecar) on both jars. -
GpuDevice—count(),get(int),all(),queryable(), and compute capability. Built on four entry points CratonVM has always registered and that nothing on the Java side ever called, so there was no way to ask how many devices existed:GpuExecutor.open(3)on a one-GPU box was a native failure carrying a string. -
GpuArray.allocateInt/Long/Float/Double(int)— device-resident buffers with no host mirror, for the pipeline intermediates the host never reads. ThearrayAllocate*natives have been registered by CratonVM since 2026-07-11 with no Java caller. -
GpuArray.toHost(dest)uses the direct read-back entry point when the bridge has one, removing a full-size allocation and a copy per call. These overloads previously only bounded the caller's own garbage, not the total. -
GpuFuture.get(long, TimeUnit)uses a blocking native wait when the bridge offers one, instead of pollingfutureStatuson a sleep loop. -
craton.gpu.internal.ImmediateFutures— the single already-resolvedGpuFutureimplementation, replacing three near-identical private copies that had drifted apart. -
Optional
NativeBridgeentry points for structured error classification (futureErrorKind), a blocking timed wait (futureAwait), direct read-back (arrayToHostInto) and stream-scoped dispatch (streamSubmitMethod,streamSynchronize). All defaulted; each caller degrades to the previous behaviour when a bridge does not implement them. -
The first GPU-tagged tests in either project, and a
gpu-testsprofile in craton-gpu to match craton-sidecar's. -
.gitattributesand.editorconfig. Line-ending behaviour previously depended on each contributor'score.autocrlf; two tracked files already had mixed endings within a single file. All 88 tracked files are now normalised to LF in the index. -
.github/workflows/codeql.yml(CodeQL static analysis, both projects, weekly plus per-push). -
New
gpu4j-sidecarmodule (introduced ascraton-sidecar): a supervisor for a local inference server, with a runtime-changeable active-model registry (ModelConfig) seeded fromCRATON_LLM_MODEL/CRATON_DIFFUSION_MODEL/CRATON_AI_MODELS_DIR. Independent of the CratonVM native bridge — seedocs/SIDECAR_SETUP.md. It launches each backend's prebuilt Windows+CUDA server binary (llama-server.exe,sd-server.exe) as a child process and talks to it over HTTP (SidecarProcess,SidecarConfig) rather than through an in-process JNI/JNA binding: neither project publishes a prebuilt Windows+CUDA binding library, so this is the path that works without a C++ toolchain.(This entry described a prompt-and-inference API when it was written. That half was removed later in the same unreleased range — see Removed — so what it describes now is the supervisor, which is what actually ships.)
-
GpuExecutor.releaseSubmission(long)— frees a submission handle returned bydispatchNamedHandle. Defaulted on the interface, so no implementor breaks. Without it there was no way at all to give such a handle back; see Fixed. -
GpuExecutor.dispatchNamedHandle/awaitSubmission— fire-and-forget dispatch that skips theGpuFuturewrapper, for queueing a chain of kernels on one stream and waiting only on the last. (Present in the tree since the 0.3.0 work but never recorded here.) -
craton.gpu.internal.GpuErrors— classifies a native failure string intoGpuCompileException/GpuOutOfMemoryException/GpuLaunchException, used byGpuFuture.get()andawaitSubmission. -
GpuException(Throwable, int),GpuCompileException(String, Throwable, int, String)andGpuLaunchException(String, Throwable, int, String, String)constructor overloads. -
DiffusionOptions.defaults(), plus validation of every component. -
build.qualifierMaven property on both POMs, appended to the artifact file name. Empty by default, so release output is unchanged. -
CI now builds and tests
craton-sidecar(previously never built by CI at all) and compiles and runsexamples/Demo.java. CI also triggers on pushes todev. -
Dependabot now watches
craton-sidecar's dependencies.
Changed
-
Breaking.
craton-java-aiis renamedcraton-sidecar; packagecraton.aibecomescraton.sidecar, the directory and artifactId follow, anddocs/AI_SETUP.mdbecomesdocs/SIDECAR_SETUP.md. The old name promised an AI library and the module supervises subprocesses; worse, sitting besidecraton-gpuit implied GPU-from-Java, which is the one thing it deliberately does not do.AiExceptionandAiInferenceExceptioncollapse into a singleSidecarException, the root of the module's hierarchy, for the same reason: inference is not something this module can fail at. The system propertycraton.ai.llm.gpuLayersbecomescraton.sidecar.llm.gpuLayers; environment variable names are unchanged. -
GpuFutureImplcarries its completion state in onevolatile intinstead of twoAtomicBooleanfields: two fewer heap objects per future, on a path that mints one per kernel and where building the future was already measured as the largest single item in a dispatch. -
Argument validation for a named dispatch lives in one place (
craton.gpu.internal.KernelArgs) rather than inline inGpuExecutor.submit, so the executor, stream and prepared-kernel paths cannot disagree about what a valid dispatch looks like. -
Breaking (behavioural).
GpuFuture.thenApplyGpunow reports every failure through the returned future and never throws. It used to do both depending on which stage failed — an upstream failure came back as a failed future, a dispatch failure was thrown — which a caller could not tell apart without reading the implementation. -
GeneratedImagebehaves like a value: content-basedequals/hashCode, atoStringthat does not print[B@1b6d3586, and copies in and out so the array cannot change under its holder. AddsbyteCount(),writeTo(OutputStream)andwriteTo(Path)as the copy-free paths. -
StreamCleanercreates itsCleanerlazily. It used to spawn a daemon thread the moment the class initialised, which happens as soon as anything touchescraton.gpu— including a process that only reads an enum constant. -
JaCoCo 0.8.12 → 0.8.15, which removes the wall of
IllegalClassFormatExceptioninstrumentation errors on current JDKs; Mockito 5.12.0 → 5.23.0, which removes the-Dnet.bytebuddy.experimental=trueworkaround from both projects. -
All six GitHub Actions references pinned to commit SHAs.
-
GpuFuture.get(long, TimeUnit)consults the memoized outcome before pollingfutureStatus, removing a native round-trip per poll (up to ~1000/second per waiter at the 1 ms steady-state interval). -
StreamCleanerbinds theNativeBridgeat registration time rather than resolving it when cleanup runs, so a handle is always released by the backend that allocated it. -
docs/SIDECAR_SETUP.mdrewritten. It described an in-process JNA/JNI design the code does not use: acraton.ai.diffusion.StableDiffusionNativeclass that does not exist, and ade.kherud:llama-javadependency the POM explicitly does not declare. Both engines drive a prebuilt server binary over HTTP. -
README corrected: the exception list omitted the three
GpuExceptionsubtypes,StreamCleanerwas described as cleaning up streams only, the 0.3.0 dispatch API was missing, and the CratonVMbuild.rssource path quoted was not where this repository lives.
Deprecated
-
Native.futureGetErrorMessage, in favour ofNative.futureGetError. CratonVM registers both names to the same Rust function, and every call site here tries one and falls back to the other. -
GpuExecutor.handleForDispatch(), for removal in 1.0.0. Nothing in the library has read it sincedispatchNamedlanded, and it hands out a raw native pointer that outlives the closed-state check that guarded it.
Removed
-
Breaking.
craton-sidecar's inference client:LlmEngine,DiffusionEngine,LlamaCppLlmEngine,StableDiffusionCppEngine,GeneratedImage,DiffusionOptions, and the hand-rolledJsonLite. What remains is the supervisor, which is the part that was not commodity.llama-serverspeaks the OpenAI wire protocol, so the ~520 lines of HTTP and JSON here duplicated what LangChain4j, Spring AI and the OpenAI Java SDK already do against that endpoint — and did it worse:JsonLitewas a hand-rolled parser this audit found three encoding defects in. Callers now point an HTTP client of their choice atLocalModelServer.baseUri().AiInferenceExceptionis renamedSidecarException, since inference is no longer something this module can fail at. -
Breaking.
@LlmPrompt,@DiffusionPrompt,@PromptVar,AiClients,PromptInvocationHandlerandTemplateRenderer— the annotation-driven prompt layer — along with thecraton-sidecar-servicetriage demo that existed to showcase it. Together with the inference client removed above, that leaves the supervisor: start a server withLocalModelServer, then point an HTTP client of your choice atbaseUri().Two behaviours the layer provided now belong to the caller: interface validation at wiring time (the compiler covers this when prompts are built in Java), and reading
ModelConfigfresh on every call, which is what makes a runtime model switch take effect on the next request. -
DiffusionOptions.from(DiffusionPrompt), with the annotation it read.
Fixed
-
examples/Demo.javadid not compile.SimExecutornever grew the seven graph methodsGpuExecutorgained in 0.3.0. The CI step that compiles and runs the example exists to catch exactly this — it was added after the same drift once before — and it happened again anyway. The example now implements them, and exercisesGpuBlas,Half,copyFromHostand the graph API, none of which it covered while CI described it as a smoke test of "the whole public surface". -
GpuBlascould free its own operands mid-call.dispatchextracted threeGpuArrayhandles and oneGpuStreamhandle as barelongs and then calledgemm. Eachhandle()carries areachabilityFence, but that fence ends whenhandle()returns; from there the caller holds onlylongs, which keep nothing alive, so the collector was free to runreleaseArrayon a buffer the GEMM was reading. Every other dispatch path was safe by accident, because it passes theGpuArrayobjects to the native call. Fenced explicitly, andGpuStreamImpl.handle()now fences likeGpuArray.handle()does. -
The timed
getdefeated its own blocking wait.GpuFuture.get(long, TimeUnit)passed the exponential-backoff interval — capped at 1 ms — toNativeBridge.futureAwaitinstead of the deadline, so a bridge implementing a real device wait was still woken up to a thousand times a second and still paid a native crossing per wake, which is exactly the cost that entry point was added to remove. It is now given a 50 ms slice of the remaining deadline: sliced rather than handed the whole thing because a native wait does not observeThread.interrupt(), and costing nothing in latency becausefutureAwaitreturns on completion rather than sitting out its timeout. The backoff now applies only to theAWAIT_UNSUPPORTEDpolling path, and an unsupported answer is remembered for the rest of the call. -
GpuStream.dispatchdropped a refused kernel silently. It returned the backend's0sentinel while its javadoc documented five exceptions and no zero return — and the usage that javadoc documents (for (l : layers) s.dispatch(...); s.synchronize();) never inspects the value, so a refused kernel produced a stale answer rather than a failure. It now throwsGpuException, naming the kernel and the stream, like every sibling path in the library. -
GpuStream's pending-submission queue was unbounded. The queue exists to fix a leak — nothing collects a bare submission handle — and turned it into a deferral, which is worse than it sounds: an un-finalized submission owns host-side writeback buffers, device buffers, and a GC-critical token that stops CratonVM's collector. A generation loop issuing 453 kernels per token over a 256-token reply would hold ~115,000 of them. It now releases finished submissions once the queue passes 1024, stopping at the first one still running (stream order means everything after it is running too), and holds a growablelong[]instead of aList<Long>. -
fp16 buffers were write-only.
wrapHalfandallocateHalfproduced arrays with notoHost(short[])and nocopyFromHost(short[]), andprimitiveArrayLengthhad noshortbranch — so the buffer-reusing read-back path did not exist for the element type that exists for inference loops.KernelArgsalso refusedshort[],byte[]andboolean[]as kernel arguments, which was a Java-side veto on a working path: CratonVM has marshalled all three since 2026-09-02. -
GpuArray.toHost()did not null-check the bridge. It handed whateverarrayToHostreturned into an already-completed future, so anullfrom the backend became a successful future carryingnulland aNullPointerExceptionsomewhere unrelated. The buffer-reusing overloads had checked this since they were written. -
The GEMM shape diagnostic named the wrong dimension. Per-operand checking reused the three-dimension message with a placeholder, so
gemm(a, b, c, 4, 8, 0)reported"M=4, N=0, K=1"— K renamed to N, and a K of 1 the caller never passed. It now names the operand and its shape:"A would be 4x0". -
A handle could be registered against the wrong backend. The
GpuArrayfactories readNative.bridge()twice, once to allocate and once to register the cleaner, so aGpuTestBridge.installbetween the two bound the handle to a backend that never made it.StreamCleaner.registernow takes the bridge the allocation actually went through. -
GpuExecutorImpl.graphNodeCountandreleaseGraphwere the only methods in that class that took neither the lifecycle read lock nor the closed check. Harmless in itself, and an unexplained exception to an invariant the class documents at length. Both now take the lock; neither throws on a closed executor —graphNodeCountanswers-1andreleaseGraphis a no-op, so it stays usable from thefinallyblock it belongs in. -
The optional native entry points with no fallback —
arrayAllocate*,arrayWrapShort,streamSubmitMethod,streamSynchronizeandgemm— threw and caught anUnsatisfiedLinkErroron every call on a VM that does not register them. They now memoise the answer like the sentinel-returning ones, through a sharedProbe.gemmis on the hot path. -
GpuDevice.all()asked the bridge for the device count n+1 times to enumerate n devices, becauseget(int)re-queried it for its own bounds check. -
Removed an unused
CompletableFutureimport fromGpuArray, left behind when the already-resolved future classes moved toImmediateFutures. -
Javadoc that never rendered: a doubled doc comment in
SidecarConfigthat silently discarded theenvPortexplanation, prose after a block tag inSidecarProcess.awaitReady, duplicated@throwsonLocalModelServer.llm()anddiffusion(), and inline@sincetags on threeGpuArray.copyFromHostoverloads. The release profile runs javadoc withfailOnError=true. -
GpuExecutor's class javadoc still said streams were "a placeholder for future enqueue APIs" two releases afterGpuStream.dispatchandsynchronize()landed as the recommended path for a chain. -
The Maven Central contact of record was
dev@craton-co.github.io, which cannot receive mail:craton-co.github.iois a GitHub Pages hostname with no MX record. The<email>element is removed rather than replaced with another address that might also be wrong; the issue tracker is the contact that works. -
project.build.outputTimestampwas still 2026-08-28, which its own comment says to bump on every release. -
GpuFuture.cancel()threw instead of returningfalse. Java has always calledNative.futureCancel; CratonVM never registered it — the name appears in no.rsfile in that workspace — so every cancellation attempt on a real CratonVM raisedUnsatisfiedLinkErrorfrom inside a method whose entire documented behaviour is to answerfalsewhen it cannot do the job. Fixed on the CratonVM side (fix(craton-gpu): register Native.futureCancel); it now answers "rejected", which is what the Javadoc already promised, because no device-side cancellation primitive exists. -
The
gpu-testsprofile never included anything. A profile's plugin<configuration>merges into the base one, so the profile's empty<excludedGroups/>did not clear the inheritedgpu. Driven by a property now, and verified: 317 → 322 tests in craton-gpu, 67 → 71 in craton-sidecar. -
Critical use-after-free issues in the executor, future, array, and stream lifecycle paths
-
JsonLitestring extraction used a backtracking regex ((?:[^"\\]|\\.)*) that recurses per matched character in Java regex engine; a large value (a diffusion response base64-encoded PNG, several hundred KB) reliably threw an uncaughtStackOverflowError. Rewritten to scan character-by-character. -
LlamaCppLlmEnginesent prompts to llama-server raw/completionendpoint (no chat markup), which caused instruct-tuned models - observed on Llama-3.1-8B-Instruct - to produce rambling, repetitive output that ran to the token cap instead of stopping naturally. Switched to/v1/chat/completionsand reducedmax_tokens512 to 256. -
SidecarProcessleaked its child process (llama-server.exe/sd-server.exe, plus the VRAM its model held) whenever the JVM exited without an explicitclose()call - no shutdown hook was registered. EachSidecarProcessnow registers one, removed again on a normalclose()so hooks do not accumulate across model switches. -
dispatchNamedHandleleaked a native submission record per kernel. The fire-and-forget path trades theGpuFuturewrapper for speed, and with it theCleanerregistration that would have freed the handle — but nothing replaced it, and no API existed to release one. On the 453-kernels-per-token decode the API was designed for, that is 453 leaked records per token. -
GpuFutureImplraced on its result cache. The resolved value lived in three plain fields written under the read lock. A read lock is shared, so concurrent resolvers raced on them, and releasing a read lock creates no happens-before edge to another read-lock holder: a caller could seeresultCached == truebeside a stale null result and return that as the kernel's output. Now one volatile immutable holder. -
cancel()could not cancel anything. It took the write lock for the whole operation including the non-blocking native cancel request, whileget()holds the read lock across its blockingfutureSynchronize— so the abort could not be issued until the kernel it was cancelling had finished. Split into a read-locked request and a write-locked free. -
Failing kernels always threw a bare
GpuException.GpuException's javadoc advertises three subclasses for targetedcatchclauses; nothing in the library ever threw one, so all three were unreachable. -
The test suite failed on
dev.StreamCleanerresolvedNative.bridge()at cleanup time, so the JDKCleanerthread calledreleaseFutureon whichever mock a later test had installed, corrupting Mockito's stubbing state (GpuFutureImplTest.toCompletableFutureWithExecutor_rejectsNullExecutor). -
examples/Demo.javadid not compile.SimExecutorstopped implementing everyGpuExecutormethod when the 0.3.0 entry points landed; nothing in the build looks atexamples/. CI now compiles and runs it. -
GpuArray.handle()was missing thereachabilityFenceevery other handle-dereferencing method has, so the array could be collected — and its handle freed — before the value was returned. -
GpuArray.toHost(dest)reports a short or mistyped native read as aGpuExceptioninstead of lettingarraycopythrowArrayIndexOutOfBoundsExceptionat the caller. -
GpuArray's internal completed future no longer strands an interrupt. -
JsonLite.escapeleft tabs and every other character belowU+0020raw, so a prompt containing a tab — ordinary in pasted code — produced an invalid request body that the server rejected. -
JsonLitedecoded\uXXXXto the literal letterufollowed by four hex digits, silently mangling any non-ASCII model output ("café" → "cafu00e9"). Surrogate pairs,\band\fhandled too. -
JsonLitefield lookup wasindexOf("\"name\""), which matches the field name anywhere — including inside a client-supplied string value. Lookup now walks the document stepping over string values. -
ModelResolveraccepted any model name and joined it onto the models directory, so../../../../Users/someone/.ssh/id_rsaresolved cleanly and whatever a caller could name, the sidecar would open. Names are now restricted to a plain file-name component, with containment and symlink-escape re-checks. -
LlamaCppLlmEnginehad no request or connect timeout, so a wedged llama-server parked the calling thread permanently. -
Both engines evicted the running model server before resolving the new model, so an unknown model name cost the caller the model that was working. A sidecar that starts but never becomes ready is now destroyed rather than left holding VRAM.
-
SidecarProcess.awaitReadybuilt a freshHttpClientper call, leaking a selector thread and worker pool per model switch.launch()now creates the log file's parent directory first. -
SidecarConfigandLlamaCppLlmEngineparsed ports and tuning values in field initialisers with a bareparseInt, so one mistyped environment variable threwExceptionInInitializerError— naming neither the variable nor its value — and left the class permanently unusable. Both now warn and fall back. -
craton-gpu's surefire config was missing the Byte Buddy flagcraton-sidecaralready carried, so its suite failed to start on newer JDKs. Applied with@{argLine}so JaCoCo's agent is not silently dropped. -
The release workflow declared a
taginput for manual dispatch and never read it, comparing the POM version against the dispatching branch name instead. It now resolves the tag up front and checks that ref out. -
submitV()/submitScale()javadoc and the README claimed these overloads skip the varargsObject[]allocation. They delegate to the varargssubmitand allocate exactly the same array. The claim is removed rather than restated: the overloads are worth having for compile-time type checking.
Security
- Native bridge hardening
SECURITY.mddirected vulnerability reports tosecurity@craton-co.github.io, andpom.xmlnamesdev@craton-co.github.ioas the developer contact that Maven Central publishes.craton-co.github.iois a GitHub Pages hostname with no MX record, so both addresses silently drop mail: the documented disclosure channel did not exist. The policy now leads with GitHub Private Vulnerability Reporting, which needs no mail infrastructure. The POM address still needs a real mailbox, and Private Vulnerability Reporting has to be enabled in repository settings before the link works.- Model-name path traversal (see Fixed) — reachable from anywhere an application lets its users influence the active model name.
0.2.0 - 2026-05-19
First standalone release of the Java GPU API, extracted from the CratonVM workspace for independent versioning and Maven Central publication.
Added
- Public API:
GpuExecutor,GpuStream,GpuFuture,GpuArray,GpuKernel - Annotation surface:
@EnableGpuAsync,@GpuExclude,AdmissionHint,GridShape - SAM types:
GpuRunnable,GpuCallable,GpuFunction - Exception types:
GpuException,GpuCompileException,GpuLaunchException,GpuOutOfMemoryException - Internal runtime:
GpuExecutorImpl,GpuStreamImpl,GpuFutureImpl,StreamCleaner, native bridge seam (Native,NativeBridge,NativeImpl) - Primitive overloads on hot paths for allocation and transfer
- Unit tests for executor, stream, future, array lifecycle, grid shape, and annotations
- Contributor documentation and GitHub PR template
Changed
- Library packaged as
io.github.craton-co:craton-gpu(JDK 17+)
Fixed
- Critical concurrency issues in stream and future completion paths