command_tower 0.16.0 → 0.18.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -8
  3. data/app/controllers/command_tower/application_controller.rb +1 -0
  4. data/app/controllers/command_tower/me/inbox_controller.rb +16 -0
  5. data/app/controllers/concerns/command_tower/execution/client_compatibility_boundary.rb +39 -0
  6. data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +85 -0
  7. data/app/errors/command_tower/errors/client_update_required_error.rb +27 -0
  8. data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +2 -0
  9. data/app/models/command_tower/messaging/communication.rb +31 -0
  10. data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -1
  11. data/app/services/command_tower/email_theme/resolver.rb +45 -0
  12. data/app/services/command_tower/messaging/accept/coordinator.rb +16 -1
  13. data/app/services/command_tower/messaging/accept/persister.rb +13 -0
  14. data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +7 -0
  15. data/app/services/command_tower/messaging/contract/results/communication_result.rb +1 -0
  16. data/app/services/command_tower/messaging/inbox/conversation_result.rb +31 -0
  17. data/app/services/command_tower/messaging/inbox/entry_result.rb +36 -0
  18. data/app/services/command_tower/messaging/inbox/reader.rb +231 -18
  19. data/app/services/command_tower/messaging/inbox.rb +4 -0
  20. data/app/services/command_tower/messaging/notification_types/declaration.rb +14 -1
  21. data/app/services/command_tower/messaging/rendering/channel_renderer.rb +44 -13
  22. data/app/services/command_tower/messaging/rendering/inbox_document.rb +131 -0
  23. data/app/services/command_tower/messaging/rendering/inbox_document_renderer.rb +224 -0
  24. data/app/services/command_tower/messaging/rendering/inbox_presentation_resolver.rb +52 -0
  25. data/app/services/command_tower/messaging/rendering/inbox_presentation_snapshot.rb +64 -0
  26. data/app/services/command_tower/messaging/rendering/template_resolver.rb +128 -0
  27. data/app/services/command_tower/messaging.rb +5 -1
  28. data/app/services/command_tower/services/client_compatibility/evaluate.rb +219 -0
  29. data/app/services/command_tower/services/messaging/communications/produce.rb +4 -0
  30. data/app/services/command_tower/services/messaging/communications/produce_many.rb +6 -0
  31. data/app/services/command_tower/services/messaging/inbox.rb +55 -2
  32. data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +18 -17
  33. data/app/views/command_tower/messaging/rendering/email.html.erb +6 -5
  34. data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +24 -23
  35. data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +3 -0
  36. data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +3 -0
  37. data/app/workflows/command_tower/workflows/client_compatibility/evaluate_workflow.rb +83 -0
  38. data/app/workflows/command_tower/workflows/client_compatibility/recommendation_meta.rb +25 -0
  39. data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +5 -1
  40. data/app/workflows/command_tower/workflows/messaging/inbox.rb +15 -1
  41. data/config/routes.rb +1 -0
  42. data/db/migrate/20261010000001_add_inbox_presentation_snapshot_to_messaging_communications.rb +7 -0
  43. data/db/migrate/20261010000002_add_conversation_identity_to_messaging_communications.rb +10 -0
  44. data/docs/api_reference.md +13 -2
  45. data/docs/authorization.md +1 -1
  46. data/docs/bootstrap/00-ownership.md +77 -0
  47. data/docs/bootstrap/01-docker-make-compose.md +267 -0
  48. data/docs/bootstrap/02-create-the-rails-app.md +75 -0
  49. data/docs/bootstrap/03-pin-the-gem.md +53 -0
  50. data/docs/bootstrap/04-secrets-and-env.md +59 -0
  51. data/docs/bootstrap/05-install-migrate-doctor.md +74 -0
  52. data/docs/bootstrap/06-mount-and-health.md +53 -0
  53. data/docs/bootstrap/07-execution-bases.md +38 -0
  54. data/docs/bootstrap/08-initializer.md +90 -0
  55. data/docs/bootstrap/09-rbac.md +83 -0
  56. data/docs/bootstrap/10-roles-and-gates.md +62 -0
  57. data/docs/bootstrap/11-auth-client-path.md +57 -0
  58. data/docs/bootstrap/12-smoke-check.md +81 -0
  59. data/docs/bootstrap/13-optional.md +50 -0
  60. data/docs/bootstrap/14-sanity-checks.md +44 -0
  61. data/docs/bootstrap/README.md +90 -0
  62. data/docs/cookie_authentication_guide.md +11 -0
  63. data/docs/extending.md +5 -4
  64. data/docs/host_integration_guide.md +3 -246
  65. data/docs/initializing.md +37 -28
  66. data/docs/messaging_integration_guide.md +39 -1
  67. data/docs/principal_capabilities.md +1 -1
  68. data/docs/upgrades/0.10.0.md +5 -5
  69. data/docs/upgrades/0.11.0.md +5 -5
  70. data/docs/upgrades/0.17.0.md +33 -0
  71. data/docs/upgrades/0.18.0.md +35 -0
  72. data/docs/upgrades/README.md +3 -1
  73. data/lib/command_tower/authorization/default.yml +1 -0
  74. data/lib/command_tower/client_compatibility/version.rb +45 -0
  75. data/lib/command_tower/client_compatibility.rb +23 -0
  76. data/lib/command_tower/configuration/config.rb +6 -0
  77. data/lib/command_tower/configuration/email_theme/config.rb +69 -0
  78. data/lib/command_tower/configuration/registry/client_compatibility/binding_definition.rb +42 -0
  79. data/lib/command_tower/configuration/registry/client_compatibility/config.rb +353 -0
  80. data/lib/command_tower/configuration/registry/client_compatibility/contract_definition.rb +16 -0
  81. data/lib/command_tower/configuration/registry/client_compatibility/entity_requirement_definition.rb +67 -0
  82. data/lib/command_tower/configuration/registry/client_compatibility/minimum_overrides.rb +46 -0
  83. data/lib/command_tower/configuration/registry/client_compatibility/platform_definition.rb +66 -0
  84. data/lib/command_tower/configuration/registry/config.rb +14 -0
  85. data/lib/command_tower/configuration/registry/inbox_presentations/config.rb +105 -0
  86. data/lib/command_tower/configuration/registry/inbox_presentations/presentation_definition.rb +71 -0
  87. data/lib/command_tower/current.rb +1 -0
  88. data/lib/command_tower/engine.rb +4 -0
  89. data/lib/command_tower/inbox_presentations.rb +19 -0
  90. data/lib/command_tower/install/baseline.rb +2 -0
  91. data/lib/command_tower/version.rb +1 -1
  92. metadata +47 -2
@@ -0,0 +1,50 @@
1
+ # 13 — Optional (messaging, tests, client-version compatibility)
2
+
3
+ ## Purpose
4
+
5
+ After `GET /me` works, add platform extras **only if the product needs them**. A platform-proof host may skip this file and run [`14-sanity-checks.md`](14-sanity-checks.md).
6
+
7
+ ## Prerequisites
8
+
9
+ - [`12-smoke-check.md`](12-smoke-check.md) passed
10
+
11
+ Do not start here. Messaging and CVC assume install, RBAC, and gates already exist.
12
+
13
+ ## Messaging
14
+
15
+ When the host **emits** notifications or uses phone/Pushover:
16
+
17
+ - Catalog, channel policy, adapters, credentials: [`../messaging_integration_guide.md`](../messaging_integration_guide.md)
18
+ - Admin announcements: [`../admin_workspace.md`](../admin_workspace.md)
19
+
20
+ Complete steps 00–12 first. This bootstrap tree does not duplicate the messaging guide.
21
+
22
+ ## Host tests
23
+
24
+ Shared FactoryBot definitions ship with the gem. In the host test boot path:
25
+
26
+ ```ruby
27
+ require "command_tower/testing"
28
+ CommandTower::Testing.install!
29
+ ```
30
+
31
+ Then `FactoryBot.modify` as needed. Contract: [`../testing.md`](../testing.md).
32
+
33
+ Run specs **in Compose**:
34
+
35
+ ```bash
36
+ make rspec
37
+ make spec SPEC=spec/path/to/file_spec.rb
38
+ ```
39
+
40
+ Do not run `rspec` on the Mac.
41
+
42
+ ## Client-version compatibility (CVC)
43
+
44
+ If the frontend must block incompatible native/web builds, register CVC in the host (registry / initializer). Frontend recovery UX is a client concern; the backend must serve the compatibility contract the client expects.
45
+
46
+ Follow the gem’s registry comments and [`../extending.md`](../extending.md) for `config.registry` patterns. Do not invent a parallel version table.
47
+
48
+ ## Stop
49
+
50
+ Either you skipped this step, or messaging/tests/CVC point at specialty docs and run via Make. Next: [`14-sanity-checks.md`](14-sanity-checks.md).
@@ -0,0 +1,44 @@
1
+ # 14 — Sanity checks
2
+
3
+ ## Purpose
4
+
5
+ Stop condition for **local** bootstrap. This is not a production launch checklist.
6
+
7
+ ## Prerequisites
8
+
9
+ - Steps 00–12 complete
10
+ - Step 13 only if you opted in
11
+
12
+ ## Checks
13
+
14
+ Run inside Compose (Make wrappers from [`01-docker-make-compose.md`](01-docker-make-compose.md)):
15
+
16
+ | Check | Command | Expect |
17
+ |-------|---------|--------|
18
+ | Doctor | `make doctor` | Exit 0 |
19
+ | Server | `make server` | API listens on the Compose port |
20
+ | Me smoke | [`12-smoke-check.md`](12-smoke-check.md) | `GET /me` **200** |
21
+ | Host specs (if you added them) | `make rspec` | Pass |
22
+ | RuboCop (if you added it) | `make rubocop` | Pass |
23
+
24
+ 401 vs 403 vs 404: still [`12-smoke-check.md`](12-smoke-check.md).
25
+
26
+ ## Not this manual
27
+
28
+ Do not treat the following as remaining bootstrap steps:
29
+
30
+ - Production deploys, systemd, reverse proxies, LXC
31
+ - Production secret distribution
32
+ - Multi-host clustering
33
+ - App-store / frontend release (see the Command Tower **frontend** bootstrap tree)
34
+
35
+ ## After this tree
36
+
37
+ - Product routes and workflows: [`../extending.md`](../extending.md), [`../architecture.md`](../architecture.md)
38
+ - HTTP you did not smoke: [`../api_reference.md`](../api_reference.md)
39
+ - Gem upgrades: [`../upgrades/README.md`](../upgrades/README.md)
40
+ - Command Tower frontend on this API: [Local FE↔BE join](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md)
41
+
42
+ ## Stop
43
+
44
+ Doctor is green and `GET /me` is **200**. Local Command Tower backend bootstrap is complete.
@@ -0,0 +1,90 @@
1
+ # Bootstrap a new Command Tower backend host
2
+
3
+ Bring up a new Rails API host that **mounts** CommandTower, in order.
4
+
5
+ This directory is the host bring-up authority. Specialty contracts stay in sibling docs (`initializing`, `authorization`, `api_reference`, `extending`). This tree owns **order**, **which files the host authors** (including Docker and Make), and **how to invoke Rails** — always inside Compose.
6
+
7
+ Do **not** copy another product host’s tree. Do **not** copy [`../docker-compose.yaml`](../docker-compose.yaml) from this gem (that file is for **engine development**). The dummy app under [`../../rails_app/`](../../rails_app/) is an in-engine example for initializer and RBAC shape, not a product template.
8
+
9
+ ## Docker-only
10
+
11
+ Every Ruby/Rails action runs inside Docker Compose.
12
+
13
+ **Host-machine tools:** Docker, the Docker Compose plugin, Make, Git. That is the entire local toolchain.
14
+
15
+ Do **not** run on the Mac (or any unsandboxed host OS):
16
+
17
+ - `gem install rails`
18
+ - `rails new`
19
+ - `bundle`
20
+ - `bin/rails` / `bin/rake`
21
+ - `rspec` / `rubocop`
22
+
23
+ Those names appear in this tree as **task names** inside the container, always invoked as `make …`.
24
+
25
+ **Chicken-and-egg:** author `Dockerfile`, `docker-compose.yml`, and `Makefile` **first** ([`01-docker-make-compose.md`](01-docker-make-compose.md)). `make build` produces a Ruby image. `make rails-new` runs `rails new` **in that image**. Only then pin the gem and install CommandTower.
26
+
27
+ ## This is not deployment
28
+
29
+ This manual is **local bring-up**. It ends when the host runs in Compose and passes the checks in [`14-sanity-checks.md`](14-sanity-checks.md): `make doctor` and `GET /me` **200** for a member user.
30
+
31
+ It does **not** cover production deploys, systemd, reverse proxies, LXC, or how secrets are distributed in production. Do not add a deploy step to this tree without a separate decision.
32
+
33
+ ## How to read
34
+
35
+ 1. Follow **Implementation order** below. That list is the only sequence.
36
+ 2. Open one numbered file per step. Later files assume earlier steps exist.
37
+ 3. Each step states purpose, prerequisites, files to create, the procedure (`make` only), and the stop condition.
38
+ 4. For field contracts, follow the linked specialty doc. Do not treat this tree as a second copy of those contracts.
39
+
40
+ A **platform-proof** host is: Docker/Make, Rails API in the container, CommandTower installed, `host_key`, host RBAC grants, feature gates, register → login → `GET /me`. Product domain and messaging adapters are optional ([`13-optional.md`](13-optional.md)).
41
+
42
+ Attaching a Command Tower **frontend** is a separate step after this tree: [Local FE↔BE join](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md).
43
+
44
+ ## Implementation order
45
+
46
+ | Step | File | What you do |
47
+ |------|------|-------------|
48
+ | 00 | [`00-ownership.md`](00-ownership.md) | Engine vs host; schema; Docker-only; dummy is not a product template |
49
+ | 01 | [`01-docker-make-compose.md`](01-docker-make-compose.md) | Dockerfile, Compose, Makefile, `.dockerignore` |
50
+ | 02 | [`02-create-the-rails-app.md`](02-create-the-rails-app.md) | `make rails-new` (Rails 8 API) inside the container |
51
+ | 03 | [`03-pin-the-gem.md`](03-pin-the-gem.md) | `gem "command_tower"` from RubyGems (or git tag) |
52
+ | 04 | [`04-secrets-and-env.md`](04-secrets-and-env.md) | JWT / signup-session / password-recovery secrets in Compose |
53
+ | 05 | [`05-install-migrate-doctor.md`](05-install-migrate-doctor.md) | `command_tower:install`, migrate, doctor |
54
+ | 06 | [`06-mount-and-health.md`](06-mount-and-health.md) | Engine mount path; host healthz |
55
+ | 07 | [`07-execution-bases.md`](07-execution-bases.md) | Inherit CommandTower controller/job bases |
56
+ | 08 | [`08-initializer.md`](08-initializer.md) | `host_key`, cookies if needed, email knobs |
57
+ | 09 | [`09-rbac.md`](09-rbac.md) | Host `rbac_groups.yml` grants CT entity names |
58
+ | 10 | [`10-roles-and-gates.md`](10-roles-and-gates.md) | Default `member`; enable login / verify / reset gates |
59
+ | 11 | [`11-auth-client-path.md`](11-auth-client-path.md) | Bearer vs cookie+CSRF |
60
+ | 12 | [`12-smoke-check.md`](12-smoke-check.md) | Register → login → `GET /me` **200** |
61
+ | 13 | [`13-optional.md`](13-optional.md) | Optional: messaging, host tests, client-version compatibility |
62
+ | 14 | [`14-sanity-checks.md`](14-sanity-checks.md) | Stop: doctor + Me smoke; not deployment |
63
+
64
+ Step 13 is optional after 12.
65
+
66
+ ## File index
67
+
68
+ Same files as the table. Filename numbers match the order list. If you add, remove, or reorder a step, change this README and the filenames together.
69
+
70
+ ## Specialty docs this tree does not replace
71
+
72
+ | Need | Doc |
73
+ |------|-----|
74
+ | Attach a Command Tower frontend locally | [`../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md`](../../../artifacts/COMMANDTOWER_LOCAL_JOIN.md) |
75
+ | What `command_tower:install` does, flags, schema, doctor | [`../initializing.md`](../initializing.md) |
76
+ | Layer map, workflow rule | [`../architecture.md`](../architecture.md) |
77
+ | Extension points | [`../extending.md`](../extending.md) |
78
+ | HTTP catalog | [`../api_reference.md`](../api_reference.md) |
79
+ | Route areas | [`../controllers.md`](../controllers.md) |
80
+ | RBAC | [`../authorization.md`](../authorization.md), [`../authentication_authorization_guide.md`](../authentication_authorization_guide.md) |
81
+ | Cookie / CSRF | [`../cookie_authentication_guide.md`](../cookie_authentication_guide.md) |
82
+ | Messaging | [`../messaging_integration_guide.md`](../messaging_integration_guide.md) |
83
+ | Testing factories | [`../testing.md`](../testing.md) |
84
+ | Admin Workspace | [`../admin_workspace.md`](../admin_workspace.md) |
85
+ | Principal capabilities | [`../principal_capabilities.md`](../principal_capabilities.md) |
86
+ | Audit registry | [`../audit.md`](../audit.md) |
87
+ | Eventing | [`../eventing.md`](../eventing.md) |
88
+ | Release upgrades | [`../upgrades/README.md`](../upgrades/README.md) |
89
+
90
+ Older entry: [`../host_integration_guide.md`](../host_integration_guide.md) is a pointer here.
@@ -37,6 +37,17 @@ end
37
37
  | CSRF `same_site` / `secure` / `path` / `domain` | `nil` (inherit JWT cookie) |
38
38
  | CSRF `ttl` | `7.days` |
39
39
 
40
+ ### Two web apps on one hostname
41
+
42
+ The browser keys `ct_jwt` and `ct_csrf` by hostname. The port is not part of that key. Two Command Tower web apps on `localhost` overwrite each other’s session. Logout expires both cookies, so a shared CSRF name still logs the other app out on its next POST.
43
+
44
+ A second product sets both names. The frontend `session.csrfCookieName` matches `csrf.cookie_name`. Leave `header_name` as `X-CSRF-Token`. A host that runs alone keeps `ct_jwt` and `ct_csrf`.
45
+
46
+ ```ruby
47
+ config.jwt.cookie.name = "other_jwt"
48
+ config.jwt.cookie.csrf.cookie_name = "other_csrf"
49
+ ```
50
+
40
51
  ## Token extraction order
41
52
 
42
53
  1. `Authorization: Bearer <token>` header (wins when present)
data/docs/extending.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  How hosts customize and integrate with CommandTower **without forking platform internals**.
4
4
 
5
- **New host?** Start with [Host integration](host_integration_guide.md).
5
+ **New host?** Start with [Bootstrap a new host](bootstrap/README.md).
6
6
 
7
7
  Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor: [initializing.md](initializing.md). Testing API: [testing.md](testing.md). HTTP catalog: [api_reference.md](api_reference.md).
8
8
 
@@ -19,6 +19,7 @@ Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor:
19
19
  | Model reopen | Product associations / behavior |
20
20
  | Initializers | Configuration, including `config.registry.audit.event`, `config.registry.admin_workspace.tool`, and `config.registry.principal_capabilities.capability` |
21
21
  | Notification catalogs / channel policy | Host-owned messaging customization |
22
+ | `app/views/command_tower/messaging/rendering/**` | Host override of generic and/or per-`notification_type_key` rendered Email/SMS/Pushover/Push templates — see [messaging_integration_guide.md](messaging_integration_guide.md#rendering-template-overrides) |
22
23
 
23
24
  ## Internal platform — do not extend
24
25
 
@@ -28,7 +29,7 @@ Layer map: [architecture.md](architecture.md). Install/configure/migrate/doctor:
28
29
  | ServiceBase | Shared service framework |
29
30
  | Serializers | Platform response shaping |
30
31
  | Deserializers | Platform request trust boundary |
31
- | Messaging execution pipeline | Handoff / execution / accept internals |
32
+ | Messaging execution pipeline | Handoff / execution / accept internals, including `Messaging::Rendering::ChannelRenderer` / `TemplateResolver` Ruby classes — hosts customize rendering by dropping ERB views (above), never by reopening or calling these classes |
32
33
  | RequestContext | Framework request context |
33
34
  | JWT primitives | Token issue / validate plumbing |
34
35
  | Internal framework plumbing | Envelope renderer, workflow base mechanics, etc. |
@@ -41,11 +42,11 @@ Full install narrative: [initializing.md](initializing.md).
41
42
 
42
43
  | Surface | Notes |
43
44
  |---------|--------|
44
- | `bin/rails command_tower:install` | Migrations + optional configure |
45
+ | `command_tower:install` (`make rails ARGS='command_tower:install'`) | Task name inside the container; migrations + optional configure |
45
46
  | `SKIP_CONFIGURE=1` | Migrations only |
46
47
  | `SKIP_MOUNT=1` | Initializer without mount |
47
48
  | `FORCE=1` | Overwrite initializer |
48
- | `rails g command_tower:configure` | `--skip-routes`, `--force` |
49
+ | `command_tower:configure` (`make rails ARGS='generate command_tower:configure'`) | `--skip-routes`, `--force` |
49
50
 
50
51
  ## Mounting and configuration
51
52
 
@@ -1,250 +1,7 @@
1
1
  # Host Integration Guide
2
2
 
3
- **Start here** for integrating CommandTower into a new Rails host.
3
+ The new-host manual lives in [`bootstrap/README.md`](bootstrap/README.md).
4
4
 
5
- This page owns the **order of operations**. It does not replace specialty docs: install details stay in [Initializing](initializing.md), HTTP contracts in [API reference](api_reference.md), boundaries in [Extending](extending.md).
5
+ That directory is the bring-up authority: implementation order, Docker-only Make wrappers, one file per step, and an explicit note that the manual is **local bring-up** (not deployment).
6
6
 
7
- `command_tower:doctor` green means secrets/migrations look sane. It does **not** prove Me/Auth authorization works.
8
-
9
- ## Prerequisites
10
-
11
- - A Rails host application (engine is mounted into the host)
12
- - `gem "command_tower"` in the host Gemfile (path, git, or released gem)
13
- - Host-owned database (and any host Redis/job stack your app already uses)
14
-
15
- ## Step 1 — Install
16
-
17
- Follow [Initializing](initializing.md):
18
-
19
- ```bash
20
- bundle install
21
- bin/rails command_tower:install
22
- bin/rails db:migrate
23
- bin/rails command_tower:doctor
24
- ```
25
-
26
- That copies CT migrations, generates `config/initializers/command_tower.rb` (unless skipped), and mounts `CommandTower::Engine` (unless skipped).
27
-
28
- ## Step 2 — Secrets and initializer
29
-
30
- In the host initializer, set at least:
31
-
32
- - `config.jwt.hmac_secret`
33
- - `config.signup_session.jwt_secret` (or `SIGNUP_SESSION_JWT_SECRET`)
34
- - `config.password_recovery_session.jwt_secret` (or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`)
35
- - `config.application.host_key` — server-bound host/product identity for CT-generic user-scoped state (for example experience states). Not client-supplied. Blank values cause Me experience-state routes to return **503** `experience_states_host_unconfigured`.
36
-
37
- Re-run `bin/rails command_tower:doctor`. Details: [Initializing — Configuration](initializing.md#configuration).
38
-
39
- Dummy-host reference: [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb) (sets `host_key` to `"command_tower"`).
40
-
41
- ### Email / SMTP
42
-
43
- CommandTower owns ActionMailer delivery for engine mailers (email verification, password reset, messaging channel mail).
44
-
45
- | Environment | Delivery |
46
- |-------------|----------|
47
- | `test` | `:test` (in-memory `ActionMailer::Base.deliveries`; no external SMTP) |
48
- | development / production | `:smtp` unless the host explicitly sets `config.email.delivery_method` |
49
-
50
- Non-secret knobs live on `config.email.*` (defaults: `smtp.gmail.com`, port `587`, `plain`, STARTTLS auto). Secrets come from Credential Resolution:
51
-
52
- - `config.credentials.smtp.user_name` / `password`, or
53
- - ENV `GMAIL_USER_NAME` / `GMAIL_PASSWORD`
54
-
55
- `SmtpActionMailerBridge` merges resolved credentials into `action_mailer.smtp_settings` at the end of `CommandTower.configure`. Missing credentials fail at send when `raise_delivery_errors` is true. `From` uses the resolved SMTP username when present.
56
-
57
- Doctor does **not** probe SMTP connectivity.
58
-
59
- ## Step 3 — Confirm mount path
60
-
61
- Ensure routes include something like:
62
-
63
- ```ruby
64
- mount CommandTower::Engine => "/" # or "/api"
65
- ```
66
-
67
- Engine paths below are **relative to that mount**. Route area index: [Controllers](controllers.md).
68
-
69
- ## Step 3b — Inherit execution-boundary bases
70
-
71
- Greenfield hosts should inherit CommandTower bases so Execution Context is established automatically:
72
-
73
- ```ruby
74
- class ApplicationController < CommandTower::ApplicationController
75
- end
76
-
77
- class ApplicationJob < CommandTower::ApplicationJob
78
- end
79
- ```
80
-
81
- Unauthenticated host endpoints (for example health checks) still receive HTTP `execution_uuid` / `correlation_id`. Successful authentication enriches the **same** context with `user_id` / `effective_user_id`.
82
-
83
- Workflows and services consume `CommandTower::Current` (or `execution_context`); they do not establish a new execution. For Rake/console, use `CommandTower.with_execution(source: :rake) { ... }`.
84
-
85
- `Auth::RequestContext` is JWT request/response transport, not Execution Context. Details: [Extending — Execution Context](extending.md#execution-context).
86
-
87
- Lifecycle and semantic events: [Eventing](eventing.md).
88
-
89
- If superclass inheritance is technically blocked, include `CommandTower::Execution::HttpBoundary` / `JobBoundary` on the host bases. That is an escape hatch, not the preferred contract.
90
-
91
- ## Step 4 — Host RBAC (required)
92
-
93
- AuthorizeRequest **fails closed**. CommandTower ships **CT-owned** entity definitions (Me, session, inbox, Admin Workspace capabilities, …) and the platform full-access role (`owner`) in `lib/command_tower/authorization/default.yml`. CommandTower does **not** ship an operational `admin` role.
94
-
95
- The host YAML (default `config/rbac_groups.yml`) is a **second source**. Composition is additive. Hosts:
96
-
97
- 1. Define product roles (typically `member`).
98
- 2. **Grant names** of already-defined CT entities to those roles.
99
- 3. Optionally define **host-owned** entities for **host** controllers.
100
- 4. Deliberately compose operational Admin roles (least privilege or a broad host-owned `admin`).
101
-
102
- Do **not** copy CommandTower controller/entity blocks into the host file. Do **not** redefine `owner` or CT entity names. A host **may** define an `admin` group as host policy. Conflicts fail at boot (no last-write-wins).
103
-
104
- Dummy host grants-only example: [`rails_app/config/rbac_groups.yml`](../rails_app/config/rbac_groups.yml).
105
-
106
- ```ruby
107
- CommandTower.configure do |c|
108
- c.authorization.rbac_group_path = Rails.root.join("config/rbac_groups.yml")
109
- c.authorization.default_membership_role = "member" # optional; nil disables
110
- end
111
- ```
112
-
113
- `default_membership_role` is validated against the **composed** graph at boot. Unknown names fail configuration finalization.
114
-
115
- More: [Authorization](authorization.md), [Authentication & authorization guide](authentication_authorization_guide.md).
116
-
117
- Product audit names (later) register additively:
118
-
119
- ```ruby
120
- CommandTower.configure do |c|
121
- c.registry.audit.event :wager_placed do |event|
122
- event.allowed_changes = %i[status]
123
- event.user_history = true
124
- end
125
- end
126
- ```
127
-
128
- Do **not** redefine CommandTower-owned audit names. Audit rows persist in CommandTower after `command_tower:install:migrations` and `db:migrate`. See [Audit](audit.md).
129
-
130
- Admin Workspace tools register additively (navigation metadata, not a dispatcher):
131
-
132
- ```ruby
133
- CommandTower.configure do |c|
134
- c.registry.admin_workspace.tool :host_example do |tool|
135
- tool.label = "Example"
136
- tool.description = "Short launcher explanation of what this tool does."
137
- tool.route = "/admin/example"
138
- tool.group = :product
139
- tool.sort_order = 300
140
- tool.required_entity = :host_example_entity
141
- end
142
- end
143
- ```
144
-
145
- Do **not** redefine CommandTower-owned tool ids (`users`, `audit`, `messaging`). Hosts own copy for host tools (`description` soft ≤100 / hard ≤160). Manifest: [Admin Workspace](admin_workspace.md).
146
-
147
- Principal capabilities register additively (frontend-projectable ids, not a dump of all entities):
148
-
149
- ```ruby
150
- CommandTower.configure do |c|
151
- c.registry.principal_capabilities.capability :host_example do |capability|
152
- capability.required_entity = :host_example_entity
153
- end
154
- end
155
- ```
156
-
157
- Do **not** redefine CommandTower-owned capability ids (`admin_workspace`, `admin_users`, `admin_impersonation`, `admin_audit_events`, `admin_messaging_announcements`). Projection: [Principal capabilities](principal_capabilities.md). Grant `admin_impersonation` only to roles that should start impersonation of visible users. Dummy `admin` does not include it.
158
-
159
- ## Step 5 — Roles on users
160
-
161
- RBAC YAML defines what a role **may** do. Users still need that role assigned.
162
-
163
- - Specs and the dummy pattern use role name **`member`** for Me/Auth surfaces (including entity `principal_capabilities`).
164
- - When `authorization.default_membership_role` is set (for example `"member"`), register assigns that role **in the same transaction** as user create. Failure rolls back the user.
165
- - When it is `nil`, register does not attach roles. Hosts may assign via product logic, ops (`command_tower:users:create`), or other host workflows.
166
- - Installing CommandTower does **not** grant operational Admin access. Hosts must deliberately grant Admin entities.
167
- - Admin announcements need a host role that includes `admin_messaging_announcements`.
168
- - Admin Workspace manifest needs a host role that includes `admin_workspace`. Tool visibility still depends on each tool's `required_entity`.
169
- - `GET /auth/principal-capabilities` needs entity `principal_capabilities` on the caller’s roles (grant on `member` like session/me). Possessed Admin projectables still depend on the Admin entity grants above.
170
- - A host may define a broad `admin` role that grants many Admin entities — that is host policy, not a CommandTower default.
171
- ## Step 6 — Enable feature gates you need
172
-
173
- When a gate is off, the route is **not drawn** → **404**.
174
-
175
- | Capability | Config |
176
- |------------|--------|
177
- | Plain-text login | `config.login.plain_text.enable = true` |
178
- | Email verification routes | `config.login.plain_text.email_verify = true` |
179
- | Password reset routes | `config.login.plain_text.password_reset = true` |
180
- | Email availability | `config.signup_session.email_availability = true` |
181
- | Username availability | `config.username.realtime_username_check = true` |
182
-
183
- Full gate list: [API reference — Feature gates](api_reference.md#feature-gates).
184
-
185
- ## Step 7 — Choose auth client path
186
-
187
- - **API / mobile:** `Authorization: Bearer <jwt>` (default)
188
- - **Browser / SPA cookies:** enable `config.jwt.cookie` (+ CSRF as needed) — [Cookie authentication](cookie_authentication_guide.md)
189
-
190
- Engine HTTP success/error bodies use the application envelope `{ data, meta, errors }`. Host provisional helpers (`authenticate_user!`) can differ — [Authentication](authentication.md).
191
-
192
- ## Step 8 — Smoke check
193
-
194
- With gates and RBAC in place (paths relative to your mount):
195
-
196
- 1. `POST /auth/register` (always drawn) — with `default_membership_role` configured, the user is created **and** assigned that role in one transaction.
197
- 2. `POST /auth/plain-text/login` (if enabled) — receive `data.token`.
198
- 3. `GET /me` with Bearer token — expect **200** and envelope `data` (account payload).
199
-
200
- If you get **401**, authn failed. If you get **403**, fix Steps 4–5 before debugging clients.
201
-
202
- Contracts: [API reference](api_reference.md).
203
-
204
- ## Step 9 — Messaging (when needed)
205
-
206
- Host owns:
207
-
208
- - Notification catalog / types
209
- - `platform_enabled_channels` / channel policy
210
- - Adapter credentials (email / SMS / Pushover)
211
-
212
- Emit from a **host product workflow** via `Communications::Produce` / `ProduceMany` — not by bypassing workflows. See [Messaging](messaging_integration_guide.md) and [Extending](extending.md).
213
-
214
- Phone/Pushover routes are always drawn; missing product readiness returns **503** capability errors.
215
-
216
- ## Step 10 — Host tests
217
-
218
- ```ruby
219
- require "command_tower/testing"
220
- CommandTower::Testing.install!
221
- ```
222
-
223
- Details: [Testing](testing.md). Prefer `spec/requests/` patterns in the gem as HTTP contract proof.
224
-
225
- ## Done when
226
-
227
- - Doctor passes for secrets/migrations
228
- - Host `rbac_groups.yml` maps Me/Auth entities
229
- - Users who should use Me surfaces have the host `member` (or equivalent) role
230
- - Needed feature gates are enabled
231
- - `GET /me` returns **200** with a Bearer token for a member user
232
-
233
- ## Common failures
234
-
235
- | Symptom | Likely cause |
236
- |---------|----------------|
237
- | Doctor green, `GET /me` is **403** | Missing host RBAC YAML or user lacks `member` (Steps 4–5) |
238
- | `POST /auth/plain-text/login` is **404** | `login.plain_text.enable?` off (Step 6) |
239
- | Password-reset / availability **404** | Matching feature gate off |
240
- | Phone / Pushover **503** | Capability / adapters not ready (Step 9) |
241
- | Confused error JSON on host controllers | Provisional `authenticate_user!` vs engine envelope (Step 7) |
242
-
243
- ## Related
244
-
245
- - [Initializing](initializing.md)
246
- - [Extending](extending.md)
247
- - [API reference](api_reference.md)
248
- - [Authentication & authorization guide](authentication_authorization_guide.md)
249
- - [Messaging](messaging_integration_guide.md)
250
- - [README](../README.md)
7
+ Do not treat this page as a second checklist. Follow the bootstrap README.
data/docs/initializing.md CHANGED
@@ -1,31 +1,38 @@
1
1
  # Initializing CommandTower
2
2
 
3
- CommandTower is a Rails engine. The install flow below gets the engine **mounted, migrated, and doctor-checked**. That alone is not a fully usable Me/Auth host — complete RBAC, feature gates, and a smoke check via the [Host integration guide](host_integration_guide.md).
3
+ CommandTower is a Rails engine. This page is the **install / flags / schema / doctor contract**. A new host does not start here — follow [Bootstrap a new host](bootstrap/README.md) (Docker + Make, in order). That alone is not a fully usable Me/Auth host; bootstrap continues through RBAC, feature gates, and `GET /me`.
4
+
5
+ Ruby/Rails runs **inside Compose**. Operator commands below are `make …`. `bin/rails command_tower:install` is the **task name** inside the container, not a host-machine command.
4
6
 
5
7
  Back to [README](../README.md).
6
8
 
7
9
  ## Quick start
8
10
 
9
- ```bash
10
- # Gemfile: gem "command_tower" (or path/git source)
11
- bundle install
11
+ New hosts: [Bootstrap](bootstrap/README.md) (author Docker/Make first, then `make rails-new`, then pin the gem). After the gem is pinned:
12
12
 
13
- bin/rails command_tower:install
14
- bin/rails db:migrate
15
- bin/rails command_tower:doctor
13
+ ```bash
14
+ make rails ARGS='command_tower:install'
15
+ make migrate
16
+ make doctor
16
17
  ```
17
18
 
18
19
  `command_tower:install` does **not** run `db:migrate`. Installing and migrating stay separate on purpose.
19
20
 
21
+ If the host owns `/api/healthz` (or any route that must sit **before** the engine mount):
22
+
23
+ ```bash
24
+ SKIP_MOUNT=1 make rails ARGS='command_tower:install'
25
+ ```
26
+
20
27
  ### After install
21
28
 
22
- Continue with [Host integration](host_integration_guide.md):
29
+ Continue with [Bootstrap](bootstrap/README.md) from the RBAC / gates / smoke steps:
23
30
 
24
- 1. Host `rbac_groups.yml` product role that **grants** CT-owned Me/Auth entity names (required — otherwise authenticated Me/Auth calls **403**)
31
+ 1. Host `rbac_groups.yml` product role that **grants** CT-owned Me/Auth entity names (required — otherwise authenticated Me/Auth calls **403**) — [`bootstrap/09-rbac.md`](bootstrap/09-rbac.md)
25
32
  2. Set `authorization.default_membership_role` (for example `"member"`) or assign roles some other supported way
26
33
  3. Enable feature gates you need (login, reset, availability, …)
27
- 4. Smoke-check `GET /me` with a Bearer token
28
- 5. Wire messaging catalog/adapters when you emit or use phone/Pushover
34
+ 4. Smoke-check `GET /me` — [`bootstrap/12-smoke-check.md`](bootstrap/12-smoke-check.md)
35
+ 5. Wire messaging catalog/adapters when you emit or use phone/Pushover — [`bootstrap/13-optional.md`](bootstrap/13-optional.md)
29
36
 
30
37
  ### What `command_tower:install` does
31
38
 
@@ -41,22 +48,24 @@ Continue with [Host integration](host_integration_guide.md):
41
48
  | `SKIP_MOUNT=1` | Generate initializer but do not mount routes |
42
49
  | `FORCE=1` | Overwrite an existing `config/initializers/command_tower.rb` |
43
50
 
44
- Examples:
51
+ Examples (Make; flags enter the container via the `rails` recipe in [`bootstrap/01-docker-make-compose.md`](bootstrap/01-docker-make-compose.md)):
45
52
 
46
53
  ```bash
47
- SKIP_CONFIGURE=1 bin/rails command_tower:install
48
- SKIP_MOUNT=1 bin/rails command_tower:install
49
- FORCE=1 bin/rails command_tower:install
54
+ SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'
55
+ SKIP_MOUNT=1 make rails ARGS='command_tower:install'
56
+ FORCE=1 make rails ARGS='command_tower:install'
50
57
  ```
51
58
 
59
+ Task names inside the container remain `command_tower:install` (and `command_tower:install:migrations`). Do not run `bin/rails` on the Mac.
60
+
52
61
  ## Configure generator (optional)
53
62
 
54
63
  Prefer `command_tower:install` for greenfield hosts. The generator remains available:
55
64
 
56
65
  ```bash
57
- bin/rails generate command_tower:configure
58
- bin/rails generate command_tower:configure --skip-routes
59
- bin/rails generate command_tower:configure --force
66
+ make rails ARGS='generate command_tower:configure'
67
+ make rails ARGS='generate command_tower:configure --skip-routes'
68
+ make rails ARGS='generate command_tower:configure --force'
60
69
  ```
61
70
 
62
71
  The generator:
@@ -75,8 +84,8 @@ CommandTower is the **sole authoring authority** for CommandTower-owned schema (
75
84
  Handled by `command_tower:install` (step 1). Equivalent migration-only command:
76
85
 
77
86
  ```bash
78
- bin/rails command_tower:install:migrations
79
- bin/rails db:migrate
87
+ make rails ARGS='command_tower:install:migrations'
88
+ make migrate
80
89
  ```
81
90
 
82
91
  `command_tower:install:migrations` is the standard Rails engine task (wrapper around `railties:install:migrations`). Re-running installation is **idempotent**: already-installed CommandTower migrations are skipped.
@@ -86,9 +95,9 @@ Rails may **retimestamp** newly copied host files. That is normal. Do not hand-e
86
95
  ### Upgrade workflow
87
96
 
88
97
  1. Bump/release the CommandTower gem dependency in the host.
89
- 2. Run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`).
90
- 3. Run `bin/rails db:migrate`.
91
- 4. Optionally run `bin/rails command_tower:doctor`.
98
+ 2. Run `SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'` (or `make rails ARGS='command_tower:install:migrations'`).
99
+ 3. Run `make migrate`.
100
+ 4. Optionally run `make doctor`.
92
101
 
93
102
  Do not re-run configure on hosts that already customize the initializer unless you intend to regenerate it (`FORCE=1`).
94
103
 
@@ -120,7 +129,7 @@ The generated initializer documents available options via class_composer. Defaul
120
129
  ## Doctor
121
130
 
122
131
  ```bash
123
- bin/rails command_tower:doctor
132
+ make doctor
124
133
  ```
125
134
 
126
135
  Checks Rails version compatibility, engine baseline migrations, host-installed migration copies, JWT / session secrets, and messaging adapter names. Failures abort with remediation text; warnings print but allow success.
@@ -151,12 +160,12 @@ Doctor does **not** probe Redis/SMTP connectivity.
151
160
  If the host already has a bespoke initializer and mount:
152
161
 
153
162
  ```bash
154
- SKIP_CONFIGURE=1 bin/rails command_tower:install
155
- # same as: bin/rails command_tower:install:migrations
156
- bin/rails db:migrate
163
+ SKIP_CONFIGURE=1 make rails ARGS='command_tower:install'
164
+ # same task as: make rails ARGS='command_tower:install:migrations'
165
+ make migrate
157
166
  ```
158
167
 
159
- Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding.
168
+ Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding (`make rails ARGS='generate command_tower:configure'`).
160
169
 
161
170
  ## Test suite factories
162
171