Rotator base exposes the four lifecycle methods as abstract; subclasses
register at compile time via 'register_as :kind' macro. REGISTRY is
populated as soon as the rotator file is required - drop a new file in
src/cre/rotators/ and the orchestrator can dispatch to it.
EnvFileRotator implements the simplest rotation:
- generate: 32 random bytes -> base64-urlsafe (no padding)
- apply: write to PATH.pending atomically (in-place line replace, 0600)
- verify: parse pending file, confirm key=value present and non-empty
- commit: rename PATH.pending -> PATH (atomic on POSIX)
- rollback_apply: unlink PATH.pending
RotationOrchestrator runs the 4-step contract with full event publishing,
state machine transitions in DB (generating -> applying -> verifying ->
committing -> completed | failed), and rollback_apply on apply/verify
failure. 8 specs cover happy path + raised-during-apply + rollback verification.
- Policy struct: matcher Proc, max_age, warn_at, enforce_action, notify_channels, triggers
- Builder validates all required fields at .build, raises BuilderError with the policy name
- DSL exposes top-level 'policy' method via 'with builder yield' so all
builder methods (description, match, max_age, enforce, notify_via, etc.)
are receiver-less inside the block
- Symbol literals autocast to Action/Channel/Trigger enums on direct calls;
Symbol overload for splat parameters (notify_via :telegram, :email)
- Evaluator subscribes to bus, fires PolicyViolation + RotationScheduled
(rotate_immediately) or AlertRaised (notify_only/quarantine) when overdue
- 15 unit specs cover Policy.matches?/overdue?/in_warning_window?,
Builder validation, DSL syntax with closures, evaluator action dispatch
Events form a Crystal class hierarchy so subscribers pattern-match
exhaustively. EventBus delivers to subscribers via Crystal channels with
per-subscriber overflow policy (Block for audit, Drop for best-effort).
AuditSubscriber listens for all rotation/policy/drift/alert events and
serializes them into the hash-chained audit log via AuditLog.append.
Engine wires Persistence + AuditLog + AuditSubscriber + EventBus together
so callers just .start/.stop. 7 unit specs verify boot, fanout to N
subscribers, drop-on-overflow semantics, and end-to-end event -> audit row.
Three layers of integrity:
- Hash chain (SHA-256 chained) catches silent edits
- HMAC ratchet rotates keys every N entries with HKDF-style derivation
and zeroizes old key bytes from memory
- Merkle batch sealing builds tree over content_hashes, signs root
with Ed25519 (FFI to libcrypto EVP_PKEY_ED25519 directly since stdlib
lacks the high-level OpenSSL::PKey wrapper)
AuditLog.append is mutex-guarded for serial writes; verify_chain detects
both single-row tampering and structural inconsistency. BatchSealer's
pack_message format (BE start_seq || BE end_seq || merkle_root) is shared
between signer and verifier so external auditors can validate offline.
- Random with constant-time-equal helper
- KEK loader from env (Tier 1) with InvalidKekError on bad/missing var
- AEAD via direct LibCrypto FFI bindings (Crystal 1.20 stdlib OpenSSL::Cipher
doesn't expose GCM auth_data/auth_tag, so we extend LibCrypto with
EVP_CIPHER_CTX_ctrl + EVP_aes_256_gcm and call them directly)
- Envelope: per-row DEK wrapped by KEK, AAD-bound, algorithm_id reserved
for crypto agility (0x01 = AES-256-GCM today, 0x02/0x03 reserved)
- 17 unit specs including a 100-iteration random property test
- Tag-tampered ciphertext / wrong-AAD / wrong-key all raise Aead::Error
- All 4 repos mirror the SQLite contracts using PG-native types
(UUID, BYTEA, JSONB, BIGSERIAL, TIMESTAMPTZ, SMALLINT)
- audit_no_modify() trigger refuses UPDATE/DELETE/TRUNCATE on audit_events
- pg_advisory_xact_lock for cross-process row locking
- Integration tests verify trigger fires correctly on tamper attempt
- Test cleanup helper temporarily disables trigger to reset between specs
- 4 integration specs all green against PostgreSQL 16
All four repos implement their abstract contracts. Migrations create
tables idempotently. Bytes round-trip through SQLite BLOB columns
correctly. RotatorKind and RotationState enums replace Symbol fields
in record types so they round-trip safely through DB string columns.
8 unit specs verify CRUD, find_by_external, in_flight filtering,
revocation, and audit append/range/latest_hash genesis behavior.
- shard.yml with db, pg, sqlite3, tourmaline (deps), webmock (dev)
- Makefile with build/test/format/lint/demo targets
- .gitignore, .editorconfig, LICENSE (MIT), README skeleton
- ameba lint deferred until libpcre3-dev is available on this env
- Add DEMO.md and screenshots for bug-bounty-platform, hash-cracker,
linux-cis-hardening-auditor, simple-port-scanner, simple-vulnerability-scanner,
systemd-persistence-scanner, base64-tool, caesar-cipher, dns-lookup,
metadata-scrubber-tool, network-traffic-analyzer, siem-dashboard
- Link DEMO.md from project READMEs
- Add .gitignore entries for DEMO-TRACKER and simple-port-scanner build dir
- Restructure haskell-reverse-proxy with DDoS, Fingerprint, ML, RateLimit, WAF,
Geo, and Honeypot modules; drop superseded research docs and old Makefile
- Refresh siem-dashboard dashboard.png and rename alerts.png to alert-detail.png