sentinel

Mutex-synchronized in-memory write registry with inline TTL eviction for suppressing filesystem watcher echo feedback loops in Crystal.
Sentinel Logo

Thread-Safe In-Memory Write Registry & Anti-Echo Suppression for Crystal

CI Status Crystal Version License: GPL-3.0 SHA-256 Repository Ameba Verified Liberapay Support

Donate using Liberapay


Overview

When a Crystal application writes files to disk that are concurrently monitored by a filesystem watcher (such as in static site generators, live compilers, or dev servers), the watcher receives notification events for those internal writes. Without suppression, this creates infinite echo feedback loops.

sentinel provides Sentinel::AntiEcho, a high-performance, concurrency-safe registry that lets background writers register impending writes before disk commit. Filesystem watchers query the sentinel to instantly distinguish between internal programmatic writes and external user modifications.


Key Features

  • πŸ›‘οΈ Zero Background Timer Overhead: Expired suppression entries are evicted inline during reads and writes under mutex synchronization, eliminating fiber leaks and GC pressure.
  • ⚑ Execution Contexts Ready: Thread-safe synchronization backed by Sync::Mutex for multi-threaded Crystal runtimes (-Dpreview_mt).
  • πŸ”„ Cross-Platform CRLF Normalization: Pre-computes normalized SHA-256 checksums converting CRLF to LF to prevent phantom modification loops across different operating systems.
  • πŸ“¦ Pure Crystal: Zero external shard dependencies. Uses only standard library primitives.

Architecture & Workflow

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  Writer Fiber / Task β”‚                     β”‚   Watcher Routine    β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚ 1. calculate_checksum                      β”‚
            β”‚ 2. register_write(path, hash)              β”‚
            β–Ό                                            β”‚
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                  β”‚
   β”‚  Disk Write      β”‚                                  β”‚
   β”‚  (File.write)    β”‚ ─── [OS Inotify / FS Event] ───► β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                  β–Ό
                                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                              β”‚ 3. suppressed?(...)  β”‚
                                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                         β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                    β–Ό                                         β–Ό
                             [ Match Found ]                           [ No Match ]
                     (Drop event: Internal Write)              (Process: User Modification)

Installation

Add sentinel to your shard.yml:

dependencies:
  sentinel:
    gitlab: renich/sentinel
    version: ~> 0.1.0

Run shards install.


Quick Start

require "sentinel"

# Initialize with 5-second suppression TTL
sentinel = Sentinel::AntiEcho.new(ttl: 5.seconds)

# 1. Compute checksum before writing to disk
content = "# Hello World\n"
checksum = Sentinel::AntiEcho.calculate_checksum(content)

# 2. Register write in sentinel and commit to disk
sentinel.register_write("content/post.md", checksum)
File.write("content/post.md", content)

# 3. Inside your filesystem watcher event callback:
file_on_disk = File.read("content/post.md")
current_checksum = Sentinel::AntiEcho.calculate_checksum(file_on_disk)

if sentinel.suppressed?("content/post.md", current_checksum)
  puts "Suppressed echo event for internal write: content/post.md"
else
  puts "External modification detected on content/post.md. Triggering build..."
end

API Reference

Sentinel::AntiEcho

Method Parameters Description
new(ttl : Time::Span = 10.seconds) ttl Creates a new anti-echo registry with given retention window
register_write(path : String, checksum : String) path, checksum Registers an impending write in the thread-safe registry
suppressed?(path : String, checksum : String) : Bool path, checksum Checks if a file event matches a known write and consumes/suppresses it
size : Int32 None Returns active tracked entry count after inline TTL eviction
clear None Clears all registered write entries
Sentinel::AntiEcho.calculate_checksum(content : String) : String content Computes SHA-256 after CRLF-to-LF normalization

Development & Testing

# Run Crystal specs
make spec

# Run Ameba static analysis
make lint

# Format codebase
make format

Support & Donation

If you find sentinel or other tools in this ecosystem valuable, consider supporting continued FOSS development:

Donate using Liberapay


License

GNU General Public License v3.0 or later (GPL-3.0-or-later).

Copyright © 2026 Rénich Bon Ćirić and Contributors.

Repository

sentinel

Owner
Statistic
  • 0
  • 0
  • 0
  • 1
  • 1
  • about 3 hours ago
  • August 26, 2026
License

GNU General Public License v3.0 only

Links
Synced at

Wed, 26 Aug 2026 04:03:57 GMT

Languages