SSTable Tools queries and mutates explicitly selected, stopped Cassandra
SSTables with the matching installed Cassandra release and its stock cqlsh.
It is under active development and is not yet an operator-ready production tool.
Install either Linux package from a release, or unpack the standalone archive:
# Debian or Ubuntu
sudo dpkg -i sstable-tools_1.2.3-1_all.deb
# RHEL, Rocky Linux, AlmaLinux, or another RPM-based distribution
sudo rpm -i sstable-tools-1.2.3-1.noarch.rpm
# Standalone archive
tar -xzf sstable-tools-1.2.3.tar.gzThe packages deliberately do not declare a Java or Cassandra package
dependency. The machine must already have a Java runtime supported by the
selected Cassandra release. The packaged command is /usr/bin/sstable-tools;
the archive contains ./sstable-tools.
Point the launcher at Cassandra once. Packaged Cassandra normally uses
/usr/share/cassandra; a tarball installation normally uses its lib
directory:
export CASSANDRA_LIB_DIR=/usr/share/cassandra
# Or: export CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib
sstable-tools --version
sstable-tools runtime preflightPrepare these inputs before opening an SSTable:
- A stopped table directory, completed snapshot, or backup copied outside the live Cassandra data directories.
- A CQL schema bundle containing the exact keyspace, table, table ID, primary key, columns, and referenced UDTs for those SSTables.
- Every SSTable that contributes to the logical table. Cassandra may spread
one table across multiple
data_file_directories.
Query every SSTable in one complete table directory without publishing files:
TABLE_DIR=/archive/acme/users-7ad54392bcdd35a684174e047860b377
SCHEMA=/archive/acme-users.cql
sstable-tools \
--output-dir "$TABLE_DIR" \
--schema "$SCHEMA" \
cqlsh --execute \
"SELECT id, name FROM acme.users WHERE id = 1;"Update that table by supplying a timestamp in Unix microseconds:
NOW_MICROS=$(date +%s%6N)
sstable-tools \
--output-dir "$TABLE_DIR" \
--schema "$SCHEMA" \
cqlsh --execute \
"UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"Use the current clock only when it is ahead of the source timestamps. If the source contains future-dated cells, choose a value above the reported source maximum; see Write timestamps.
The update never modifies an existing SSTable. It publishes one new verified
component set beside the originals and reports it as
published.sstables=<descriptor>. Running the SELECT command again with the
same complete --output-dir reads the originals and the new delta together.
For a table split across two Cassandra data directories, select both complete table directories and name exactly one publication directory:
PRIMARY=/data/acme/users-7ad54392bcdd35a684174e047860b377
SECONDARY=/log/data/acme/users-7ad54392bcdd35a684174e047860b377
NOW_MICROS=$(date +%s%6N)
sstable-tools \
--sstables "$PRIMARY" \
--sstables "$SECONDARY" \
--output-dir "$PRIMARY" \
--schema "$SCHEMA" \
cqlsh --execute \
"UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"
sstable-tools \
--sstables "$PRIMARY" \
--sstables "$SECONDARY" \
--output-dir "$PRIMARY" \
--schema "$SCHEMA" \
cqlsh --execute \
"SELECT id, name FROM acme.users WHERE id = 1;"The second command is the required read-back pattern: it includes the source
SSTables from both directories plus the delta published into PRIMARY. For an
empty destination, use the first-SSTable example.
For system.local, including a table spread across multiple directories, use
the system-table example.
Use sstable-tools from the DEB/RPM, or ./sstable-tools from the standalone
archive. Point it at Cassandra's tarball lib directory or its packaged
runtime root with either --cassandra-lib-dir or CASSANDRA_LIB_DIR:
tar -xzf sstable-tools-1.2.3.tar.gz
cd sstable-tools-1.2.3
./sstable-tools \
--cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--versionThe launcher scans the selected directory plus its nested or adjacent lib
directory. It finds exactly one apache-cassandra-<version>.jar or
cassandra-all-<version>.jar, maps versions 3.11.x, 4.0.x, 4.1.x, and
5.0.x to the matching adapter, derives the Cassandra home from the core JAR
location, and forwards the remaining arguments. This supports tarballs such as
/opt/apache-cassandra-5.0.8/lib and DEB/RPM installations rooted at
/usr/share/cassandra. Multiple or unsupported core JARs fail instead of
guessing.
The launcher chooses Java in this order:
SSTABLE_TOOLS_JAVA, JAVA_HOME/bin/java, then java from PATH. It always
enables headless mode and UTF-8. Add whitespace-delimited launcher-JVM options
with SSTABLE_TOOLS_JAVA_OPTS:
CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib \
SSTABLE_TOOLS_JAVA_OPTS="-Xms256m -Xmx2g" \
sstable-tools --versionsstable-tools is the only launcher command. The release-specific adapter JARs
are internal implementation artifacts selected automatically. Prefix the
command with ./ when running from an unpacked release archive.
The primary interface is one command. Use --sstables to select explicit
Data.db/TOC.txt files or complete table directories, plus a schema bundle
and the matching Cassandra installation. If the selected SSTables span more
than one Cassandra data directory, also use --output-dir to choose the table
directory where new SSTables will be published. --output-dir can still be
used alone to treat one complete directory as both the baseline and the
publication destination. The tool opens stock cqlsh; on a clean exit after a
mutation it publishes verified new SSTable component sets into the chosen
directory.
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
--schema /archive/acme-users.cql \
cqlshThe schema bundle must describe the exact keyspace, table, primary key, column types, table ID, and any UDTs used by the selected SSTables.
Partitioner limitation: SSTable Tools currently supports only
org.apache.cassandra.dht.Murmur3Partitioner. SSTables produced withRandomPartitioner,ByteOrderedPartitioner,LocalPartitioner, or another partitioner are not supported.
Use --execute to run one statement without opening an interactive shell. A
read-only session validates and queries the selected data but publishes no new
SSTables:
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
--schema /archive/acme-users.cql \
cqlsh --execute "SELECT id, name FROM acme.users WHERE id = 1;"Repeat --sstables or provide a comma-separated list. SSTables for one logical
table may come from any number of Cassandra data_file_directories. When they
span physical directories, supply --output-dir as the single publication
destination:
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--sstables /data/acme/users-7ad54392bcdd35a684174e047860b377/nb-41-big-Data.db \
--sstables /data/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
--sstables /log/data/acme/users-7ad54392bcdd35a684174e047860b377/nb-43-big-Data.db \
--sstables /log/data/acme/users-7ad54392bcdd35a684174e047860b377/nb-44-big-Data.db \
--output-dir /data/acme/users-7ad54392bcdd35a684174e047860b377 \
--schema /archive/acme-users.cql \
cqlsh --execute "SELECT id, name FROM acme.users;"The direct command accepts SELECT plus non-conditional INSERT and UPDATE
for the selected table. It does not scan data roots, keyspaces, or unrelated
tables. Every selected descriptor must belong to the same logical table and
match the supplied schema. When --sstables and --output-dir are combined,
every pre-existing SSTable in the output directory must be part of the
selection; the tool checks that target subset again before publication. With
--sstables alone, all selected SSTables must still reside in one directory so
the publication destination is unambiguous.
An INSERT does not need an existing SSTable. Use --output-dir instead of
--sstables when the destination table directory is the source of truth:
mkdir -p /archive/acme/users-7ad54392bcdd35a684174e047860b377
NOW_MICROS=$(date +%s%6N)
INSERT_CQL="INSERT INTO acme.users (id, name) VALUES (1, 'Ada') USING TIMESTAMP $NOW_MICROS;"
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
--schema /archive/acme-users.cql \
cqlsh --execute "$INSERT_CQL"The directory must already exist and must not be a symlink. If it is empty,
direct CQLSH requires --execute with an INSERT statement. Cassandra 5.0
uses UUID-style identifiers for the first SSTable; older release lines use a
numeric identifier.
If the directory already contains complete SSTables, the tool inventories and imports all of them as the baseline. Numeric output is allocated at one above the highest numeric identifier in the directory. UUID-style output keeps the fresh Cassandra-generated identifier; UUIDs are not numerically incremented. The complete directory is checked again immediately before publication, and publication fails if it changed after the baseline was imported.
For automation, generate the CQL separately so the microsecond timestamp is obvious:
NOW_MICROS=$(date +%s%6N)
WRITE_CQL="UPDATE acme.users USING TIMESTAMP $NOW_MICROS SET name = 'Grace' WHERE id = 1;"
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--tmp-dir /var/tmp/sstable-tools \
--output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
--schema /archive/acme-users.cql \
cqlsh --execute "$WRITE_CQL"--tmp-dir changes the parent of the private temporary workspace. The default
is /tmp/sstable-tools/. A successful invocation removes its private child;
a failed invocation retains it and prints the path for diagnosis.
INSERT and UPDATE statements must include an explicit USING TIMESTAMP.
The value is a Unix timestamp in microseconds and must be greater than the
maximum timestamp in the selected source SSTables. Cassandra's CQL now()
function returns a timeuuid and cannot be used for this clause.
If the local clock is ahead of every source timestamp, generate a suitable
current timestamp with date +%s%6N:
INSERT INTO acme.users (id, name)
VALUES (1, 'Ada')
USING TIMESTAMP 1785170000000000;
UPDATE acme.users
USING TIMESTAMP 1785170000000001
SET name = 'Grace'
WHERE id = 1;The Cassandra 4.0, 4.1, and 5.0 guards reject a missing timestamp, or an explicit timestamp that is not greater than the imported source maximum. If a source contains future-dated cells, do not use the current clock value; choose a microsecond value above the reported source maximum instead.
Stopped SSTables from Cassandra's built-in system keyspace are supported.
The schema bundle identifies exactly one fully qualified built-in table; the
matching Cassandra runtime supplies its actual metadata.
NOW_MICROS=$(date +%s%6N)
SYSTEM_CQL="UPDATE system.local USING TIMESTAMP $NOW_MICROS SET cluster_name = 'recovered-cluster' WHERE key = 'local';"
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--output-dir /archive/system/local-7ad54392bcdd35a684174e047860b377 \
--schema /archive/system.local.cql \
cqlsh --execute "$SYSTEM_CQL"This inventories the complete stopped table directory, changes only the isolated copy, and publishes verified sibling SSTables into the same directory. It does not change a running node or Cassandra installation.
If a system table spans Cassandra data directories, select every physical table directory and choose one as the publication target:
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-4.0.18/lib \
--sstables /data/system/local-7ad54392bcdd35a684174e047860b377 \
--sstables /log/data/system/local-7ad54392bcdd35a684174e047860b377 \
--output-dir /data/system/local-7ad54392bcdd35a684174e047860b377 \
--schema /archive/system.local.cql \
cqlsh --execute "$SYSTEM_CQL"Use SSTable Tools v1.0.5 or newer when mutating system.local. Version
1.0.5 added the sandbox topology overrides required when the imported node's
datacenter or rack differs from the isolated worker's synthetic topology.
SSTable Tools follows the selected Cassandra installation's cassandra.yaml;
it does not offer a format-conversion mode. The effective output is:
storage_compatibility_mode |
sstable.selected_format |
Output |
|---|---|---|
CASSANDRA_4 |
big |
Big nb |
UPGRADING |
big |
Big oa |
NONE |
big |
Big oa |
UPGRADING or NONE |
bti |
BTI da |
CASSANDRA_4 with bti is invalid. The tool reads cassandra.yaml from
CASSANDRA_CONF, the selected tarball's conf directory, or /etc/cassandra
for a packaged /usr/share/cassandra runtime. An omitted
storage_compatibility_mode defaults to CASSANDRA_4; an omitted
sstable.selected_format defaults to big. If no configuration is available,
the tool falls back to the selected SSTable inventory and refuses ambiguous
mixed Big/BTI input.
--output-format is only an optional assertion. It cannot override
cassandra.yaml; a mismatch fails before import or publication with a clear
"format conversion is not supported" error. Normally, omit it:
NOW_MICROS=$(date +%s%6N)
INSERT_CQL="INSERT INTO acme.users (id, name) VALUES (2, 'Ada') USING TIMESTAMP $NOW_MICROS;"
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--output-dir /archive/acme/users-7ad54392bcdd35a684174e047860b377 \
--schema /archive/acme-users.cql \
cqlsh --execute "$INSERT_CQL"Original components are never modified. Every adapter allocates the next
available numeric SSTable identifier above every descriptor present in the
selected source directories and the publication directory when the baseline
uses numeric identifiers. For Cassandra 5.0, selecting a descriptor with Cassandra's
28-character UUID/ULID-style identifier enables
uuid_sstable_identifiers_enabled in the private sandbox and publishes the
Cassandra-generated UUID-style delta unchanged. A mixed numeric and UUID-style
selection uses UUID-style output. With --sstables, inference uses only the
explicitly selected descriptors. With --output-dir, it uses every complete
SSTable in that directory. Output format and Cassandra 5.0 format version come
from the selected installation's cassandra.yaml.
After a successful direct mutation, later commands for that table should select
both the original descriptors and every published sibling descriptor that
should participate in reconciliation. Using the same --output-dir does this
automatically.
Existing SSTable components are immutable, so the tool permits reading or copying selected files even when Cassandra has them open. The isolated worker uses only private storage and loopback networking.
DANGER: Publishing newly generated SSTables into a running Cassandra node's live table directory is different from reading existing components. It can race with Cassandra lifecycle operations and damage or confuse the node. Prefer a stopped node or a separate output directory.
If direct CQLSH produces no new SSTables, no live-output confirmation is
needed. If it does produce new SSTables and the publication directory belongs
to a running Cassandra process, an interactive terminal displays the matched
PID and directory and requires typing yes.
For an intentional non-interactive workflow, acknowledge the same risk with
--allow-live-cassandra-output:
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--output-dir /var/lib/cassandra/data/acme/users-7ad54392bcdd35a684174e047860b377 \
--schema /archive/acme-users.cql \
--allow-live-cassandra-output \
cqlsh --execute "$INSERT_CQL"This option bypasses only the live-publication confirmation. It does not disable source inventory verification, output collision checks, or final publication verification.
The general forms are:
sstable-tools --cassandra-lib-dir <path> [tool options] <command>
CASSANDRA_LIB_DIR=<path> sstable-tools [tool options] <command>
--cassandra-lib-dir is a launcher option and must immediately follow
sstable-tools. Forwarded tool options may appear before or after the command.
| Option | Value and scope |
|---|---|
--cassandra-lib-dir <path> |
Cassandra tarball lib directory or packaged runtime root used to detect the release and select an adapter. Equivalent to CASSANDRA_LIB_DIR. |
--cassandra-home <path> |
Cassandra installation used by direct cqlsh, runtime commands, and workspace import, start, or cqlsh. |
--java-home <path> |
Compatible Java installation for the selected Cassandra worker. |
--sstables <path> |
Explicit Data.db, TOC.txt, or table-directory source. Repeat it or use comma-separated paths; one logical table may span physical directories. Valid only with direct cqlsh and workspace create. |
--output-dir <path> |
Existing non-symlink publication directory for direct cqlsh. Used alone, it is also the complete baseline. Combined with --sstables, it selects where deltas are published and its existing SSTables must all be included in the selected source inventory. |
--schema <path> |
UTF-8 CQL schema bundle. Required by direct cqlsh; recorded by workspace create. |
--timestamp-policy <policy> |
wall-clock or after-source. Valid with direct cqlsh and workspace start; the choice is persisted for a managed workspace. |
--output-format <format> |
Optional big or bti assertion for direct cqlsh and workspace create. It cannot override Cassandra 5.0 sstable.selected_format; mismatches fail rather than convert. |
--tmp-dir <path> |
Parent for private direct-CQLSH workspaces. Default: /tmp/sstable-tools. |
--execute <cql> |
Run one CQL statement and exit. Valid with direct cqlsh and workspace cqlsh. |
--allow-live-cassandra-output |
Bypass the confirmation before direct CQLSH publishes new SSTables into a directory owned by a running Cassandra process. Intended for explicit non-interactive acknowledgement. |
--mode <mode> |
delta or snapshot; required by workspace export. |
--output <path> |
Atomic publication destination; required by workspace export. |
--confirm-workspace-id <uuid> |
Exact manifest UUID required by workspace destroy. |
--version |
Print the tool, adapter, and compile-target versions. |
--help, -h |
Print command and option help. |
after-source maintains a durable timestamp high-water above the imported
source maximum for timestamp-free requests that support it. It does not remove
the explicit USING TIMESTAMP requirement enforced by the Cassandra 4.0, 4.1,
and 5.0 query guards.
| Variable | Purpose |
|---|---|
CASSANDRA_LIB_DIR |
Cassandra tarball lib directory or packaged runtime root; the launcher scans the selected directory and its nested or adjacent lib. |
CASSANDRA_HOME |
Fallback Cassandra home; the launcher scans the home and its lib child. |
CASSANDRA_CONF |
Optional Cassandra configuration directory (or cassandra.yaml path). On Cassandra 5.0, it has precedence when resolving storage_compatibility_mode and sstable.selected_format. |
SSTABLE_TOOLS_JAVA |
Exact Java executable used by the launcher. |
JAVA_HOME |
Supplies $JAVA_HOME/bin/java when SSTABLE_TOOLS_JAVA is unset. |
SSTABLE_TOOLS_JAVA_OPTS |
Additional whitespace-delimited launcher-JVM options, such as -Xms256m -Xmx2g. |
SSTABLE_TOOLS_HOME |
Override the installed JAR directory; normally /usr/share/sstable-tools. |
SSTABLE_TOOLS_JAR |
Override the exact adapter JAR path for development or diagnosis. |
The isolated Cassandra worker is started separately with -Xms512m,
-Xmx512m, attach disabled, and the release-specific Java module options it
requires. --java-home selects that worker's compatible Java installation;
SSTABLE_TOOLS_JAVA_OPTS tunes only the small launcher JVM.
| Command | Purpose |
|---|---|
cqlsh |
Query explicit SSTables, or inventory and publish verified mutations into --output-dir. |
runtime inspect |
Print resolved Cassandra paths, versions, classpath, and JAR hashes. |
runtime preflight |
Start a separate worker JVM and verify required Cassandra API linkage. |
workspace create <path> |
Inventory sources and create a validated managed workspace. |
workspace import <path> |
Validate and copy selected SSTables into the workspace. |
workspace start <path> |
Start the isolated Cassandra sandbox. |
workspace cqlsh <path> |
Launch the selected installation's stock cqlsh. |
workspace status <path> |
Verify sources and display persisted lifecycle state. |
workspace flush <path> |
Quiesce CQL and flush the workspace table. |
workspace export <path> |
Publish a verified delta or self-contained snapshot. |
workspace stop <path> |
Drain and stop the workspace sandbox. |
workspace recover <path> |
Recover a failed workspace to its last stable state. |
workspace destroy <path> |
Permanently delete an inactive workspace after UUID confirmation. |
Each thin JAR uses the Cassandra installation supplied by --cassandra-home.
It does not package Cassandra server classes or attach to the installed
Cassandra process. The installation's cassandra.yaml is neither required nor
read. The tool generates a private sandbox configuration beneath its workspace.
Optional distribution JVM options and client resources are discovered
automatically from the selected installation; --java-home selects the worker
JVM.
sstable-tools --cassandra-lib-dir /usr/share/cassandra \
--java-home /usr/lib/jvm/java-11-openjdk \
runtime preflightruntime inspect prints the discovered runtime paths, Cassandra version, child
classpath, and JAR identities. runtime preflight starts a separate worker JVM
and verifies the Cassandra APIs required by that adapter before opening an
SSTable.
Stock cqlsh is resolved from <cassandra-home>/bin/cqlsh for tarball
installations. For the DEB/RPM /usr/share/cassandra layout, the tool uses
/usr/bin/cqlsh (or the equivalent path beneath an extracted package root).
It does not select an unrelated cqlsh from PATH.
Inspect runtime discovery without starting the worker:
sstable-tools --cassandra-lib-dir /opt/apache-cassandra-5.0.8/lib \
--java-home /usr/lib/jvm/java-17-openjdk \
runtime inspectThe direct cqlsh command is preferred. For diagnostic or recovery workflows
that need explicit lifecycle control:
TOOL=sstable-tools
CASE=/var/tmp/sstable-tools-case
export CASSANDRA_LIB_DIR=/opt/apache-cassandra-5.0.8/lib
"$TOOL" \
--sstables /archive/acme/users-7ad54392bcdd35a684174e047860b377/nb-42-big-Data.db \
--schema /archive/acme-users.cql \
workspace create "$CASE"
"$TOOL" workspace import "$CASE"
"$TOOL" \
--timestamp-policy after-source \
workspace start "$CASE"
"$TOOL" \
workspace cqlsh "$CASE" \
--execute "SELECT id, name FROM acme.users WHERE id = 1;"
"$TOOL" workspace flush "$CASE"
"$TOOL" workspace export "$CASE" \
--mode delta \
--output /archive/acme-users-delta
"$TOOL" workspace stop "$CASE"Use --mode snapshot instead when the export must contain both the imported
base and the new SSTables.
All release adapters currently require
org.apache.cassandra.dht.Murmur3Partitioner.
The CI matrix creates stopped SSTables with stock Cassandra cqlsh, then runs
the matching thin JAR and stock cqlsh direct workflow. Its source tables
exercise scalar values, sets, maps, tuples, frozen UDTs, TTLs, and deleted
cells. It verifies INSERT, UPDATE, DELETE, SELECT, flush, sibling
publication, direct reopen, and unchanged source component hashes.
| Artifact | Supported Cassandra patches | Java runtime | Direct output |
|---|---|---|---|
cassandra-3.11 |
3.11.19 | 8 | Big me |
cassandra-4.0 |
4.0.0-4.0.18 | 8-11 | Big nb |
cassandra-4.1 |
4.1.0-4.1.11 | 11 | Big nb |
cassandra-5.0 |
5.0.4-5.0.8 | 17 | Big nb (CASSANDRA_4), Big oa/BTI da (UPGRADING or NONE) |
The 4.0 adapter compiles against the first patch in its release line. The 4.1 adapter carries both native query-handler ABIs used across the 4.1 patch line. CI resolves Cassandra's real Maven runtime and starts the adapter against every declared 4.0.x, 4.1.x, and 5.0.x patch. Cassandra 4.0 is checked on both Java 8 and Java 11.
Real Cassandra producer/import coverage includes 3.11.0 and 3.11.19, 4.0.0 and 4.0.18, 4.1.0 and 4.1.11, and 5.0.4 and 5.0.8. Cassandra 3.11.0 is an older-format producer imported by the supported 3.11.19 runtime; it is not an additional supported runtime. The latest patch in each release line runs the full stopped-SSTable and stock-cqlsh workflow, and Cassandra 5.0.4 also runs the direct workflow as the minimum supported 5.0 runtime.
The Cassandra 5.0.8 acceptance job uses real two-data_file_directories
system.local SSTables and tests each supported YAML-driven output:
storage_compatibility_mode |
sstable.selected_format |
Required output |
|---|---|---|
CASSANDRA_4 |
big |
nb Big |
UPGRADING |
big |
oa Big |
NONE |
big |
oa Big |
UPGRADING |
bti |
da BTI |
NONE |
bti |
da BTI |
Those tests reopen the combined table through stock Cassandra after publishing
into one of the two data directories. The BTI path additionally rejects sets
missing Partitions.db or Rows.db, repeats multi-directory imports to catch
ordering races, and the Big path includes an uncompressed table.
Release-package CI installs the dependency-free DEB and RPM into separate clean Java 17 containers and invokes the installed universal launcher. See Real Cassandra CI coverage for the full test topology and the distinction between full-node and linkage coverage.
The direct workflow intentionally has no sstableloader, streaming, clean-node
import, or broad filesystem discovery.
This is one Maven multi-module build. Compile and run the shared and version-specific unit tests with:
mvn testBuild the four thin JARs and verify that they do not embed Cassandra runtime classes:
mvn package
scripts/verify-thin-jarsArtifacts are written to workers/cassandra-<line>/target/.
The Git tag is the source of truth for a published version. A stable tag such
as v1.2.3 builds JARs and packages with embedded version 1.2.3; there is no
separate release-version file. Untagged development builds use the default
0.1.0-SNAPSHOT Maven revision.
Release packaging requires Java 17, Go 1.26.4 or newer, nFPM, dpkg-deb,
rpm, rpm2cpio, and cpio. CI pins Go 1.26.4 and nFPM v2.47.0;
install the same nFPM version with:
go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0Build and verify a candidate with the exact version that will be tagged:
RELEASE_VERSION=0.1.0
mvn clean verify -Drevision="$RELEASE_VERSION"
scripts/package-release \
--output target/release \
--version "$RELEASE_VERSION"
scripts/package-linux-packages \
--release-dir "target/release/sstable-tools-$RELEASE_VERSION"
scripts/verify-linux-packages \
"target/release/sstable-tools-$RELEASE_VERSION"
scripts/verify-release-bundle \
"target/release/sstable-tools-$RELEASE_VERSION"This creates one auto-detecting sstable-tools launcher, four internal adapter
JARs, an architecture-independent DEB, and a noarch RPM under
target/release/sstable-tools-$RELEASE_VERSION/. It also creates the
executable-mode-preserving standalone archive
target/release/sstable-tools-$RELEASE_VERSION.tar.gz. Both Linux packages
install one command and deliberately declare no package-manager dependencies:
sstable-tools
Use --sign on scripts/package-linux-packages to create a detached
SHA256SUMS.asc signature after the DEB and RPM have been included in
SHA256SUMS. For a packaging-only rebuild of the same upstream version, use
--package-release 2 (then increment it for later rebuilds).
After the candidate is verified, the release commit is pushed, and CI is passing, create and push an annotated stable-version tag:
RELEASE_VERSION=0.1.0
git tag -a "v$RELEASE_VERSION" -m "SSTable Tools $RELEASE_VERSION"
git push origin "v$RELEASE_VERSION"The Release workflow first requires a successful full CI workflow for the exact release commit. It reuses matching evidence when available; otherwise it dispatches CI for the release ref and waits for it to pass. Only then does it build and test every adapter with that exact Maven revision, reject mismatched embedded JAR versions, create and verify both Linux packages, run the release security gate, and publish the resulting directory as a workflow artifact and GitHub Release. After the security gate passes, it also publishes the DEB and RPM to the configured Google Artifact Registry Apt and Yum repositories.
The security gate scans the built release with ClamAV, analyzes Java source with
CodeQL security-extended, checks source secrets and configuration with Trivy,
records the full Maven build/compatibility graph, and scans the published SPDX
SBOM for known dependency vulnerabilities. The full Maven graph is report-only
because it includes Cassandra compatibility and provided dependencies that are
not shipped. Malware, scanner errors, missing reports, or HIGH/CRITICAL CodeQL,
source-security, or published-SBOM findings block publication. The workflow
always retains diagnostic reports when the scan step runs; successful GitHub
Releases include
sstable-tools-<version>-security-reports.tar.gz with the Markdown summary,
tool metadata, ClamAV logs, CodeQL SARIF, Trivy JSON, and Maven dependency trees.
If the RELEASE_GPG_PRIVATE_KEY repository secret is configured, the workflow
imports it and signs the final checksum file. The workflow can also be started
manually with an explicit stable version for release testing or recovery.
Google Artifact Registry publication requires the GCP_CREDENTIALS repository
secret. The release publisher uploads directly to the AxonOps repositories
axonops-public/europe/axonops-apt and
axonops-public/europe/axonops-yum.
See the release guide for the complete artifact list and package-revision details.
- Direct CQLSH design
- System keyspace direct-CQLSH design
- Cassandra node deployment guide
- Release guide
- Real Cassandra CI coverage
- Thin JAR packaging dependencies
- Workspace manifest and lifecycle contract
The workspace ... commands remain an advanced diagnostic and recovery
interface. They are not the normal operator workflow.