source_monitor 0.13.1 → 0.15.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/sm-configuration-setting/reference/settings-catalog.md +1 -0
  3. data/.claude/skills/sm-configure/SKILL.md +8 -1
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +11 -0
  5. data/.claude/skills/sm-host-setup/SKILL.md +13 -3
  6. data/.claude/skills/sm-host-setup/reference/initializer-template.md +11 -0
  7. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +9 -1
  8. data/.claude/skills/sm-upgrade/reference/version-history.md +26 -0
  9. data/AGENTS.md +145 -38
  10. data/CHANGELOG.md +29 -0
  11. data/CLAUDE.md +6 -116
  12. data/Gemfile.lock +86 -86
  13. data/README.md +3 -3
  14. data/VERSION +1 -1
  15. data/app/controllers/source_monitor/application_controller.rb +73 -14
  16. data/app/views/layouts/source_monitor/application.html.erb +6 -0
  17. data/docs/configuration.md +18 -1
  18. data/docs/deployment.md +1 -1
  19. data/docs/goals/engine-hardening/.goalbuddy-board/app.js +543 -0
  20. data/docs/goals/engine-hardening/.goalbuddy-board/goalbuddy-mark.png +0 -0
  21. data/docs/goals/engine-hardening/.goalbuddy-board/index.html +111 -0
  22. data/docs/goals/engine-hardening/.goalbuddy-board/styles.css +991 -0
  23. data/docs/goals/engine-hardening/goal.md +97 -0
  24. data/docs/goals/engine-hardening/notes/T001-spec-validation.md +37 -0
  25. data/docs/goals/engine-hardening/state.yaml +324 -0
  26. data/docs/setup.md +4 -4
  27. data/docs/upgrade.md +41 -0
  28. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +10 -0
  29. data/lib/source_monitor/configuration/authentication_settings.rb +5 -1
  30. data/lib/source_monitor/security/authentication.rb +10 -0
  31. data/lib/source_monitor/version.rb +1 -1
  32. data/source_monitor.gemspec +8 -3
  33. metadata +10 -67
  34. data/.claude/agent-memory/vbw-vbw-debugger/MEMORY.md +0 -15
  35. data/.claude/agent-memory/vbw-vbw-dev/MEMORY.md +0 -34
  36. data/.claude/agent-memory/vbw-vbw-lead/MEMORY.md +0 -49
  37. data/.claude/agents/rails-concern.md +0 -464
  38. data/.claude/agents/rails-controller.md +0 -424
  39. data/.claude/agents/rails-hotwire.md +0 -446
  40. data/.claude/agents/rails-implement.md +0 -374
  41. data/.claude/agents/rails-job.md +0 -334
  42. data/.claude/agents/rails-lint.md +0 -294
  43. data/.claude/agents/rails-mailer.md +0 -371
  44. data/.claude/agents/rails-migration.md +0 -449
  45. data/.claude/agents/rails-model.md +0 -420
  46. data/.claude/agents/rails-policy.md +0 -443
  47. data/.claude/agents/rails-presenter.md +0 -427
  48. data/.claude/agents/rails-query.md +0 -412
  49. data/.claude/agents/rails-review.md +0 -490
  50. data/.claude/agents/rails-service.md +0 -458
  51. data/.claude/agents/rails-state-records.md +0 -465
  52. data/.claude/agents/rails-tdd.md +0 -314
  53. data/.claude/agents/rails-test.md +0 -441
  54. data/.claude/agents/rails-view-component.md +0 -418
  55. data/.claude/commands/rails-audit.md +0 -77
  56. data/.claude/commands/release.md +0 -366
  57. data/.claude/hooks/block-secrets.sh +0 -52
  58. data/.claude/settings.json +0 -85
  59. data/.claude/skills/action-cable-patterns/SKILL.md +0 -296
  60. data/.claude/skills/action-mailer-patterns/SKILL.md +0 -295
  61. data/.claude/skills/active-storage-setup/SKILL.md +0 -311
  62. data/.claude/skills/api-versioning/SKILL.md +0 -294
  63. data/.claude/skills/authentication-flow/SKILL.md +0 -335
  64. data/.claude/skills/authentication-flow/reference/current.md +0 -248
  65. data/.claude/skills/authentication-flow/reference/passwordless.md +0 -253
  66. data/.claude/skills/authentication-flow/reference/sessions.md +0 -201
  67. data/.claude/skills/authorization-pundit/SKILL.md +0 -462
  68. data/.claude/skills/caching-strategies/SKILL.md +0 -350
  69. data/.claude/skills/database-migrations/SKILL.md +0 -354
  70. data/.claude/skills/form-object-patterns/SKILL.md +0 -399
  71. data/.claude/skills/hotwire-patterns/SKILL.md +0 -247
  72. data/.claude/skills/hotwire-patterns/reference/stimulus.md +0 -307
  73. data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +0 -112
  74. data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +0 -158
  75. data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +0 -218
  76. data/.claude/skills/i18n-patterns/SKILL.md +0 -320
  77. data/.claude/skills/install/SKILL.md +0 -367
  78. data/.claude/skills/performance-optimization/SKILL.md +0 -311
  79. data/.claude/skills/rails-architecture/SKILL.md +0 -259
  80. data/.claude/skills/rails-architecture/reference/error-handling.md +0 -333
  81. data/.claude/skills/rails-architecture/reference/event-tracking.md +0 -142
  82. data/.claude/skills/rails-architecture/reference/layer-interactions.md +0 -417
  83. data/.claude/skills/rails-architecture/reference/multi-tenancy.md +0 -152
  84. data/.claude/skills/rails-architecture/reference/query-patterns.md +0 -342
  85. data/.claude/skills/rails-architecture/reference/service-patterns.md +0 -286
  86. data/.claude/skills/rails-architecture/reference/state-records.md +0 -250
  87. data/.claude/skills/rails-architecture/reference/testing-strategy.md +0 -326
  88. data/.claude/skills/rails-concern/SKILL.md +0 -399
  89. data/.claude/skills/rails-controller/SKILL.md +0 -336
  90. data/.claude/skills/rails-model-generator/SKILL.md +0 -321
  91. data/.claude/skills/rails-model-generator/reference/validations.md +0 -298
  92. data/.claude/skills/rails-presenter/SKILL.md +0 -274
  93. data/.claude/skills/rails-query-object/SKILL.md +0 -289
  94. data/.claude/skills/rails-service-object/SKILL.md +0 -349
  95. data/.claude/skills/solid-queue-setup/SKILL.md +0 -307
  96. data/.claude/skills/tdd-cycle/SKILL.md +0 -359
  97. data/.claude/skills/viewcomponent-patterns/SKILL.md +0 -333
data/CLAUDE.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  **Core value:** Drop-in Rails engine for feed monitoring, content scraping, and operational dashboards.
4
4
 
5
+ > **Engine conventions live in [`AGENTS.md`](AGENTS.md)** — the canonical, cross-agent reference for tech stack, architecture, testing, quality gates, the pre-push CI checklist, security rules, configuration DSL, and commands. This file holds only Claude Code-specific context: working memory, VBW commands, and the `.claude/` agent/skill catalogs. When a convention changes, edit `AGENTS.md`, not here.
6
+
5
7
  ## Active Context
6
8
 
7
9
  **Last shipped:** rails-audit-and-refactoring (7 phases, 30 plans)
@@ -10,7 +12,7 @@
10
12
  ## Key Decisions
11
13
 
12
14
  - Keep PostgreSQL-only for now
13
- - Keep host-app auth model
15
+ - Keep host-app auth model (fail-closed by default since #129)
14
16
  - Ruby autoload for lib/ modules (not Zeitwerk)
15
17
  - PG parallel fork segfault resolved: switched to thread-based parallelism in aia-ssl-fix milestone
16
18
 
@@ -23,13 +25,6 @@
23
25
  - vastai (global)
24
26
  - find-skills (global)
25
27
 
26
- ## Learned Patterns
27
-
28
- - Sub-module extraction: create `module/submodule.rb` with `require_relative`, lazy accessors, forwarding methods for backward compat
29
- - Coverage runs need `COVERAGE=1 PARALLEL_WORKERS=1` with threads (not forks) to avoid PG segfault and SimpleCov data loss
30
- - Test isolation: scope queries to specific source/item to prevent cross-test contamination in parallel runs
31
- - RuboCop omakase: only 45/775 cops enabled, all Metrics cops disabled -- no file size enforcement
32
-
33
28
  ## VBW Commands
34
29
 
35
30
  This project uses VBW (Vibe Better with Claude Code).
@@ -38,111 +33,10 @@ Run /vbw:help for all commands.
38
33
 
39
34
  ---
40
35
 
41
- # Rails Development Conventions
42
-
43
- ## Tech Stack
44
-
45
- | Layer | Technology |
46
- |-------|------------|
47
- | Ruby | 4.0+ |
48
- | Rails | 8.x |
49
- | Testing | Minitest (no fixtures -- uses factory helpers + WebMock/VCR) |
50
- | Authorization | Host app responsibility (mountable engine) |
51
- | Jobs | Solid Queue |
52
- | Frontend | Hotwire (Turbo + Stimulus) + Tailwind CSS |
53
- | Linting | RuboCop (omakase) + Brakeman |
54
- | Database | PostgreSQL only |
55
-
56
- ## Architecture Conventions
57
-
58
- ### Models First
59
- - Business logic lives in models. Use concerns for horizontal sharing.
60
- - Service objects ONLY for operations spanning 3+ models or external integrations.
61
- - Query objects for complex queries that don't fit a single scope.
62
- - Presenters (SimpleDelegator) for view-specific formatting.
63
-
64
- ### Everything-is-CRUD Routing
65
- - Prefer creating a new resource over adding custom actions.
66
- - `POST /posts/:id/publications` over `POST /posts/:id/publish`.
67
- - RESTful routes only; no `member` or `collection` blocks with custom verbs.
68
-
69
- ### State as Records
70
- - Track business state transitions as separate records (who/when/why).
71
- - Boolean columns ONLY for technical flags (e.g., `email_verified`).
72
-
73
- ### Jobs
74
- - Shallow jobs: call `_later` or `_now` methods on models/services.
75
- - Jobs contain only deserialization + delegation. No business logic.
76
- - Use Solid Queue recurring jobs for scheduled work.
77
-
78
- ### Frontend
79
- - Turbo Frames for partial page updates.
80
- - Turbo Streams for real-time broadcasts.
81
- - Stimulus controllers: small, focused, one behavior each.
82
- - Tailwind CSS utility classes; extract components for repeated patterns.
83
-
84
- ## Testing Conventions
85
-
86
- - **Framework:** Minitest. NEVER use RSpec or FactoryBot.
87
- - **Helpers:** `create_source!` factory, `with_inline_jobs`, `with_queue_adapter`.
88
- - **HTTP:** WebMock disables external HTTP; VCR for recorded cassettes.
89
- - **Config:** Reset every test with `SourceMonitor.reset_configuration!`.
90
- - **TDD workflow:** Red (failing test) -> Green (minimal pass) -> Refactor.
91
- - **Coverage:** Every model validation, scope, and public method. Every controller action.
92
-
93
- ## Quality Gates
94
-
95
- - `bin/rubocop` -- zero offenses before commit.
96
- - `bin/brakeman --no-pager` -- zero warnings before merge.
97
- - `bin/rails test` -- all tests pass.
98
- - `yarn build` -- rebuild JS assets if any `.js` files changed (ESLint runs in CI).
99
- - No N+1 queries (use `includes`/`preload`).
100
- - No hardcoded credentials (use Rails credentials or ENV).
101
-
102
- ### Pre-Push CI Checklist (run ALL before pushing to GitHub)
103
-
104
- Before pushing any branch (especially release branches), run the full CI equivalent locally:
105
-
106
- 1. `bin/rubocop` -- catches Ruby lint issues
107
- 2. `PARALLEL_WORKERS=1 bin/rails test` -- catches test failures AND diff coverage gaps
108
- 3. `bin/brakeman --no-pager` -- catches security issues
109
- 4. `yarn build` -- rebuilds JS and catches ESLint issues (CI runs ESLint separately)
110
-
111
- **Why:** CI failures cost ~5 min per round-trip. In v0.8.0, skipping ESLint and diff coverage checks locally caused 2 wasted CI cycles. Common blind spots:
112
- - JS files need `/* global */` declarations for browser APIs (MutationObserver, requestAnimationFrame, etc.)
113
- - Every `rescue` / fallback / error path in new source code needs test coverage (CI diff coverage gate rejects uncovered lines)
114
- - `yarn build` must run after JS changes to sync sourcemaps
115
-
116
- ## QA and UAT Rules
36
+ ## QA and UAT Rules (Claude Code)
117
37
 
118
38
  - **Browser-first verification:** During VBW QA (`/vbw:qa`) and UAT (`/vbw:verify`), ALWAYS start by using `agent-browser` to test UI scenarios yourself before presenting checkpoints to the user. Navigate to the dummy app (port 3002), take snapshots/screenshots, and verify visual and functional behavior with agents first.
119
- - **Automate what you can:** Any test that can be verified programmatically (config defaults, job enqueue behavior, controller responses) should be automated -- only present truly visual/interactive tests to the user.
120
- - **Dummy app port:** The SourceMonitor dummy app runs on port 3002 (`cd test/dummy && bin/rails server -p 3002`).
121
-
122
- ## Security Rules
123
-
124
- ### Protected Files (NEVER read or output)
125
- - `.env`, `.env.*`
126
- - `config/master.key`, `config/credentials.yml.enc`
127
- - `.kamal/secrets`
128
- - Any `*.pem`, `*.key` files
129
-
130
- ### Forbidden Operations
131
- - `git push --force` to main/master/production
132
- - `git reset --hard` without explicit user confirmation
133
- - `rm -rf` on root, home, or parent directories
134
- - `chmod 777`
135
-
136
- ## Development Commands
137
-
138
- ```bash
139
- bin/dev # Start dev server
140
- bin/rails test # Run all tests
141
- bin/rubocop # Check style
142
- bin/rubocop -a # Auto-fix style
143
- bin/brakeman --no-pager # Security scan
144
- bin/rails db:migrate # Run migrations
145
- ```
39
+ - **Automate what you can:** anything verifiable programmatically should be a test see the Testing section in [`AGENTS.md`](AGENTS.md) and only present truly visual/interactive checks to the user.
146
40
 
147
41
  ## Agent Catalog
148
42
 
@@ -200,7 +94,7 @@ These skills are available in `.claude/skills/`:
200
94
 
201
95
  ## Source Monitor Skills
202
96
 
203
- Engine-specific skills (`sm-*` prefix). Consumer skills install by default; contributor skills are opt-in.
97
+ Engine-specific skills (`sm-*` prefix). Consumer skills install by default; contributor skills are opt-in. The **Skills & Docs Alignment** maintenance rule lives in [`AGENTS.md`](AGENTS.md).
204
98
 
205
99
  ### Consumer Skills (default install)
206
100
 
@@ -237,7 +131,3 @@ bin/rails source_monitor:skills:contributor # Contributor skills
237
131
  bin/rails source_monitor:skills:all # All skills
238
132
  bin/rails source_monitor:skills:remove # Remove all sm-* skills
239
133
  ```
240
-
241
- ## Maintenance Rules
242
-
243
- - **Skills & docs alignment**: Whenever engine code changes (models, configuration, pipeline, jobs, migrations, scrapers, events, health rules, or dashboard), the corresponding `sm-*` skill and its `reference/` files MUST be updated in the same PR to ensure skills always reflect current engine behavior.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- source_monitor (0.13.1)
4
+ source_monitor (0.15.0)
5
5
  cssbundling-rails (~> 1.4)
6
6
  faraday (~> 2.9)
7
7
  faraday-follow_redirects (~> 0.4)
@@ -16,36 +16,36 @@ PATH
16
16
  solid_cable (>= 3.0, < 4.0)
17
17
  solid_queue (>= 0.3, < 3.0)
18
18
  turbo-rails (~> 2.0)
19
- view_component (>= 3.0, < 4.0)
19
+ view_component (>= 3.0, < 5.0)
20
20
 
21
21
  GEM
22
22
  remote: https://rubygems.org/
23
23
  specs:
24
- action_text-trix (2.1.16)
24
+ action_text-trix (2.1.17)
25
25
  railties
26
- actioncable (8.1.2)
27
- actionpack (= 8.1.2)
28
- activesupport (= 8.1.2)
26
+ actioncable (8.1.3)
27
+ actionpack (= 8.1.3)
28
+ activesupport (= 8.1.3)
29
29
  nio4r (~> 2.0)
30
30
  websocket-driver (>= 0.6.1)
31
31
  zeitwerk (~> 2.6)
32
- actionmailbox (8.1.2)
33
- actionpack (= 8.1.2)
34
- activejob (= 8.1.2)
35
- activerecord (= 8.1.2)
36
- activestorage (= 8.1.2)
37
- activesupport (= 8.1.2)
32
+ actionmailbox (8.1.3)
33
+ actionpack (= 8.1.3)
34
+ activejob (= 8.1.3)
35
+ activerecord (= 8.1.3)
36
+ activestorage (= 8.1.3)
37
+ activesupport (= 8.1.3)
38
38
  mail (>= 2.8.0)
39
- actionmailer (8.1.2)
40
- actionpack (= 8.1.2)
41
- actionview (= 8.1.2)
42
- activejob (= 8.1.2)
43
- activesupport (= 8.1.2)
39
+ actionmailer (8.1.3)
40
+ actionpack (= 8.1.3)
41
+ actionview (= 8.1.3)
42
+ activejob (= 8.1.3)
43
+ activesupport (= 8.1.3)
44
44
  mail (>= 2.8.0)
45
45
  rails-dom-testing (~> 2.2)
46
- actionpack (8.1.2)
47
- actionview (= 8.1.2)
48
- activesupport (= 8.1.2)
46
+ actionpack (8.1.3)
47
+ actionview (= 8.1.3)
48
+ activesupport (= 8.1.3)
49
49
  nokogiri (>= 1.8.5)
50
50
  rack (>= 2.2.4)
51
51
  rack-session (>= 1.0.1)
@@ -53,36 +53,36 @@ GEM
53
53
  rails-dom-testing (~> 2.2)
54
54
  rails-html-sanitizer (~> 1.6)
55
55
  useragent (~> 0.16)
56
- actiontext (8.1.2)
56
+ actiontext (8.1.3)
57
57
  action_text-trix (~> 2.1.15)
58
- actionpack (= 8.1.2)
59
- activerecord (= 8.1.2)
60
- activestorage (= 8.1.2)
61
- activesupport (= 8.1.2)
58
+ actionpack (= 8.1.3)
59
+ activerecord (= 8.1.3)
60
+ activestorage (= 8.1.3)
61
+ activesupport (= 8.1.3)
62
62
  globalid (>= 0.6.0)
63
63
  nokogiri (>= 1.8.5)
64
- actionview (8.1.2)
65
- activesupport (= 8.1.2)
64
+ actionview (8.1.3)
65
+ activesupport (= 8.1.3)
66
66
  builder (~> 3.1)
67
67
  erubi (~> 1.11)
68
68
  rails-dom-testing (~> 2.2)
69
69
  rails-html-sanitizer (~> 1.6)
70
- activejob (8.1.2)
71
- activesupport (= 8.1.2)
70
+ activejob (8.1.3)
71
+ activesupport (= 8.1.3)
72
72
  globalid (>= 0.3.6)
73
- activemodel (8.1.2)
74
- activesupport (= 8.1.2)
75
- activerecord (8.1.2)
76
- activemodel (= 8.1.2)
77
- activesupport (= 8.1.2)
73
+ activemodel (8.1.3)
74
+ activesupport (= 8.1.3)
75
+ activerecord (8.1.3)
76
+ activemodel (= 8.1.3)
77
+ activesupport (= 8.1.3)
78
78
  timeout (>= 0.4.0)
79
- activestorage (8.1.2)
80
- actionpack (= 8.1.2)
81
- activejob (= 8.1.2)
82
- activerecord (= 8.1.2)
83
- activesupport (= 8.1.2)
79
+ activestorage (8.1.3)
80
+ actionpack (= 8.1.3)
81
+ activejob (= 8.1.3)
82
+ activerecord (= 8.1.3)
83
+ activesupport (= 8.1.3)
84
84
  marcel (~> 1.0)
85
- activesupport (8.1.2)
85
+ activesupport (8.1.3)
86
86
  base64
87
87
  bigdecimal
88
88
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -95,12 +95,12 @@ GEM
95
95
  securerandom (>= 0.3)
96
96
  tzinfo (~> 2.0, >= 2.0.5)
97
97
  uri (>= 0.13.1)
98
- addressable (2.8.7)
99
- public_suffix (>= 2.0.2, < 7.0)
98
+ addressable (2.9.0)
99
+ public_suffix (>= 2.0.2, < 8.0)
100
100
  ast (2.4.3)
101
101
  base64 (0.3.0)
102
- bigdecimal (4.0.1)
103
- brakeman (8.0.2)
102
+ bigdecimal (4.1.2)
103
+ brakeman (8.0.4)
104
104
  racc
105
105
  builder (3.3.0)
106
106
  capybara (3.40.0)
@@ -125,7 +125,7 @@ GEM
125
125
  digest (3.2.1)
126
126
  docile (1.4.1)
127
127
  drb (2.2.3)
128
- erb (6.0.1)
128
+ erb (6.0.2)
129
129
  erubi (1.13.1)
130
130
  et-orbi (1.4.0)
131
131
  tzinfo
@@ -163,11 +163,11 @@ GEM
163
163
  reline (>= 0.4.2)
164
164
  jsbundling-rails (1.3.1)
165
165
  railties (>= 6.0.0)
166
- json (2.18.1)
166
+ json (2.19.2)
167
167
  language_server-protocol (3.17.0.5)
168
168
  lint_roller (1.1.0)
169
169
  logger (1.7.0)
170
- loofah (2.25.0)
170
+ loofah (2.25.1)
171
171
  crass (~> 1.0.2)
172
172
  nokogiri (>= 1.12.0)
173
173
  mail (2.9.0)
@@ -178,17 +178,17 @@ GEM
178
178
  net-smtp
179
179
  marcel (1.1.0)
180
180
  matrix (0.4.3)
181
- method_source (1.1.0)
182
181
  mini_magick (5.3.1)
183
182
  logger
184
183
  mini_mime (1.1.5)
185
184
  mini_portile2 (2.8.9)
186
- minitest (6.0.1)
185
+ minitest (6.0.2)
186
+ drb (~> 2.0)
187
187
  prism (~> 1.5)
188
188
  minitest-mock (5.27.0)
189
189
  net-http (0.9.1)
190
190
  uri (>= 0.11.1)
191
- net-imap (0.6.2)
191
+ net-imap (0.6.3)
192
192
  date
193
193
  net-protocol
194
194
  net-pop (0.1.2)
@@ -198,18 +198,18 @@ GEM
198
198
  net-smtp (0.5.1)
199
199
  net-protocol
200
200
  nio4r (2.7.5)
201
- nokogiri (1.19.0)
201
+ nokogiri (1.19.2)
202
202
  mini_portile2 (~> 2.8.2)
203
203
  racc (~> 1.4)
204
- nokogiri (1.19.0-aarch64-linux-gnu)
204
+ nokogiri (1.19.2-aarch64-linux-gnu)
205
205
  racc (~> 1.4)
206
- nokogiri (1.19.0-aarch64-linux-musl)
206
+ nokogiri (1.19.2-aarch64-linux-musl)
207
207
  racc (~> 1.4)
208
- nokogiri (1.19.0-arm-linux-gnu)
208
+ nokogiri (1.19.2-arm-linux-gnu)
209
209
  racc (~> 1.4)
210
- nokogiri (1.19.0-arm-linux-musl)
210
+ nokogiri (1.19.2-arm-linux-musl)
211
211
  racc (~> 1.4)
212
- nokolexbor (0.6.2)
212
+ nokolexbor (0.6.4)
213
213
  ostruct (0.6.3)
214
214
  parallel (1.27.0)
215
215
  parser (3.3.10.1)
@@ -229,12 +229,12 @@ GEM
229
229
  psych (5.3.1)
230
230
  date
231
231
  stringio
232
- public_suffix (6.0.2)
232
+ public_suffix (7.0.5)
233
233
  puma (7.2.0)
234
234
  nio4r (~> 2.0)
235
235
  raabro (1.4.0)
236
236
  racc (1.8.1)
237
- rack (3.2.4)
237
+ rack (3.2.5)
238
238
  rack-session (2.1.1)
239
239
  base64 (>= 0.1.0)
240
240
  rack (>= 3.0.0)
@@ -242,30 +242,30 @@ GEM
242
242
  rack (>= 1.3)
243
243
  rackup (2.3.1)
244
244
  rack (>= 3)
245
- rails (8.1.2)
246
- actioncable (= 8.1.2)
247
- actionmailbox (= 8.1.2)
248
- actionmailer (= 8.1.2)
249
- actionpack (= 8.1.2)
250
- actiontext (= 8.1.2)
251
- actionview (= 8.1.2)
252
- activejob (= 8.1.2)
253
- activemodel (= 8.1.2)
254
- activerecord (= 8.1.2)
255
- activestorage (= 8.1.2)
256
- activesupport (= 8.1.2)
245
+ rails (8.1.3)
246
+ actioncable (= 8.1.3)
247
+ actionmailbox (= 8.1.3)
248
+ actionmailer (= 8.1.3)
249
+ actionpack (= 8.1.3)
250
+ actiontext (= 8.1.3)
251
+ actionview (= 8.1.3)
252
+ activejob (= 8.1.3)
253
+ activemodel (= 8.1.3)
254
+ activerecord (= 8.1.3)
255
+ activestorage (= 8.1.3)
256
+ activesupport (= 8.1.3)
257
257
  bundler (>= 1.15.0)
258
- railties (= 8.1.2)
258
+ railties (= 8.1.3)
259
259
  rails-dom-testing (2.3.0)
260
260
  activesupport (>= 5.0.0)
261
261
  minitest
262
262
  nokogiri (>= 1.6)
263
- rails-html-sanitizer (1.6.2)
264
- loofah (~> 2.21)
263
+ rails-html-sanitizer (1.7.0)
264
+ loofah (~> 2.25)
265
265
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
266
- railties (8.1.2)
267
- actionpack (= 8.1.2)
268
- activesupport (= 8.1.2)
266
+ railties (8.1.3)
267
+ actionpack (= 8.1.3)
268
+ activesupport (= 8.1.3)
269
269
  irb (~> 1.13)
270
270
  rackup (>= 1.0.0)
271
271
  rake (>= 12.2)
@@ -321,7 +321,7 @@ GEM
321
321
  rubyzip (3.2.2)
322
322
  sax-machine (1.3.2)
323
323
  securerandom (0.4.1)
324
- selenium-webdriver (4.40.0)
324
+ selenium-webdriver (4.41.0)
325
325
  base64 (~> 0.2)
326
326
  logger (~> 1.4)
327
327
  rexml (~> 3.2, >= 3.2.5)
@@ -338,18 +338,18 @@ GEM
338
338
  activejob (>= 7.2)
339
339
  activerecord (>= 7.2)
340
340
  railties (>= 7.2)
341
- solid_queue (1.3.1)
341
+ solid_queue (1.4.0)
342
342
  activejob (>= 7.1)
343
343
  activerecord (>= 7.1)
344
344
  concurrent-ruby (>= 1.3.1)
345
345
  fugit (~> 1.11)
346
346
  railties (>= 7.1)
347
347
  thor (>= 1.3.1)
348
- stackprof (0.2.27)
348
+ stackprof (0.2.28)
349
349
  stringio (3.2.0)
350
- test-prof (1.5.2)
350
+ test-prof (1.6.0)
351
351
  thor (1.5.0)
352
- timeout (0.6.0)
352
+ timeout (0.6.1)
353
353
  tsort (0.2.0)
354
354
  turbo-rails (2.0.23)
355
355
  actionpack (>= 7.1.0)
@@ -362,11 +362,11 @@ GEM
362
362
  uri (1.1.1)
363
363
  useragent (0.16.11)
364
364
  vcr (6.4.0)
365
- view_component (3.24.0)
366
- activesupport (>= 5.2.0, < 8.2)
365
+ view_component (4.5.0)
366
+ actionview (>= 7.1.0)
367
+ activesupport (>= 7.1.0)
367
368
  concurrent-ruby (~> 1)
368
- method_source (~> 1.0)
369
- webmock (3.26.1)
369
+ webmock (3.26.2)
370
370
  addressable (>= 2.8.0)
371
371
  crack (>= 0.3.2)
372
372
  hashdiff (>= 0.4.0, < 2.0.0)
@@ -377,7 +377,7 @@ GEM
377
377
  websocket-extensions (0.1.5)
378
378
  xpath (3.2.0)
379
379
  nokogiri (~> 1.8)
380
- zeitwerk (2.7.4)
380
+ zeitwerk (2.7.5)
381
381
  zlib (3.2.2)
382
382
 
383
383
  PLATFORMS
data/README.md CHANGED
@@ -9,8 +9,8 @@ SourceMonitor is a production-ready Rails 8 mountable engine for ingesting, norm
9
9
  In your host Rails app:
10
10
 
11
11
  ```bash
12
- bundle add source_monitor --version "~> 0.13.1"
13
- # or add `gem "source_monitor", "~> 0.13.1"` manually, then run:
12
+ bundle add source_monitor --version "~> 0.15.0"
13
+ # or add `gem "source_monitor", "~> 0.15.0"` manually, then run:
14
14
  bundle install
15
15
  ```
16
16
 
@@ -46,7 +46,7 @@ This exposes `bin/source_monitor` (via Bundler binstubs) so you can run the guid
46
46
  Before running any SourceMonitor commands inside your host app, add the gem and install dependencies:
47
47
 
48
48
  ```bash
49
- bundle add source_monitor --version "~> 0.13.1"
49
+ bundle add source_monitor --version "~> 0.15.0"
50
50
  # or edit your Gemfile, then run
51
51
  bundle install
52
52
  ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.13.0
1
+ 0.15.0
@@ -4,11 +4,13 @@ module SourceMonitor
4
4
  class ApplicationController < ActionController::Base
5
5
  protect_from_forgery with: :exception, prepend: true
6
6
 
7
+ before_action :enforce_source_monitor_access_default
7
8
  before_action :authenticate_source_monitor_user
8
9
  before_action :authorize_source_monitor_access
9
10
 
10
- helper_method :source_monitor_current_user, :source_monitor_user_signed_in?
11
- after_action :broadcast_flash_toasts
11
+ helper_method :source_monitor_current_user, :source_monitor_user_signed_in?,
12
+ :source_monitor_flash_toasts
13
+ after_action :append_flash_toasts_to_turbo_stream
12
14
 
13
15
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
14
16
 
@@ -40,6 +42,30 @@ module SourceMonitor
40
42
  TOAST_DURATION_DEFAULT = 5000
41
43
  TOAST_DURATION_ERROR = 6000
42
44
 
45
+ # Fail-closed guard: when the host app has configured no authentication or
46
+ # authorization handler and has not explicitly opted into open access, deny
47
+ # all engine routes. Configured handlers short-circuit this and decide for
48
+ # themselves (see SourceMonitor::Security::Authentication).
49
+ def enforce_source_monitor_access_default
50
+ return unless SourceMonitor::Security::Authentication.access_denied_by_default?(self)
51
+
52
+ source_monitor_access_forbidden
53
+ end
54
+
55
+ def source_monitor_access_forbidden
56
+ message = "SourceMonitor access is not configured"
57
+ respond_to do |format|
58
+ format.html { render plain: message, status: :forbidden }
59
+ format.turbo_stream do
60
+ render turbo_stream: turbo_stream.append("flash",
61
+ partial: "source_monitor/shared/toast",
62
+ locals: { message: message, level: :error }),
63
+ status: :forbidden
64
+ end
65
+ format.json { render json: { error: message }, status: :forbidden }
66
+ end
67
+ end
68
+
43
69
  def authenticate_source_monitor_user
44
70
  SourceMonitor::Security::Authentication.authenticate!(self)
45
71
  end
@@ -60,22 +86,55 @@ module SourceMonitor
60
86
  level.to_sym == :error ? TOAST_DURATION_ERROR : TOAST_DURATION_DEFAULT
61
87
  end
62
88
 
63
- def broadcast_flash_toasts
64
- return if flash.empty?
65
- return unless request.format.html? || request.format.turbo_stream?
89
+ # Request flashes are delivered response-local, never broadcast over the
90
+ # global ActionCable notification stream. On full-page HTML loads the layout
91
+ # renders the toasts inline (see +source_monitor_flash_toasts+); on
92
+ # turbo_stream responses we append the toasts to the response body so they
93
+ # reach only the requesting tab.
94
+ def source_monitor_flash_toasts
95
+ payloads = flash_toast_payloads
96
+ # Reading via the layout consumes the flash for this request so it does
97
+ # not linger into the next one.
98
+ flash.discard unless payloads.empty?
99
+ payloads
100
+ end
66
101
 
67
- flash.each do |key, message|
68
- next if message.blank?
102
+ def append_flash_toasts_to_turbo_stream
103
+ return unless request.format.turbo_stream?
104
+ return if response.redirect?
105
+
106
+ payloads = flash_toast_payloads
107
+ return if payloads.empty?
108
+
109
+ streams = payloads.map do |payload|
110
+ view_context.turbo_stream.append(
111
+ "source_monitor_notifications",
112
+ partial: "source_monitor/shared/toast",
113
+ locals: {
114
+ message: payload[:message],
115
+ level: payload[:level],
116
+ delay_ms: toast_delay_for(payload[:level])
117
+ }
118
+ )
119
+ end
69
120
 
70
- Array(message).each do |msg|
71
- SourceMonitor::Realtime.broadcast_toast(
72
- message: msg,
73
- level: FLASH_LEVELS[key.to_sym] || :info
74
- )
121
+ response.body = "#{response.body}#{streams.join}"
122
+ flash.discard
123
+ end
124
+
125
+ # Builds the list of toast payloads ({ message:, level: }) for the current
126
+ # request's flash. Used by both the inline layout renderer and the
127
+ # turbo_stream after_action so request flashes stay response-local.
128
+ def flash_toast_payloads
129
+ return [] if flash.empty?
130
+
131
+ flash.flat_map do |key, message|
132
+ Array(message).filter_map do |msg|
133
+ next if msg.blank?
134
+
135
+ { message: msg, level: FLASH_LEVELS[key.to_sym] || :info }
75
136
  end
76
137
  end
77
- ensure
78
- flash.discard
79
138
  end
80
139
  end
81
140
  end
@@ -19,6 +19,12 @@
19
19
  <div id="source_monitor_notifications"
20
20
  data-notification-container-target="list"
21
21
  class="flex w-full flex-col gap-3">
22
+ <%# Request flashes render response-local here (never broadcast to all tabs). %>
23
+ <% source_monitor_flash_toasts.each do |toast| %>
24
+ <%= render "source_monitor/shared/toast",
25
+ message: toast[:message],
26
+ level: toast[:level] %>
27
+ <% end %>
22
28
  </div>
23
29
  <div data-notification-container-target="badge"
24
30
  class="pointer-events-auto hidden">
@@ -137,6 +137,11 @@ Call `config.realtime.action_cable_config` if you need a full hash for environme
137
137
 
138
138
  ## Authentication Helpers
139
139
 
140
+ **Fail-closed by default.** SourceMonitor denies access to every engine route
141
+ (returning `403 Forbidden`) unless you configure an authentication or
142
+ authorization handler. This prevents the engine's create/update/delete/enqueue
143
+ routes from being public by accident.
144
+
140
145
  Protect the dashboard with host-specific auth in one place:
141
146
 
142
147
  ```ruby
@@ -148,7 +153,19 @@ config.authentication.current_user_method = :current_user
148
153
  config.authentication.user_signed_in_method = :user_signed_in?
149
154
  ```
150
155
 
151
- Handlers can be symbols (invoked on the controller) or callables. Return `false` or raise to deny access.
156
+ Handlers can be symbols (invoked on the controller) or callables. Return `false` or raise to deny access. As soon as either handler is configured, the handler decides access and the fail-closed guard no longer applies.
157
+
158
+ ### Open access opt-in (non-production)
159
+
160
+ For local demos or sandboxes where engine routes are deliberately public, you
161
+ can explicitly opt out of the fail-closed guard:
162
+
163
+ ```ruby
164
+ config.authentication.open_access = true # default: false
165
+ ```
166
+
167
+ This is intended for non-production/demo environments only. Configuring a
168
+ handler always takes precedence over this flag.
152
169
 
153
170
  ## Health Model
154
171
 
data/docs/deployment.md CHANGED
@@ -33,7 +33,7 @@ SourceMonitor assumes the standard Rails 8 process split:
33
33
 
34
34
  ## Security & Authentication
35
35
 
36
- - Lock down the engine routes with authentication hooks (`config.authentication.authenticate_with` / `authorize_with`).
36
+ - SourceMonitor is **fail-closed by default**: without a configured handler every engine route returns `403 Forbidden`. Lock down the routes with authentication hooks (`config.authentication.authenticate_with` / `authorize_with`). Only set `config.authentication.open_access = true` for non-production demos where public access is intentional.
37
37
  - Configure HTTPS for Action Cable if you expose Solid Cable over the public internet.
38
38
  - Store API keys for authenticated feeds in encrypted credentials and inject them via per-source custom headers.
39
39