GardeLogStore is a Java 21/Kotlin append-only log library. The JVM backend is the reference implementation of binary format v1; the repository also contains Java, import/export, payload, JDBC, viewer, CLI, and Minecraft-focused modules, plus experimental read-only C, C++, Rust, and .NET parsers.
Current development version: 0.2.0-dev. The project is pre-1.0: source APIs
can still evolve, while existing format-v1 fixtures remain compatibility tests.
| Status | Components |
|---|---|
| Beta/reference | garde-log-api, garde-log-format, garde-log-jvm |
| Beta | Java facade, JSONL import/export, TXT export, viewer, CLI, Minecraft schema codec |
| Experimental | payload compression/encryption helpers, JDBC transfer, C, C++, Rust, .NET |
| Planned | Android storage adapter, browser/WebAssembly viewer |
“Experimental” is deliberate: native and .NET readers pass shared fixtures, but have less production history and no write path. Android and Web are not runtime artifacts and are not presented as implemented backends.
garde-log-api— public records, options, exceptions, and store API.garde-log-format— format-v1 encoding and validation.garde-log-jvm— file channels, locking, durability, verification, recovery.garde-log-java-api— Java-oriented builder and facade.garde-log-payload— bounded envelopes, gzip, and experimental AES-GCM.garde-log-export/garde-log-import— atomic export and bounded import.garde-log-sql— transactional JDBC transfer without a bundled driver.garde-log-viewer— UI-independent paging/filtering.garde-log-minecraft— schema 1/2 payloads without a Minecraft dependency.garde-log-cli— standalone command-line application.garde-log-benchmark— JMH only; excluded from normal build execution.
modules/** is the only canonical, maintained JVM source tree. The root-level
src/** tree is a restored archive kept for recovery traceability; it is not
compiled, packaged, linted, or used by tests. New code and fixes belong in the
matching module. The verifySourceTopology gate keeps the archive isolated and
checks that non-exempt archived sources have canonical module successors.
The public GardeLogStore.open facade discovers its filesystem implementation
through the GardeLogStoreProvider SPI. The parallel backend registry is a
separate SPI: installed bridge modules publish GardeLogBackendProvider with
ServiceLoader, a descriptor, and an explicit capability set. The capability
vocabulary is READ, APPEND, VERIFY, and REPAIR; availability and
capabilities are checked before a provider is opened. Read-only options require
READ plus VERIFY; writable options require all four capabilities.
GardeLogBackendSelector.open is fail-closed: fallbackToJvm defaults to
false. Callers must opt in explicitly or use select, whose result makes the
decision auditable. GardeLogBackendSelection exposes requestedMode,
selectedMode (the concrete result of resolving AUTO before fallback),
resolvedMode (the backend actually opened), requiredCapabilities,
usedFallback, and fallbackReason.
Fallback is safe only for known backend unavailability, including a provider
throwing GardeLogBackendUnavailableException. I/O, corruption, lock,
permission, provider-configuration, and other operational failures propagate
unchanged and never trigger fallback. Multiple providers for one concrete mode
throw GardeLogBackendProviderConflictException, even when fallback was
enabled. Individual providers never fall back on their own.
Minecraft is a schema and dependency-free adapter over an already opened JVM store, not a runtime backend. Likewise, the standalone C, C++, Rust, and .NET readers do not become JVM backends unless a real provider bridge is installed. The Rust reader supports concurrent callers, but its shared seek cursor is protected by a mutex, so file-position operations are serialized rather than performed as parallel positional reads.
dependencies {
implementation("ru.garde:garde-log-jvm:0.2.0-dev")
}import java.nio.file.Path
import ru.garde.logstore.GardeLogStore
import ru.garde.logstore.core.DurabilityMode
import ru.garde.logstore.core.LogStoreOptions
GardeLogStore.open(
Path.of("logs"),
"server",
LogStoreOptions(durabilityMode = DurabilityMode.DATA_AND_INDEX),
).use { store ->
val id = store.appendText(type = 1, text = "server started")
println(store.readById(id)?.payloadAsString())
}import java.nio.file.Path;
import ru.garde.logstore.core.DurabilityMode;
import ru.garde.logstore.javaapi.JavaGardeLogStore;
try (var store = JavaGardeLogStore.builder(Path.of("logs"), "server")
.durability(DurabilityMode.DATA_AND_INDEX)
.open()) {
long id = store.appendText("server started");
store.findById(id).ifPresent(record ->
System.out.println(record.payloadAsString()));
}Use ru.garde:garde-log-java-api:0.2.0-dev for the Java facade. Artifacts can
be tested locally with ./gradlew publishToMavenLocal; no signing credentials
or publication secrets are stored in this repository.
One writable opener is allowed per store through <name>.lock and FileLock.
Any number of read-only openers may coexist with it. Read-only mode never
creates or repairs files. Inside one JVM store instance, reads share a read lock;
append, repair, and close are exclusive.
NONE: no per-appendforcecall.DATA_ONLY: force the.glogrecord before publishing its index entry.DATA_AND_INDEX: force both data and index.
The legacy flushOnAppend=true remains compatible and selects full forcing.
Append remembers both original file sizes and attempts to roll both files back
on failure. A valid data record missing from the index is recovered on open;
an incomplete or corrupt tail is reported and requires explicit tail repair.
Every accepted index must describe contiguous records starting immediately
after the GLOG header: gaps, overlaps, and an end offset inconsistent with the
data file are corruption, not sparse-index semantics.
verify() is read-only. Index repair writes a complete sibling temporary file,
forces and validates it, then uses atomic replacement where supported. To
truncate an incomplete final record, call
repairPartialLastRecord(truncate = true) or run repair-tail ... --yes.
Keep a backup before modifying a damaged store; CRC32 detects accidental damage
but is not authentication.
info <directory> <name>
view|tail <directory> <name> [--last <count>]
export-jsonl|export-txt <directory> <name> <target>
import-jsonl <directory> <name> <source>
verify <directory> <name>
repair-index|repair-tail <directory> <name> --yes
dump-meta <directory> <name> [--last <count>]
generate-vectors <directory> --yes
Run it with ./gradlew :garde-log-cli:run --args="verify logs server".
Exit codes are 0 for success, 1 for an operational error, 2 for usage errors,
and 3 for a completed verification that found corruption.
The Minecraft module has no Fabric, Forge, NeoForge, Paper, Bukkit, NMS, or Minecraft dependency and is not a separately selectable storage backend. Convert platform components in the mod/plugin boundary, open the normal JVM store, then encode a schema payload:
val payload = MinecraftLogPayloads.incomingChat(
timestampMillis = System.currentTimeMillis(),
plainText = "[Player] hello",
senderUuid = playerUuid.toString(),
)
MinecraftLogRecords.appendTo(store, payload)Schema 1 and 2 decode remain supported; unknown JSON fields are ignored for forward compatibility. See Minecraft schema.
- Format fields are signed big-endian integers; record IDs must be positive and strictly increasing.
- The format parser rejects payload declarations above 256 MiB. JVM writes use
the lower configurable
maxPayloadByteslimit (1 MiB by default). - List-returning JVM APIs default to at most 100,000 records; sequence APIs are available for lazy traversal.
- Store names must be a single safe filename component.
- Payload bytes, unknown record types, and unknown flag bits remain opaque.
The normative layout is format-v1, with operational notes in FORMAT.md.
Java 21 is required. The release/CI-equivalent Windows command is:
.\scripts\check-all.ps1 -StrictUnix:
./scripts/check-all.sh --strictStrict mode treats missing Cargo, CMake, or .NET as a failure. Without
-Strict/--strict, unavailable optional toolchains are reported and skipped.
Both scripts run the Gradle verifyIntegration gate, native format/lint/tests,
and .NET build/tests. Temporary native builds use unique directories and are
removed afterwards.
test-vectors/manifest.tsv is the canonical inventory of real format-v1
fixtures and their expected validity/error. JVM, C, C++, Rust, and .NET tests
consume it and reject missing or unregistered fixture directories. The Gradle
vector gate also regenerates the complete registered tree outside the committed
directory and compares every generated file byte-for-byte with the committed
fixtures.
Useful focused commands:
./gradlew verifyIntegration
./gradlew publishToMavenLocal
./gradlew verifyTestVectors
./gradlew :garde-log-benchmark:jmh
MIT licensed; see LICENSE.