From d3722cdc88602a20cdcbfae662407aa07655d46e Mon Sep 17 00:00:00 2001 From: Leumor <116955025+leumor@users.noreply.github.com> Date: Tue, 24 Mar 2026 00:45:35 +0000 Subject: [PATCH] refactor(store): Prepare store boundary for extraction - move store stats contracts into foundation-store-contracts and add the store alert SPI - move the support helper subset into foundation-support with updated ownership metadata - replace direct store alert manager wiring with a runtime adapter and narrow remaining root dependencies - extend focused tests for the runtime adapter and BucketFactory-based slashdot store path --- .../gradle/owned-output-patterns.txt | 3 +- .../stats/StatsNotAvailableException.java | 5 +- .../crypta/node/stats/StoreAccessStats.java | 19 +- .../crypta/store/alerts/StoreAlertSink.java | 40 +++ .../alerts/StoreMaintenanceAlertKind.java | 26 ++ .../alerts/StoreMaintenanceAlertSource.java | 102 ++++++++ foundation-support/build.gradle.kts | 3 + .../gradle/owned-output-patterns.txt | 12 + .../network/crypta/node/FastRunnable.java | 23 ++ .../crypta/node/SemiOrderedShutdownHook.java | 14 +- .../crypta/support/ByteArrayWrapper.java | 6 +- .../crypta/support/DoublyLinkedList.java | 0 .../crypta/support/DoublyLinkedListImpl.java | 6 +- .../java/network/crypta/support/LRUMap.java | 18 +- .../crypta/support/LightweightException.java | 0 .../support/PromiscuousItemException.java | 0 .../crypta/support/VirginItemException.java | 0 .../crypta/support/WrapperKeepalive.java | 2 +- .../network/crypta/support/io/Fallocate.java | 4 +- .../crypta/support/io/NativeThread.java | 0 .../network/crypta/node/FastRunnable.java | 28 --- .../UserAlertManagerStoreAlertSink.java | 162 ++++++++++++ .../node/subsystem/NodeStorageSubsystem.java | 20 +- .../network/crypta/store/FreenetStore.java | 9 +- .../crypta/store/NullFreenetStore.java | 6 +- .../crypta/store/ProxyFreenetStore.java | 8 +- .../network/crypta/store/RAMFreenetStore.java | 4 +- .../network/crypta/store/SlashdotStore.java | 16 +- .../store/saltedhash/CipherManager.java | 9 +- .../saltedhash/SaltedHashFreenetStore.java | 122 ++------- .../UserAlertManagerStoreAlertSinkTest.java | 237 ++++++++++++++++++ .../crypta/store/NullFreenetStoreTest.java | 10 +- .../crypta/store/RAMFreenetStoreTest.java | 10 +- .../crypta/store/SlashdotStoreTest.java | 28 +++ 34 files changed, 743 insertions(+), 209 deletions(-) rename {src => foundation-store-contracts/src}/main/java/network/crypta/node/stats/StatsNotAvailableException.java (96%) rename {src => foundation-store-contracts/src}/main/java/network/crypta/node/stats/StoreAccessStats.java (88%) create mode 100644 foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreAlertSink.java create mode 100644 foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertKind.java create mode 100644 foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertSource.java create mode 100644 foundation-support/src/main/java/network/crypta/node/FastRunnable.java rename {src => foundation-support/src}/main/java/network/crypta/node/SemiOrderedShutdownHook.java (88%) rename {src => foundation-support/src}/main/java/network/crypta/support/ByteArrayWrapper.java (96%) rename {src => foundation-support/src}/main/java/network/crypta/support/DoublyLinkedList.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/DoublyLinkedListImpl.java (99%) rename {src => foundation-support/src}/main/java/network/crypta/support/LRUMap.java (96%) rename {src => foundation-support/src}/main/java/network/crypta/support/LightweightException.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/PromiscuousItemException.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/VirginItemException.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/WrapperKeepalive.java (97%) rename {src => foundation-support/src}/main/java/network/crypta/support/io/Fallocate.java (98%) rename {src => foundation-support/src}/main/java/network/crypta/support/io/NativeThread.java (100%) delete mode 100644 src/main/java/network/crypta/node/FastRunnable.java create mode 100644 src/main/java/network/crypta/node/runtime/UserAlertManagerStoreAlertSink.java create mode 100644 src/test/java/network/crypta/node/runtime/UserAlertManagerStoreAlertSinkTest.java diff --git a/foundation-store-contracts/gradle/owned-output-patterns.txt b/foundation-store-contracts/gradle/owned-output-patterns.txt index 171714276bb..78d129a0bb4 100644 --- a/foundation-store-contracts/gradle/owned-output-patterns.txt +++ b/foundation-store-contracts/gradle/owned-output-patterns.txt @@ -3,4 +3,5 @@ network/crypta/store/StorableBlock* network/crypta/store/BlockMetadata* network/crypta/store/GetPubkey* - +network/crypta/node/stats/StoreAccessStats* +network/crypta/node/stats/StatsNotAvailableException* diff --git a/src/main/java/network/crypta/node/stats/StatsNotAvailableException.java b/foundation-store-contracts/src/main/java/network/crypta/node/stats/StatsNotAvailableException.java similarity index 96% rename from src/main/java/network/crypta/node/stats/StatsNotAvailableException.java rename to foundation-store-contracts/src/main/java/network/crypta/node/stats/StatsNotAvailableException.java index a0517c81883..3a70e222c2a 100644 --- a/src/main/java/network/crypta/node/stats/StatsNotAvailableException.java +++ b/foundation-store-contracts/src/main/java/network/crypta/node/stats/StatsNotAvailableException.java @@ -25,13 +25,12 @@ *
Instances of this abstract type expose read-oriented counters (hits, misses, false positives) * and write counters as observed by a particular store. Implementations typically back these values * with thread-safe counters updated by the store’s I/O path and may compute derived rates from the - * exposed primitives. The contract is deliberately minimal so it can model both in-memory caches + * exposed primitives. The contract is deliberately minimal, so it can model both in-memory caches * and persistent stores with different eviction and validation strategies. * *
The methods are intended for metrics dashboards, admin endpoints, and diagnostics. Values @@ -18,10 +18,9 @@ *
False positives generally occur when a preliminary test (such as a probabilistic filter) - * indicates presence, yet the object is not actually retrievable. This counter helps assess - * filter tuning and its impact on unnecessary follow-on work. + * indicates presence, yet the object is not retrievable. This counter helps assess filter tuning + * and its impact on unnecessary follow-on work. * * @return non-negative count of false positives observed so far; monotonically non-decreasing per * process unless counters are reset by the implementation. @@ -73,8 +72,8 @@ protected StoreAccessStats() {} /** * Returns the number of write operations issued to the store. * - *
Depending on the store, a write may represent an insert, update, or a completed fill after a - * miss. The counter reflects successfully issued writes; failures may or may not be included + *
Depending on the store, a writing may represent an insert, update, or a completed fill after + * a miss. The counter reflects successfully issued writes; failures may or may not be included * based on implementation policy. * * @return non-negative count of writes observed so far; monotonically non-decreasing per process @@ -111,8 +110,8 @@ public long successfulReads() { * Returns the read success rate as a percentage of total reads. * *
Calculated as {@code (100.0 * hits() / readRequests())}. When no reads have been observed - * yet, the rate is undefined and this method signals unavailability rather than returning a value - * that could be misinterpreted. + * yet, the rate is undefined, and this method signals unavailability rather than returning a + * value that could be misinterpreted. * * @return a percentage in the range {@code [0.0, 100.0]} when at least one read has been * observed. The exact rounding behavior follows IEEE-754 double arithmetic. @@ -140,7 +139,7 @@ public double accessRate(long nodeUptimeSeconds) { } /** - * Returns the average write rate per second for the node uptime period. + * Returns the average writing rate per second for the node uptime period. * *
Computed as {@code writes() / nodeUptimeSeconds}. As with {@link #accessRate(long)}, callers * should provide a strictly positive uptime to avoid undefined ratios at startup. diff --git a/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreAlertSink.java b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreAlertSink.java new file mode 100644 index 00000000000..c589c0099e8 --- /dev/null +++ b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreAlertSink.java @@ -0,0 +1,40 @@ +package network.crypta.store.alerts; + +/** + * Receives dynamic store-maintenance alert sources published by store implementations. + * + *
This interface is the narrow boundary between the leaf-owned store layer and the root-owned + * runtime alert system. Stores do not format text, localize strings, or build UI fragments. + * Instead, they register a live {@link StoreMaintenanceAlertSource} and allow the runtime layer to + * decide whether that source becomes a user alert, log entry, diagnostics panel, or no visible + * output at all. + * + *
The contract is intentionally small so the store layer can remain reusable and testable. A + * sink implementation may keep the source for repeated polling, transform it into another alert + * representation, or ignore it completely when the current runtime does not expose maintenance + * alerts. + * + * @see StoreMaintenanceAlertSource + */ +public interface StoreAlertSink { + /** + * Sink that silently drops all registrations. + * + *
Use this when a caller wants to avoid null checks but has no alert destination for store + * maintenance progress. + */ + StoreAlertSink NO_OP = _ -> {}; + + /** + * Registers a store-maintenance alert source with this sink. + * + *
The source is expected to remain dynamic after registration. Implementations may poll it + * repeatedly, snapshot it immediately, or decide not to surface it. Callers should typically + * register long-lived sources once rather than creating new source objects for every progress + * update. + * + * @param alert live source describing a maintenance operation and its current progress; sink + * implementations may ignore it when the runtime does not expose alerts + */ + void register(StoreMaintenanceAlertSource alert); +} diff --git a/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertKind.java b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertKind.java new file mode 100644 index 00000000000..6d613bf49f0 --- /dev/null +++ b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertKind.java @@ -0,0 +1,26 @@ +package network.crypta.store.alerts; + +/** + * Lists the maintenance progress categories that stores can expose through the alert SPI. + * + *
Runtime adapters use these values to pick localization keys and presentation details while the + * store layer remains free of user-interface code. The enum is intentionally small because the + * current boundary only needs to describe long-running resize and rebuild work. + */ +public enum StoreMaintenanceAlertKind { + /** + * Progress for a store resize operation that changes the effective capacity of the datastore. + * + *
Typical runtimes present this as an operator-visible task because resizing can run for an + * extended period and may temporarily reduce performance. + */ + RESIZE_PROGRESS, + + /** + * Progress for a store rebuild or maintenance pass over existing store data. + * + *
This covers both legacy slot-filter rebuilds after an unclean shutdown and rebuilds that + * convert data into a newer maintenance format. + */ + REBUILD_PROGRESS +} diff --git a/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertSource.java b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertSource.java new file mode 100644 index 00000000000..d55eecf58ab --- /dev/null +++ b/foundation-store-contracts/src/main/java/network/crypta/store/alerts/StoreMaintenanceAlertSource.java @@ -0,0 +1,102 @@ +package network.crypta.store.alerts; + +/** + * Provides live, store-facing data for a maintenance progress alert. + * + *
Implementations expose a small, polling-friendly view of an in-flight store operation such as + * a resize or rebuild. The values returned from this interface are intentionally presentation-free: + * they identify the affected store, describe the kind of work in progress, and report progress + * counters that the runtime can localize and render however it needs. This keeps formatting, HTML + * generation, and runtime policy outside the store layer. + * + *
Callers should treat instances as dynamic and read-mostly. Repeated invocations may return + * different values as maintenance proceeds, and {@link #isValid()} may switch from {@code true} to + * {@code false} when the operation completes or is canceled. + * + * @see StoreAlertSink + * @see StoreMaintenanceAlertKind + */ +public interface StoreMaintenanceAlertSource { + /** + * Returns a stable anchor string for this maintenance alert. + * + *
The anchor is used by runtime adapters as a durable identifier for deduplication, fragment + * links, or UI element IDs. It should remain stable for the lifetime of the underlying + * maintenance task and avoid characters that would be awkward in URLs or HTML identifiers. + * + * @return a short, stable identifier for the alert source that remains constant while the + * maintenance operation is active + */ + String anchor(); + + /** + * Returns the human-readable name of the affected store. + * + *
This value is intended for interpolation into localized runtime messages. It should be + * concise and recognizable to operators, for example, the configured datastore name or cache + * label. + * + * @return store name suitable for operator-facing alert text and diagnostics + */ + String storeName(); + + /** + * Returns the kind of maintenance task currently represented by this source. + * + *
Runtime adapters use this value to select the appropriate localization keys, severity, and + * surrounding presentation. Implementations should return the same value throughout one logical + * maintenance task. + * + * @return enum value describing whether the alert represents resize progress, rebuild progress, + * or another future maintenance category + */ + StoreMaintenanceAlertKind kind(); + + /** + * Returns the amount of maintenance work completed so far. + * + *
The unit must match {@link #total()}. Implementations typically report processed slots, + * entries, or similar work units. The value should be non-negative and usually monotonic while + * the task is valid. + * + * @return completed work units for the current maintenance task, in the same units as {@link + * #total()} + */ + long processed(); + + /** + * Returns the total amount of work expected for the maintenance task. + * + *
This provides the denominator for progress displays. Implementations should keep the unit + * consistent with {@link #processed()}. The value may be zero when the total is unknown or when a + * task has not started computing progress yet. + * + * @return total expected work units for the current task, using the same unit system as {@link + * #processed()} + */ + long total(); + + /** + * Reports whether the task is rebuilding into the newer slot-filter format. + * + *
This flag lets the runtime choose between the legacy rebuild wording and the newer + * conversion wording without forcing the store to produce preformatted strings. Callers should + * interpret the flag only for rebuild-style alerts. + * + * @return {@code true} when the rebuild path is converting to the new slot-filter format; {@code + * false} for legacy rebuilds and non-rebuild tasks + */ + boolean newSlotFilter(); + + /** + * Reports whether this alert source should still be shown. + * + *
Once this method returns {@code false}, runtime adapters should treat the maintenance alert + * as finished or obsolete and stop presenting it. Implementations may return {@code false} when + * the task completes, is canceled, or is replaced by a new source. + * + * @return {@code true} while the source represents an active maintenance condition; {@code false} + * when the alert should be considered stale or complete + */ + boolean isValid(); +} diff --git a/foundation-support/build.gradle.kts b/foundation-support/build.gradle.kts index 18595b36f55..932bcad345c 100644 --- a/foundation-support/build.gradle.kts +++ b/foundation-support/build.gradle.kts @@ -6,9 +6,12 @@ plugins { version = rootProject.version dependencies { + implementation(project(":foundation-fs")) implementation(libs.slf4jApi) implementation(project(":thirdparty-legacy")) implementation(libs.commonsCompress) + implementation(libs.jna) + implementation(libs.jnaPlatform) implementation(files(rootProject.file("libs/wrapper.jar"))) compileOnly(libs.jetbrainsAnnotations) } diff --git a/foundation-support/gradle/owned-output-patterns.txt b/foundation-support/gradle/owned-output-patterns.txt index 2507e7fcf53..779ee962d7a 100644 --- a/foundation-support/gradle/owned-output-patterns.txt +++ b/foundation-support/gradle/owned-output-patterns.txt @@ -2,6 +2,8 @@ # Keep this list limited to the stable generic support subset extracted into :foundation-support. network/crypta/node/FSParseException* +network/crypta/node/FastRunnable* +network/crypta/node/SemiOrderedShutdownHook* network/crypta/io/WritableToDataOutputStream* network/crypta/support/api/Bucket* network/crypta/support/api/BucketFactory* @@ -11,12 +13,18 @@ network/crypta/support/api/RandomAccessBucket* network/crypta/support/api/RandomAccessBuffer* network/crypta/support/api/ResumeContext* network/crypta/support/Base64* +network/crypta/support/DoublyLinkedList* +network/crypta/support/DoublyLinkedListImpl* +network/crypta/support/ByteArrayWrapper* network/crypta/support/Fields* network/crypta/support/HTMLEncoder* network/crypta/support/HTMLEntities* network/crypta/support/HTMLNode* network/crypta/support/HexUtil* network/crypta/support/IllegalBase64Exception* +network/crypta/support/LRUMap* +network/crypta/support/LightweightException* +network/crypta/support/PromiscuousItemException* network/crypta/support/PriorityAwareExecutor* network/crypta/support/Loader* network/crypta/support/SimpleReadOnlyArrayBucket* @@ -27,6 +35,8 @@ network/crypta/support/TimeUtil* network/crypta/support/URLDecoder* network/crypta/support/URLEncodedFormatException* network/crypta/support/URLEncoder* +network/crypta/support/VirginItemException* +network/crypta/support/WrapperKeepalive* network/crypta/support/XMLCharacterClasses* network/crypta/support/io/AtomicFileMoves* network/crypta/support/io/ArrayBucket* @@ -43,6 +53,7 @@ network/crypta/support/io/DiskSpaceChecker* network/crypta/support/io/HeaderStreams* network/crypta/support/io/IOUtils* network/crypta/support/io/InetAddressComparator* +network/crypta/support/io/Fallocate* network/crypta/support/io/FilenameGenerator* network/crypta/support/io/FilenameSanitizer* network/crypta/support/io/InsufficientDiskSpaceException* @@ -58,6 +69,7 @@ network/crypta/support/io/NullRandomAccessBuffer* network/crypta/support/io/NullWriter* network/crypta/support/io/PersistentFileTracker* network/crypta/support/io/PersistentFilenameGenerator* +network/crypta/support/io/NativeThread* network/crypta/support/io/RAFInputStream* network/crypta/support/io/RandomAccessFileOutputStream* network/crypta/support/io/ReadOnlyFileSliceBucket* diff --git a/foundation-support/src/main/java/network/crypta/node/FastRunnable.java b/foundation-support/src/main/java/network/crypta/node/FastRunnable.java new file mode 100644 index 00000000000..2eceaa9fca2 --- /dev/null +++ b/foundation-support/src/main/java/network/crypta/node/FastRunnable.java @@ -0,0 +1,23 @@ +package network.crypta.node; + +/** + * Marker interface for short, non-blocking tasks that are safe to execute inline on + * latency-sensitive threads. + * + *
Schedulers in this codebase, including the packet sender and ticker implementations in the + * runtime layer, may check whether a task implements {@code FastRunnable}. When it does, they can + * invoke {@link #run()} directly on the calling thread to minimize scheduling overhead and wake-up + * latency. Tasks that do not implement this interface are typically offloaded to an executor. + * + *
Implementations are expected to keep {@link #run()} extremely short and to avoid blocking + * operations such as sleep, I/O, lock contention, or long computations. A slow "fast" task can + * stall a networking or ticker thread and reduce overall throughput. Callers may invoke these tasks + * on shared infrastructure threads, so implementations should not rely on thread-local state or + * thread-affinity behavior. Unchecked exceptions are handled by the calling scheduler, but + * implementations should still avoid throwing when practical. + * + *
Memory visibility follows the usual {@link Runnable} contract. This marker does not add any + * extra happens-before guarantees beyond those established by the scheduler that invokes {@link + * #run()}. + */ +public interface FastRunnable extends Runnable {} diff --git a/src/main/java/network/crypta/node/SemiOrderedShutdownHook.java b/foundation-support/src/main/java/network/crypta/node/SemiOrderedShutdownHook.java similarity index 88% rename from src/main/java/network/crypta/node/SemiOrderedShutdownHook.java rename to foundation-support/src/main/java/network/crypta/node/SemiOrderedShutdownHook.java index e7df1a6cac6..4fe3900a850 100644 --- a/src/main/java/network/crypta/node/SemiOrderedShutdownHook.java +++ b/foundation-support/src/main/java/network/crypta/node/SemiOrderedShutdownHook.java @@ -12,14 +12,14 @@ *
On shutdown, this hook starts all early jobs concurrently and joins each with a fixed * per-thread timeout. It then starts late jobs and joins them with the same timeout. If an * interruption occurs while joining, it is remembered and the interrupted status is restored after - * both join loops complete. This allows the remaining joins to proceed while still propagating the - * interrupt to callers. + * both join loops are complete. This allows the remaining joins to proceed while still propagating + * the interrupt to callers. * *
Thread safety: registration methods are synchronized. Snapshots of job lists are taken at - * {@link #run()} time to avoid holding locks during joins. Callers should register jobs before + * {@link #run()} time to avoid holding locks during joins. Callers should register jobs before the * shutdown begins. * - *
Usage: obtain the singleton via {@link #get()} and register unstarted threads with {@link + *
Usage: get the singleton via {@link #get()} and register unstarted threads with {@link
* #addEarlyJob(Thread)} or {@link #addLateJob(Thread)}. The hook calls {@link Thread#start()}.
*/
@SuppressWarnings({"java:S6548", "java:S2142"})
@@ -91,7 +91,7 @@ public void run() {
try {
r.join(TIMEOUT);
} catch (InterruptedException _) {
- // Remember interruption and continue joining remaining threads.
+ // Remember the interruption and continue joining remaining threads.
wasInterrupted = true;
}
}
@@ -108,14 +108,14 @@ public void run() {
try {
r.join(TIMEOUT);
} catch (InterruptedException _) {
- // Remember interruption and continue joining remaining threads.
+ // Remember the interruption and continue joining remaining threads.
wasInterrupted = true;
}
}
if (wasInterrupted) {
// Restore the interrupted status after all joins so callers can observe it
- // without causing subsequent joins in this method to fail immediately.
+ // without causing later joins in this method to fail immediately.
Thread.currentThread().interrupt();
}
}
diff --git a/src/main/java/network/crypta/support/ByteArrayWrapper.java b/foundation-support/src/main/java/network/crypta/support/ByteArrayWrapper.java
similarity index 96%
rename from src/main/java/network/crypta/support/ByteArrayWrapper.java
rename to foundation-support/src/main/java/network/crypta/support/ByteArrayWrapper.java
index b45566a351e..51409220ff7 100644
--- a/src/main/java/network/crypta/support/ByteArrayWrapper.java
+++ b/foundation-support/src/main/java/network/crypta/support/ByteArrayWrapper.java
@@ -34,7 +34,7 @@ public class ByteArrayWrapper implements Comparable This comparator may be faster for mostly-distinct values because it compares {@link
+ * This comparator may be faster for mostly distinct values because it compares {@link
* #hashCode()} first (an {@code int} comparison) and only performs a full lexicographic
* comparison when hash codes collide. The resulting order is total and consistent with {@link
* #equals(Object)} because ties on the hash are broken by {@link #compareTo(ByteArrayWrapper)}.
@@ -74,8 +74,8 @@ public boolean equals(Object other) {
/**
* Returns a cached, content-based hash code.
*
- * The hash is computed once from the wrapped array and stored, making subsequent calls
- * constant time. The hash code is consistent with {@link #equals(Object)}.
+ * The hash is computed once from the wrapped array and stored, making later calls constant
+ * time. The hash code is consistent with {@link #equals(Object)}.
*
* @return the cached hash code based on the array contents
*/
diff --git a/src/main/java/network/crypta/support/DoublyLinkedList.java b/foundation-support/src/main/java/network/crypta/support/DoublyLinkedList.java
similarity index 100%
rename from src/main/java/network/crypta/support/DoublyLinkedList.java
rename to foundation-support/src/main/java/network/crypta/support/DoublyLinkedList.java
diff --git a/src/main/java/network/crypta/support/DoublyLinkedListImpl.java b/foundation-support/src/main/java/network/crypta/support/DoublyLinkedListImpl.java
similarity index 99%
rename from src/main/java/network/crypta/support/DoublyLinkedListImpl.java
rename to foundation-support/src/main/java/network/crypta/support/DoublyLinkedListImpl.java
index 4c2d0dc39ba..58b2bb30675 100644
--- a/src/main/java/network/crypta/support/DoublyLinkedListImpl.java
+++ b/foundation-support/src/main/java/network/crypta/support/DoublyLinkedListImpl.java
@@ -160,7 +160,7 @@ public final T shift() {
* this returns an empty list and does not modify this list.
*
* @param n number of items to remove; non-positive values yield an empty result
- * @return a list containing the removed prefix, preserving original order
+ * @return a list containing the removed prefix, preserving the original order
*/
@Override
public DoublyLinkedList When a mapping is {@linkplain #push(Object, Object) pushed}, the entry becomes the most
* recently used, even if the key already exists. Removal and peeking operations work from the
- * least-recently-used side (i.e., the entry that was pushed furthest in the past). The caller is
+ * least-recently used side (i.e., the entry that was pushed furthest in the past). The caller is
* responsible for enforcing any size limits or eviction policies on top of this primitive.
*
* In many cases, a {@link java.util.LinkedHashMap} configured for access order can offer similar
@@ -64,7 +64,7 @@ public LRUMap() {
/**
* Creates an instance reusing the provided backing map.
*
- * Implementation detail: used by safe factory methods to switch map type.
+ * Implementation detail: used by safe factory methods to switch the map type.
*/
private LRUMap(Map Also removes the corresponding mapping.
*
@@ -150,7 +150,7 @@ public final synchronized K popKey() {
}
/**
- * Removes and returns the least-recently-used value.
+ * Removes and returns the least-recently used value.
*
* Also removes the corresponding mapping.
*
@@ -172,7 +172,7 @@ public final synchronized V popValue() {
}
/**
- * Returns the least-recently-used value without removing it.
+ * Returns the least-recently used value without removing it.
*
* @return the LRU value, or {@code null} if empty
*/
@@ -190,7 +190,7 @@ public final synchronized V peekValue() {
}
/**
- * Returns the least-recently-used key without removing it.
+ * Returns the least-recently used key without removing it.
*
* @return the LRU key, or {@code null} if empty
*/
@@ -269,7 +269,7 @@ public final synchronized V get(K key) {
/**
* Returns a snapshot {@link Iterator} of keys in LRU→MRU order.
*
- * The snapshot is built under synchronization and is not affected by subsequent changes.
+ * The snapshot is built under synchronization and is not affected by further changes.
*
* @return iterator of keys from least- to most-recently-used
*/
@@ -287,7 +287,7 @@ public Iterator The snapshot is built under synchronization and is not affected by subsequent changes.
+ * The snapshot is built under synchronization and is not affected by further changes.
*
* @return iterator of values from least- to most-recently-used
*/
diff --git a/src/main/java/network/crypta/support/LightweightException.java b/foundation-support/src/main/java/network/crypta/support/LightweightException.java
similarity index 100%
rename from src/main/java/network/crypta/support/LightweightException.java
rename to foundation-support/src/main/java/network/crypta/support/LightweightException.java
diff --git a/src/main/java/network/crypta/support/PromiscuousItemException.java b/foundation-support/src/main/java/network/crypta/support/PromiscuousItemException.java
similarity index 100%
rename from src/main/java/network/crypta/support/PromiscuousItemException.java
rename to foundation-support/src/main/java/network/crypta/support/PromiscuousItemException.java
diff --git a/src/main/java/network/crypta/support/VirginItemException.java b/foundation-support/src/main/java/network/crypta/support/VirginItemException.java
similarity index 100%
rename from src/main/java/network/crypta/support/VirginItemException.java
rename to foundation-support/src/main/java/network/crypta/support/VirginItemException.java
diff --git a/src/main/java/network/crypta/support/WrapperKeepalive.java b/foundation-support/src/main/java/network/crypta/support/WrapperKeepalive.java
similarity index 97%
rename from src/main/java/network/crypta/support/WrapperKeepalive.java
rename to foundation-support/src/main/java/network/crypta/support/WrapperKeepalive.java
index fd48796fef5..345962db504 100644
--- a/src/main/java/network/crypta/support/WrapperKeepalive.java
+++ b/foundation-support/src/main/java/network/crypta/support/WrapperKeepalive.java
@@ -32,7 +32,7 @@
*
* Threading: the instance owns its thread. {@link #close()} is thread-safe and may be invoked
* from any thread. Closing requests termination; the loop exits after the current sleep interval or
- * sooner if the thread is interrupted by the caller. This class never interrupts itself.
+ * sooner if the caller interrupts the thread. This class never interrupts itself.
*
* Interrupts: if interrupted while sleeping, the thread logs at DEBUG level, clears the
* interrupted status via {@link Thread#interrupted()}, and continues looping until closed.
diff --git a/src/main/java/network/crypta/support/io/Fallocate.java b/foundation-support/src/main/java/network/crypta/support/io/Fallocate.java
similarity index 98%
rename from src/main/java/network/crypta/support/io/Fallocate.java
rename to foundation-support/src/main/java/network/crypta/support/io/Fallocate.java
index f30064702bb..25040ba8032 100644
--- a/src/main/java/network/crypta/support/io/Fallocate.java
+++ b/foundation-support/src/main/java/network/crypta/support/io/Fallocate.java
@@ -191,7 +191,7 @@ private static int getDescriptor(FileChannel channel) {
final Field field = channel.getClass().getDeclaredField("fd");
// Attempt to make the field accessible; fall back to legacy when forbidden.
if (!field.canAccess(channel) && !field.trySetAccessible()) {
- return 0; // Trigger legacy path when descriptor is not accessible
+ return 0; // Trigger a legacy path when the descriptor is not accessible
}
return getDescriptor((FileDescriptor) field.get(channel));
} catch (final Exception _) {
@@ -212,7 +212,7 @@ private static int getDescriptor(FileDescriptor descriptor) {
final Field field = descriptor.getClass().getDeclaredField(IS_ANDROID ? "descriptor" : "fd");
// Attempt to make the field accessible; fall back to legacy when forbidden.
if (!field.canAccess(descriptor) && !field.trySetAccessible()) {
- return 0; // Trigger legacy path when descriptor is not accessible
+ return 0; // Trigger a legacy path when the descriptor is not accessible
}
return (int) field.get(descriptor);
} catch (final Exception _) {
diff --git a/src/main/java/network/crypta/support/io/NativeThread.java b/foundation-support/src/main/java/network/crypta/support/io/NativeThread.java
similarity index 100%
rename from src/main/java/network/crypta/support/io/NativeThread.java
rename to foundation-support/src/main/java/network/crypta/support/io/NativeThread.java
diff --git a/src/main/java/network/crypta/node/FastRunnable.java b/src/main/java/network/crypta/node/FastRunnable.java
deleted file mode 100644
index d754a7b54ae..00000000000
--- a/src/main/java/network/crypta/node/FastRunnable.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package network.crypta.node;
-
-/**
- * Marker interface for short, non‑blocking tasks that are safe to execute inline on
- * latency‑sensitive threads.
- *
- * Schedulers in this codebase (for example {@link PacketSender} as well as ticker
- * implementations such as {@link network.crypta.support.PrioritizedTicker} and {@link
- * network.crypta.support.TrivialTicker}) may check whether a task implements {@code FastRunnable}.
- * When it does, they can invoke {@link #run()} directly on the calling thread to minimize
- * scheduling overhead and wake‑up latency. Tasks that do not implement this interface are typically
- * offloaded to an executor.
- *
- * Contract for implementers: - Keep the body of {@link #run()} very short and avoid blocking
- * operations (sleep, I/O, waiting on locks, long computations). A slow "fast" task can stall the
- * networking/ticker thread and degrade throughput. - Assume execution occurs on a shared
- * infrastructure thread; do not perform work that may rely on thread‑local context or
- * thread‑affinity semantics. - Unchecked exceptions will be handled by the calling scheduler;
- * implementations should avoid throwing when possible.
- *
- * Memory visibility follows the usual {@link Runnable} semantics; there are no extra
- * happens‑before guarantees beyond those established by the scheduler invoking {@link #run()}.
- *
- * @see PacketSender
- * @see network.crypta.support.PrioritizedTicker
- * @see network.crypta.support.TrivialTicker
- */
-public interface FastRunnable extends Runnable {}
diff --git a/src/main/java/network/crypta/node/runtime/UserAlertManagerStoreAlertSink.java b/src/main/java/network/crypta/node/runtime/UserAlertManagerStoreAlertSink.java
new file mode 100644
index 00000000000..e57bf8d0744
--- /dev/null
+++ b/src/main/java/network/crypta/node/runtime/UserAlertManagerStoreAlertSink.java
@@ -0,0 +1,162 @@
+package network.crypta.node.runtime;
+
+import java.util.Objects;
+import network.crypta.l10n.NodeL10n;
+import network.crypta.node.useralerts.AbstractUserAlert;
+import network.crypta.node.useralerts.UserAlert;
+import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
+import network.crypta.store.alerts.StoreMaintenanceAlertKind;
+import network.crypta.store.alerts.StoreMaintenanceAlertSource;
+import network.crypta.support.HTMLNode;
+
+/**
+ * Bridges store-maintenance alert sources into the runtime {@link UserAlertManager}.
+ *
+ * The extracted store boundary exposes only live maintenance state via {@link
+ * StoreMaintenanceAlertSource}. This adapter keeps that boundary narrow while preserving the
+ * existing runtime behavior: it translates the source into a runtime-local {@link UserAlert},
+ * resolves localized strings through {@link NodeL10n}, and registers the resulting alert with the
+ * shared alert manager used by HTTP, FCP, and other operator-facing surfaces.
+ *
+ * Each registration produces a lightweight wrapper that delegates back to the source whenever
+ * alert text or validity is queried. That means progress values stay current without the store
+ * having to emit pre-rendered text snapshots. The adapter is stateless apart from its reference to
+ * the target alert manager and is safe to reuse for multiple stores.
+ */
+public final class UserAlertManagerStoreAlertSink implements StoreAlertSink {
+ private static final String ALERTS_PREFIX = "SaltedHashCryptaStore.";
+
+ private final UserAlertManager userAlertManager;
+
+ /**
+ * Creates an adapter that forwards store-maintenance alerts into the given runtime manager.
+ *
+ * The manager is retained for the lifetime of this adapter and receives one runtime alert
+ * wrapper for each registered maintenance source. Callers typically create one sink per node
+ * storage subsystem and share it across multiple stores.
+ *
+ * @param userAlertManager runtime alert registry that should receive wrapped maintenance alerts;
+ * must not be {@code null}
+ * @throws NullPointerException if {@code userAlertManager} is {@code null}
+ */
+ public UserAlertManagerStoreAlertSink(UserAlertManager userAlertManager) {
+ this.userAlertManager = Objects.requireNonNull(userAlertManager, "userAlertManager");
+ }
+
+ /**
+ * Wraps the store-maintenance source in a runtime alert and registers it with the manager.
+ *
+ * The wrapper remains dynamic: text, progress, and validity are computed from the source each
+ * time the runtime alert is queried. This preserves the old user-visible cleaner-progress
+ * behavior while keeping localization and HTML generation outside the store layer.
+ *
+ * @param alert live maintenance source to expose through the runtime alert system; must not be
+ * {@code null}
+ * @throws NullPointerException if {@code alert} is {@code null}
+ */
+ @Override
+ public void register(StoreMaintenanceAlertSource alert) {
+ userAlertManager.register(new StoreMaintenanceUserAlert(alert));
+ }
+
+ private static final class StoreMaintenanceUserAlert extends AbstractUserAlert {
+ private static final String[] PROGRESS_PATTERNS = {"name", "processed", "total"};
+ private static final String[] TITLE_PATTERNS = {"name"};
+
+ private final StoreMaintenanceAlertSource source;
+
+ private StoreMaintenanceUserAlert(StoreMaintenanceAlertSource source) {
+ this.source = Objects.requireNonNull(source, "source");
+ }
+
+ @Override
+ public String anchor() {
+ return source.anchor();
+ }
+
+ @Override
+ public String dismissButtonText() {
+ return NodeL10n.getBase().getString("UserAlert.hide");
+ }
+
+ @Override
+ public HTMLNode getHTMLText() {
+ return new HTMLNode("#", getText());
+ }
+
+ @Override
+ public short getPriorityClass() {
+ return UserAlert.ERROR;
+ }
+
+ @Override
+ public String getShortText() {
+ return localizedProgressText(shortKey());
+ }
+
+ @Override
+ public String getText() {
+ return localizedProgressText(longKey());
+ }
+
+ @Override
+ public String getTitle() {
+ return NodeL10n.getBase()
+ .getString(ALERTS_PREFIX + "cleanerAlertTitle", TITLE_PATTERNS, titleValues());
+ }
+
+ @Override
+ public boolean isValid() {
+ return source.isValid();
+ }
+
+ @Override
+ public void isValid(boolean validity) {
+ // Runtime validity is driven by the dynamic source.
+ }
+
+ @Override
+ public void onDismiss() {
+ // Non-dismissible alert; nothing to do.
+ }
+
+ @Override
+ public boolean shouldUnregisterOnDismiss() {
+ return true;
+ }
+
+ @Override
+ public boolean userCanDismiss() {
+ return false;
+ }
+
+ private String localizedProgressText(String key) {
+ return NodeL10n.getBase().getString(ALERTS_PREFIX + key, PROGRESS_PATTERNS, progressValues());
+ }
+
+ private String shortKey() {
+ if (source.kind() == StoreMaintenanceAlertKind.RESIZE_PROGRESS) {
+ return "shortResizeProgress";
+ }
+ return source.newSlotFilter() ? "shortRebuildProgressNew" : "shortRebuildProgress";
+ }
+
+ private String longKey() {
+ if (source.kind() == StoreMaintenanceAlertKind.RESIZE_PROGRESS) {
+ return "longResizeProgress";
+ }
+ return source.newSlotFilter() ? "longRebuildProgressNew" : "longRebuildProgress";
+ }
+
+ private String[] progressValues() {
+ return new String[] {
+ source.storeName(), String.valueOf(source.processed()), String.valueOf(source.total())
+ };
+ }
+
+ private String[] titleValues() {
+ return new String[] {source.storeName()};
+ }
+ }
+}
diff --git a/src/main/java/network/crypta/node/subsystem/NodeStorageSubsystem.java b/src/main/java/network/crypta/node/subsystem/NodeStorageSubsystem.java
index 9b62c1a6514..e4b42d65db4 100644
--- a/src/main/java/network/crypta/node/subsystem/NodeStorageSubsystem.java
+++ b/src/main/java/network/crypta/node/subsystem/NodeStorageSubsystem.java
@@ -40,6 +40,7 @@
import network.crypta.node.NodeStoreStatsProvider;
import network.crypta.node.SecurityLevels.PHYSICAL_THREAT_LEVEL;
import network.crypta.node.SemiOrderedShutdownHook;
+import network.crypta.node.runtime.UserAlertManagerStoreAlertSink;
import network.crypta.node.stats.DataStoreInstanceType;
import network.crypta.node.stats.DataStoreKeyType;
import network.crypta.node.stats.DataStoreStats;
@@ -58,6 +59,7 @@
import network.crypta.store.SlashdotStore;
import network.crypta.store.StorableBlock;
import network.crypta.store.StoreCallback;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.store.caching.CachingFreenetStore;
import network.crypta.store.caching.CachingFreenetStoreTracker;
import network.crypta.store.saltedhash.ResizablePersistentIntBuffer;
@@ -155,7 +157,7 @@ public final class NodeStorageSubsystem {
* Estimated bytes consumed per logical key across primary data and metadata structures.
*
* This heuristic drives key-count derivation from configured byte capacities. It is not a
- * precise on-disk accounting number, but a stable planning estimate used for store partitioning.
+ * precise on-disk accounting number but a stable planning estimate used for store partitioning.
*/
public static final int SIZE_PER_KEY =
network.crypta.keys.CHKBlock.DATA_LENGTH
@@ -1864,7 +1866,7 @@ public boolean isDatabaseAwaitingPassword() {
/**
* Clears password-waiting flags for both client-cache and database initialization paths.
*
- * Callers typically invoke this after successfully applying credentials or resetting setup
+ * Callers typically invoke this after successfully applying credentials or resetting the setup
* state.
*/
public void clearAwaitingPasswords() {
@@ -2233,12 +2235,14 @@ public void initNoClientCacheFS() {
private void finishInitSaltHashFS() {
if (node.services().clientCore().getAlerts() == null) throw new NullPointerException();
- chkDatastore.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
- chkDatacache.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
- pubKeyDatastore.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
- pubKeyDatacache.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
- sskDatastore.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
- sskDatacache.getStore().setUserAlertManager(node.services().clientCore().getAlerts());
+ StoreAlertSink storeAlertSink =
+ new UserAlertManagerStoreAlertSink(node.services().clientCore().getAlerts());
+ chkDatastore.getStore().setStoreAlertSink(storeAlertSink);
+ chkDatacache.getStore().setStoreAlertSink(storeAlertSink);
+ pubKeyDatastore.getStore().setStoreAlertSink(storeAlertSink);
+ pubKeyDatacache.getStore().setStoreAlertSink(storeAlertSink);
+ sskDatastore.getStore().setStoreAlertSink(storeAlertSink);
+ sskDatacache.getStore().setStoreAlertSink(storeAlertSink);
}
/**
diff --git a/src/main/java/network/crypta/store/FreenetStore.java b/src/main/java/network/crypta/store/FreenetStore.java
index 840ead75cf1..a0985266a67 100644
--- a/src/main/java/network/crypta/store/FreenetStore.java
+++ b/src/main/java/network/crypta/store/FreenetStore.java
@@ -3,7 +3,7 @@
import java.io.Closeable;
import java.io.IOException;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.support.Ticker;
/**
@@ -201,13 +201,12 @@ void put(T block, byte[] data, byte[] header, boolean overwrite, boolean oldBloc
void close();
/**
- * Set the user alert manager used to publish non-fatal warnings and notices related to the store
- * (for example, background rebuild progress or recoverable errors).
+ * Set the sink used to publish non-fatal store-maintenance alerts.
*
- * @param userAlertManager manager used to post user-facing alerts; may be ignored by some
+ * @param alertSink sink used to register dynamic store-maintenance alerts; may be ignored by some
* implementations.
*/
- void setUserAlertManager(UserAlertManager userAlertManager);
+ void setStoreAlertSink(StoreAlertSink alertSink);
/**
* Return the underlying store when this instance is a wrapper.
diff --git a/src/main/java/network/crypta/store/NullFreenetStore.java b/src/main/java/network/crypta/store/NullFreenetStore.java
index c07971d8b76..b61ce279470 100644
--- a/src/main/java/network/crypta/store/NullFreenetStore.java
+++ b/src/main/java/network/crypta/store/NullFreenetStore.java
@@ -2,7 +2,7 @@
import java.io.IOException;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.support.Ticker;
/**
@@ -205,9 +205,9 @@ public boolean start(Ticker ticker, boolean longStart) throws IOException {
return false;
}
- /** Sets a {@link UserAlertManager}; ignored because the store has no alerts to raise. */
+ /** Sets a store alert sink; ignored because the store has no alerts to raise. */
@Override
- public void setUserAlertManager(UserAlertManager userAlertManager) {
+ public void setStoreAlertSink(StoreAlertSink alertSink) {
// No-op
}
diff --git a/src/main/java/network/crypta/store/ProxyFreenetStore.java b/src/main/java/network/crypta/store/ProxyFreenetStore.java
index 11e1bc62e38..81ecd71e96e 100644
--- a/src/main/java/network/crypta/store/ProxyFreenetStore.java
+++ b/src/main/java/network/crypta/store/ProxyFreenetStore.java
@@ -2,7 +2,7 @@
import java.io.IOException;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.support.Ticker;
/**
@@ -83,10 +83,10 @@ public StoreAccessStats getTotalAccessStats() {
return backDatastore.getTotalAccessStats();
}
- /** {@inheritDoc} Forwards the manager to the underlying store. */
+ /** {@inheritDoc} Forwards the sink to the underlying store. */
@Override
- public void setUserAlertManager(UserAlertManager userAlertManager) {
- this.backDatastore.setUserAlertManager(userAlertManager);
+ public void setStoreAlertSink(StoreAlertSink alertSink) {
+ this.backDatastore.setStoreAlertSink(alertSink);
}
/** {@inheritDoc} Returns the delegate for this proxy. */
diff --git a/src/main/java/network/crypta/store/RAMFreenetStore.java b/src/main/java/network/crypta/store/RAMFreenetStore.java
index 44388e3f46e..a629fe2e89f 100644
--- a/src/main/java/network/crypta/store/RAMFreenetStore.java
+++ b/src/main/java/network/crypta/store/RAMFreenetStore.java
@@ -5,7 +5,7 @@
import java.util.Iterator;
import network.crypta.keys.KeyVerifyException;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.support.ByteArrayWrapper;
import network.crypta.support.LRUMap;
import network.crypta.support.Ticker;
@@ -353,7 +353,7 @@ public boolean start(Ticker ticker, boolean longStart) throws IOException {
}
@Override
- public void setUserAlertManager(UserAlertManager userAlertManager) {
+ public void setStoreAlertSink(StoreAlertSink alertSink) {
// Do nothing
}
diff --git a/src/main/java/network/crypta/store/SlashdotStore.java b/src/main/java/network/crypta/store/SlashdotStore.java
index 1d2a5a867ec..dbb436feb88 100644
--- a/src/main/java/network/crypta/store/SlashdotStore.java
+++ b/src/main/java/network/crypta/store/SlashdotStore.java
@@ -8,12 +8,12 @@
import java.util.List;
import network.crypta.keys.KeyVerifyException;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.UserAlertManager;
+import network.crypta.store.alerts.StoreAlertSink;
import network.crypta.support.ByteArrayWrapper;
import network.crypta.support.LRUMap;
import network.crypta.support.Ticker;
import network.crypta.support.api.Bucket;
-import network.crypta.support.io.TempBucketFactory;
+import network.crypta.support.api.BucketFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,7 +29,7 @@
* Block payloads are written into temporary buckets provided by {@link TempBucketFactory};
+ * Block payloads are written into temporary buckets provided by {@link BucketFactory};
* implementations commonly encrypt buckets at rest. The in‑memory index maps routing keys to disk
* buckets and is synchronized for thread safety. Disk I/O is performed outside critical sections to
* avoid holding locks during blocking operations.
@@ -45,7 +45,7 @@ private static class DiskBlock {
long lastAccessed;
}
- private final TempBucketFactory bf;
+ private final BucketFactory bf;
private long maxLifetime;
@@ -81,7 +81,7 @@ private static class DiskBlock {
* regardless of LRU position
* @param purgePeriod interval in milliseconds between scheduled purge runs
* @param ticker scheduler used for periodic purge tasks
- * @param tbf factory for on‑disk temporary buckets used to persist cached bytes
+ * @param bucketFactory factory for on‑disk temporary buckets used to persist cached bytes
*/
public SlashdotStore(
StoreCallback Overwrites the in-memory {@code salt} and {@code diskSalt} arrays via {@link
- * MasterKeys#clear(byte[])}. After shutdown, this instance should not be used.
+ * Overwrites the in-memory {@code salt} and {@code diskSalt} arrays directly. After shutdown,
+ * this instance should not be used.
*/
public void shutdown() {
- MasterKeys.clear(salt);
- MasterKeys.clear(diskSalt);
+ Arrays.fill(salt, (byte) 0);
+ Arrays.fill(diskSalt, (byte) 0);
}
}
diff --git a/src/main/java/network/crypta/store/saltedhash/SaltedHashFreenetStore.java b/src/main/java/network/crypta/store/saltedhash/SaltedHashFreenetStore.java
index 7dc371ba8e0..e4061b5c0de 100644
--- a/src/main/java/network/crypta/store/saltedhash/SaltedHashFreenetStore.java
+++ b/src/main/java/network/crypta/store/saltedhash/SaltedHashFreenetStore.java
@@ -32,26 +32,24 @@
import network.crypta.crypt.ciphers.Rijndael;
import network.crypta.keys.KeyVerifyException;
import network.crypta.keys.SSKBlock;
-import network.crypta.l10n.NodeL10n;
import network.crypta.node.FastRunnable;
import network.crypta.node.SemiOrderedShutdownHook;
import network.crypta.node.stats.StoreAccessStats;
-import network.crypta.node.useralerts.AbstractUserAlert;
-import network.crypta.node.useralerts.UserAlert;
-import network.crypta.node.useralerts.UserAlertManager;
import network.crypta.store.BlockMetadata;
import network.crypta.store.FetchOptions;
import network.crypta.store.FreenetStore;
import network.crypta.store.KeyCollisionException;
import network.crypta.store.StorableBlock;
import network.crypta.store.StoreCallback;
+import network.crypta.store.alerts.StoreAlertSink;
+import network.crypta.store.alerts.StoreMaintenanceAlertKind;
+import network.crypta.store.alerts.StoreMaintenanceAlertSource;
import network.crypta.support.Fields;
-import network.crypta.support.HTMLNode;
import network.crypta.support.HexUtil;
import network.crypta.support.Ticker;
import network.crypta.support.WrapperKeepalive;
+import network.crypta.support.io.AtomicFileMoves;
import network.crypta.support.io.Fallocate;
-import network.crypta.support.io.FileUtil;
import network.crypta.support.io.NativeThread;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@@ -128,9 +126,6 @@ public final class SaltedHashFreenetStore