Module: Asherah
- Defined in:
- lib/asherah.rb,
lib/asherah/error.rb,
lib/asherah/hooks.rb,
lib/asherah/config.rb,
lib/asherah/native.rb,
lib/asherah/session.rb,
lib/asherah/version.rb,
lib/asherah/session_factory.rb
Defined Under Namespace
Modules: Hooks, Native Classes: Config, Error, Session, SessionFactory
Constant Summary collapse
- DEFAULT_SESSION_CACHE_MAX_SIZE =
1000- VERSION =
"0.10.1"
Class Method Summary collapse
-
.clear_log_hook ⇒ Object
Remove the active log hook, if any.
-
.clear_metrics_hook ⇒ Object
Remove the active metrics hook and disable metrics.
-
.configure ⇒ Object
Configure Asherah using a block with snake_case accessors.
- .decrypt(partition_id, data_row_record) ⇒ Object
-
.decrypt_async(partition_id, data_row_record, &block) ⇒ Object
Run decrypt in a background Thread; see ‘setup_async` for the exception-propagation contract.
- .decrypt_string(partition_id, data_row_record) ⇒ Object
- .encrypt(partition_id, payload) ⇒ Object
-
.encrypt_async(partition_id, payload, &block) ⇒ Object
Run encrypt in a background Thread; see ‘setup_async` for the exception-propagation contract.
- .encrypt_string(partition_id, text) ⇒ Object
- .get_setup_status ⇒ Object
-
.set_log_hook(callback = nil, &block) ⇒ Object
Install a log hook.
-
.set_metrics_hook(callback = nil, &block) ⇒ Object
Install a metrics hook.
- .setenv(env = {}) ⇒ Object (also: set_env)
-
.setup(config) ⇒ Object
Initialize Asherah with a PascalCase config hash.
-
.setup_async(config, &block) ⇒ Object
Run setup in a background Thread.
- .shutdown ⇒ Object
-
.shutdown_async(&block) ⇒ Object
Run shutdown in a background Thread; see ‘setup_async` for the exception-propagation contract.
Class Method Details
.clear_log_hook ⇒ Object
Remove the active log hook, if any. Idempotent.
221 222 223 |
# File 'lib/asherah.rb', line 221 def clear_log_hook Hooks.clear_log_hook end |
.clear_metrics_hook ⇒ Object
Remove the active metrics hook and disable metrics. Idempotent.
239 240 241 |
# File 'lib/asherah.rb', line 239 def clear_metrics_hook Hooks.clear_metrics_hook end |
.configure ⇒ Object
Configure Asherah using a block with snake_case accessors. Compatible with the canonical godaddy/asherah-ruby gem API.
Asherah.configure do |config|
config.service_name = "MyService"
config.product_id = "MyProduct"
config.kms = "static"
config. = "memory"
end
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/asherah.rb', line 40 def configure @mutex.synchronize do raise Error::AlreadyInitialized if @initialized config = Config.new yield config config.validate! json = config.to_json pointer = Native.asherah_factory_new_with_config(json) @factory = SessionFactory.new(pointer) @sessions = {} @initialized = true @session_cache_enabled = config.enable_session_caching != false @session_cache_max_size = positive_int(config.session_cache_max_size) || DEFAULT_SESSION_CACHE_MAX_SIZE @verbose = config.verbose == true end end |
.decrypt(partition_id, data_row_record) ⇒ Object
176 177 178 179 180 |
# File 'lib/asherah.rb', line 176 def decrypt(partition_id, data_row_record) raise ArgumentError, "data_row_record cannot be nil" if data_row_record.nil? session = resolve_session(partition_id) session.decrypt_bytes(data_row_record).force_encoding(Encoding::UTF_8) end |
.decrypt_async(partition_id, data_row_record, &block) ⇒ Object
Run decrypt in a background Thread; see ‘setup_async` for the exception-propagation contract.
200 201 202 203 204 205 206 207 |
# File 'lib/asherah.rb', line 200 def decrypt_async(partition_id, data_row_record, &block) Thread.new do Thread.current.report_on_exception = true result = decrypt(partition_id, data_row_record) block&.call(result) result end end |
.decrypt_string(partition_id, data_row_record) ⇒ Object
182 183 184 185 |
# File 'lib/asherah.rb', line 182 def decrypt_string(partition_id, data_row_record) raise ArgumentError, "data_row_record cannot be nil" if data_row_record.nil? decrypt(partition_id, data_row_record).force_encoding(Encoding::UTF_8) end |
.encrypt(partition_id, payload) ⇒ Object
165 166 167 168 169 |
# File 'lib/asherah.rb', line 165 def encrypt(partition_id, payload) raise ArgumentError, "payload cannot be nil" if payload.nil? session = resolve_session(partition_id) session.encrypt_bytes(payload) end |
.encrypt_async(partition_id, payload, &block) ⇒ Object
Run encrypt in a background Thread; see ‘setup_async` for the exception-propagation contract.
189 190 191 192 193 194 195 196 |
# File 'lib/asherah.rb', line 189 def encrypt_async(partition_id, payload, &block) Thread.new do Thread.current.report_on_exception = true result = encrypt(partition_id, payload) block&.call(result) result end end |
.encrypt_string(partition_id, text) ⇒ Object
171 172 173 174 |
# File 'lib/asherah.rb', line 171 def encrypt_string(partition_id, text) raise ArgumentError, "text cannot be nil" if text.nil? encrypt(partition_id, text) end |
.get_setup_status ⇒ Object
140 141 142 |
# File 'lib/asherah.rb', line 140 def get_setup_status @mutex.synchronize { @initialized } end |
.set_log_hook(callback = nil, &block) ⇒ Object
Install a log hook. Yields a Hash {level:, target:, message:} for every log record emitted by the underlying Rust crates. The block may fire from any thread; implementations must be thread-safe and non-blocking. Pass nil to clear (equivalent to clear_log_hook).
Replaces any previously installed log hook. Exceptions raised from the callback are caught and silently swallowed.
216 217 218 |
# File 'lib/asherah.rb', line 216 def set_log_hook(callback = nil, &block) Hooks.set_log_hook(callback, &block) end |
.set_metrics_hook(callback = nil, &block) ⇒ Object
Install a metrics hook. Yields a Hash {type:, duration_ns:, name:} for every metrics event. Timing events (:decrypt, :store, :load) carry a positive duration_ns and a nil name; cache events (:cache_miss, :cache_stale) carry duration_ns == 0 and the cache identifier in name.
Installing a hook implicitly enables the global metrics gate; clearing it disables the gate. Replaces any previously installed metrics hook. Pass nil to clear (equivalent to clear_metrics_hook).
234 235 236 |
# File 'lib/asherah.rb', line 234 def set_metrics_hook(callback = nil, &block) Hooks.set_metrics_hook(callback, &block) end |
.setenv(env = {}) ⇒ Object Also known as: set_env
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/asherah.rb', line 144 def setenv(env = {}) data = case env when String JSON.parse(env) else env end unless data.respond_to?(:each_pair) raise ArgumentError, "environment payload must be a Hash or JSON object" end data.each_pair do |k, v| if v.nil? ENV.delete(String(k)) else ENV[String(k)] = v.to_s end end nil end |
.setup(config) ⇒ Object
Initialize Asherah with a PascalCase config hash. Also accepts snake_case string/symbol keys (auto-normalized).
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/asherah.rb', line 61 def setup(config) normalized = normalize_config(config) json = JSON.generate(normalized) pointer = Native.asherah_factory_new_with_config(json) factory = SessionFactory.new(pointer) @mutex.synchronize do raise Error::AlreadyInitialized if @initialized @factory = factory @sessions = {} @initialized = true @session_cache_enabled = truthy(normalized["EnableSessionCaching"], default: true) @session_cache_max_size = positive_int(normalized["SessionCacheMaxSize"]) || DEFAULT_SESSION_CACHE_MAX_SIZE @verbose = truthy(normalized["Verbose"], default: false) end nil rescue StandardError factory&.close if defined?(factory) && factory raise end |
.setup_async(config, &block) ⇒ Object
Run setup in a background Thread. Yields the result to block on success. Exceptions raised by setup propagate via the Thread’s joined error — the previous implementation called the block with the result on success but silently dropped the Thread’s error state on failure, leaving callers unable to distinguish completion from an unsetup factory. The Thread aborts on exception (‘Thread.report_on_exception = true`), so a stack trace lands on stderr; callers that need programmatic access should `thread.join` to re-raise. T-finding “setup_async/shutdown_async/ encrypt_async/decrypt_async swallow Thread exceptions” in `docs/review-2026-05-05-findings.md`.
96 97 98 99 100 101 102 103 |
# File 'lib/asherah.rb', line 96 def setup_async(config, &block) Thread.new do Thread.current.report_on_exception = true result = setup(config) block&.call(result) result end end |
.shutdown ⇒ Object
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/asherah.rb', line 105 def shutdown factory = nil sessions = nil @mutex.synchronize do raise Error::NotInitialized unless @initialized factory = @factory sessions = @sessions.values @factory = nil @sessions = {} @initialized = false end Array(sessions).each do |session| begin session.close unless session.closed? rescue StandardError => e warn "asherah: error closing session during shutdown: #{e.}" end end factory&.close unless factory&.closed? nil end |
.shutdown_async(&block) ⇒ Object
Run shutdown in a background Thread; see ‘setup_async` for the exception-propagation contract.
131 132 133 134 135 136 137 138 |
# File 'lib/asherah.rb', line 131 def shutdown_async(&block) Thread.new do Thread.current.report_on_exception = true result = shutdown block&.call(result) result end end |