standard_audit 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6dff9827071db99dfcb9dc184f3cc20d0bcfd208de8f5d41c86efff0f7e4c2be
4
- data.tar.gz: 979f93e8ed8d293695337e453ade9926b553be7904617d7c61d653ef5997a3ea
3
+ metadata.gz: 84f59bffd4df4a9174ba0a88645a4c7d5818eddf862b5badfb55c3880fb5849d
4
+ data.tar.gz: 7747fdff93658f34f3ba3a9bfa70ca40fdc581125d182c996319a161f5c30809
5
5
  SHA512:
6
- metadata.gz: 98bf60f4d4d40d46ff28d9203c8686a5c57765a86c4c3ef1db97c59d32b27e355ac7b585730cd69865e54cf8540109c87546259ea19e4afdfa9a3510b5b52e1f
7
- data.tar.gz: dda3b2376d9afcd2a61808fc79025e246365302f7b097c17a801bf47cc6a8f3e26a3e11b411dc19b31f6ebac32dc44107e92582e02813a22476be2fa1eaca32a
6
+ metadata.gz: 03f2f84e0418106b8899b9aaff009e95fdbb116f9eb98c37308d2b4ae41df3cfbfe9d9b1615bdd4beb4391d983989cb658e4195451cb56214a75e4cf46ba4375
7
+ data.tar.gz: 6b2e84e5bcd6e6e933535a6ae9e654deb3502e69b575d613044c7b02de843e2a8886c43a0c82bd8cb6313ff012510d36a52f290ca36dd778634c4b3c70cefb80
data/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-06-24
11
+
12
+ ### Added
13
+
14
+ - `config.retention_days` now defaults from the `STANDARD_AUDIT_RETENTION_DAYS` environment variable, so a deployment can opt into a retention window without a code change. Unset/blank/zero/negative/non-numeric resolves to `nil` (infinite retention — the compliance-safe default that never auto-deletes). Host apps can still override `config.retention_days` in their initializer.
15
+ - `StandardAudit::Checks::Retention` — a StandardHealth-compatible (duck-typed, no hard dependency) readiness check that flags unbounded retention on **production** deployments. Register it non-critical in `config/initializers/standard_health.rb`:
16
+ ```ruby
17
+ c.register_check :audit_retention, StandardAudit::Checks::Retention, critical: false
18
+ ```
19
+ When `APP_ENVIRONMENT == "production"` (falling back to `Rails.env.production?` so staging is not flagged) and `retention_days` is nil, it returns `:warn`, rolling `GET /health/ready` to `:degraded` — still HTTP 200, so it surfaces the advisory without failing the probe or blocking a deploy.
20
+
10
21
  ## [0.5.0] - 2026-04-29
11
22
 
12
23
  ### Changed
@@ -1,4 +1,6 @@
1
- Copyright (c) 2026 Jaryl Sim
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Rarebit One
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -209,6 +209,8 @@ StandardAudit.configure do |config|
209
209
  config.anonymizable_metadata_keys = %i[email name ip_address]
210
210
 
211
211
  # -- Retention (schedule StandardAudit::CleanupJob to enforce) --
212
+ # Defaults from STANDARD_AUDIT_RETENTION_DAYS (see Retention below); set here
213
+ # to override per app. Leave unset for infinite retention.
212
214
  config.retention_days = 90
213
215
  end
214
216
  ```
@@ -339,6 +341,45 @@ File.write("export.json", JSON.pretty_generate(data))
339
341
 
340
342
  Returns a hash with `subject`, `exported_at`, `total_records`, and a `records` array.
341
343
 
344
+ ## Retention
345
+
346
+ `config.retention_days` controls how long audit logs are kept. It is only
347
+ enforced when you actually run cleanup (the `StandardAudit::CleanupJob` or the
348
+ `standard_audit:cleanup` rake task) — setting it alone deletes nothing.
349
+
350
+ It defaults from the `STANDARD_AUDIT_RETENTION_DAYS` environment variable, so a
351
+ deployment can opt into a retention window without a code change:
352
+
353
+ ```bash
354
+ STANDARD_AUDIT_RETENTION_DAYS=365 # keep 365 days
355
+ # unset / blank / 0 / negative / non-numeric => nil => infinite retention
356
+ ```
357
+
358
+ Infinite retention (the default) is the compliance-safe behavior: nothing is
359
+ ever auto-deleted. For financial/legal domains that is usually what you want;
360
+ enabling a finite window is a deliberate decision.
361
+
362
+ ### Production retention warning (StandardHealth)
363
+
364
+ `StandardAudit::Checks::Retention` is a [StandardHealth](https://github.com/rarebit-one/standard_health)-compatible
365
+ check that flags unbounded retention **on production deployments** as an
366
+ advisory. Register it (non-critical) in `config/initializers/standard_health.rb`:
367
+
368
+ ```ruby
369
+ StandardHealth.configure do |c|
370
+ c.register_check :audit_retention,
371
+ StandardAudit::Checks::Retention,
372
+ critical: false
373
+ end
374
+ ```
375
+
376
+ When `APP_ENVIRONMENT == "production"` (falling back to `Rails.env.production?`
377
+ when that var is unset — so staging is not flagged) and `retention_days` is nil,
378
+ the check returns `:warn`. That rolls `GET /health/ready` up to `:degraded`,
379
+ which is **still HTTP 200** — it surfaces the advisory in the readiness JSON
380
+ without failing the probe or blocking a deploy. The check is duck-typed and has
381
+ no hard dependency on `standard_health`.
382
+
342
383
  ## Rake Tasks
343
384
 
344
385
  ```bash
@@ -0,0 +1,58 @@
1
+ module StandardAudit
2
+ module Checks
3
+ # A StandardHealth-compatible readiness check that warns when audit_logs
4
+ # retention is unbounded on a production deployment.
5
+ #
6
+ # It is intentionally duck-typed (no hard dependency on standard_health):
7
+ # it exposes the `#initialize(name:, critical:)` + `#run` contract the
8
+ # StandardHealth aggregator calls, so it loads even where standard_health
9
+ # is absent.
10
+ #
11
+ # Register it (NON-critical) in config/initializers/standard_health.rb:
12
+ #
13
+ # c.register_check :audit_retention,
14
+ # StandardAudit::Checks::Retention,
15
+ # critical: false
16
+ #
17
+ # A :warn result rolls /health/ready up to :degraded, which is still
18
+ # HTTP 200 — it surfaces the advisory in the readiness JSON WITHOUT failing
19
+ # the probe or blocking a deploy. Only a *critical* check failure returns
20
+ # 503, and this check is never critical.
21
+ #
22
+ # "Production" is ENV["APP_ENVIRONMENT"] == "production" when that var is
23
+ # set (so staging — which also runs RAILS_ENV=production — is not flagged);
24
+ # otherwise it falls back to Rails.env.production?.
25
+ class Retention
26
+ def initialize(name: :audit_retention, critical: false)
27
+ @name = name
28
+ @critical = critical
29
+ end
30
+
31
+ def run
32
+ unless production?
33
+ return { status: :ok, detail: "retention advisory only runs on production deployments" }
34
+ end
35
+
36
+ days = StandardAudit.config.retention_days
37
+ return { status: :ok, retention_days: days } if days
38
+
39
+ {
40
+ status: :warn,
41
+ message: "audit_logs retention is unbounded on production. Set " \
42
+ "STANDARD_AUDIT_RETENTION_DAYS (or config.retention_days) and schedule " \
43
+ "StandardAudit::CleanupJob, or treat indefinite retention as a deliberate " \
44
+ "compliance decision."
45
+ }
46
+ end
47
+
48
+ private
49
+
50
+ def production?
51
+ app_env = ENV["APP_ENVIRONMENT"].to_s
52
+ return app_env == "production" unless app_env.empty?
53
+
54
+ defined?(Rails) && Rails.respond_to?(:env) && Rails.env.production?
55
+ end
56
+ end
57
+ end
58
+ end
@@ -45,7 +45,22 @@ module StandardAudit
45
45
  ]
46
46
  @metadata_builder = nil
47
47
  @anonymizable_metadata_keys = %i[email name ip_address]
48
- @retention_days = nil
48
+
49
+ # Retention defaults from ENV so it can be set per-environment without a
50
+ # code change. Unset/blank/non-positive => nil (infinite retention, the
51
+ # compliance-safe default that never auto-deletes). A host app can still
52
+ # override with `config.retention_days = N` in its initializer.
53
+ @retention_days = self.class.retention_days_from_env
54
+ end
55
+
56
+ # Parses STANDARD_AUDIT_RETENTION_DAYS into a positive Integer, or nil when
57
+ # unset/blank/zero/negative/non-numeric (=> infinite retention).
58
+ def self.retention_days_from_env
59
+ raw = ENV["STANDARD_AUDIT_RETENTION_DAYS"]
60
+ return nil if raw.nil? || raw.strip.empty?
61
+
62
+ days = Integer(raw, exception: false)
63
+ days&.positive? ? days : nil
49
64
  end
50
65
 
51
66
  def subscribe_to(pattern)
@@ -1,3 +1,3 @@
1
1
  module StandardAudit
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -5,6 +5,7 @@ require "standard_audit/subscriber"
5
5
  require "standard_audit/event_subscriber"
6
6
  require "standard_audit/auditable"
7
7
  require "standard_audit/audit_scope"
8
+ require "standard_audit/checks/retention"
8
9
 
9
10
  module StandardAudit
10
11
  # Metadata keys owned internally by StandardAudit. Never filtered by
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_audit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaryl Sim
@@ -89,7 +89,7 @@ extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
91
  - CHANGELOG.md
92
- - MIT-LICENSE
92
+ - LICENSE
93
93
  - README.md
94
94
  - Rakefile
95
95
  - app/jobs/standard_audit/cleanup_job.rb
@@ -105,6 +105,7 @@ files:
105
105
  - lib/standard_audit.rb
106
106
  - lib/standard_audit/audit_scope.rb
107
107
  - lib/standard_audit/auditable.rb
108
+ - lib/standard_audit/checks/retention.rb
108
109
  - lib/standard_audit/configuration.rb
109
110
  - lib/standard_audit/engine.rb
110
111
  - lib/standard_audit/event_subscriber.rb